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 const char *yaffs_checkptrw_c_version = 15 "$Id: yaffs_checkptrw.c,v 1.14 2007/05/15 20:07:40 charles Exp $"; 16 17 18 #include "yaffs_checkptrw.h" 19 20 21 static int yaffs_CheckpointSpaceOk(yaffs_Device *dev) 22 { 23 24 int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks; 25 26 T(YAFFS_TRACE_CHECKPOINT, 27 (TSTR("checkpt blocks available = %d" TENDSTR), 28 blocksAvailable)); 29 30 31 return (blocksAvailable <= 0) ? 0 : 1; 32 } 33 34 35 static int yaffs_CheckpointErase(yaffs_Device *dev) 36 { 37 38 int i; 39 40 41 if(!dev->eraseBlockInNAND) 42 return 0; 43 T(YAFFS_TRACE_CHECKPOINT,(TSTR("checking blocks %d to %d"TENDSTR), 44 dev->internalStartBlock,dev->internalEndBlock)); 45 46 for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) { 47 yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); 48 if(bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT){ 49 T(YAFFS_TRACE_CHECKPOINT,(TSTR("erasing checkpt block %d"TENDSTR),i)); 50 if(dev->eraseBlockInNAND(dev,i- dev->blockOffset /* realign */)){ 51 bi->blockState = YAFFS_BLOCK_STATE_EMPTY; 52 dev->nErasedBlocks++; 53 dev->nFreeChunks += dev->nChunksPerBlock; 54 } 55 else { 56 dev->markNANDBlockBad(dev,i); 57 bi->blockState = YAFFS_BLOCK_STATE_DEAD; 58 } 59 } 60 } 61 62 dev->blocksInCheckpoint = 0; 63 64 return 1; 65 } 66 67 68 static void yaffs_CheckpointFindNextErasedBlock(yaffs_Device *dev) 69 { 70 int i; 71 int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks; 72 T(YAFFS_TRACE_CHECKPOINT, 73 (TSTR("allocating checkpt block: erased %d reserved %d avail %d next %d "TENDSTR), 74 dev->nErasedBlocks,dev->nReservedBlocks,blocksAvailable,dev->checkpointNextBlock)); 75 76 if(dev->checkpointNextBlock >= 0 && 77 dev->checkpointNextBlock <= dev->internalEndBlock && 78 blocksAvailable > 0){ 79 80 for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){ 81 yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); 82 if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY){ 83 dev->checkpointNextBlock = i + 1; 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 T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: start: blocks %d next %d" TENDSTR), 102 dev->blocksInCheckpoint, dev->checkpointNextBlock)); 103 104 if(dev->blocksInCheckpoint < dev->checkpointMaxBlocks) 105 for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){ 106 int chunk = i * dev->nChunksPerBlock; 107 int realignedChunk = chunk - dev->chunkOffset; 108 109 dev->readChunkWithTagsFromNAND(dev,realignedChunk,NULL,&tags); 110 T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: search: block %d oid %d seq %d eccr %d" TENDSTR), 111 i, tags.objectId,tags.sequenceNumber,tags.eccResult)); 112 113 if(tags.sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA){ 114 /* Right kind of block */ 115 dev->checkpointNextBlock = tags.objectId; 116 dev->checkpointCurrentBlock = i; 117 dev->checkpointBlockList[dev->blocksInCheckpoint] = i; 118 dev->blocksInCheckpoint++; 119 T(YAFFS_TRACE_CHECKPOINT,(TSTR("found checkpt block %d"TENDSTR),i)); 120 return; 121 } 122 } 123 124 T(YAFFS_TRACE_CHECKPOINT,(TSTR("found no more checkpt blocks"TENDSTR))); 125 126 dev->checkpointNextBlock = -1; 127 dev->checkpointCurrentBlock = -1; 128 } 129 130 131 int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting) 132 { 133 134 /* Got the functions we need? */ 135 if (!dev->writeChunkWithTagsToNAND || 136 !dev->readChunkWithTagsFromNAND || 137 !dev->eraseBlockInNAND || 138 !dev->markNANDBlockBad) 139 return 0; 140 141 if(forWriting && !yaffs_CheckpointSpaceOk(dev)) 142 return 0; 143 144 if(!dev->checkpointBuffer) 145 dev->checkpointBuffer = YMALLOC_DMA(dev->nDataBytesPerChunk); 146 if(!dev->checkpointBuffer) 147 return 0; 148 149 150 dev->checkpointPageSequence = 0; 151 152 dev->checkpointOpenForWrite = forWriting; 153 154 dev->checkpointByteCount = 0; 155 dev->checkpointSum = 0; 156 dev->checkpointXor = 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 int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum) 185 { 186 __u32 compositeSum; 187 compositeSum = (dev->checkpointSum << 8) | (dev->checkpointXor & 0xFF); 188 *sum = compositeSum; 189 return 1; 190 } 191 192 static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev) 193 { 194 195 int chunk; 196 int realignedChunk; 197 198 yaffs_ExtendedTags tags; 199 200 if(dev->checkpointCurrentBlock < 0){ 201 yaffs_CheckpointFindNextErasedBlock(dev); 202 dev->checkpointCurrentChunk = 0; 203 } 204 205 if(dev->checkpointCurrentBlock < 0) 206 return 0; 207 208 tags.chunkDeleted = 0; 209 tags.objectId = dev->checkpointNextBlock; /* Hint to next place to look */ 210 tags.chunkId = dev->checkpointPageSequence + 1; 211 tags.sequenceNumber = YAFFS_SEQUENCE_CHECKPOINT_DATA; 212 tags.byteCount = dev->nDataBytesPerChunk; 213 if(dev->checkpointCurrentChunk == 0){ 214 /* First chunk we write for the block? Set block state to 215 checkpoint */ 216 yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointCurrentBlock); 217 bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT; 218 dev->blocksInCheckpoint++; 219 } 220 221 chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + dev->checkpointCurrentChunk; 222 223 224 T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint wite buffer nand %d(%d:%d) objid %d chId %d" TENDSTR), 225 chunk, dev->checkpointCurrentBlock, dev->checkpointCurrentChunk,tags.objectId,tags.chunkId)); 226 227 realignedChunk = chunk - dev->chunkOffset; 228 229 dev->writeChunkWithTagsToNAND(dev,realignedChunk,dev->checkpointBuffer,&tags); 230 dev->checkpointByteOffset = 0; 231 dev->checkpointPageSequence++; 232 dev->checkpointCurrentChunk++; 233 if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock){ 234 dev->checkpointCurrentChunk = 0; 235 dev->checkpointCurrentBlock = -1; 236 } 237 memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk); 238 239 return 1; 240 } 241 242 243 int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes) 244 { 245 int i=0; 246 int ok = 1; 247 248 249 __u8 * dataBytes = (__u8 *)data; 250 251 252 253 if(!dev->checkpointBuffer) 254 return 0; 255 256 if(!dev->checkpointOpenForWrite) 257 return -1; 258 259 while(i < nBytes && ok) { 260 261 262 263 dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes ; 264 dev->checkpointSum += *dataBytes; 265 dev->checkpointXor ^= *dataBytes; 266 267 dev->checkpointByteOffset++; 268 i++; 269 dataBytes++; 270 dev->checkpointByteCount++; 271 272 273 if(dev->checkpointByteOffset < 0 || 274 dev->checkpointByteOffset >= dev->nDataBytesPerChunk) 275 ok = yaffs_CheckpointFlushBuffer(dev); 276 277 } 278 279 return i; 280 } 281 282 int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes) 283 { 284 int i=0; 285 int ok = 1; 286 yaffs_ExtendedTags tags; 287 288 289 int chunk; 290 int realignedChunk; 291 292 __u8 *dataBytes = (__u8 *)data; 293 294 if(!dev->checkpointBuffer) 295 return 0; 296 297 if(dev->checkpointOpenForWrite) 298 return -1; 299 300 while(i < nBytes && ok) { 301 302 303 if(dev->checkpointByteOffset < 0 || 304 dev->checkpointByteOffset >= dev->nDataBytesPerChunk) { 305 306 if(dev->checkpointCurrentBlock < 0){ 307 yaffs_CheckpointFindNextCheckpointBlock(dev); 308 dev->checkpointCurrentChunk = 0; 309 } 310 311 if(dev->checkpointCurrentBlock < 0) 312 ok = 0; 313 else { 314 315 chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + 316 dev->checkpointCurrentChunk; 317 318 realignedChunk = chunk - dev->chunkOffset; 319 320 /* read in the next chunk */ 321 /* printf("read checkpoint page %d\n",dev->checkpointPage); */ 322 dev->readChunkWithTagsFromNAND(dev, realignedChunk, 323 dev->checkpointBuffer, 324 &tags); 325 326 if(tags.chunkId != (dev->checkpointPageSequence + 1) || 327 tags.sequenceNumber != YAFFS_SEQUENCE_CHECKPOINT_DATA) 328 ok = 0; 329 330 dev->checkpointByteOffset = 0; 331 dev->checkpointPageSequence++; 332 dev->checkpointCurrentChunk++; 333 334 if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock) 335 dev->checkpointCurrentBlock = -1; 336 } 337 } 338 339 if(ok){ 340 *dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset]; 341 dev->checkpointSum += *dataBytes; 342 dev->checkpointXor ^= *dataBytes; 343 dev->checkpointByteOffset++; 344 i++; 345 dataBytes++; 346 dev->checkpointByteCount++; 347 } 348 } 349 350 return i; 351 } 352 353 int yaffs_CheckpointClose(yaffs_Device *dev) 354 { 355 356 if(dev->checkpointOpenForWrite){ 357 if(dev->checkpointByteOffset != 0) 358 yaffs_CheckpointFlushBuffer(dev); 359 } else { 360 int i; 361 for(i = 0; i < dev->blocksInCheckpoint && dev->checkpointBlockList[i] >= 0; i++){ 362 yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointBlockList[i]); 363 if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY) 364 bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT; 365 else { 366 // Todo this looks odd... 367 } 368 } 369 YFREE(dev->checkpointBlockList); 370 dev->checkpointBlockList = NULL; 371 } 372 373 dev->nFreeChunks -= dev->blocksInCheckpoint * dev->nChunksPerBlock; 374 dev->nErasedBlocks -= dev->blocksInCheckpoint; 375 376 377 T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint byte count %d" TENDSTR), 378 dev->checkpointByteCount)); 379 380 if(dev->checkpointBuffer){ 381 /* free the buffer */ 382 YFREE(dev->checkpointBuffer); 383 dev->checkpointBuffer = NULL; 384 return 1; 385 } 386 else 387 return 0; 388 389 } 390 391 int yaffs_CheckpointInvalidateStream(yaffs_Device *dev) 392 { 393 /* Erase the first checksum block */ 394 395 T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint invalidate"TENDSTR))); 396 397 if(!yaffs_CheckpointSpaceOk(dev)) 398 return 0; 399 400 return yaffs_CheckpointErase(dev); 401 } 402 403 404 405