2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
4 * Copyright (C) 2002-2011 Aleph One Ltd.
5 * for Toby Churchill Ltd and Brightstar Engineering
7 * Created by Charles Manning <charles@aleph1.co.uk>
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.
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
19 * Chunks holding summaries are marked with tags making it look like
20 * they are part of a fake file.
22 * The summary could also be used during gc.
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"
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.
39 /* Summary tags don't need the sequence number because that is redundant. */
40 struct yaffs_summary_tags
{
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 */
55 static void yaffs_summary_clear(struct yaffs_dev
*dev
)
59 memset(dev
->sum_tags
, 0, dev
->chunks_per_summary
*
60 sizeof(struct yaffs_summary_tags
));
64 void yaffs_summary_deinit(struct yaffs_dev
*dev
)
68 kfree(dev
->gc_sum_tags
);
69 dev
->gc_sum_tags
= NULL
;
70 dev
->chunks_per_summary
= 0;
73 int yaffs_summary_init(struct yaffs_dev
*dev
)
76 int chunks_used
; /* Number of chunks used by summary */
79 sum_bytes
= dev
->param
.chunks_per_block
*
80 sizeof(struct yaffs_summary_tags
);
82 chunks_used
= (sum_bytes
+ dev
->data_bytes_per_chunk
- 1)/
83 (dev
->data_bytes_per_chunk
-
84 sizeof(struct yaffs_summary_header
));
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
);
96 yaffs_summary_clear(dev
);
101 static unsigned yaffs_summary_sum(struct yaffs_dev
*dev
)
103 u8
*sum_buffer
= (u8
*)dev
->sum_tags
;
107 i
= sizeof(struct yaffs_summary_tags
) *
108 dev
->chunks_per_summary
;
118 static int yaffs_summary_write(struct yaffs_dev
*dev
, int blk
)
120 struct yaffs_ext_tags tags
;
122 u8
*sum_buffer
= (u8
*)dev
->sum_tags
;
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
);
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
;
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
;
143 hdr
.seq
= bi
->seq_number
;
144 hdr
.sum
= yaffs_summary_sum(dev
);
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
,
156 if (result
!= YAFFS_OK
)
158 yaffs_set_chunk_bit(dev
, blk
, chunk_in_block
);
160 dev
->n_free_chunks
--;
163 sum_buffer
+= this_tx
;
167 } while (result
== YAFFS_OK
&& n_bytes
> 0);
168 yaffs_release_temp_buffer(dev
, buffer
);
171 if (result
== YAFFS_OK
)
178 int yaffs_summary_read(struct yaffs_dev
*dev
,
179 struct yaffs_summary_tags
*st
,
182 struct yaffs_ext_tags tags
;
184 u8
*sum_buffer
= (u8
*)st
;
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
);
194 #ifndef CONFIG_YAFFS_OP
197 sum_tags_bytes
= sizeof(struct yaffs_summary_tags
) *
198 dev
->chunks_per_summary
;
201 buffer
= yaffs_get_temp_buffer(dev
);
202 n_bytes
= sizeof(struct yaffs_summary_tags
) * dev
->chunks_per_summary
;
203 chunk_in_block
= dev
->chunks_per_summary
;
204 chunk_in_nand
= blk
* dev
->param
.chunks_per_block
+
205 dev
->chunks_per_summary
;
209 if (this_tx
> sum_bytes_per_chunk
)
210 this_tx
= sum_bytes_per_chunk
;
211 result
= yaffs_rd_chunk_tags_nand(dev
, chunk_in_nand
,
214 if ((int)tags
.chunk_id
!= chunk_id
||
215 tags
.obj_id
!= YAFFS_OBJECTID_SUMMARY
||
216 tags
.chunk_used
== 0 ||
217 tags
.ecc_result
> YAFFS_ECC_RESULT_FIXED
||
218 tags
.n_bytes
!= (this_tx
+ sizeof(hdr
)))
220 if (result
!= YAFFS_OK
)
223 if (st
== dev
->sum_tags
) {
224 /* If we're scanning then update the block info */
225 yaffs_set_chunk_bit(dev
, blk
, chunk_in_block
);
228 memcpy(&hdr
, buffer
, sizeof(hdr
));
229 memcpy(sum_buffer
, buffer
+ sizeof(hdr
), this_tx
);
231 sum_buffer
+= this_tx
;
235 } while (result
== YAFFS_OK
&& n_bytes
> 0);
236 yaffs_release_temp_buffer(dev
, buffer
);
238 if (result
== YAFFS_OK
) {
240 if (hdr
.version
!= YAFFS_SUMMARY_VERSION
||
241 hdr
.seq
!= bi
->seq_number
||
242 hdr
.sum
!= yaffs_summary_sum(dev
))
246 if (st
== dev
->sum_tags
&& result
== YAFFS_OK
)
252 int yaffs_summary_add(struct yaffs_dev
*dev
,
253 struct yaffs_ext_tags
*tags
,
256 struct yaffs_packed_tags2_tags_only tags_only
;
257 struct yaffs_summary_tags
*sum_tags
;
258 int block_in_nand
= chunk_in_nand
/ dev
->param
.chunks_per_block
;
259 int chunk_in_block
= chunk_in_nand
% dev
->param
.chunks_per_block
;
264 if (chunk_in_block
>= 0 && chunk_in_block
< dev
->chunks_per_summary
) {
265 yaffs_pack_tags2_tags_only(&tags_only
, tags
);
266 sum_tags
= &dev
->sum_tags
[chunk_in_block
];
267 sum_tags
->chunk_id
= tags_only
.chunk_id
;
268 sum_tags
->n_bytes
= tags_only
.n_bytes
;
269 sum_tags
->obj_id
= tags_only
.obj_id
;
271 if (chunk_in_block
== dev
->chunks_per_summary
- 1) {
272 /* Time to write out the summary */
273 yaffs_summary_write(dev
, block_in_nand
);
274 yaffs_summary_clear(dev
);
275 yaffs_skip_rest_of_block(dev
);
281 int yaffs_summary_fetch(struct yaffs_dev
*dev
,
282 struct yaffs_ext_tags
*tags
,
285 struct yaffs_packed_tags2_tags_only tags_only
;
286 struct yaffs_summary_tags
*sum_tags
;
287 if (chunk_in_block
>= 0 && chunk_in_block
< dev
->chunks_per_summary
) {
288 sum_tags
= &dev
->sum_tags
[chunk_in_block
];
289 tags_only
.chunk_id
= sum_tags
->chunk_id
;
290 tags_only
.n_bytes
= sum_tags
->n_bytes
;
291 tags_only
.obj_id
= sum_tags
->obj_id
;
292 yaffs_unpack_tags2_tags_only(tags
, &tags_only
);
298 void yaffs_summary_gc(struct yaffs_dev
*dev
, int blk
)
300 struct yaffs_block_info
*bi
= yaffs_get_block_info(dev
, blk
);
303 if (!bi
->has_summary
)
306 for (i
= dev
->chunks_per_summary
;
307 i
< dev
->param
.chunks_per_block
;
309 if (yaffs_check_chunk_bit(dev
, blk
, i
)) {
310 yaffs_clear_chunk_bit(dev
, blk
, i
);
312 dev
->n_free_chunks
++;