8478eb88185fc4e1b271f161f31062e1f84bffb4
[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->param.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->param.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->param.bad_block_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->param.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->param.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->param.write_chunk_tags_fn ||
204             !dev->param.read_chunk_tags_fn ||
205             !dev->param.erase_fn || !dev->param.bad_block_fn)
206                 return 0;
207
208         if (writing && !yaffs2_checkpt_space_ok(dev))
209                 return 0;
210
211         if (!dev->checkpt_buffer)
212                 dev->checkpt_buffer =
213                     kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);
214         if (!dev->checkpt_buffer)
215                 return 0;
216
217         dev->checkpt_page_seq = 0;
218         dev->checkpt_byte_count = 0;
219         dev->checkpt_sum = 0;
220         dev->checkpt_xor = 0;
221         dev->checkpt_cur_block = -1;
222         dev->checkpt_cur_chunk = -1;
223         dev->checkpt_next_block = dev->internal_start_block;
224
225         if (writing) {
226                 memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
227                 yaffs2_checkpt_init_chunk_hdr(dev);
228                 return yaffs_checkpt_erase(dev);
229         }
230
231         /* Opening for a read */
232         /* Set to a value that will kick off a read */
233         dev->checkpt_byte_offs = dev->data_bytes_per_chunk;
234         /* A checkpoint block list of 1 checkpoint block per 16 block is
235          * (hopefully) going to be way more than we need */
236         dev->blocks_in_checkpt = 0;
237         dev->checkpt_max_blocks =
238             (dev->internal_end_block - dev->internal_start_block) / 16 + 2;
239         dev->checkpt_block_list =
240             kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS);
241
242         if (!dev->checkpt_block_list)
243                 return 0;
244
245         for (i = 0; i < dev->checkpt_max_blocks; i++)
246                 dev->checkpt_block_list[i] = -1;
247
248         return 1;
249 }
250
251 int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum)
252 {
253         u32 composite_sum;
254
255         composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff);
256         *sum = composite_sum;
257         return 1;
258 }
259
260 static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev)
261 {
262         int chunk;
263         int offset_chunk;
264         struct yaffs_ext_tags tags;
265
266         if (dev->checkpt_cur_block < 0) {
267                 yaffs2_checkpt_find_erased_block(dev);
268                 dev->checkpt_cur_chunk = 0;
269         }
270
271         if (dev->checkpt_cur_block < 0)
272                 return 0;
273
274         tags.is_deleted = 0;
275         tags.obj_id = dev->checkpt_next_block;  /* Hint to next place to look */
276         tags.chunk_id = dev->checkpt_page_seq + 1;
277         tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA;
278         tags.n_bytes = dev->data_bytes_per_chunk;
279         if (dev->checkpt_cur_chunk == 0) {
280                 /* First chunk we write for the block? Set block state to
281                    checkpoint */
282                 struct yaffs_block_info *bi =
283                     yaffs_get_block_info(dev, dev->checkpt_cur_block);
284                 bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
285                 dev->blocks_in_checkpt++;
286         }
287
288         chunk =
289             dev->checkpt_cur_block * dev->param.chunks_per_block +
290             dev->checkpt_cur_chunk;
291
292         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
293                 "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d",
294                 chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk,
295                 tags.obj_id, tags.chunk_id);
296
297         offset_chunk = apply_chunk_offset(dev, chunk);
298
299         dev->n_page_writes++;
300
301         dev->param.write_chunk_tags_fn(dev, offset_chunk,
302                                        dev->checkpt_buffer, &tags);
303         dev->checkpt_page_seq++;
304         dev->checkpt_cur_chunk++;
305         if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) {
306                 dev->checkpt_cur_chunk = 0;
307                 dev->checkpt_cur_block = -1;
308         }
309         memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
310
311         yaffs2_checkpt_init_chunk_hdr(dev);
312
313
314         return 1;
315 }
316
317 int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes)
318 {
319         int i = 0;
320         int ok = 1;
321         u8 *data_bytes = (u8 *) data;
322
323         if (!dev->checkpt_buffer)
324                 return 0;
325
326         if (!dev->checkpt_open_write)
327                 return -1;
328
329         while (i < n_bytes && ok) {
330                 dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes;
331                 dev->checkpt_sum += *data_bytes;
332                 dev->checkpt_xor ^= *data_bytes;
333
334                 dev->checkpt_byte_offs++;
335                 i++;
336                 data_bytes++;
337                 dev->checkpt_byte_count++;
338
339                 if (dev->checkpt_byte_offs < 0 ||
340                     dev->checkpt_byte_offs >= dev->data_bytes_per_chunk)
341                         ok = yaffs2_checkpt_flush_buffer(dev);
342         }
343
344         return i;
345 }
346
347 int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes)
348 {
349         int i = 0;
350         int ok = 1;
351         struct yaffs_ext_tags tags;
352         int chunk;
353         int offset_chunk;
354         u8 *data_bytes = (u8 *) data;
355
356         if (!dev->checkpt_buffer)
357                 return 0;
358
359         if (dev->checkpt_open_write)
360                 return -1;
361
362         while (i < n_bytes && ok) {
363
364                 if (dev->checkpt_byte_offs < 0 ||
365                     dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) {
366
367                         if (dev->checkpt_cur_block < 0) {
368                                 yaffs2_checkpt_find_block(dev);
369                                 dev->checkpt_cur_chunk = 0;
370                         }
371
372                         if (dev->checkpt_cur_block < 0) {
373                                 ok = 0;
374                                 break;
375                         }
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->param.read_chunk_tags_fn(dev,
386                                                 offset_chunk,
387                                                 dev->checkpt_buffer,
388                                                 &tags);
389
390                         if (tags.chunk_id != (dev->checkpt_page_seq + 1) ||
391                             tags.ecc_result > YAFFS_ECC_RESULT_FIXED ||
392                             tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) {
393                                 ok = 0;
394                                 break;
395                         }
396                         if(!yaffs2_checkpt_check_chunk_hdr(dev)) {
397                                 ok = 0;
398                                 break;
399                         }
400
401                         dev->checkpt_page_seq++;
402                         dev->checkpt_cur_chunk++;
403
404                         if (dev->checkpt_cur_chunk >=
405                                         dev->param.chunks_per_block)
406                                 dev->checkpt_cur_block = -1;
407
408                 }
409
410                 *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs];
411                 dev->checkpt_sum += *data_bytes;
412                 dev->checkpt_xor ^= *data_bytes;
413                 dev->checkpt_byte_offs++;
414                 i++;
415                 data_bytes++;
416                 dev->checkpt_byte_count++;
417         }
418
419         return i;
420 }
421
422 int yaffs_checkpt_close(struct yaffs_dev *dev)
423 {
424         int i;
425
426         if (dev->checkpt_open_write) {
427                 if (dev->checkpt_byte_offs !=
428                         sizeof(sizeof(struct yaffs_checkpt_chunk_hdr)))
429                         yaffs2_checkpt_flush_buffer(dev);
430         } else if (dev->checkpt_block_list) {
431                 for (i = 0;
432                      i < dev->blocks_in_checkpt &&
433                      dev->checkpt_block_list[i] >= 0; i++) {
434                         int blk = dev->checkpt_block_list[i];
435                         struct yaffs_block_info *bi = NULL;
436
437                         if (dev->internal_start_block <= blk &&
438                             blk <= dev->internal_end_block)
439                                 bi = yaffs_get_block_info(dev, blk);
440                         if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY)
441                                 bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
442                 }
443                 kfree(dev->checkpt_block_list);
444                 dev->checkpt_block_list = NULL;
445         }
446
447         dev->n_free_chunks -=
448                 dev->blocks_in_checkpt * dev->param.chunks_per_block;
449         dev->n_erased_blocks -= dev->blocks_in_checkpt;
450
451         yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d",
452                 dev->checkpt_byte_count);
453
454         if (dev->checkpt_buffer) {
455                 /* free the buffer */
456                 kfree(dev->checkpt_buffer);
457                 dev->checkpt_buffer = NULL;
458                 return 1;
459         } else {
460                 return 0;
461         }
462 }
463
464 int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev)
465 {
466         /* Erase the checkpoint data */
467
468         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
469                 "checkpoint invalidate of %d blocks",
470                 dev->blocks_in_checkpt);
471
472         return yaffs_checkpt_erase(dev);
473 }