diff options
Diffstat (limited to 'fs/yaffs2/yaffs_checkptrw.c')
| -rw-r--r-- | fs/yaffs2/yaffs_checkptrw.c | 405 | 
1 files changed, 405 insertions, 0 deletions
| diff --git a/fs/yaffs2/yaffs_checkptrw.c b/fs/yaffs2/yaffs_checkptrw.c new file mode 100644 index 000000000..f97ba4b42 --- /dev/null +++ b/fs/yaffs2/yaffs_checkptrw.c @@ -0,0 +1,405 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + *   for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* XXX U-BOOT XXX */ +#include <common.h> +#include <malloc.h> + +const char *yaffs_checkptrw_c_version = +    "$Id: yaffs_checkptrw.c,v 1.14 2007/05/15 20:07:40 charles Exp $"; + + +#include "yaffs_checkptrw.h" + + +static int yaffs_CheckpointSpaceOk(yaffs_Device *dev) +{ + +	int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks; +	 +	T(YAFFS_TRACE_CHECKPOINT, +		(TSTR("checkpt blocks available = %d" TENDSTR), +		blocksAvailable)); +		 +	 +	return (blocksAvailable <= 0) ? 0 : 1; +} + + +static int yaffs_CheckpointErase(yaffs_Device *dev) +{ +	 +	int i; +	 + +	if(!dev->eraseBlockInNAND)	 +		return 0; +	T(YAFFS_TRACE_CHECKPOINT,(TSTR("checking blocks %d to %d"TENDSTR), +		dev->internalStartBlock,dev->internalEndBlock)); +		 +	for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) { +		yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); +		if(bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT){ +			T(YAFFS_TRACE_CHECKPOINT,(TSTR("erasing checkpt block %d"TENDSTR),i)); +			if(dev->eraseBlockInNAND(dev,i- dev->blockOffset /* realign */)){ +				bi->blockState = YAFFS_BLOCK_STATE_EMPTY; +				dev->nErasedBlocks++; +				dev->nFreeChunks += dev->nChunksPerBlock; +			} +			else { +				dev->markNANDBlockBad(dev,i); +				bi->blockState = YAFFS_BLOCK_STATE_DEAD; +			} +		} +	} +	 +	dev->blocksInCheckpoint = 0; +	 +	return 1; +} + + +static void yaffs_CheckpointFindNextErasedBlock(yaffs_Device *dev) +{ +	int  i; +	int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks; +	T(YAFFS_TRACE_CHECKPOINT, +		(TSTR("allocating checkpt block: erased %d reserved %d avail %d next %d "TENDSTR), +		dev->nErasedBlocks,dev->nReservedBlocks,blocksAvailable,dev->checkpointNextBlock)); +		 +	if(dev->checkpointNextBlock >= 0 && +	   dev->checkpointNextBlock <= dev->internalEndBlock && +	   blocksAvailable > 0){ +	 +		for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){ +			yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); +			if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY){ +				dev->checkpointNextBlock = i + 1; +				dev->checkpointCurrentBlock = i; +				T(YAFFS_TRACE_CHECKPOINT,(TSTR("allocating checkpt block %d"TENDSTR),i)); +				return; +			} +		} +	} +	T(YAFFS_TRACE_CHECKPOINT,(TSTR("out of checkpt blocks"TENDSTR))); +	 +	dev->checkpointNextBlock = -1; +	dev->checkpointCurrentBlock = -1; +} + +static void yaffs_CheckpointFindNextCheckpointBlock(yaffs_Device *dev) +{ +	int  i; +	yaffs_ExtendedTags tags; +	 +	T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: start:  blocks %d next %d" TENDSTR), +		dev->blocksInCheckpoint, dev->checkpointNextBlock)); +		 +	if(dev->blocksInCheckpoint < dev->checkpointMaxBlocks)  +		for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){ +			int chunk = i * dev->nChunksPerBlock; +			int realignedChunk = chunk - dev->chunkOffset; + +			dev->readChunkWithTagsFromNAND(dev,realignedChunk,NULL,&tags); +			T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: search: block %d oid %d seq %d eccr %d" TENDSTR),  +				i, tags.objectId,tags.sequenceNumber,tags.eccResult)); +						       +			if(tags.sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA){ +				/* Right kind of block */ +				dev->checkpointNextBlock = tags.objectId; +				dev->checkpointCurrentBlock = i; +				dev->checkpointBlockList[dev->blocksInCheckpoint] = i; +				dev->blocksInCheckpoint++; +				T(YAFFS_TRACE_CHECKPOINT,(TSTR("found checkpt block %d"TENDSTR),i)); +				return; +			} +		} + +	T(YAFFS_TRACE_CHECKPOINT,(TSTR("found no more checkpt blocks"TENDSTR))); + +	dev->checkpointNextBlock = -1; +	dev->checkpointCurrentBlock = -1; +} + + +int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting) +{ +	 +	/* Got the functions we need? */ +	if (!dev->writeChunkWithTagsToNAND || +	    !dev->readChunkWithTagsFromNAND || +	    !dev->eraseBlockInNAND || +	    !dev->markNANDBlockBad) +		return 0; + +	if(forWriting && !yaffs_CheckpointSpaceOk(dev)) +		return 0; +			 +	if(!dev->checkpointBuffer) +		dev->checkpointBuffer = YMALLOC_DMA(dev->nDataBytesPerChunk); +	if(!dev->checkpointBuffer) +		return 0; + +	 +	dev->checkpointPageSequence = 0; +	 +	dev->checkpointOpenForWrite = forWriting; +	 +	dev->checkpointByteCount = 0; +	dev->checkpointSum = 0; +	dev->checkpointXor = 0; +	dev->checkpointCurrentBlock = -1; +	dev->checkpointCurrentChunk = -1; +	dev->checkpointNextBlock = dev->internalStartBlock; +	 +	/* Erase all the blocks in the checkpoint area */ +	if(forWriting){ +		memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk); +		dev->checkpointByteOffset = 0; +		return yaffs_CheckpointErase(dev); +		 +		 +	} else { +		int i; +		/* Set to a value that will kick off a read */ +		dev->checkpointByteOffset = dev->nDataBytesPerChunk; +		/* A checkpoint block list of 1 checkpoint block per 16 block is (hopefully) +		 * going to be way more than we need */ +		dev->blocksInCheckpoint = 0; +		dev->checkpointMaxBlocks = (dev->internalEndBlock - dev->internalStartBlock)/16 + 2; +		dev->checkpointBlockList = YMALLOC(sizeof(int) * dev->checkpointMaxBlocks); +		for(i = 0; i < dev->checkpointMaxBlocks; i++) +			dev->checkpointBlockList[i] = -1; +	} +	 +	return 1; +} + +int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum) +{ +	__u32 compositeSum; +	compositeSum =  (dev->checkpointSum << 8) | (dev->checkpointXor & 0xFF); +	*sum = compositeSum; +	return 1; +} + +static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev) +{ + +	int chunk; +	int realignedChunk; + +	yaffs_ExtendedTags tags; +	 +	if(dev->checkpointCurrentBlock < 0){ +		yaffs_CheckpointFindNextErasedBlock(dev); +		dev->checkpointCurrentChunk = 0; +	} +	 +	if(dev->checkpointCurrentBlock < 0) +		return 0; +	 +	tags.chunkDeleted = 0; +	tags.objectId = dev->checkpointNextBlock; /* Hint to next place to look */ +	tags.chunkId = dev->checkpointPageSequence + 1; +	tags.sequenceNumber =  YAFFS_SEQUENCE_CHECKPOINT_DATA; +	tags.byteCount = dev->nDataBytesPerChunk; +	if(dev->checkpointCurrentChunk == 0){ +		/* First chunk we write for the block? Set block state to +		   checkpoint */ +		yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointCurrentBlock); +		bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT; +		dev->blocksInCheckpoint++; +	} +	 +	chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + dev->checkpointCurrentChunk; + +	 +	T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint wite buffer nand %d(%d:%d) objid %d chId %d" TENDSTR), +		chunk, dev->checkpointCurrentBlock, dev->checkpointCurrentChunk,tags.objectId,tags.chunkId));  +	 +	realignedChunk = chunk - dev->chunkOffset; +	 +	dev->writeChunkWithTagsToNAND(dev,realignedChunk,dev->checkpointBuffer,&tags); +	dev->checkpointByteOffset = 0; +	dev->checkpointPageSequence++;	    +	dev->checkpointCurrentChunk++; +	if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock){ +		dev->checkpointCurrentChunk = 0; +		dev->checkpointCurrentBlock = -1; +	} +	memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk); +	 +	return 1; +} + + +int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes) +{ +	int i=0; +	int ok = 1; + +	 +	__u8 * dataBytes = (__u8 *)data; +	 +	 + +	if(!dev->checkpointBuffer) +		return 0; +		 +	if(!dev->checkpointOpenForWrite) +		return -1; + +	while(i < nBytes && ok) { +		 + +		 +		dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes ; +		dev->checkpointSum += *dataBytes; +		dev->checkpointXor ^= *dataBytes; +		  +		dev->checkpointByteOffset++; +		i++; +		dataBytes++; +		dev->checkpointByteCount++; +		 +		 +		if(dev->checkpointByteOffset < 0 || +		   dev->checkpointByteOffset >= dev->nDataBytesPerChunk)  +			ok = yaffs_CheckpointFlushBuffer(dev); + +	} +	 +	return 	i; +} + +int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes) +{ +	int i=0; +	int ok = 1; +	yaffs_ExtendedTags tags; + +	 +	int chunk; +	int realignedChunk; + +	__u8 *dataBytes = (__u8 *)data; +		 +	if(!dev->checkpointBuffer) +		return 0; + +	if(dev->checkpointOpenForWrite) +		return -1; + +	while(i < nBytes && ok) { +	 +	 +		if(dev->checkpointByteOffset < 0 || +		   dev->checkpointByteOffset >= dev->nDataBytesPerChunk) { +		    +		   	if(dev->checkpointCurrentBlock < 0){ +				yaffs_CheckpointFindNextCheckpointBlock(dev); +				dev->checkpointCurrentChunk = 0; +			} +			 +			if(dev->checkpointCurrentBlock < 0) +				ok = 0; +			else { +			 +				chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock +  +				          dev->checkpointCurrentChunk; + +				realignedChunk = chunk - dev->chunkOffset; + +	   			/* read in the next chunk */ +	   			/* printf("read checkpoint page %d\n",dev->checkpointPage); */ +				dev->readChunkWithTagsFromNAND(dev, realignedChunk,  +							       dev->checkpointBuffer, +							      &tags); +						       +				if(tags.chunkId != (dev->checkpointPageSequence + 1) || +				   tags.sequenceNumber != YAFFS_SEQUENCE_CHECKPOINT_DATA) +				   ok = 0; + +				dev->checkpointByteOffset = 0; +				dev->checkpointPageSequence++; +				dev->checkpointCurrentChunk++; +			 +				if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock) +					dev->checkpointCurrentBlock = -1; +			} +		} +		 +		if(ok){ +			*dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset]; +			dev->checkpointSum += *dataBytes; +			dev->checkpointXor ^= *dataBytes; +			dev->checkpointByteOffset++; +			i++; +			dataBytes++; +			dev->checkpointByteCount++; +		} +	} +	 +	return 	i; +} + +int yaffs_CheckpointClose(yaffs_Device *dev) +{ + +	if(dev->checkpointOpenForWrite){	 +		if(dev->checkpointByteOffset != 0) +			yaffs_CheckpointFlushBuffer(dev); +	} else { +		int i; +		for(i = 0; i < dev->blocksInCheckpoint && dev->checkpointBlockList[i] >= 0; i++){ +			yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointBlockList[i]); +			if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY) +				bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT; +			else { +				// Todo this looks odd... +			} +		} +		YFREE(dev->checkpointBlockList); +		dev->checkpointBlockList = NULL; +	} + +	dev->nFreeChunks -= dev->blocksInCheckpoint * dev->nChunksPerBlock; +	dev->nErasedBlocks -= dev->blocksInCheckpoint; + +		 +	T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint byte count %d" TENDSTR), +			dev->checkpointByteCount)); +			 +	if(dev->checkpointBuffer){ +		/* free the buffer */	 +		YFREE(dev->checkpointBuffer); +		dev->checkpointBuffer = NULL; +		return 1; +	} +	else +		return 0; +	 +} + +int yaffs_CheckpointInvalidateStream(yaffs_Device *dev) +{ +	/* Erase the first checksum block */ + +	T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint invalidate"TENDSTR))); + +	if(!yaffs_CheckpointSpaceOk(dev)) +		return 0; + +	return yaffs_CheckpointErase(dev); +} |