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