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