More checkpoint logging
[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.8 2006-11-09 19:55:24 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         dev->writeChunkWithTagsToNAND(dev,chunk,dev->checkpointBuffer,&tags);
209         dev->checkpointByteOffset = 0;
210         dev->checkpointPageSequence++;     
211         dev->checkpointCurrentChunk++;
212         if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock){
213                 dev->checkpointCurrentChunk = 0;
214                 dev->checkpointCurrentBlock = -1;
215         }
216         memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk);
217         
218         return 1;
219 }
220
221
222 int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes)
223 {
224         int i=0;
225         int ok = 1;
226
227         
228         __u8 * dataBytes = (__u8 *)data;
229         
230         
231
232         if(!dev->checkpointBuffer)
233                 return 0;
234
235         while(i < nBytes && ok) {
236                 
237
238                 
239                  dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes ;
240                 dev->checkpointByteOffset++;
241                 i++;
242                 dataBytes++;
243                 dev->checkpointByteCount++;
244                 
245                 
246                 if(dev->checkpointByteOffset < 0 ||
247                    dev->checkpointByteOffset >= dev->nDataBytesPerChunk) 
248                         ok = yaffs_CheckpointFlushBuffer(dev);
249
250         }
251         
252         return  i;
253 }
254
255 int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes)
256 {
257         int i=0;
258         int ok = 1;
259         yaffs_ExtendedTags tags;
260
261         
262         int chunk;
263
264         __u8 *dataBytes = (__u8 *)data;
265                 
266         if(!dev->checkpointBuffer)
267                 return 0;
268
269         while(i < nBytes && ok) {
270         
271         
272                 if(dev->checkpointByteOffset < 0 ||
273                    dev->checkpointByteOffset >= dev->nDataBytesPerChunk) {
274                    
275                         if(dev->checkpointCurrentBlock < 0){
276                                 yaffs_CheckpointFindNextCheckpointBlock(dev);
277                                 dev->checkpointCurrentChunk = 0;
278                         }
279                         
280                         if(dev->checkpointCurrentBlock < 0)
281                                 ok = 0;
282                         else {
283                         
284                                 chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + 
285                                           dev->checkpointCurrentChunk;
286
287                                 /* read in the next chunk */
288                                 /* printf("read checkpoint page %d\n",dev->checkpointPage); */
289                                 dev->readChunkWithTagsFromNAND(dev, chunk, 
290                                                                dev->checkpointBuffer,
291                                                               &tags);
292                                                       
293                                 if(tags.chunkId != (dev->checkpointPageSequence + 1) ||
294                                    tags.sequenceNumber != YAFFS_SEQUENCE_CHECKPOINT_DATA)
295                                    ok = 0;
296
297                                 dev->checkpointByteOffset = 0;
298                                 dev->checkpointPageSequence++;
299                                 dev->checkpointCurrentChunk++;
300                         
301                                 if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock)
302                                         dev->checkpointCurrentBlock = -1;
303                         }
304                 }
305                 
306                 if(ok){
307                         *dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset];
308                         dev->checkpointByteOffset++;
309                         i++;
310                         dataBytes++;
311                         dev->checkpointByteCount++;
312                 }
313         }
314         
315         return  i;
316 }
317
318 int yaffs_CheckpointClose(yaffs_Device *dev)
319 {
320
321         if(dev->checkpointOpenForWrite){        
322                 if(dev->checkpointByteOffset != 0)
323                         yaffs_CheckpointFlushBuffer(dev);
324         } else {
325                 int i;
326                 for(i = 0; i < dev->blocksInCheckpoint && dev->checkpointBlockList[i] >= 0; i++){
327                         yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointBlockList[i]);
328                         if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
329                                 bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT;
330                         else {
331                                 // Todo this looks odd...
332                         }
333                 }
334                 YFREE(dev->checkpointBlockList);
335                 dev->checkpointBlockList = NULL;
336         }
337
338         dev->nFreeChunks -= dev->blocksInCheckpoint * dev->nChunksPerBlock;
339         dev->nErasedBlocks -= dev->blocksInCheckpoint;
340
341                 
342         T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint byte count %d" TENDSTR),
343                         dev->checkpointByteCount));
344                         
345         if(dev->checkpointBuffer){
346                 /* free the buffer */   
347                 YFREE(dev->checkpointBuffer);
348                 dev->checkpointBuffer = NULL;
349                 return 1;
350         }
351         else
352                 return 0;
353         
354 }
355
356 int yaffs_CheckpointInvalidateStream(yaffs_Device *dev)
357 {
358         /* Erase the first checksum block */
359
360         T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint invalidate"TENDSTR)));
361
362         if(!yaffs_CheckpointSpaceOk(dev))
363                 return 0;
364
365         return yaffs_CheckpointErase(dev);
366 }
367
368
369