]>
Commit | Line | Data |
---|---|---|
753ac610 CM |
1 | /* |
2 | * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. | |
3 | * | |
4 | * Copyright (C) 2002-2011 Aleph One Ltd. | |
5 | * for Toby Churchill Ltd and Brightstar Engineering | |
6 | * | |
7 | * Created by Charles Manning <charles@aleph1.co.uk> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License version 2 as | |
11 | * published by the Free Software Foundation. | |
12 | */ | |
13 | ||
14 | /* Summaries write the useful part of the tags for the chunks in a block into an | |
15 | * an array which is written to the last n chunks of the block. | |
16 | * Reading the summaries gives all the tags for the block in one read. Much | |
17 | * faster. | |
18 | * | |
19 | * Chunks holding summaries are marked with tags making it look like | |
20 | * they are part of a fake file. | |
21 | * | |
22 | * The summary could also be used during gc. | |
23 | * | |
24 | */ | |
25 | ||
26 | #include "yaffs_summary.h" | |
27 | #include "yaffs_packedtags2.h" | |
28 | #include "yaffs_nand.h" | |
29 | #include "yaffs_getblockinfo.h" | |
30 | #include "yaffs_bitmap.h" | |
31 | ||
32 | /* | |
33 | * The summary is built up in an array of summary tags. | |
34 | * This gets written to the last one or two (maybe more) chunks in a block. | |
35 | * A summary header is written as the first part of each chunk of summary data. | |
36 | * The summary header must match or the summary is rejected. | |
37 | */ | |
38 | ||
39 | /* Summary tags don't need the sequence number because that is redundant. */ | |
40 | struct yaffs_summary_tags { | |
41 | unsigned obj_id; | |
42 | unsigned chunk_id; | |
43 | unsigned n_bytes; | |
44 | }; | |
45 | ||
46 | /* Summary header */ | |
47 | struct yaffs_summary_header { | |
48 | unsigned version; /* Must match current version */ | |
49 | unsigned block; /* Must be this block */ | |
50 | unsigned seq; /* Must be this sequence number */ | |
51 | unsigned sum; /* Just add up all the bytes in the tags */ | |
52 | }; | |
53 | ||
54 | ||
55 | static void yaffs_summary_clear(struct yaffs_dev *dev) | |
56 | { | |
57 | if (!dev->sum_tags) | |
58 | return; | |
59 | memset(dev->sum_tags, 0, dev->chunks_per_summary * | |
60 | sizeof(struct yaffs_summary_tags)); | |
61 | } | |
62 | ||
63 | ||
64 | void yaffs_summary_deinit(struct yaffs_dev *dev) | |
65 | { | |
66 | kfree(dev->sum_tags); | |
67 | dev->sum_tags = NULL; | |
68 | kfree(dev->gc_sum_tags); | |
69 | dev->gc_sum_tags = NULL; | |
70 | dev->chunks_per_summary = 0; | |
71 | } | |
72 | ||
73 | int yaffs_summary_init(struct yaffs_dev *dev) | |
74 | { | |
75 | int sum_bytes; | |
76 | int chunks_used; /* Number of chunks used by summary */ | |
77 | int sum_tags_bytes; | |
78 | ||
79 | sum_bytes = dev->param.chunks_per_block * | |
80 | sizeof(struct yaffs_summary_tags); | |
81 | ||
82 | chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/ | |
83 | (dev->data_bytes_per_chunk - | |
84 | sizeof(struct yaffs_summary_header)); | |
85 | ||
86 | dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used; | |
87 | sum_tags_bytes = sizeof(struct yaffs_summary_tags) * | |
88 | dev->chunks_per_summary; | |
89 | dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); | |
90 | dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); | |
91 | if (!dev->sum_tags || !dev->gc_sum_tags) { | |
92 | yaffs_summary_deinit(dev); | |
93 | return YAFFS_FAIL; | |
94 | } | |
95 | ||
96 | yaffs_summary_clear(dev); | |
97 | ||
98 | return YAFFS_OK; | |
99 | } | |
100 | ||
101 | static unsigned yaffs_summary_sum(struct yaffs_dev *dev) | |
102 | { | |
103 | u8 *sum_buffer = (u8 *)dev->sum_tags; | |
104 | int i; | |
105 | unsigned sum = 0; | |
106 | ||
107 | i = sizeof(struct yaffs_summary_tags) * | |
108 | dev->chunks_per_summary; | |
109 | while (i > 0) { | |
110 | sum += *sum_buffer; | |
111 | sum_buffer++; | |
112 | i--; | |
113 | } | |
114 | ||
115 | return sum; | |
116 | } | |
117 | ||
118 | static int yaffs_summary_write(struct yaffs_dev *dev, int blk) | |
119 | { | |
120 | struct yaffs_ext_tags tags; | |
121 | u8 *buffer; | |
122 | u8 *sum_buffer = (u8 *)dev->sum_tags; | |
123 | int n_bytes; | |
124 | int chunk_in_nand; | |
125 | int chunk_in_block; | |
126 | int result; | |
127 | int this_tx; | |
128 | struct yaffs_summary_header hdr; | |
129 | int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); | |
130 | struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); | |
131 | ||
132 | buffer = yaffs_get_temp_buffer(dev); | |
133 | n_bytes = sizeof(struct yaffs_summary_tags) * | |
134 | dev->chunks_per_summary; | |
135 | memset(&tags, 0, sizeof(struct yaffs_ext_tags)); | |
136 | tags.obj_id = YAFFS_OBJECTID_SUMMARY; | |
137 | tags.chunk_id = 1; | |
138 | chunk_in_block = dev->chunks_per_summary; | |
139 | chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block + | |
140 | dev->chunks_per_summary; | |
141 | hdr.version = YAFFS_SUMMARY_VERSION; | |
142 | hdr.block = blk; | |
143 | hdr.seq = bi->seq_number; | |
144 | hdr.sum = yaffs_summary_sum(dev); | |
145 | ||
146 | do { | |
147 | this_tx = n_bytes; | |
148 | if (this_tx > sum_bytes_per_chunk) | |
149 | this_tx = sum_bytes_per_chunk; | |
150 | memcpy(buffer, &hdr, sizeof(hdr)); | |
151 | memcpy(buffer + sizeof(hdr), sum_buffer, this_tx); | |
152 | tags.n_bytes = this_tx + sizeof(hdr); | |
153 | result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand, | |
154 | buffer, &tags); | |
155 | ||
156 | if (result != YAFFS_OK) | |
157 | break; | |
158 | yaffs_set_chunk_bit(dev, blk, chunk_in_block); | |
159 | bi->pages_in_use++; | |
160 | dev->n_free_chunks--; | |
161 | ||
162 | n_bytes -= this_tx; | |
163 | sum_buffer += this_tx; | |
164 | chunk_in_nand++; | |
165 | chunk_in_block++; | |
166 | tags.chunk_id++; | |
167 | } while (result == YAFFS_OK && n_bytes > 0); | |
168 | yaffs_release_temp_buffer(dev, buffer); | |
169 | ||
170 | ||
171 | if (result == YAFFS_OK) | |
172 | bi->has_summary = 1; | |
173 | ||
174 | ||
175 | return result; | |
176 | } | |
177 | ||
178 | int yaffs_summary_read(struct yaffs_dev *dev, | |
179 | struct yaffs_summary_tags *st, | |
180 | int blk) | |
181 | { | |
182 | struct yaffs_ext_tags tags; | |
183 | u8 *buffer; | |
184 | u8 *sum_buffer = (u8 *)st; | |
185 | int n_bytes; | |
186 | int chunk_id; | |
187 | int chunk_in_nand; | |
188 | int chunk_in_block; | |
189 | int result; | |
190 | int this_tx; | |
191 | struct yaffs_summary_header hdr; | |
192 | struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); | |
193 | int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); | |
753ac610 | 194 | |
753ac610 CM |
195 | buffer = yaffs_get_temp_buffer(dev); |
196 | n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; | |
197 | chunk_in_block = dev->chunks_per_summary; | |
198 | chunk_in_nand = blk * dev->param.chunks_per_block + | |
199 | dev->chunks_per_summary; | |
200 | chunk_id = 1; | |
201 | do { | |
202 | this_tx = n_bytes; | |
203 | if (this_tx > sum_bytes_per_chunk) | |
204 | this_tx = sum_bytes_per_chunk; | |
205 | result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand, | |
206 | buffer, &tags); | |
207 | ||
208 | if (tags.chunk_id != chunk_id || | |
209 | tags.obj_id != YAFFS_OBJECTID_SUMMARY || | |
210 | tags.chunk_used == 0 || | |
211 | tags.ecc_result > YAFFS_ECC_RESULT_FIXED || | |
212 | tags.n_bytes != (this_tx + sizeof(hdr))) | |
213 | result = YAFFS_FAIL; | |
214 | if (result != YAFFS_OK) | |
215 | break; | |
216 | ||
217 | if (st == dev->sum_tags) { | |
218 | /* If we're scanning then update the block info */ | |
219 | yaffs_set_chunk_bit(dev, blk, chunk_in_block); | |
220 | bi->pages_in_use++; | |
221 | } | |
222 | memcpy(&hdr, buffer, sizeof(hdr)); | |
223 | memcpy(sum_buffer, buffer + sizeof(hdr), this_tx); | |
224 | n_bytes -= this_tx; | |
225 | sum_buffer += this_tx; | |
226 | chunk_in_nand++; | |
227 | chunk_in_block++; | |
228 | chunk_id++; | |
229 | } while (result == YAFFS_OK && n_bytes > 0); | |
230 | yaffs_release_temp_buffer(dev, buffer); | |
231 | ||
232 | if (result == YAFFS_OK) { | |
233 | /* Verify header */ | |
234 | if (hdr.version != YAFFS_SUMMARY_VERSION || | |
753ac610 CM |
235 | hdr.seq != bi->seq_number || |
236 | hdr.sum != yaffs_summary_sum(dev)) | |
237 | result = YAFFS_FAIL; | |
238 | } | |
239 | ||
240 | if (st == dev->sum_tags && result == YAFFS_OK) | |
241 | bi->has_summary = 1; | |
242 | ||
243 | return result; | |
244 | } | |
245 | ||
246 | int yaffs_summary_add(struct yaffs_dev *dev, | |
247 | struct yaffs_ext_tags *tags, | |
248 | int chunk_in_nand) | |
249 | { | |
250 | struct yaffs_packed_tags2_tags_only tags_only; | |
251 | struct yaffs_summary_tags *sum_tags; | |
252 | int block_in_nand = chunk_in_nand / dev->param.chunks_per_block; | |
253 | int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block; | |
254 | ||
255 | if (!dev->sum_tags) | |
256 | return YAFFS_OK; | |
257 | ||
258 | if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { | |
259 | yaffs_pack_tags2_tags_only(&tags_only, tags); | |
260 | sum_tags = &dev->sum_tags[chunk_in_block]; | |
261 | sum_tags->chunk_id = tags_only.chunk_id; | |
262 | sum_tags->n_bytes = tags_only.n_bytes; | |
263 | sum_tags->obj_id = tags_only.obj_id; | |
264 | ||
265 | if (chunk_in_block == dev->chunks_per_summary - 1) { | |
266 | /* Time to write out the summary */ | |
267 | yaffs_summary_write(dev, block_in_nand); | |
268 | yaffs_summary_clear(dev); | |
269 | yaffs_skip_rest_of_block(dev); | |
270 | } | |
271 | } | |
272 | return YAFFS_OK; | |
273 | } | |
274 | ||
275 | int yaffs_summary_fetch(struct yaffs_dev *dev, | |
276 | struct yaffs_ext_tags *tags, | |
277 | int chunk_in_block) | |
278 | { | |
279 | struct yaffs_packed_tags2_tags_only tags_only; | |
280 | struct yaffs_summary_tags *sum_tags; | |
281 | if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { | |
282 | sum_tags = &dev->sum_tags[chunk_in_block]; | |
283 | tags_only.chunk_id = sum_tags->chunk_id; | |
284 | tags_only.n_bytes = sum_tags->n_bytes; | |
285 | tags_only.obj_id = sum_tags->obj_id; | |
286 | yaffs_unpack_tags2_tags_only(tags, &tags_only); | |
287 | return YAFFS_OK; | |
288 | } | |
289 | return YAFFS_FAIL; | |
290 | } | |
291 | ||
292 | void yaffs_summary_gc(struct yaffs_dev *dev, int blk) | |
293 | { | |
294 | struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); | |
295 | int i; | |
296 | ||
297 | if (!bi->has_summary) | |
298 | return; | |
299 | ||
300 | for (i = dev->chunks_per_summary; | |
301 | i < dev->param.chunks_per_block; | |
302 | i++) { | |
303 | if (yaffs_check_chunk_bit(dev, blk, i)) { | |
304 | yaffs_clear_chunk_bit(dev, blk, i); | |
305 | bi->pages_in_use--; | |
306 | dev->n_free_chunks++; | |
307 | } | |
308 | } | |
309 | } |