Improve sync to flush metadata
[yaffs2.git] / yaffs_checkptrw.c
1 /*
2  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3  *
4  * Copyright (C) 2002-2007 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 const char *yaffs_checkptrw_c_version =
15         "$Id: yaffs_checkptrw.c,v 1.20 2009-09-09 03:03:01 charles Exp $";
16
17
18 #include "yaffs_checkptrw.h"
19 #include "yaffs_getblockinfo.h"
20
21 static int yaffs_CheckpointSpaceOk(yaffs_Device *dev)
22 {
23         int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks;
24
25         T(YAFFS_TRACE_CHECKPOINT,
26                 (TSTR("checkpt blocks available = %d" TENDSTR),
27                 blocksAvailable));
28
29         return (blocksAvailable <= 0) ? 0 : 1;
30 }
31
32
33 static int yaffs_CheckpointErase(yaffs_Device *dev)
34 {
35         int i;
36
37         if (!dev->eraseBlockInNAND)
38                 return 0;
39         T(YAFFS_TRACE_CHECKPOINT, (TSTR("checking blocks %d to %d"TENDSTR),
40                 dev->internalStartBlock, dev->internalEndBlock));
41
42         for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) {
43                 yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, i);
44                 if (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT) {
45                         T(YAFFS_TRACE_CHECKPOINT, (TSTR("erasing checkpt block %d"TENDSTR), i));
46
47                         dev->nBlockErasures++;
48
49                         if (dev->eraseBlockInNAND(dev, i - dev->blockOffset /* realign */)) {
50                                 bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
51                                 dev->nErasedBlocks++;
52                                 dev->nFreeChunks += dev->nChunksPerBlock;
53                         } else {
54                                 dev->markNANDBlockBad(dev, i);
55                                 bi->blockState = YAFFS_BLOCK_STATE_DEAD;
56                         }
57                 }
58         }
59
60         dev->blocksInCheckpoint = 0;
61
62         return 1;
63 }
64
65
66 static void yaffs_CheckpointFindNextErasedBlock(yaffs_Device *dev)
67 {
68         int  i;
69         int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks;
70         T(YAFFS_TRACE_CHECKPOINT,
71                 (TSTR("allocating checkpt block: erased %d reserved %d avail %d next %d "TENDSTR),
72                 dev->nErasedBlocks, dev->nReservedBlocks, blocksAvailable, dev->checkpointNextBlock));
73
74         if (dev->checkpointNextBlock >= 0 &&
75                         dev->checkpointNextBlock <= dev->internalEndBlock &&
76                         blocksAvailable > 0) {
77
78                 for (i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++) {
79                         yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, i);
80                         if (bi->blockState == YAFFS_BLOCK_STATE_EMPTY) {
81                                 dev->checkpointNextBlock = i + 1;
82                                 dev->checkpointCurrentBlock = i;
83                                 T(YAFFS_TRACE_CHECKPOINT, (TSTR("allocating checkpt block %d"TENDSTR), i));
84                                 return;
85                         }
86                 }
87         }
88         T(YAFFS_TRACE_CHECKPOINT, (TSTR("out of checkpt blocks"TENDSTR)));
89
90         dev->checkpointNextBlock = -1;
91         dev->checkpointCurrentBlock = -1;
92 }
93
94 static void yaffs_CheckpointFindNextCheckpointBlock(yaffs_Device *dev)
95 {
96         int  i;
97         yaffs_ExtendedTags tags;
98
99         T(YAFFS_TRACE_CHECKPOINT, (TSTR("find next checkpt block: start:  blocks %d next %d" TENDSTR),
100                 dev->blocksInCheckpoint, dev->checkpointNextBlock));
101
102         if (dev->blocksInCheckpoint < dev->checkpointMaxBlocks)
103                 for (i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++) {
104                         int chunk = i * dev->nChunksPerBlock;
105                         int realignedChunk = chunk - dev->chunkOffset;
106
107                         dev->readChunkWithTagsFromNAND(dev, realignedChunk,
108                                         NULL, &tags);
109                         T(YAFFS_TRACE_CHECKPOINT, (TSTR("find next checkpt block: search: block %d oid %d seq %d eccr %d" TENDSTR),
110                                 i, tags.objectId, tags.sequenceNumber, tags.eccResult));
111
112                         if (tags.sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA) {
113                                 /* Right kind of block */
114                                 dev->checkpointNextBlock = tags.objectId;
115                                 dev->checkpointCurrentBlock = i;
116                                 dev->checkpointBlockList[dev->blocksInCheckpoint] = i;
117                                 dev->blocksInCheckpoint++;
118                                 T(YAFFS_TRACE_CHECKPOINT, (TSTR("found checkpt block %d"TENDSTR), i));
119                                 return;
120                         }
121                 }
122
123         T(YAFFS_TRACE_CHECKPOINT, (TSTR("found no more checkpt blocks"TENDSTR)));
124
125         dev->checkpointNextBlock = -1;
126         dev->checkpointCurrentBlock = -1;
127 }
128
129
130 int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting)
131 {
132
133         /* Got the functions we need? */
134         if (!dev->writeChunkWithTagsToNAND ||
135                         !dev->readChunkWithTagsFromNAND ||
136                         !dev->eraseBlockInNAND ||
137                         !dev->markNANDBlockBad)
138                 return 0;
139
140         if (forWriting && !yaffs_CheckpointSpaceOk(dev))
141                 return 0;
142
143         if (!dev->checkpointBuffer)
144                 dev->checkpointBuffer = YMALLOC_DMA(dev->totalBytesPerChunk);
145         if (!dev->checkpointBuffer)
146                 return 0;
147
148
149         dev->checkpointPageSequence = 0;
150
151         dev->checkpointOpenForWrite = forWriting;
152
153         dev->checkpointByteCount = 0;
154         dev->checkpointSum = 0;
155         dev->checkpointXor = 0;
156         dev->checkpointCurrentBlock = -1;
157         dev->checkpointCurrentChunk = -1;
158         dev->checkpointNextBlock = dev->internalStartBlock;
159
160         /* Erase all the blocks in the checkpoint area */
161         if (forWriting) {
162                 memset(dev->checkpointBuffer, 0, dev->nDataBytesPerChunk);
163                 dev->checkpointByteOffset = 0;
164                 return yaffs_CheckpointErase(dev);
165         } else {
166                 int i;
167                 /* Set to a value that will kick off a read */
168                 dev->checkpointByteOffset = dev->nDataBytesPerChunk;
169                 /* A checkpoint block list of 1 checkpoint block per 16 block is (hopefully)
170                  * going to be way more than we need */
171                 dev->blocksInCheckpoint = 0;
172                 dev->checkpointMaxBlocks = (dev->internalEndBlock - dev->internalStartBlock)/16 + 2;
173                 dev->checkpointBlockList = YMALLOC(sizeof(int) * dev->checkpointMaxBlocks);
174                 if(!dev->checkpointBlockList)
175                         return 0;
176
177                 for (i = 0; i < dev->checkpointMaxBlocks; i++)
178                         dev->checkpointBlockList[i] = -1;
179         }
180
181         return 1;
182 }
183
184 int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum)
185 {
186         __u32 compositeSum;
187         compositeSum =  (dev->checkpointSum << 8) | (dev->checkpointXor & 0xFF);
188         *sum = compositeSum;
189         return 1;
190 }
191
192 static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev)
193 {
194         int chunk;
195         int realignedChunk;
196
197         yaffs_ExtendedTags tags;
198
199         if (dev->checkpointCurrentBlock < 0) {
200                 yaffs_CheckpointFindNextErasedBlock(dev);
201                 dev->checkpointCurrentChunk = 0;
202         }
203
204         if (dev->checkpointCurrentBlock < 0)
205                 return 0;
206
207         tags.chunkDeleted = 0;
208         tags.objectId = dev->checkpointNextBlock; /* Hint to next place to look */
209         tags.chunkId = dev->checkpointPageSequence + 1;
210         tags.sequenceNumber =  YAFFS_SEQUENCE_CHECKPOINT_DATA;
211         tags.byteCount = dev->nDataBytesPerChunk;
212         if (dev->checkpointCurrentChunk == 0) {
213                 /* First chunk we write for the block? Set block state to
214                    checkpoint */
215                 yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, dev->checkpointCurrentBlock);
216                 bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT;
217                 dev->blocksInCheckpoint++;
218         }
219
220         chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + dev->checkpointCurrentChunk;
221
222
223         T(YAFFS_TRACE_CHECKPOINT, (TSTR("checkpoint wite buffer nand %d(%d:%d) objid %d chId %d" TENDSTR),
224                 chunk, dev->checkpointCurrentBlock, dev->checkpointCurrentChunk, tags.objectId, tags.chunkId));
225
226         realignedChunk = chunk - dev->chunkOffset;
227
228         dev->nPageWrites++;
229
230         dev->writeChunkWithTagsToNAND(dev, realignedChunk,
231                         dev->checkpointBuffer, &tags);
232         dev->checkpointByteOffset = 0;
233         dev->checkpointPageSequence++;
234         dev->checkpointCurrentChunk++;
235         if (dev->checkpointCurrentChunk >= dev->nChunksPerBlock) {
236                 dev->checkpointCurrentChunk = 0;
237                 dev->checkpointCurrentBlock = -1;
238         }
239         memset(dev->checkpointBuffer, 0, dev->nDataBytesPerChunk);
240
241         return 1;
242 }
243
244
245 int yaffs_CheckpointWrite(yaffs_Device *dev, const void *data, int nBytes)
246 {
247         int i = 0;
248         int ok = 1;
249
250
251         __u8 * dataBytes = (__u8 *)data;
252
253
254
255         if (!dev->checkpointBuffer)
256                 return 0;
257
258         if (!dev->checkpointOpenForWrite)
259                 return -1;
260
261         while (i < nBytes && ok) {
262                 dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes;
263                 dev->checkpointSum += *dataBytes;
264                 dev->checkpointXor ^= *dataBytes;
265
266                 dev->checkpointByteOffset++;
267                 i++;
268                 dataBytes++;
269                 dev->checkpointByteCount++;
270
271
272                 if (dev->checkpointByteOffset < 0 ||
273                    dev->checkpointByteOffset >= dev->nDataBytesPerChunk)
274                         ok = yaffs_CheckpointFlushBuffer(dev);
275         }
276
277         return i;
278 }
279
280 int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes)
281 {
282         int i = 0;
283         int ok = 1;
284         yaffs_ExtendedTags tags;
285
286
287         int chunk;
288         int realignedChunk;
289
290         __u8 *dataBytes = (__u8 *)data;
291
292         if (!dev->checkpointBuffer)
293                 return 0;
294
295         if (dev->checkpointOpenForWrite)
296                 return -1;
297
298         while (i < nBytes && ok) {
299
300
301                 if (dev->checkpointByteOffset < 0 ||
302                         dev->checkpointByteOffset >= dev->nDataBytesPerChunk) {
303
304                         if (dev->checkpointCurrentBlock < 0) {
305                                 yaffs_CheckpointFindNextCheckpointBlock(dev);
306                                 dev->checkpointCurrentChunk = 0;
307                         }
308
309                         if (dev->checkpointCurrentBlock < 0)
310                                 ok = 0;
311                         else {
312                                 chunk = dev->checkpointCurrentBlock *
313                                         dev->nChunksPerBlock +
314                                         dev->checkpointCurrentChunk;
315
316                                 realignedChunk = chunk - dev->chunkOffset;
317                                 
318                                 dev->nPageReads++;
319
320                                 /* read in the next chunk */
321                                 /* printf("read checkpoint page %d\n",dev->checkpointPage); */
322                                 dev->readChunkWithTagsFromNAND(dev,
323                                                 realignedChunk,
324                                                 dev->checkpointBuffer,
325                                                 &tags);
326
327                                 if (tags.chunkId != (dev->checkpointPageSequence + 1) ||
328                                         tags.eccResult > YAFFS_ECC_RESULT_FIXED ||
329                                         tags.sequenceNumber != YAFFS_SEQUENCE_CHECKPOINT_DATA)
330                                         ok = 0;
331
332                                 dev->checkpointByteOffset = 0;
333                                 dev->checkpointPageSequence++;
334                                 dev->checkpointCurrentChunk++;
335
336                                 if (dev->checkpointCurrentChunk >= dev->nChunksPerBlock)
337                                         dev->checkpointCurrentBlock = -1;
338                         }
339                 }
340
341                 if (ok) {
342                         *dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset];
343                         dev->checkpointSum += *dataBytes;
344                         dev->checkpointXor ^= *dataBytes;
345                         dev->checkpointByteOffset++;
346                         i++;
347                         dataBytes++;
348                         dev->checkpointByteCount++;
349                 }
350         }
351
352         return  i;
353 }
354
355 int yaffs_CheckpointClose(yaffs_Device *dev)
356 {
357
358         if (dev->checkpointOpenForWrite) {
359                 if (dev->checkpointByteOffset != 0)
360                         yaffs_CheckpointFlushBuffer(dev);
361         } else {
362                 int i;
363                 for (i = 0; i < dev->blocksInCheckpoint && dev->checkpointBlockList[i] >= 0; i++) {
364                         yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, dev->checkpointBlockList[i]);
365                         if (bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
366                                 bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT;
367                         else {
368                                 /* Todo this looks odd... */
369                         }
370                 }
371                 YFREE(dev->checkpointBlockList);
372                 dev->checkpointBlockList = NULL;
373         }
374
375         dev->nFreeChunks -= dev->blocksInCheckpoint * dev->nChunksPerBlock;
376         dev->nErasedBlocks -= dev->blocksInCheckpoint;
377
378
379         T(YAFFS_TRACE_CHECKPOINT, (TSTR("checkpoint byte count %d" TENDSTR),
380                         dev->checkpointByteCount));
381
382         if (dev->checkpointBuffer) {
383                 /* free the buffer */
384                 YFREE(dev->checkpointBuffer);
385                 dev->checkpointBuffer = NULL;
386                 return 1;
387         } else
388                 return 0;
389 }
390
391 int yaffs_CheckpointInvalidateStream(yaffs_Device *dev)
392 {
393         /* Erase the first checksum block */
394
395         T(YAFFS_TRACE_CHECKPOINT, (TSTR("checkpoint invalidate"TENDSTR)));
396
397         if (!yaffs_CheckpointSpaceOk(dev))
398                 return 0;
399
400         return yaffs_CheckpointErase(dev);
401 }
402
403
404