Fix some checkpoint stuff, add qsort
[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.3 2006-05-21 09:39:12 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->startBlock,dev->endBlock));
47                 
48         for(i = dev->startBlock; i <= dev->endBlock; i++) {
49                 yaffs_BlockInfo *bi = &dev->blockInfo[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                 
75         if(dev->checkpointNextBlock >= 0 &&
76            dev->checkpointNextBlock <= dev->endBlock &&
77            blocksAvailable > 0){
78         
79                 for(i = dev->checkpointNextBlock; i <= dev->endBlock; i++){
80                         yaffs_BlockInfo *bi = &dev->blockInfo[i];
81                         if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY){
82                                 dev->checkpointNextBlock = i + 1;
83                                 dev->nErasedBlocks--;
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         if(dev->blocksInCheckpoint < dev->checkpointMaxBlocks) 
102                 for(i = dev->checkpointNextBlock; i <= dev->endBlock; i++){
103                         int chunk = i * dev->nChunksPerBlock;
104
105                         dev->readChunkWithTagsFromNAND(dev,chunk,NULL,&tags);
106                                                       
107                         if(tags.sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA){
108                                 /* Right kind of block */
109                                 dev->checkpointNextBlock = tags.objectId;
110                                 dev->checkpointCurrentBlock = i;
111                                 dev->checkpointBlockList[dev->blocksInCheckpoint] = i;
112                                 dev->blocksInCheckpoint++;
113                                 T(YAFFS_TRACE_CHECKPOINT,(TSTR("found checkpt block %d"TENDSTR),i));
114                                 return;
115                         }
116                 }
117
118         T(YAFFS_TRACE_CHECKPOINT,(TSTR("found no more checkpt blocks"TENDSTR)));
119
120         dev->checkpointNextBlock = -1;
121         dev->checkpointCurrentBlock = -1;
122 }
123
124
125 int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting)
126 {
127         
128         /* Got the functions we need? */
129         if (!dev->writeChunkWithTagsToNAND ||
130             !dev->readChunkWithTagsFromNAND ||
131             !dev->eraseBlockInNAND ||
132             !dev->markNANDBlockBad)
133                 return 0;
134
135         if(forWriting && !yaffs_CheckpointSpaceOk(dev))
136                 return 0;
137                         
138         if(!dev->checkpointBuffer)
139                 dev->checkpointBuffer = YMALLOC(dev->nBytesPerChunk);
140         if(!dev->checkpointBuffer)
141                 return 0;
142
143         
144         dev->checkpointPageSequence = 0;
145         
146         dev->checkpointOpenForWrite = forWriting;
147         
148         dev->checkpointByteCount = 0;
149         dev->checkpointCurrentBlock = -1;
150         dev->checkpointCurrentChunk = -1;
151         dev->checkpointNextBlock = dev->startBlock;
152         
153         /* Erase all the blocks in the checkpoint area */
154         if(forWriting){
155                 memset(dev->checkpointBuffer,0,dev->nBytesPerChunk);
156                 dev->checkpointByteOffset = 0;
157                 return yaffs_CheckpointErase(dev);
158                 
159                 
160         } else {
161                 int i;
162                 /* Set to a value that will kick off a read */
163                 dev->checkpointByteOffset = dev->nBytesPerChunk;
164                 /* A checkpoint block list of 1 checkpoint block per 16 block is (hopefully)
165                  * going to be way more than we need */
166                 dev->blocksInCheckpoint = 0;
167                 dev->checkpointMaxBlocks = (dev->endBlock - dev->startBlock)/16 + 2;
168                 dev->checkpointBlockList = YMALLOC(sizeof(int) * dev->checkpointMaxBlocks);
169                 for(i = 0; i < dev->checkpointMaxBlocks; i++)
170                         dev->checkpointBlockList[i] = -1;
171         }
172         
173         return 1;
174 }
175
176 static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev)
177 {
178
179         int chunk;
180
181         yaffs_ExtendedTags tags;
182         
183         if(dev->checkpointCurrentBlock < 0){
184                 yaffs_CheckpointFindNextErasedBlock(dev);
185                 dev->checkpointCurrentChunk = 0;
186         }
187         
188         if(dev->checkpointCurrentBlock < 0)
189                 return 0;
190         
191         tags.chunkDeleted = 0;
192         tags.objectId = dev->checkpointNextBlock; /* Hint to next place to look */
193         tags.chunkId = dev->checkpointPageSequence + 1;
194         tags.sequenceNumber =  YAFFS_SEQUENCE_CHECKPOINT_DATA;
195         tags.byteCount = dev->nBytesPerChunk;
196         
197         chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + dev->checkpointCurrentChunk;
198                 
199         dev->writeChunkWithTagsToNAND(dev,chunk,dev->checkpointBuffer,&tags);
200         dev->checkpointByteOffset = 0;
201         dev->checkpointPageSequence++;     
202         dev->checkpointCurrentChunk++;
203         if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock){
204                 dev->checkpointCurrentChunk = 0;
205                 dev->checkpointCurrentBlock = -1;
206         }
207         memset(dev->checkpointBuffer,0,dev->nBytesPerChunk);
208         
209         return 1;
210 }
211
212
213 int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes)
214 {
215         int i=0;
216         int ok = 1;
217
218         
219         __u8 * dataBytes = (__u8 *)data;
220         
221         
222
223         if(!dev->checkpointBuffer)
224                 return 0;
225
226         while(i < nBytes && ok) {
227                 
228
229                 
230                  dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes ;
231                 dev->checkpointByteOffset++;
232                 i++;
233                 dataBytes++;
234                 dev->checkpointByteCount++;
235                 
236                 
237                 if(dev->checkpointByteOffset < 0 ||
238                    dev->checkpointByteOffset >= dev->nBytesPerChunk) 
239                         ok = yaffs_CheckpointFlushBuffer(dev);
240
241         }
242         
243         return  i;
244 }
245
246 int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes)
247 {
248         int i=0;
249         int ok = 1;
250         yaffs_ExtendedTags tags;
251
252         
253         int chunk;
254
255         __u8 *dataBytes = (__u8 *)data;
256                 
257         if(!dev->checkpointBuffer)
258                 return 0;
259
260         while(i < nBytes && ok) {
261         
262         
263                 if(dev->checkpointByteOffset < 0 ||
264                    dev->checkpointByteOffset >= dev->nBytesPerChunk) {
265                    
266                         if(dev->checkpointCurrentBlock < 0){
267                                 yaffs_CheckpointFindNextCheckpointBlock(dev);
268                                 dev->checkpointCurrentChunk = 0;
269                         }
270                         
271                         if(dev->checkpointCurrentBlock < 0)
272                                 ok = 0;
273                         else {
274                         
275                                 chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + 
276                                           dev->checkpointCurrentChunk;
277
278                                 /* read in the next chunk */
279                                 /* printf("read checkpoint page %d\n",dev->checkpointPage); */
280                                 dev->readChunkWithTagsFromNAND(dev, chunk, 
281                                                                dev->checkpointBuffer,
282                                                               &tags);
283                                                       
284                                 if(tags.chunkId != (dev->checkpointPageSequence + 1) ||
285                                    tags.sequenceNumber != YAFFS_SEQUENCE_CHECKPOINT_DATA)
286                                    ok = 0;
287
288                                 dev->checkpointByteOffset = 0;
289                                 dev->checkpointPageSequence++;
290                                 dev->checkpointCurrentChunk++;
291                         
292                                 if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock)
293                                         dev->checkpointCurrentBlock = -1;
294                         }
295                 }
296                 
297                 if(ok){
298                         *dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset];
299                         dev->checkpointByteOffset++;
300                         i++;
301                         dataBytes++;
302                         dev->checkpointByteCount++;
303                 }
304         }
305         
306         return  i;
307 }
308
309 int yaffs_CheckpointClose(yaffs_Device *dev)
310 {
311
312         if(dev->checkpointOpenForWrite){        
313                 if(dev->checkpointByteOffset != 0)
314                         yaffs_CheckpointFlushBuffer(dev);
315         } else {
316                 int i;
317                 for(i = 0; i < dev->blocksInCheckpoint && dev->checkpointBlockList[i] >= 0; i++){
318                         yaffs_BlockInfo *bi = &dev->blockInfo[dev->checkpointBlockList[i]];
319                         if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
320                                 bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT;
321                         else {
322                                 // Todo this looks odd...
323                         }
324                 }
325                 YFREE(dev->checkpointBlockList);
326                 dev->checkpointBlockList = NULL;
327         }
328
329         dev->nFreeChunks -= dev->blocksInCheckpoint * dev->nChunksPerBlock;
330         dev->nErasedBlocks -= dev->blocksInCheckpoint;
331
332                 
333         T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint byte count %d" TENDSTR),
334                         dev->checkpointByteCount));
335                         
336         if(dev->checkpointBuffer){
337                 /* free the buffer */   
338                 YFREE(dev->checkpointBuffer);
339                 dev->checkpointBuffer = NULL;
340                 return 1;
341         }
342         else
343                 return 0;
344         
345 }
346
347 int yaffs_CheckpointInvalidateStream(yaffs_Device *dev)
348 {
349         /* Erase the first checksum block */
350
351         T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint invalidate"TENDSTR)));
352
353         if(!yaffs_CheckpointSpaceOk(dev))
354                 return 0;
355
356         return yaffs_CheckpointErase(dev);
357 }
358
359
360