Home | History | Annotate | Download | only in yaffs2
      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