2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
4 * Copyright (C) 2002-2010 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 static int yaffs2_checkpt_space_ok(yaffs_dev_t
*dev
)
19 int blocksAvailable
= dev
->n_erased_blocks
- dev
->param
.n_reserved_blocks
;
21 T(YAFFS_TRACE_CHECKPOINT
,
22 (TSTR("checkpt blocks available = %d" TENDSTR
),
25 return (blocksAvailable
<= 0) ? 0 : 1;
29 static int yaffs_checkpt_erase(yaffs_dev_t
*dev
)
33 if (!dev
->param
.erase_fn
)
35 T(YAFFS_TRACE_CHECKPOINT
, (TSTR("checking blocks %d to %d"TENDSTR
),
36 dev
->internal_start_block
, dev
->internal_end_block
));
38 for (i
= dev
->internal_start_block
; i
<= dev
->internal_end_block
; i
++) {
39 yaffs_block_info_t
*bi
= yaffs_get_block_info(dev
, i
);
40 if (bi
->block_state
== YAFFS_BLOCK_STATE_CHECKPOINT
) {
41 T(YAFFS_TRACE_CHECKPOINT
, (TSTR("erasing checkpt block %d"TENDSTR
), i
));
45 if (dev
->param
.erase_fn(dev
, i
- dev
->block_offset
/* realign */)) {
46 bi
->block_state
= YAFFS_BLOCK_STATE_EMPTY
;
47 dev
->n_erased_blocks
++;
48 dev
->n_free_chunks
+= dev
->param
.chunks_per_block
;
50 dev
->param
.bad_block_fn(dev
, i
);
51 bi
->block_state
= YAFFS_BLOCK_STATE_DEAD
;
56 dev
->blocks_in_checkpt
= 0;
62 static void yaffs2_checkpt_find_erased_block(yaffs_dev_t
*dev
)
65 int blocksAvailable
= dev
->n_erased_blocks
- dev
->param
.n_reserved_blocks
;
66 T(YAFFS_TRACE_CHECKPOINT
,
67 (TSTR("allocating checkpt block: erased %d reserved %d avail %d next %d "TENDSTR
),
68 dev
->n_erased_blocks
, dev
->param
.n_reserved_blocks
, blocksAvailable
, dev
->checkpt_next_block
));
70 if (dev
->checkpt_next_block
>= 0 &&
71 dev
->checkpt_next_block
<= dev
->internal_end_block
&&
72 blocksAvailable
> 0) {
74 for (i
= dev
->checkpt_next_block
; i
<= dev
->internal_end_block
; i
++) {
75 yaffs_block_info_t
*bi
= yaffs_get_block_info(dev
, i
);
76 if (bi
->block_state
== YAFFS_BLOCK_STATE_EMPTY
) {
77 dev
->checkpt_next_block
= i
+ 1;
78 dev
->checkpt_cur_block
= i
;
79 T(YAFFS_TRACE_CHECKPOINT
, (TSTR("allocating checkpt block %d"TENDSTR
), i
));
84 T(YAFFS_TRACE_CHECKPOINT
, (TSTR("out of checkpt blocks"TENDSTR
)));
86 dev
->checkpt_next_block
= -1;
87 dev
->checkpt_cur_block
= -1;
90 static void yaffs2_checkpt_find_block(yaffs_dev_t
*dev
)
95 T(YAFFS_TRACE_CHECKPOINT
, (TSTR("find next checkpt block: start: blocks %d next %d" TENDSTR
),
96 dev
->blocks_in_checkpt
, dev
->checkpt_next_block
));
98 if (dev
->blocks_in_checkpt
< dev
->checkpt_max_blocks
)
99 for (i
= dev
->checkpt_next_block
; i
<= dev
->internal_end_block
; i
++) {
100 int chunk
= i
* dev
->param
.chunks_per_block
;
101 int realignedChunk
= chunk
- dev
->chunk_offset
;
103 dev
->param
.read_chunk_tags_fn(dev
, realignedChunk
,
105 T(YAFFS_TRACE_CHECKPOINT
, (TSTR("find next checkpt block: search: block %d oid %d seq %d eccr %d" TENDSTR
),
106 i
, tags
.obj_id
, tags
.seq_number
, tags
.ecc_result
));
108 if (tags
.seq_number
== YAFFS_SEQUENCE_CHECKPOINT_DATA
) {
109 /* Right kind of block */
110 dev
->checkpt_next_block
= tags
.obj_id
;
111 dev
->checkpt_cur_block
= i
;
112 dev
->checkpt_block_list
[dev
->blocks_in_checkpt
] = i
;
113 dev
->blocks_in_checkpt
++;
114 T(YAFFS_TRACE_CHECKPOINT
, (TSTR("found checkpt block %d"TENDSTR
), i
));
119 T(YAFFS_TRACE_CHECKPOINT
, (TSTR("found no more checkpt blocks"TENDSTR
)));
121 dev
->checkpt_next_block
= -1;
122 dev
->checkpt_cur_block
= -1;
126 int yaffs2_checkpt_open(yaffs_dev_t
*dev
, int forWriting
)
130 dev
->checkpt_open_write
= forWriting
;
132 /* Got the functions we need? */
133 if (!dev
->param
.write_chunk_tags_fn
||
134 !dev
->param
.read_chunk_tags_fn
||
135 !dev
->param
.erase_fn
||
136 !dev
->param
.bad_block_fn
)
139 if (forWriting
&& !yaffs2_checkpt_space_ok(dev
))
142 if (!dev
->checkpt_buffer
)
143 dev
->checkpt_buffer
= YMALLOC_DMA(dev
->param
.total_bytes_per_chunk
);
144 if (!dev
->checkpt_buffer
)
148 dev
->checkpt_page_seq
= 0;
149 dev
->checkpt_byte_count
= 0;
150 dev
->checkpt_sum
= 0;
151 dev
->checkpt_xor
= 0;
152 dev
->checkpt_cur_block
= -1;
153 dev
->checkpt_cur_chunk
= -1;
154 dev
->checkpt_next_block
= dev
->internal_start_block
;
156 /* Erase all the blocks in the checkpoint area */
158 memset(dev
->checkpt_buffer
, 0, dev
->data_bytes_per_chunk
);
159 dev
->checkpt_byte_offs
= 0;
160 return yaffs_checkpt_erase(dev
);
163 /* Set to a value that will kick off a read */
164 dev
->checkpt_byte_offs
= dev
->data_bytes_per_chunk
;
165 /* A checkpoint block list of 1 checkpoint block per 16 block is (hopefully)
166 * going to be way more than we need */
167 dev
->blocks_in_checkpt
= 0;
168 dev
->checkpt_max_blocks
= (dev
->internal_end_block
- dev
->internal_start_block
)/16 + 2;
169 dev
->checkpt_block_list
= YMALLOC(sizeof(int) * dev
->checkpt_max_blocks
);
170 if(!dev
->checkpt_block_list
)
173 for (i
= 0; i
< dev
->checkpt_max_blocks
; i
++)
174 dev
->checkpt_block_list
[i
] = -1;
180 int yaffs2_get_checkpt_sum(yaffs_dev_t
*dev
, __u32
*sum
)
183 compositeSum
= (dev
->checkpt_sum
<< 8) | (dev
->checkpt_xor
& 0xFF);
188 static int yaffs2_checkpt_flush_buffer(yaffs_dev_t
*dev
)
195 if (dev
->checkpt_cur_block
< 0) {
196 yaffs2_checkpt_find_erased_block(dev
);
197 dev
->checkpt_cur_chunk
= 0;
200 if (dev
->checkpt_cur_block
< 0)
204 tags
.obj_id
= dev
->checkpt_next_block
; /* Hint to next place to look */
205 tags
.chunk_id
= dev
->checkpt_page_seq
+ 1;
206 tags
.seq_number
= YAFFS_SEQUENCE_CHECKPOINT_DATA
;
207 tags
.n_bytes
= dev
->data_bytes_per_chunk
;
208 if (dev
->checkpt_cur_chunk
== 0) {
209 /* First chunk we write for the block? Set block state to
211 yaffs_block_info_t
*bi
= yaffs_get_block_info(dev
, dev
->checkpt_cur_block
);
212 bi
->block_state
= YAFFS_BLOCK_STATE_CHECKPOINT
;
213 dev
->blocks_in_checkpt
++;
216 chunk
= dev
->checkpt_cur_block
* dev
->param
.chunks_per_block
+ dev
->checkpt_cur_chunk
;
219 T(YAFFS_TRACE_CHECKPOINT
, (TSTR("checkpoint wite buffer nand %d(%d:%d) objid %d chId %d" TENDSTR
),
220 chunk
, dev
->checkpt_cur_block
, dev
->checkpt_cur_chunk
, tags
.obj_id
, tags
.chunk_id
));
222 realignedChunk
= chunk
- dev
->chunk_offset
;
224 dev
->n_page_writes
++;
226 dev
->param
.write_chunk_tags_fn(dev
, realignedChunk
,
227 dev
->checkpt_buffer
, &tags
);
228 dev
->checkpt_byte_offs
= 0;
229 dev
->checkpt_page_seq
++;
230 dev
->checkpt_cur_chunk
++;
231 if (dev
->checkpt_cur_chunk
>= dev
->param
.chunks_per_block
) {
232 dev
->checkpt_cur_chunk
= 0;
233 dev
->checkpt_cur_block
= -1;
235 memset(dev
->checkpt_buffer
, 0, dev
->data_bytes_per_chunk
);
241 int yaffs2_checkpt_wr(yaffs_dev_t
*dev
, const void *data
, int n_bytes
)
247 __u8
* dataBytes
= (__u8
*)data
;
251 if (!dev
->checkpt_buffer
)
254 if (!dev
->checkpt_open_write
)
257 while (i
< n_bytes
&& ok
) {
258 dev
->checkpt_buffer
[dev
->checkpt_byte_offs
] = *dataBytes
;
259 dev
->checkpt_sum
+= *dataBytes
;
260 dev
->checkpt_xor
^= *dataBytes
;
262 dev
->checkpt_byte_offs
++;
265 dev
->checkpt_byte_count
++;
268 if (dev
->checkpt_byte_offs
< 0 ||
269 dev
->checkpt_byte_offs
>= dev
->data_bytes_per_chunk
)
270 ok
= yaffs2_checkpt_flush_buffer(dev
);
276 int yaffs2_checkpt_rd(yaffs_dev_t
*dev
, void *data
, int n_bytes
)
286 __u8
*dataBytes
= (__u8
*)data
;
288 if (!dev
->checkpt_buffer
)
291 if (dev
->checkpt_open_write
)
294 while (i
< n_bytes
&& ok
) {
297 if (dev
->checkpt_byte_offs
< 0 ||
298 dev
->checkpt_byte_offs
>= dev
->data_bytes_per_chunk
) {
300 if (dev
->checkpt_cur_block
< 0) {
301 yaffs2_checkpt_find_block(dev
);
302 dev
->checkpt_cur_chunk
= 0;
305 if (dev
->checkpt_cur_block
< 0)
308 chunk
= dev
->checkpt_cur_block
*
309 dev
->param
.chunks_per_block
+
310 dev
->checkpt_cur_chunk
;
312 realignedChunk
= chunk
- dev
->chunk_offset
;
316 /* read in the next chunk */
317 /* printf("read checkpoint page %d\n",dev->checkpointPage); */
318 dev
->param
.read_chunk_tags_fn(dev
,
323 if (tags
.chunk_id
!= (dev
->checkpt_page_seq
+ 1) ||
324 tags
.ecc_result
> YAFFS_ECC_RESULT_FIXED
||
325 tags
.seq_number
!= YAFFS_SEQUENCE_CHECKPOINT_DATA
)
328 dev
->checkpt_byte_offs
= 0;
329 dev
->checkpt_page_seq
++;
330 dev
->checkpt_cur_chunk
++;
332 if (dev
->checkpt_cur_chunk
>= dev
->param
.chunks_per_block
)
333 dev
->checkpt_cur_block
= -1;
338 *dataBytes
= dev
->checkpt_buffer
[dev
->checkpt_byte_offs
];
339 dev
->checkpt_sum
+= *dataBytes
;
340 dev
->checkpt_xor
^= *dataBytes
;
341 dev
->checkpt_byte_offs
++;
344 dev
->checkpt_byte_count
++;
351 int yaffs_checkpt_close(yaffs_dev_t
*dev
)
354 if (dev
->checkpt_open_write
) {
355 if (dev
->checkpt_byte_offs
!= 0)
356 yaffs2_checkpt_flush_buffer(dev
);
357 } else if(dev
->checkpt_block_list
){
359 for (i
= 0; i
< dev
->blocks_in_checkpt
&& dev
->checkpt_block_list
[i
] >= 0; i
++) {
360 int blk
= dev
->checkpt_block_list
[i
];
361 yaffs_block_info_t
*bi
= NULL
;
362 if( dev
->internal_start_block
<= blk
&& blk
<= dev
->internal_end_block
)
363 bi
= yaffs_get_block_info(dev
, blk
);
364 if (bi
&& bi
->block_state
== YAFFS_BLOCK_STATE_EMPTY
)
365 bi
->block_state
= YAFFS_BLOCK_STATE_CHECKPOINT
;
367 /* Todo this looks odd... */
370 YFREE(dev
->checkpt_block_list
);
371 dev
->checkpt_block_list
= NULL
;
374 dev
->n_free_chunks
-= dev
->blocks_in_checkpt
* dev
->param
.chunks_per_block
;
375 dev
->n_erased_blocks
-= dev
->blocks_in_checkpt
;
378 T(YAFFS_TRACE_CHECKPOINT
, (TSTR("checkpoint byte count %d" TENDSTR
),
379 dev
->checkpt_byte_count
));
381 if (dev
->checkpt_buffer
) {
382 /* free the buffer */
383 YFREE(dev
->checkpt_buffer
);
384 dev
->checkpt_buffer
= NULL
;
390 int yaffs2_checkpt_invalidate_stream(yaffs_dev_t
*dev
)
392 /* Erase the checkpoint data */
394 T(YAFFS_TRACE_CHECKPOINT
, (TSTR("checkpoint invalidate of %d blocks"TENDSTR
),
395 dev
->blocks_in_checkpt
));
397 return yaffs_checkpt_erase(dev
);