16ee1e0695c6b468941f4fcd0c6b983324e6d7db
[yaffs2.git] / yaffs_checkptrw.c
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 #include "yaffs_checkptrw.h"
15 #include "yaffs_getblockinfo.h"
16
17 struct yaffs_checkpt_chunk_hdr {
18         int version;
19         int seq;
20         u32 sum;
21         u32 xor;
22 } ;
23
24
25 static int apply_chunk_offset(struct yaffs_dev *dev, int chunk)
26 {
27         return chunk - dev->chunk_offset;
28 }
29
30 static int apply_block_offset(struct yaffs_dev *dev, int block)
31 {
32         return block - dev->block_offset;
33 }
34
35 static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev)
36 {
37         struct yaffs_checkpt_chunk_hdr hdr;
38
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;
43
44         dev->checkpt_byte_offs = sizeof(hdr);
45
46         memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr));
47 }
48
49 static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev)
50 {
51         struct yaffs_checkpt_chunk_hdr hdr;
52
53         memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr));
54
55         dev->checkpt_byte_offs = sizeof(hdr);
56
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;
61 }
62
63 static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev)
64 {
65         int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;
66
67         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
68                 "checkpt blocks_avail = %d", blocks_avail);
69
70         return (blocks_avail <= 0) ? 0 : 1;
71 }
72
73 static int yaffs_checkpt_erase(struct yaffs_dev *dev)
74 {
75         int i;
76
77         if (!dev->drv.drv_erase_fn)
78                 return 0;
79         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
80                 "checking blocks %d to %d",
81                 dev->internal_start_block, dev->internal_end_block);
82
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);
86                 int result;
87
88                 if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) {
89                         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
90                         "erasing checkpt block %d", i);
91
92                         dev->n_erasures++;
93
94                         result = dev->drv.drv_erase_fn(dev, offset_i);
95                         if(result) {
96                                 bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
97                                 dev->n_erased_blocks++;
98                                 dev->n_free_chunks +=
99                                     dev->param.chunks_per_block;
100                         } else {
101                                 dev->drv.drv_mark_bad_fn(dev, offset_i);
102                                 bi->block_state = YAFFS_BLOCK_STATE_DEAD;
103                         }
104                 }
105         }
106
107         dev->blocks_in_checkpt = 0;
108
109         return 1;
110 }
111
112 static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev)
113 {
114         int i;
115         int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;
116
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);
121
122         if (dev->checkpt_next_block >= 0 &&
123             dev->checkpt_next_block <= dev->internal_end_block &&
124             blocks_avail > 0) {
125
126                 for (i = dev->checkpt_next_block; i <= dev->internal_end_block;
127                      i++) {
128                         struct yaffs_block_info *bi;
129
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);
136                                 return;
137                         }
138                 }
139         }
140         yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks");
141
142         dev->checkpt_next_block = -1;
143         dev->checkpt_cur_block = -1;
144 }
145
146 static void yaffs2_checkpt_find_block(struct yaffs_dev *dev)
147 {
148         int i;
149         struct yaffs_ext_tags tags;
150
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);
154
155         if (dev->blocks_in_checkpt < dev->checkpt_max_blocks)
156                 for (i = dev->checkpt_next_block; i <= dev->internal_end_block;
157                      i++) {
158                         int chunk = i * dev->param.chunks_per_block;
159                         enum yaffs_block_state state;
160                         u32 seq;
161
162                         dev->tagger.read_chunk_tags_fn(dev,
163                                         apply_chunk_offset(dev, chunk),
164                                         NULL, &tags);
165                         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
166                                 "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d",
167                                 i, (int) state,
168                                 tags.obj_id, tags.seq_number,
169                                 tags.ecc_result);
170
171                         if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA)
172                                 continue;
173
174                         dev->tagger.query_block_fn(dev,
175                                                 apply_block_offset(dev, i),
176                                                 &state, &seq);
177                         if (state == YAFFS_BLOCK_STATE_DEAD)
178                                 continue;
179
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);
187                         return;
188                 }
189
190         yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks");
191
192         dev->checkpt_next_block = -1;
193         dev->checkpt_cur_block = -1;
194 }
195
196 int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing)
197 {
198         int i;
199
200         dev->checkpt_open_write = writing;
201
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)
207                 return 0;
208
209         if (writing && !yaffs2_checkpt_space_ok(dev))
210                 return 0;
211
212         if (!dev->checkpt_buffer)
213                 dev->checkpt_buffer =
214                     kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);
215         if (!dev->checkpt_buffer)
216                 return 0;
217
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;
225
226         if (writing) {
227                 memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
228                 yaffs2_checkpt_init_chunk_hdr(dev);
229                 return yaffs_checkpt_erase(dev);
230         }
231
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);
243
244         if (!dev->checkpt_block_list)
245                 return 0;
246
247         for (i = 0; i < dev->checkpt_max_blocks; i++)
248                 dev->checkpt_block_list[i] = -1;
249
250         return 1;
251 }
252
253 int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum)
254 {
255         u32 composite_sum;
256
257         composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff);
258         *sum = composite_sum;
259         return 1;
260 }
261
262 static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev)
263 {
264         int chunk;
265         int offset_chunk;
266         struct yaffs_ext_tags tags;
267
268         if (dev->checkpt_cur_block < 0) {
269                 yaffs2_checkpt_find_erased_block(dev);
270                 dev->checkpt_cur_chunk = 0;
271         }
272
273         if (dev->checkpt_cur_block < 0)
274                 return 0;
275
276         tags.is_deleted = 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
283                    checkpoint */
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++;
288         }
289
290         chunk =
291             dev->checkpt_cur_block * dev->param.chunks_per_block +
292             dev->checkpt_cur_chunk;
293
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);
298
299         offset_chunk = apply_chunk_offset(dev, chunk);
300
301         dev->n_page_writes++;
302
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;
310         }
311         memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
312
313         yaffs2_checkpt_init_chunk_hdr(dev);
314
315
316         return 1;
317 }
318
319 int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes)
320 {
321         int i = 0;
322         int ok = 1;
323         u8 *data_bytes = (u8 *) data;
324
325         if (!dev->checkpt_buffer)
326                 return 0;
327
328         if (!dev->checkpt_open_write)
329                 return -1;
330
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;
335
336                 dev->checkpt_byte_offs++;
337                 i++;
338                 data_bytes++;
339                 dev->checkpt_byte_count++;
340
341                 if (dev->checkpt_byte_offs < 0 ||
342                     dev->checkpt_byte_offs >= dev->data_bytes_per_chunk)
343                         ok = yaffs2_checkpt_flush_buffer(dev);
344         }
345
346         return i;
347 }
348
349 int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes)
350 {
351         int i = 0;
352         struct yaffs_ext_tags tags;
353         int chunk;
354         int offset_chunk;
355         u8 *data_bytes = (u8 *) data;
356
357         if (!dev->checkpt_buffer)
358                 return 0;
359
360         if (dev->checkpt_open_write)
361                 return -1;
362
363         while (i < n_bytes) {
364
365                 if (dev->checkpt_byte_offs < 0 ||
366                     dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) {
367
368                         if (dev->checkpt_cur_block < 0) {
369                                 yaffs2_checkpt_find_block(dev);
370                                 dev->checkpt_cur_chunk = 0;
371                         }
372
373                         /* Bail out if we can't find a checpoint block */
374                         if (dev->checkpt_cur_block < 0)
375                                 break;
376
377                         chunk = dev->checkpt_cur_block *
378                             dev->param.chunks_per_block +
379                             dev->checkpt_cur_chunk;
380
381                         offset_chunk = apply_chunk_offset(dev, chunk);
382                         dev->n_page_reads++;
383
384                         /* Read in the next chunk */
385                         dev->tagger.read_chunk_tags_fn(dev,
386                                                 offset_chunk,
387                                                 dev->checkpt_buffer,
388                                                 &tags);
389
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)
394                                 break;
395
396                         /* Bail out if it is not a checkpoint chunk. */
397                         if(!yaffs2_checkpt_check_chunk_hdr(dev))
398                                 break;
399
400                         dev->checkpt_page_seq++;
401                         dev->checkpt_cur_chunk++;
402
403                         if (dev->checkpt_cur_chunk >=
404                                         dev->param.chunks_per_block)
405                                 dev->checkpt_cur_block = -1;
406
407                 }
408
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++;
413                 i++;
414                 data_bytes++;
415                 dev->checkpt_byte_count++;
416         }
417
418         return i; /* Number of bytes read */
419 }
420
421 int yaffs_checkpt_close(struct yaffs_dev *dev)
422 {
423         int i;
424
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) {
430                 for (i = 0;
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;
435
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;
441                 }
442         }
443
444         dev->n_free_chunks -=
445                 dev->blocks_in_checkpt * dev->param.chunks_per_block;
446         dev->n_erased_blocks -= dev->blocks_in_checkpt;
447
448         yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d",
449                 dev->checkpt_byte_count);
450
451         if (dev->checkpt_buffer)
452                 return 1;
453         else
454                 return 0;
455 }
456
457 int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev)
458 {
459         /* Erase the checkpoint data */
460
461         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
462                 "checkpoint invalidate of %d blocks",
463                 dev->blocks_in_checkpt);
464
465         return yaffs_checkpt_erase(dev);
466 }