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 #include "yaffs_checkptrw.h"
15 #include "yaffs_getblockinfo.h"
17 struct yaffs_checkpt_chunk_hdr
{
25 static int apply_chunk_offset(struct yaffs_dev
*dev
, int chunk
)
27 return chunk
- dev
->chunk_offset
;
30 static int apply_block_offset(struct yaffs_dev
*dev
, int block
)
32 return block
- dev
->block_offset
;
35 static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev
*dev
)
37 struct yaffs_checkpt_chunk_hdr hdr
;
39 hdr
.version
= YAFFS_CHECKPOINT_VERSION
;
40 hdr
.seq
= dev
->checkpt_page_seq
;
41 hdr
.sum
= dev
->checkpt_sum
;
42 hdr
.xor = dev
->checkpt_xor
;
44 dev
->checkpt_byte_offs
= sizeof(hdr
);
46 memcpy(dev
->checkpt_buffer
, &hdr
, sizeof(hdr
));
49 static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev
*dev
)
51 struct yaffs_checkpt_chunk_hdr hdr
;
53 memcpy(&hdr
, dev
->checkpt_buffer
, sizeof(hdr
));
55 dev
->checkpt_byte_offs
= sizeof(hdr
);
57 return hdr
.version
== YAFFS_CHECKPOINT_VERSION
&&
58 hdr
.seq
== dev
->checkpt_page_seq
&&
59 hdr
.sum
== dev
->checkpt_sum
&&
60 hdr
.xor == dev
->checkpt_xor
;
63 static int yaffs2_checkpt_space_ok(struct yaffs_dev
*dev
)
65 int blocks_avail
= dev
->n_erased_blocks
- dev
->param
.n_reserved_blocks
;
67 yaffs_trace(YAFFS_TRACE_CHECKPOINT
,
68 "checkpt blocks_avail = %d", blocks_avail
);
70 return (blocks_avail
<= 0) ? 0 : 1;
73 static int yaffs_checkpt_erase(struct yaffs_dev
*dev
)
77 if (!dev
->drv
.drv_erase_fn
)
79 yaffs_trace(YAFFS_TRACE_CHECKPOINT
,
80 "checking blocks %d to %d",
81 dev
->internal_start_block
, dev
->internal_end_block
);
83 for (i
= dev
->internal_start_block
; i
<= dev
->internal_end_block
; i
++) {
84 struct yaffs_block_info
*bi
= yaffs_get_block_info(dev
, i
);
85 int offset_i
= apply_block_offset(dev
, i
);
88 if (bi
->block_state
== YAFFS_BLOCK_STATE_CHECKPOINT
) {
89 yaffs_trace(YAFFS_TRACE_CHECKPOINT
,
90 "erasing checkpt block %d", i
);
94 result
= dev
->drv
.drv_erase_fn(dev
, offset_i
);
96 bi
->block_state
= YAFFS_BLOCK_STATE_EMPTY
;
97 dev
->n_erased_blocks
++;
99 dev
->param
.chunks_per_block
;
101 dev
->drv
.drv_mark_bad_fn(dev
, offset_i
);
102 bi
->block_state
= YAFFS_BLOCK_STATE_DEAD
;
107 dev
->blocks_in_checkpt
= 0;
112 static void yaffs2_checkpt_find_erased_block(struct yaffs_dev
*dev
)
115 int blocks_avail
= dev
->n_erased_blocks
- dev
->param
.n_reserved_blocks
;
117 yaffs_trace(YAFFS_TRACE_CHECKPOINT
,
118 "allocating checkpt block: erased %d reserved %d avail %d next %d ",
119 dev
->n_erased_blocks
, dev
->param
.n_reserved_blocks
,
120 blocks_avail
, dev
->checkpt_next_block
);
122 if (dev
->checkpt_next_block
>= 0 &&
123 dev
->checkpt_next_block
<= dev
->internal_end_block
&&
126 for (i
= dev
->checkpt_next_block
; i
<= dev
->internal_end_block
;
128 struct yaffs_block_info
*bi
;
130 bi
= yaffs_get_block_info(dev
, i
);
131 if (bi
->block_state
== YAFFS_BLOCK_STATE_EMPTY
) {
132 dev
->checkpt_next_block
= i
+ 1;
133 dev
->checkpt_cur_block
= i
;
134 yaffs_trace(YAFFS_TRACE_CHECKPOINT
,
135 "allocating checkpt block %d", i
);
140 yaffs_trace(YAFFS_TRACE_CHECKPOINT
, "out of checkpt blocks");
142 dev
->checkpt_next_block
= -1;
143 dev
->checkpt_cur_block
= -1;
146 static void yaffs2_checkpt_find_block(struct yaffs_dev
*dev
)
149 struct yaffs_ext_tags tags
;
151 yaffs_trace(YAFFS_TRACE_CHECKPOINT
,
152 "find next checkpt block: start: blocks %d next %d",
153 dev
->blocks_in_checkpt
, dev
->checkpt_next_block
);
155 if (dev
->blocks_in_checkpt
< dev
->checkpt_max_blocks
)
156 for (i
= dev
->checkpt_next_block
; i
<= dev
->internal_end_block
;
158 int chunk
= i
* dev
->param
.chunks_per_block
;
159 enum yaffs_block_state state
;
162 dev
->tagger
.read_chunk_tags_fn(dev
,
163 apply_chunk_offset(dev
, chunk
),
165 yaffs_trace(YAFFS_TRACE_CHECKPOINT
,
166 "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d",
168 tags
.obj_id
, tags
.seq_number
,
171 if (tags
.seq_number
!= YAFFS_SEQUENCE_CHECKPOINT_DATA
)
174 dev
->tagger
.query_block_fn(dev
,
175 apply_block_offset(dev
, i
),
177 if (state
== YAFFS_BLOCK_STATE_DEAD
)
180 /* Right kind of block */
181 dev
->checkpt_next_block
= tags
.obj_id
;
182 dev
->checkpt_cur_block
= i
;
183 dev
->checkpt_block_list
[dev
->blocks_in_checkpt
] = i
;
184 dev
->blocks_in_checkpt
++;
185 yaffs_trace(YAFFS_TRACE_CHECKPOINT
,
186 "found checkpt block %d", i
);
190 yaffs_trace(YAFFS_TRACE_CHECKPOINT
, "found no more checkpt blocks");
192 dev
->checkpt_next_block
= -1;
193 dev
->checkpt_cur_block
= -1;
196 int yaffs2_checkpt_open(struct yaffs_dev
*dev
, int writing
)
200 dev
->checkpt_open_write
= writing
;
202 /* Got the functions we need? */
203 if (!dev
->tagger
.write_chunk_tags_fn
||
204 !dev
->tagger
.read_chunk_tags_fn
||
205 !dev
->drv
.drv_erase_fn
||
206 !dev
->drv
.drv_mark_bad_fn
)
209 if (writing
&& !yaffs2_checkpt_space_ok(dev
))
212 if (!dev
->checkpt_buffer
)
213 dev
->checkpt_buffer
=
214 kmalloc(dev
->param
.total_bytes_per_chunk
, GFP_NOFS
);
215 if (!dev
->checkpt_buffer
)
218 dev
->checkpt_page_seq
= 0;
219 dev
->checkpt_byte_count
= 0;
220 dev
->checkpt_sum
= 0;
221 dev
->checkpt_xor
= 0;
222 dev
->checkpt_cur_block
= -1;
223 dev
->checkpt_cur_chunk
= -1;
224 dev
->checkpt_next_block
= dev
->internal_start_block
;
227 memset(dev
->checkpt_buffer
, 0, dev
->data_bytes_per_chunk
);
228 yaffs2_checkpt_init_chunk_hdr(dev
);
229 return yaffs_checkpt_erase(dev
);
232 /* Opening for a read */
233 /* Set to a value that will kick off a read */
234 dev
->checkpt_byte_offs
= dev
->data_bytes_per_chunk
;
235 /* A checkpoint block list of 1 checkpoint block per 16 block is
236 * (hopefully) going to be way more than we need */
237 dev
->blocks_in_checkpt
= 0;
238 dev
->checkpt_max_blocks
=
239 (dev
->internal_end_block
- dev
->internal_start_block
) / 16 + 2;
240 if (!dev
->checkpt_block_list
)
241 dev
->checkpt_block_list
=
242 kmalloc(sizeof(int) * dev
->checkpt_max_blocks
, GFP_NOFS
);
244 if (!dev
->checkpt_block_list
)
247 for (i
= 0; i
< dev
->checkpt_max_blocks
; i
++)
248 dev
->checkpt_block_list
[i
] = -1;
253 int yaffs2_get_checkpt_sum(struct yaffs_dev
*dev
, u32
* sum
)
257 composite_sum
= (dev
->checkpt_sum
<< 8) | (dev
->checkpt_xor
& 0xff);
258 *sum
= composite_sum
;
262 static int yaffs2_checkpt_flush_buffer(struct yaffs_dev
*dev
)
266 struct yaffs_ext_tags tags
;
268 if (dev
->checkpt_cur_block
< 0) {
269 yaffs2_checkpt_find_erased_block(dev
);
270 dev
->checkpt_cur_chunk
= 0;
273 if (dev
->checkpt_cur_block
< 0)
277 tags
.obj_id
= dev
->checkpt_next_block
; /* Hint to next place to look */
278 tags
.chunk_id
= dev
->checkpt_page_seq
+ 1;
279 tags
.seq_number
= YAFFS_SEQUENCE_CHECKPOINT_DATA
;
280 tags
.n_bytes
= dev
->data_bytes_per_chunk
;
281 if (dev
->checkpt_cur_chunk
== 0) {
282 /* First chunk we write for the block? Set block state to
284 struct yaffs_block_info
*bi
=
285 yaffs_get_block_info(dev
, dev
->checkpt_cur_block
);
286 bi
->block_state
= YAFFS_BLOCK_STATE_CHECKPOINT
;
287 dev
->blocks_in_checkpt
++;
291 dev
->checkpt_cur_block
* dev
->param
.chunks_per_block
+
292 dev
->checkpt_cur_chunk
;
294 yaffs_trace(YAFFS_TRACE_CHECKPOINT
,
295 "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d",
296 chunk
, dev
->checkpt_cur_block
, dev
->checkpt_cur_chunk
,
297 tags
.obj_id
, tags
.chunk_id
);
299 offset_chunk
= apply_chunk_offset(dev
, chunk
);
301 dev
->n_page_writes
++;
303 dev
->tagger
.write_chunk_tags_fn(dev
, offset_chunk
,
304 dev
->checkpt_buffer
, &tags
);
305 dev
->checkpt_page_seq
++;
306 dev
->checkpt_cur_chunk
++;
307 if (dev
->checkpt_cur_chunk
>= dev
->param
.chunks_per_block
) {
308 dev
->checkpt_cur_chunk
= 0;
309 dev
->checkpt_cur_block
= -1;
311 memset(dev
->checkpt_buffer
, 0, dev
->data_bytes_per_chunk
);
313 yaffs2_checkpt_init_chunk_hdr(dev
);
319 int yaffs2_checkpt_wr(struct yaffs_dev
*dev
, const void *data
, int n_bytes
)
323 u8
*data_bytes
= (u8
*) data
;
325 if (!dev
->checkpt_buffer
)
328 if (!dev
->checkpt_open_write
)
331 while (i
< n_bytes
&& ok
) {
332 dev
->checkpt_buffer
[dev
->checkpt_byte_offs
] = *data_bytes
;
333 dev
->checkpt_sum
+= *data_bytes
;
334 dev
->checkpt_xor
^= *data_bytes
;
336 dev
->checkpt_byte_offs
++;
339 dev
->checkpt_byte_count
++;
341 if (dev
->checkpt_byte_offs
< 0 ||
342 dev
->checkpt_byte_offs
>= dev
->data_bytes_per_chunk
)
343 ok
= yaffs2_checkpt_flush_buffer(dev
);
349 int yaffs2_checkpt_rd(struct yaffs_dev
*dev
, void *data
, int n_bytes
)
352 struct yaffs_ext_tags tags
;
355 u8
*data_bytes
= (u8
*) data
;
357 if (!dev
->checkpt_buffer
)
360 if (dev
->checkpt_open_write
)
363 while (i
< n_bytes
) {
365 if (dev
->checkpt_byte_offs
< 0 ||
366 dev
->checkpt_byte_offs
>= dev
->data_bytes_per_chunk
) {
368 if (dev
->checkpt_cur_block
< 0) {
369 yaffs2_checkpt_find_block(dev
);
370 dev
->checkpt_cur_chunk
= 0;
373 /* Bail out if we can't find a checpoint block */
374 if (dev
->checkpt_cur_block
< 0)
377 chunk
= dev
->checkpt_cur_block
*
378 dev
->param
.chunks_per_block
+
379 dev
->checkpt_cur_chunk
;
381 offset_chunk
= apply_chunk_offset(dev
, chunk
);
384 /* Read in the next chunk */
385 dev
->tagger
.read_chunk_tags_fn(dev
,
390 /* Bail out if the chunk is corrupted. */
391 if (tags
.chunk_id
!= (dev
->checkpt_page_seq
+ 1) ||
392 tags
.ecc_result
> YAFFS_ECC_RESULT_FIXED
||
393 tags
.seq_number
!= YAFFS_SEQUENCE_CHECKPOINT_DATA
)
396 /* Bail out if it is not a checkpoint chunk. */
397 if(!yaffs2_checkpt_check_chunk_hdr(dev
))
400 dev
->checkpt_page_seq
++;
401 dev
->checkpt_cur_chunk
++;
403 if (dev
->checkpt_cur_chunk
>=
404 dev
->param
.chunks_per_block
)
405 dev
->checkpt_cur_block
= -1;
409 *data_bytes
= dev
->checkpt_buffer
[dev
->checkpt_byte_offs
];
410 dev
->checkpt_sum
+= *data_bytes
;
411 dev
->checkpt_xor
^= *data_bytes
;
412 dev
->checkpt_byte_offs
++;
415 dev
->checkpt_byte_count
++;
418 return i
; /* Number of bytes read */
421 int yaffs_checkpt_close(struct yaffs_dev
*dev
)
425 if (dev
->checkpt_open_write
) {
426 if (dev
->checkpt_byte_offs
!=
427 sizeof(sizeof(struct yaffs_checkpt_chunk_hdr
)))
428 yaffs2_checkpt_flush_buffer(dev
);
429 } else if (dev
->checkpt_block_list
) {
431 i
< dev
->blocks_in_checkpt
&&
432 dev
->checkpt_block_list
[i
] >= 0; i
++) {
433 int blk
= dev
->checkpt_block_list
[i
];
434 struct yaffs_block_info
*bi
= NULL
;
436 if (dev
->internal_start_block
<= blk
&&
437 blk
<= dev
->internal_end_block
)
438 bi
= yaffs_get_block_info(dev
, blk
);
439 if (bi
&& bi
->block_state
== YAFFS_BLOCK_STATE_EMPTY
)
440 bi
->block_state
= YAFFS_BLOCK_STATE_CHECKPOINT
;
444 dev
->n_free_chunks
-=
445 dev
->blocks_in_checkpt
* dev
->param
.chunks_per_block
;
446 dev
->n_erased_blocks
-= dev
->blocks_in_checkpt
;
448 yaffs_trace(YAFFS_TRACE_CHECKPOINT
, "checkpoint byte count %d",
449 dev
->checkpt_byte_count
);
451 if (dev
->checkpt_buffer
)
457 int yaffs2_checkpt_invalidate_stream(struct yaffs_dev
*dev
)
459 /* Erase the checkpoint data */
461 yaffs_trace(YAFFS_TRACE_CHECKPOINT
,
462 "checkpoint invalidate of %d blocks",
463 dev
->blocks_in_checkpt
);
465 return yaffs_checkpt_erase(dev
);