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