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