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