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