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