2b14006b71517fe2c5dc144fabb7af6e112aab5a
[yaffs2.git] / yaffs_checkptrw.c
1 /*
2  * YAFFS: Yet another FFS. A NAND-flash specific file system. 
3  *
4  * Copyright (C) 2002 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
15 const char *yaffs_checkptrw_c_version =
16     "$Id: yaffs_checkptrw.c,v 1.9 2006-11-09 23:57:07 charles Exp $";
17
18
19 #include "yaffs_checkptrw.h"
20
21
22 static int yaffs_CheckpointSpaceOk(yaffs_Device *dev)
23 {
24
25         int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks;
26         
27         T(YAFFS_TRACE_CHECKPOINT,
28                 (TSTR("checkpt blocks available = %d" TENDSTR),
29                 blocksAvailable));
30                 
31         
32         return (blocksAvailable <= 0) ? 0 : 1;
33 }
34
35
36
37 static int yaffs_CheckpointErase(yaffs_Device *dev)
38 {
39         
40         int i;
41         
42
43         if(!dev->eraseBlockInNAND)      
44                 return 0;
45         T(YAFFS_TRACE_CHECKPOINT,(TSTR("checking blocks %d to %d"TENDSTR),
46                 dev->internalStartBlock,dev->internalEndBlock));
47                 
48         for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) {
49                 yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i);
50                 if(bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT){
51                         T(YAFFS_TRACE_CHECKPOINT,(TSTR("erasing checkpt block %d"TENDSTR),i));
52                         if(dev->eraseBlockInNAND(dev,i)){
53                                 bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
54                                 dev->nErasedBlocks++;
55                                 dev->nFreeChunks += dev->nChunksPerBlock;
56                         }
57                         else {
58                                 dev->markNANDBlockBad(dev,i);
59                                 bi->blockState = YAFFS_BLOCK_STATE_DEAD;
60                         }
61                 }
62         }
63         
64         dev->blocksInCheckpoint = 0;
65         
66         return 1;
67 }
68
69
70 static void yaffs_CheckpointFindNextErasedBlock(yaffs_Device *dev)
71 {
72         int  i;
73         int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks;
74         T(YAFFS_TRACE_CHECKPOINT,
75                 (TSTR("allocating checkpt block: erased %d reserved %d avail %d next %d "TENDSTR),
76                 dev->nErasedBlocks,dev->nReservedBlocks,blocksAvailable,dev->checkpointNextBlock));
77                 
78         if(dev->checkpointNextBlock >= 0 &&
79            dev->checkpointNextBlock <= dev->internalEndBlock &&
80            blocksAvailable > 0){
81         
82                 for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){
83                         yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i);
84                         if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY){
85                                 dev->checkpointNextBlock = i + 1;
86                                 dev->checkpointCurrentBlock = i;
87                                 T(YAFFS_TRACE_CHECKPOINT,(TSTR("allocating checkpt block %d"TENDSTR),i));
88                                 return;
89                         }
90                 }
91         }
92         T(YAFFS_TRACE_CHECKPOINT,(TSTR("out of checkpt blocks"TENDSTR)));
93         
94         dev->checkpointNextBlock = -1;
95         dev->checkpointCurrentBlock = -1;
96 }
97
98 static void yaffs_CheckpointFindNextCheckpointBlock(yaffs_Device *dev)
99 {
100         int  i;
101         yaffs_ExtendedTags tags;
102         
103         if(dev->blocksInCheckpoint < dev->checkpointMaxBlocks) 
104                 for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){
105                         int chunk = i * dev->nChunksPerBlock;
106
107                         dev->readChunkWithTagsFromNAND(dev,chunk,NULL,&tags);
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->nDataBytesPerChunk);
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->checkpointCurrentBlock = -1;
152         dev->checkpointCurrentChunk = -1;
153         dev->checkpointNextBlock = dev->internalStartBlock;
154         
155         /* Erase all the blocks in the checkpoint area */
156         if(forWriting){
157                 memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk);
158                 dev->checkpointByteOffset = 0;
159                 return yaffs_CheckpointErase(dev);
160                 
161                 
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 static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev)
179 {
180
181         int chunk;
182
183         yaffs_ExtendedTags tags;
184         
185         if(dev->checkpointCurrentBlock < 0){
186                 yaffs_CheckpointFindNextErasedBlock(dev);
187                 dev->checkpointCurrentChunk = 0;
188         }
189         
190         if(dev->checkpointCurrentBlock < 0)
191                 return 0;
192         
193         tags.chunkDeleted = 0;
194         tags.objectId = dev->checkpointNextBlock; /* Hint to next place to look */
195         tags.chunkId = dev->checkpointPageSequence + 1;
196         tags.sequenceNumber =  YAFFS_SEQUENCE_CHECKPOINT_DATA;
197         tags.byteCount = dev->nDataBytesPerChunk;
198         if(dev->checkpointCurrentChunk == 0){
199                 /* First chunk we write for the block? Set block state to
200                    checkpoint */
201                 yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointCurrentBlock);
202                 bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT;
203                 dev->blocksInCheckpoint++;
204         }
205         
206         chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + dev->checkpointCurrentChunk;
207         
208         T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint wite buffer nand %d(%d:%d) objid %d chId %d" TENDSTR),
209                 chunk, dev->checkpointCurrentBlock, dev->checkpointCurrentChunk,tags.objectId,tags.chunkId)); 
210
211         dev->writeChunkWithTagsToNAND(dev,chunk,dev->checkpointBuffer,&tags);
212         dev->checkpointByteOffset = 0;
213         dev->checkpointPageSequence++;     
214         dev->checkpointCurrentChunk++;
215         if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock){
216                 dev->checkpointCurrentChunk = 0;
217                 dev->checkpointCurrentBlock = -1;
218         }
219         memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk);
220         
221         return 1;
222 }
223
224
225 int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes)
226 {
227         int i=0;
228         int ok = 1;
229
230         
231         __u8 * dataBytes = (__u8 *)data;
232         
233         
234
235         if(!dev->checkpointBuffer)
236                 return 0;
237
238         while(i < nBytes && ok) {
239                 
240
241                 
242                  dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes ;
243                 dev->checkpointByteOffset++;
244                 i++;
245                 dataBytes++;
246                 dev->checkpointByteCount++;
247                 
248                 
249                 if(dev->checkpointByteOffset < 0 ||
250                    dev->checkpointByteOffset >= dev->nDataBytesPerChunk) 
251                         ok = yaffs_CheckpointFlushBuffer(dev);
252
253         }
254         
255         return  i;
256 }
257
258 int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes)
259 {
260         int i=0;
261         int ok = 1;
262         yaffs_ExtendedTags tags;
263
264         
265         int chunk;
266
267         __u8 *dataBytes = (__u8 *)data;
268                 
269         if(!dev->checkpointBuffer)
270                 return 0;
271
272         while(i < nBytes && ok) {
273         
274         
275                 if(dev->checkpointByteOffset < 0 ||
276                    dev->checkpointByteOffset >= dev->nDataBytesPerChunk) {
277                    
278                         if(dev->checkpointCurrentBlock < 0){
279                                 yaffs_CheckpointFindNextCheckpointBlock(dev);
280                                 dev->checkpointCurrentChunk = 0;
281                         }
282                         
283                         if(dev->checkpointCurrentBlock < 0)
284                                 ok = 0;
285                         else {
286                         
287                                 chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + 
288                                           dev->checkpointCurrentChunk;
289
290                                 /* read in the next chunk */
291                                 /* printf("read checkpoint page %d\n",dev->checkpointPage); */
292                                 dev->readChunkWithTagsFromNAND(dev, chunk, 
293                                                                dev->checkpointBuffer,
294                                                               &tags);
295                                                       
296                                 if(tags.chunkId != (dev->checkpointPageSequence + 1) ||
297                                    tags.sequenceNumber != YAFFS_SEQUENCE_CHECKPOINT_DATA)
298                                    ok = 0;
299
300                                 dev->checkpointByteOffset = 0;
301                                 dev->checkpointPageSequence++;
302                                 dev->checkpointCurrentChunk++;
303                         
304                                 if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock)
305                                         dev->checkpointCurrentBlock = -1;
306                         }
307                 }
308                 
309                 if(ok){
310                         *dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset];
311                         dev->checkpointByteOffset++;
312                         i++;
313                         dataBytes++;
314                         dev->checkpointByteCount++;
315                 }
316         }
317         
318         return  i;
319 }
320
321 int yaffs_CheckpointClose(yaffs_Device *dev)
322 {
323
324         if(dev->checkpointOpenForWrite){        
325                 if(dev->checkpointByteOffset != 0)
326                         yaffs_CheckpointFlushBuffer(dev);
327         } else {
328                 int i;
329                 for(i = 0; i < dev->blocksInCheckpoint && dev->checkpointBlockList[i] >= 0; i++){
330                         yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointBlockList[i]);
331                         if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
332                                 bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT;
333                         else {
334                                 // Todo this looks odd...
335                         }
336                 }
337                 YFREE(dev->checkpointBlockList);
338                 dev->checkpointBlockList = NULL;
339         }
340
341         dev->nFreeChunks -= dev->blocksInCheckpoint * dev->nChunksPerBlock;
342         dev->nErasedBlocks -= dev->blocksInCheckpoint;
343
344                 
345         T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint byte count %d" TENDSTR),
346                         dev->checkpointByteCount));
347                         
348         if(dev->checkpointBuffer){
349                 /* free the buffer */   
350                 YFREE(dev->checkpointBuffer);
351                 dev->checkpointBuffer = NULL;
352                 return 1;
353         }
354         else
355                 return 0;
356         
357 }
358
359 int yaffs_CheckpointInvalidateStream(yaffs_Device *dev)
360 {
361         /* Erase the first checksum block */
362
363         T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint invalidate"TENDSTR)));
364
365         if(!yaffs_CheckpointSpaceOk(dev))
366                 return 0;
367
368         return yaffs_CheckpointErase(dev);
369 }
370
371
372