Merge branch '32-bit-loff_t'
[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 static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev)
25 {
26         struct yaffs_checkpt_chunk_hdr hdr;
27
28         hdr.version = YAFFS_CHECKPOINT_VERSION;
29         hdr.seq = dev->checkpt_page_seq;
30         hdr.sum = dev->checkpt_sum;
31         hdr.xor = dev->checkpt_xor;
32
33         dev->checkpt_byte_offs = sizeof(hdr);
34
35         memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr));
36 }
37
38 static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev)
39 {
40         struct yaffs_checkpt_chunk_hdr hdr;
41
42         memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr));
43
44         dev->checkpt_byte_offs = sizeof(hdr);
45
46         return hdr.version == YAFFS_CHECKPOINT_VERSION &&
47                 hdr.seq == dev->checkpt_page_seq &&
48                 hdr.sum == dev->checkpt_sum &&
49                 hdr.xor == dev->checkpt_xor;
50 }
51
52 static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev)
53 {
54         int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;
55
56         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
57                 "checkpt blocks_avail = %d", blocks_avail);
58
59         return (blocks_avail <= 0) ? 0 : 1;
60 }
61
62 static int yaffs_checkpt_erase(struct yaffs_dev *dev)
63 {
64         int i;
65
66         if (!dev->param.erase_fn)
67                 return 0;
68         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
69                 "checking blocks %d to %d",
70                 dev->internal_start_block, dev->internal_end_block);
71
72         for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
73                 struct yaffs_block_info *bi = yaffs_get_block_info(dev, i);
74                 if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) {
75                         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
76                         "erasing checkpt block %d", i);
77
78                         dev->n_erasures++;
79
80                         if (dev->param.
81                             erase_fn(dev,
82                                      i - dev->block_offset /* realign */)) {
83                                 bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
84                                 dev->n_erased_blocks++;
85                                 dev->n_free_chunks +=
86                                     dev->param.chunks_per_block;
87                         } else {
88                                 dev->param.bad_block_fn(dev, i);
89                                 bi->block_state = YAFFS_BLOCK_STATE_DEAD;
90                         }
91                 }
92         }
93
94         dev->blocks_in_checkpt = 0;
95
96         return 1;
97 }
98
99 static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev)
100 {
101         int i;
102         int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;
103
104         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
105                 "allocating checkpt block: erased %d reserved %d avail %d next %d ",
106                 dev->n_erased_blocks, dev->param.n_reserved_blocks,
107                 blocks_avail, dev->checkpt_next_block);
108
109         if (dev->checkpt_next_block >= 0 &&
110             dev->checkpt_next_block <= dev->internal_end_block &&
111             blocks_avail > 0) {
112
113                 for (i = dev->checkpt_next_block; i <= dev->internal_end_block;
114                      i++) {
115                         struct yaffs_block_info *bi =
116                             yaffs_get_block_info(dev, i);
117                         if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {
118                                 dev->checkpt_next_block = i + 1;
119                                 dev->checkpt_cur_block = i;
120                                 yaffs_trace(YAFFS_TRACE_CHECKPOINT,
121                                         "allocating checkpt block %d", i);
122                                 return;
123                         }
124                 }
125         }
126         yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks");
127
128         dev->checkpt_next_block = -1;
129         dev->checkpt_cur_block = -1;
130 }
131
132 static void yaffs2_checkpt_find_block(struct yaffs_dev *dev)
133 {
134         int i;
135         struct yaffs_ext_tags tags;
136
137         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
138                 "find next checkpt block: start:  blocks %d next %d",
139                 dev->blocks_in_checkpt, dev->checkpt_next_block);
140
141         if (dev->blocks_in_checkpt < dev->checkpt_max_blocks)
142                 for (i = dev->checkpt_next_block; i <= dev->internal_end_block;
143                      i++) {
144                         int chunk = i * dev->param.chunks_per_block;
145                         int realigned_chunk = chunk - dev->chunk_offset;
146                         enum yaffs_block_state state;
147                         u32 seq;
148
149                         dev->param.read_chunk_tags_fn(dev, realigned_chunk,
150                                                       NULL, &tags);
151                         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
152                                 "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d",
153                                 i, (int) state,
154                                 tags.obj_id, tags.seq_number,
155                                 tags.ecc_result);
156
157                         if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA)
158                                 continue;
159
160                         dev->param.query_block_fn(dev, i, &state, &seq);
161                         if (state == YAFFS_BLOCK_STATE_DEAD)
162                                 continue;
163
164                         /* Right kind of block */
165                         dev->checkpt_next_block = tags.obj_id;
166                         dev->checkpt_cur_block = i;
167                         dev->checkpt_block_list[dev->blocks_in_checkpt] = i;
168                         dev->blocks_in_checkpt++;
169                         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
170                                 "found checkpt block %d", i);
171                         return;
172                 }
173
174         yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks");
175
176         dev->checkpt_next_block = -1;
177         dev->checkpt_cur_block = -1;
178 }
179
180 int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing)
181 {
182         int i;
183
184         dev->checkpt_open_write = writing;
185
186         /* Got the functions we need? */
187         if (!dev->param.write_chunk_tags_fn ||
188             !dev->param.read_chunk_tags_fn ||
189             !dev->param.erase_fn || !dev->param.bad_block_fn)
190                 return 0;
191
192         if (writing && !yaffs2_checkpt_space_ok(dev))
193                 return 0;
194
195         if (!dev->checkpt_buffer)
196                 dev->checkpt_buffer =
197                     kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);
198         if (!dev->checkpt_buffer)
199                 return 0;
200
201         dev->checkpt_page_seq = 0;
202         dev->checkpt_byte_count = 0;
203         dev->checkpt_sum = 0;
204         dev->checkpt_xor = 0;
205         dev->checkpt_cur_block = -1;
206         dev->checkpt_cur_chunk = -1;
207         dev->checkpt_next_block = dev->internal_start_block;
208
209         if (writing) {
210                 memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
211                 yaffs2_checkpt_init_chunk_hdr(dev);
212                 return yaffs_checkpt_erase(dev);
213         }
214
215         /* Opening for a read */
216         /* Set to a value that will kick off a read */
217         dev->checkpt_byte_offs = dev->data_bytes_per_chunk;
218         /* A checkpoint block list of 1 checkpoint block per 16 block is
219          * (hopefully) going to be way more than we need */
220         dev->blocks_in_checkpt = 0;
221         dev->checkpt_max_blocks =
222             (dev->internal_end_block - dev->internal_start_block) / 16 + 2;
223         dev->checkpt_block_list =
224             kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS);
225
226         if (!dev->checkpt_block_list)
227                 return 0;
228
229         for (i = 0; i < dev->checkpt_max_blocks; i++)
230                 dev->checkpt_block_list[i] = -1;
231
232         return 1;
233 }
234
235 int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum)
236 {
237         u32 composite_sum;
238
239         composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff);
240         *sum = composite_sum;
241         return 1;
242 }
243
244 static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev)
245 {
246         int chunk;
247         int realigned_chunk;
248         struct yaffs_ext_tags tags;
249
250         if (dev->checkpt_cur_block < 0) {
251                 yaffs2_checkpt_find_erased_block(dev);
252                 dev->checkpt_cur_chunk = 0;
253         }
254
255         if (dev->checkpt_cur_block < 0)
256                 return 0;
257
258         tags.is_deleted = 0;
259         tags.obj_id = dev->checkpt_next_block;  /* Hint to next place to look */
260         tags.chunk_id = dev->checkpt_page_seq + 1;
261         tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA;
262         tags.n_bytes = dev->data_bytes_per_chunk;
263         if (dev->checkpt_cur_chunk == 0) {
264                 /* First chunk we write for the block? Set block state to
265                    checkpoint */
266                 struct yaffs_block_info *bi =
267                     yaffs_get_block_info(dev, dev->checkpt_cur_block);
268                 bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
269                 dev->blocks_in_checkpt++;
270         }
271
272         chunk =
273             dev->checkpt_cur_block * dev->param.chunks_per_block +
274             dev->checkpt_cur_chunk;
275
276         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
277                 "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d",
278                 chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk,
279                 tags.obj_id, tags.chunk_id);
280
281         realigned_chunk = chunk - dev->chunk_offset;
282
283         dev->n_page_writes++;
284
285         dev->param.write_chunk_tags_fn(dev, realigned_chunk,
286                                        dev->checkpt_buffer, &tags);
287         dev->checkpt_page_seq++;
288         dev->checkpt_cur_chunk++;
289         if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) {
290                 dev->checkpt_cur_chunk = 0;
291                 dev->checkpt_cur_block = -1;
292         }
293         memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
294
295         yaffs2_checkpt_init_chunk_hdr(dev);
296
297
298         return 1;
299 }
300
301 int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes)
302 {
303         int i = 0;
304         int ok = 1;
305         u8 *data_bytes = (u8 *) data;
306
307         if (!dev->checkpt_buffer)
308                 return 0;
309
310         if (!dev->checkpt_open_write)
311                 return -1;
312
313         while (i < n_bytes && ok) {
314                 dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes;
315                 dev->checkpt_sum += *data_bytes;
316                 dev->checkpt_xor ^= *data_bytes;
317
318                 dev->checkpt_byte_offs++;
319                 i++;
320                 data_bytes++;
321                 dev->checkpt_byte_count++;
322
323                 if (dev->checkpt_byte_offs < 0 ||
324                     dev->checkpt_byte_offs >= dev->data_bytes_per_chunk)
325                         ok = yaffs2_checkpt_flush_buffer(dev);
326         }
327
328         return i;
329 }
330
331 int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes)
332 {
333         int i = 0;
334         int ok = 1;
335         struct yaffs_ext_tags tags;
336         int chunk;
337         int realigned_chunk;
338         u8 *data_bytes = (u8 *) data;
339
340         if (!dev->checkpt_buffer)
341                 return 0;
342
343         if (dev->checkpt_open_write)
344                 return -1;
345
346         while (i < n_bytes && ok) {
347
348                 if (dev->checkpt_byte_offs < 0 ||
349                     dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) {
350
351                         if (dev->checkpt_cur_block < 0) {
352                                 yaffs2_checkpt_find_block(dev);
353                                 dev->checkpt_cur_chunk = 0;
354                         }
355
356                         if (dev->checkpt_cur_block < 0) {
357                                 ok = 0;
358                                 break;
359                         }
360
361                         chunk = dev->checkpt_cur_block *
362                             dev->param.chunks_per_block +
363                             dev->checkpt_cur_chunk;
364
365                         realigned_chunk = chunk - dev->chunk_offset;
366                         dev->n_page_reads++;
367
368                         /* read in the next chunk */
369                         dev->param.read_chunk_tags_fn(dev,
370                                                 realigned_chunk,
371                                                 dev->checkpt_buffer,
372                                                 &tags);
373
374                         if (tags.chunk_id != (dev->checkpt_page_seq + 1) ||
375                             tags.ecc_result > YAFFS_ECC_RESULT_FIXED ||
376                             tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) {
377                                 ok = 0;
378                                 break;
379                         }
380                         if(!yaffs2_checkpt_check_chunk_hdr(dev)) {
381                                 ok = 0;
382                                 break;
383                         }
384
385                         dev->checkpt_page_seq++;
386                         dev->checkpt_cur_chunk++;
387
388                         if (dev->checkpt_cur_chunk >=
389                                         dev->param.chunks_per_block)
390                                 dev->checkpt_cur_block = -1;
391
392                 }
393
394                 *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs];
395                 dev->checkpt_sum += *data_bytes;
396                 dev->checkpt_xor ^= *data_bytes;
397                 dev->checkpt_byte_offs++;
398                 i++;
399                 data_bytes++;
400                 dev->checkpt_byte_count++;
401         }
402
403         return i;
404 }
405
406 int yaffs_checkpt_close(struct yaffs_dev *dev)
407 {
408         int i;
409
410         if (dev->checkpt_open_write) {
411                 if (dev->checkpt_byte_offs !=
412                         sizeof(sizeof(struct yaffs_checkpt_chunk_hdr)))
413                         yaffs2_checkpt_flush_buffer(dev);
414         } else if (dev->checkpt_block_list) {
415                 for (i = 0;
416                      i < dev->blocks_in_checkpt &&
417                      dev->checkpt_block_list[i] >= 0; i++) {
418                         int blk = dev->checkpt_block_list[i];
419                         struct yaffs_block_info *bi = NULL;
420
421                         if (dev->internal_start_block <= blk &&
422                             blk <= dev->internal_end_block)
423                                 bi = yaffs_get_block_info(dev, blk);
424                         if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY)
425                                 bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
426                 }
427                 kfree(dev->checkpt_block_list);
428                 dev->checkpt_block_list = NULL;
429         }
430
431         dev->n_free_chunks -=
432                 dev->blocks_in_checkpt * dev->param.chunks_per_block;
433         dev->n_erased_blocks -= dev->blocks_in_checkpt;
434
435         yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d",
436                 dev->checkpt_byte_count);
437
438         if (dev->checkpt_buffer) {
439                 /* free the buffer */
440                 kfree(dev->checkpt_buffer);
441                 dev->checkpt_buffer = NULL;
442                 return 1;
443         } else {
444                 return 0;
445         }
446 }
447
448 int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev)
449 {
450         /* Erase the checkpoint data */
451
452         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
453                 "checkpoint invalidate of %d blocks",
454                 dev->blocks_in_checkpt);
455
456         return yaffs_checkpt_erase(dev);
457 }