-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathyaffs_cache.c
325 lines (256 loc) · 7.07 KB
/
yaffs_cache.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2018 Aleph One Ltd.
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "yaffs_cache.h"
/*------------------------ Short Operations Cache ------------------------------
* In many situations where there is no high level buffering a lot of
* reads might be short sequential reads, and a lot of writes may be short
* sequential writes. eg. scanning/writing a jpeg file.
* In these cases, a short read/write cache can provide a huge perfomance
* benefit with dumb-as-a-rock code.
* In Linux, the page cache provides read buffering and the short op cache
* provides write buffering.
*
* There are a small number (~10) of cache chunks per device so that we don't
* need a very intelligent search.
*/
int yaffs_obj_cache_dirty(struct yaffs_obj *obj)
{
struct yaffs_dev *dev = obj->my_dev;
int i;
struct yaffs_cache_manager *mgr = &dev->cache_mgr;
for (i = 0; i < mgr->n_caches; i++) {
struct yaffs_cache *cache = &mgr->cache[i];
if (cache->object == obj && cache->dirty)
return 1;
}
return 0;
}
void yaffs_flush_single_cache(struct yaffs_cache *cache, int discard)
{
if (!cache || cache->locked)
return;
/* Write it out and free it up if need be.*/
if (cache->dirty) {
yaffs_wr_data_obj(cache->object,
cache->chunk_id,
cache->data,
cache->n_bytes,
1);
cache->dirty = 0;
}
if (discard)
cache->object = NULL;
}
void yaffs_flush_file_cache(struct yaffs_obj *obj, int discard)
{
struct yaffs_dev *dev = obj->my_dev;
int i;
struct yaffs_cache_manager *mgr = &dev->cache_mgr;
if (mgr->n_caches < 1)
return;
/* Find the chunks for this object and flush them. */
for (i = 0; i < mgr->n_caches; i++) {
struct yaffs_cache *cache = &mgr->cache[i];
if (cache->object == obj)
yaffs_flush_single_cache(cache, discard);
}
}
void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard)
{
struct yaffs_cache_manager *mgr = &dev->cache_mgr;
struct yaffs_obj *obj;
int i;
/* Find a dirty object in the cache and flush it...
* until there are no further dirty objects.
*/
do {
obj = NULL;
for (i = 0; i < mgr->n_caches && !obj; i++) {
struct yaffs_cache *cache = &mgr->cache[i];
if (cache->object && cache->dirty)
obj = cache->object;
}
if (obj)
yaffs_flush_file_cache(obj, discard);
} while (obj);
}
/* Grab us an unused cache chunk for use.
* First look for an empty one.
* Then look for the least recently used non-dirty one.
* Then look for the least recently used dirty one...., flush and look again.
*/
static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev)
{
struct yaffs_cache_manager *mgr = &dev->cache_mgr;
int i;
for (i = 0; i < mgr->n_caches; i++) {
struct yaffs_cache *cache = &mgr->cache[i];
if (!cache->object)
return cache;
}
return NULL;
}
struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev)
{
struct yaffs_cache_manager *mgr = &dev->cache_mgr;
struct yaffs_cache *cache;
int usage;
int i;
if (mgr->n_caches < 1)
return NULL;
/* First look for an unused cache */
cache = yaffs_grab_chunk_worker(dev);
if (cache)
return cache;
/*
* Thery were all in use.
* Find the LRU cache and flush it if it is dirty.
*/
usage = -1;
cache = NULL;
for (i = 0; i < mgr->n_caches; i++) {
struct yaffs_cache *this_cache = &mgr->cache[i];
if (this_cache->object &&
!this_cache->locked &&
(this_cache->last_use < usage || !cache)) {
usage = this_cache->last_use;
cache = this_cache;
}
}
#if 1
yaffs_flush_single_cache(cache, 1);
#else
yaffs_flush_file_cache(cache->object, 1);
cache = yaffs_grab_chunk_worker(dev);
#endif
return cache;
}
/* Find a cached chunk */
struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj,
int chunk_id)
{
struct yaffs_dev *dev = obj->my_dev;
struct yaffs_cache_manager *mgr = &dev->cache_mgr;
int i;
if (mgr->n_caches < 1)
return NULL;
for (i = 0; i < mgr->n_caches; i++) {
struct yaffs_cache *cache = &mgr->cache[i];
if (cache->object == obj &&
cache->chunk_id == chunk_id) {
dev->cache_hits++;
return cache;
}
}
return NULL;
}
/* Mark the chunk for the least recently used algorithym */
void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache,
int is_write)
{
struct yaffs_cache_manager *mgr = &dev->cache_mgr;
int i;
if (mgr->n_caches < 1)
return;
if (mgr->cache_last_use < 0 ||
mgr->cache_last_use > 100000000) {
/* Reset the cache usages */
for (i = 1; i < mgr->n_caches; i++)
mgr->cache[i].last_use = 0;
mgr->cache_last_use = 0;
}
mgr->cache_last_use++;
cache->last_use = mgr->cache_last_use;
if (is_write)
cache->dirty = 1;
}
/* Invalidate a single cache page.
* Do this when a whole page gets written,
* ie the short cache for this page is no longer valid.
*/
void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id)
{
struct yaffs_cache *cache;
cache = yaffs_find_chunk_cache(object, chunk_id);
if (cache)
cache->object = NULL;
}
/* Invalidate all the cache pages associated with this object
* Do this whenever the file is deleted or resized.
*/
void yaffs_invalidate_file_cache(struct yaffs_obj *in)
{
int i;
struct yaffs_dev *dev = in->my_dev;
struct yaffs_cache_manager *mgr = &dev->cache_mgr;
/* Invalidate it. */
for (i = 0; i < mgr->n_caches; i++) {
struct yaffs_cache *cache = &mgr->cache[i];
if (cache->object == in)
cache->object = NULL;
}
}
int yaffs_count_dirty_caches(struct yaffs_dev *dev)
{
int n_dirty;
int i;
struct yaffs_cache_manager *mgr = &dev->cache_mgr;
for (n_dirty= 0, i = 0; i < mgr->n_caches; i++) {
if (mgr->cache[i].dirty)
n_dirty++;
}
return n_dirty;
}
int yaffs_cache_init(struct yaffs_dev *dev)
{
struct yaffs_cache_manager *mgr = &dev->cache_mgr;
int init_failed = 0;
if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES)
dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES;
mgr->n_caches = dev->param.n_caches;
if (mgr->n_caches > 0) {
int i;
void *buf;
u32 cache_bytes =
mgr->n_caches * sizeof(struct yaffs_cache);
mgr->cache = kmalloc(cache_bytes, GFP_NOFS);
buf = (u8 *) mgr->cache;
if (mgr->cache)
memset(mgr->cache, 0, cache_bytes);
for (i = 0; i < mgr->n_caches && buf; i++) {
struct yaffs_cache *cache = &mgr->cache[i];
cache->object = NULL;
cache->last_use = 0;
cache->dirty = 0;
cache->data = buf =
kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);
}
if (!buf)
init_failed = 1;
mgr->cache_last_use = 0;
}
return init_failed ? -1 : 0;
}
void yaffs_cache_deinit(struct yaffs_dev *dev)
{
struct yaffs_cache_manager *mgr = &dev->cache_mgr;
int i;
if (mgr->n_caches < 1 || !mgr->cache)
return;
for (i = 0; i < mgr->n_caches; i++) {
struct yaffs_cache *cache = &mgr->cache[i];
kfree(cache->data);
cache->data = NULL;
}
kfree(mgr->cache);
mgr->cache = NULL;
}