yaffs-direct: Fix object leak caused by defered free
[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         dev->checkpt_block_list =
241             kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS);
242
243         if (!dev->checkpt_block_list)
244                 return 0;
245
246         for (i = 0; i < dev->checkpt_max_blocks; i++)
247                 dev->checkpt_block_list[i] = -1;
248
249         return 1;
250 }
251
252 int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum)
253 {
254         u32 composite_sum;
255
256         composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff);
257         *sum = composite_sum;
258         return 1;
259 }
260
261 static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev)
262 {
263         int chunk;
264         int offset_chunk;
265         struct yaffs_ext_tags tags;
266
267         if (dev->checkpt_cur_block < 0) {
268                 yaffs2_checkpt_find_erased_block(dev);
269                 dev->checkpt_cur_chunk = 0;
270         }
271
272         if (dev->checkpt_cur_block < 0)
273                 return 0;
274
275         tags.is_deleted = 0;
276         tags.obj_id = dev->checkpt_next_block;  /* Hint to next place to look */
277         tags.chunk_id = dev->checkpt_page_seq + 1;
278         tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA;
279         tags.n_bytes = dev->data_bytes_per_chunk;
280         if (dev->checkpt_cur_chunk == 0) {
281                 /* First chunk we write for the block? Set block state to
282                    checkpoint */
283                 struct yaffs_block_info *bi =
284                     yaffs_get_block_info(dev, dev->checkpt_cur_block);
285                 bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
286                 dev->blocks_in_checkpt++;
287         }
288
289         chunk =
290             dev->checkpt_cur_block * dev->param.chunks_per_block +
291             dev->checkpt_cur_chunk;
292
293         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
294                 "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d",
295                 chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk,
296                 tags.obj_id, tags.chunk_id);
297
298         offset_chunk = apply_chunk_offset(dev, chunk);
299
300         dev->n_page_writes++;
301
302         dev->tagger.write_chunk_tags_fn(dev, offset_chunk,
303                                        dev->checkpt_buffer, &tags);
304         dev->checkpt_page_seq++;
305         dev->checkpt_cur_chunk++;
306         if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) {
307                 dev->checkpt_cur_chunk = 0;
308                 dev->checkpt_cur_block = -1;
309         }
310         memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
311
312         yaffs2_checkpt_init_chunk_hdr(dev);
313
314
315         return 1;
316 }
317
318 int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes)
319 {
320         int i = 0;
321         int ok = 1;
322         u8 *data_bytes = (u8 *) data;
323
324         if (!dev->checkpt_buffer)
325                 return 0;
326
327         if (!dev->checkpt_open_write)
328                 return -1;
329
330         while (i < n_bytes && ok) {
331                 dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes;
332                 dev->checkpt_sum += *data_bytes;
333                 dev->checkpt_xor ^= *data_bytes;
334
335                 dev->checkpt_byte_offs++;
336                 i++;
337                 data_bytes++;
338                 dev->checkpt_byte_count++;
339
340                 if (dev->checkpt_byte_offs < 0 ||
341                     dev->checkpt_byte_offs >= dev->data_bytes_per_chunk)
342                         ok = yaffs2_checkpt_flush_buffer(dev);
343         }
344
345         return i;
346 }
347
348 int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes)
349 {
350         int i = 0;
351         int ok = 1;
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 && ok) {
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                         if (dev->checkpt_cur_block < 0) {
374                                 ok = 0;
375                                 break;
376                         }
377
378                         chunk = dev->checkpt_cur_block *
379                             dev->param.chunks_per_block +
380                             dev->checkpt_cur_chunk;
381
382                         offset_chunk = apply_chunk_offset(dev, chunk);
383                         dev->n_page_reads++;
384
385                         /* read in the next chunk */
386                         dev->tagger.read_chunk_tags_fn(dev,
387                                                 offset_chunk,
388                                                 dev->checkpt_buffer,
389                                                 &tags);
390
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                                 ok = 0;
395                                 break;
396                         }
397                         if(!yaffs2_checkpt_check_chunk_hdr(dev)) {
398                                 ok = 0;
399                                 break;
400                         }
401
402                         dev->checkpt_page_seq++;
403                         dev->checkpt_cur_chunk++;
404
405                         if (dev->checkpt_cur_chunk >=
406                                         dev->param.chunks_per_block)
407                                 dev->checkpt_cur_block = -1;
408
409                 }
410
411                 *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs];
412                 dev->checkpt_sum += *data_bytes;
413                 dev->checkpt_xor ^= *data_bytes;
414                 dev->checkpt_byte_offs++;
415                 i++;
416                 data_bytes++;
417                 dev->checkpt_byte_count++;
418         }
419
420         return i;
421 }
422
423 int yaffs_checkpt_close(struct yaffs_dev *dev)
424 {
425         int i;
426
427         if (dev->checkpt_open_write) {
428                 if (dev->checkpt_byte_offs !=
429                         sizeof(sizeof(struct yaffs_checkpt_chunk_hdr)))
430                         yaffs2_checkpt_flush_buffer(dev);
431         } else if (dev->checkpt_block_list) {
432                 for (i = 0;
433                      i < dev->blocks_in_checkpt &&
434                      dev->checkpt_block_list[i] >= 0; i++) {
435                         int blk = dev->checkpt_block_list[i];
436                         struct yaffs_block_info *bi = NULL;
437
438                         if (dev->internal_start_block <= blk &&
439                             blk <= dev->internal_end_block)
440                                 bi = yaffs_get_block_info(dev, blk);
441                         if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY)
442                                 bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
443                 }
444                 kfree(dev->checkpt_block_list);
445                 dev->checkpt_block_list = NULL;
446         }
447
448         dev->n_free_chunks -=
449                 dev->blocks_in_checkpt * dev->param.chunks_per_block;
450         dev->n_erased_blocks -= dev->blocks_in_checkpt;
451
452         yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d",
453                 dev->checkpt_byte_count);
454
455         if (dev->checkpt_buffer) {
456                 /* free the buffer */
457                 kfree(dev->checkpt_buffer);
458                 dev->checkpt_buffer = NULL;
459                 return 1;
460         } else {
461                 return 0;
462         }
463 }
464
465 int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev)
466 {
467         /* Erase the checkpoint data */
468
469         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
470                 "checkpoint invalidate of %d blocks",
471                 dev->blocks_in_checkpt);
472
473         return yaffs_checkpt_erase(dev);
474 }