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