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