*/
//yaffs_guts.c
-const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.16 2003-01-14 23:15:29 charles Exp $";
+const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.17 2003-01-17 04:19:08 charles Exp $";
#include "yportenv.h"
#include "yaffs_guts.h"
-#define YAFFS_GARBAGE_COLLECT_LOW_WATER 2
+#define YAFFS_PASSIVE_GC_CHUNKS 2
// External functions for ECC on data
void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare)
{
- if(chunkInNAND < dev->startBlock * YAFFS_CHUNKS_PER_BLOCK)
+ if(chunkInNAND < dev->startBlock * dev->nChunksPerBlock)
{
T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d is not valid" TENDSTR),chunkInNAND));
return YAFFS_FAIL;
spare.blockStatus = 0;
- yaffs_WriteChunkToNAND(dev, blockInNAND * YAFFS_CHUNKS_PER_BLOCK, NULL , &spare);
- yaffs_WriteChunkToNAND(dev, blockInNAND * YAFFS_CHUNKS_PER_BLOCK + 1, NULL , &spare);
+ // TODO change this retirement marking for other NAND types
+ yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock, NULL , &spare);
+ yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock + 1, NULL , &spare);
yaffs_GetBlockInfo(dev,blockInNAND)->blockState = YAFFS_BLOCK_STATE_DEAD;
dev->nRetiredBlocks++;
static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND)
{
- int blockInNAND = chunkInNAND/YAFFS_CHUNKS_PER_BLOCK;
+ int blockInNAND = chunkInNAND/dev->nChunksPerBlock;
// Mark the block for retirement
yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;
static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND)
{
- int blockInNAND = chunkInNAND/YAFFS_CHUNKS_PER_BLOCK;
+ int blockInNAND = chunkInNAND/dev->nChunksPerBlock;
// Mark the block for retirement
yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;
{
theChunk = (tn->level0[i] << in->myDev->chunkGroupBits);
- theBlock = yaffs_GetBlockInfo(in->myDev, theChunk/ YAFFS_CHUNKS_PER_BLOCK);
+ theBlock = yaffs_GetBlockInfo(in->myDev, theChunk/in->myDev->nChunksPerBlock);
if(theBlock)
{
theBlock->softDeletions++;
list_for_each(i,&dev->objectBucket[bucket].list)
{
// If there is already one in the list
- if(list_entry(i, yaffs_Object,hashLink)->objectId == n)
+ if(i && list_entry(i, yaffs_Object,hashLink)->objectId == n)
{
found = 0;
}
list_for_each(i,&dev->objectBucket[bucket].list)
{
// Look if it is in the list
- in = list_entry(i, yaffs_Object,hashLink);
- if(in->objectId == number)
+ if(i)
{
- return in;
+ in = list_entry(i, yaffs_Object,hashLink);
+ if(in->objectId == number)
+ {
+ return in;
+ }
}
}
// FindDiretiestBlock is used to select the dirtiest block (or close enough)
// for garbage collection.
-static int yaffs_FindDirtiestBlock(yaffs_Device *dev)
+static int yaffs_FindDirtiestBlock(yaffs_Device *dev,int aggressive)
{
int b = dev->currentDirtyChecker;
int i;
+ int iterations;
int dirtiest = -1;
- int pagesInUse = dev->nChunksPerBlock;
+ int pagesInUse;
yaffs_BlockInfo *bi;
+
+ // If we're doing aggressive GC then we are happy to take a less-dirty block, and
+ // search further.
+
+ pagesInUse = (aggressive)? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1;
+ if(aggressive)
+ {
+ iterations = dev->endBlock - dev->startBlock + 1;
+ }
+ else
+ {
+ iterations = iterations / 16;
+ if(iterations > 200)
+ {
+ iterations = 200;
+ }
+ }
- for(i = dev->startBlock; i <= dev->endBlock && pagesInUse > 2 ; i++)
+ for(i = 0; i <= iterations && pagesInUse > 0 ; i++)
{
b++;
if ( b < dev->startBlock || b > dev->endBlock)
{
bi = yaffs_GetBlockInfo(dev,dev->allocationBlock);
- retVal = (dev->allocationBlock * YAFFS_CHUNKS_PER_BLOCK) +
+ retVal = (dev->allocationBlock * dev->nChunksPerBlock) +
dev->allocationPage;
bi->pagesInUse++;
yaffs_SetChunkBit(dev,dev->allocationBlock,dev->allocationPage);
dev->nFreeChunks--;
// If the block is full set the state to full
- if(dev->allocationPage >= YAFFS_CHUNKS_PER_BLOCK)
+ if(dev->allocationPage >= dev->nChunksPerBlock)
{
bi->blockState = YAFFS_BLOCK_STATE_FULL;
dev->allocationBlock = -1;
// number of erased blocks.
// The cache is allowed to use reserved blocks.
-int yaffs_CheckSpaceForChunkCache(yaffs_Device *dev)
+static int yaffs_CheckSpaceForChunkCache(yaffs_Device *dev)
{
return (dev->nErasedBlocks >= YAFFS_RESERVED_BLOCKS);
}
-int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
+static int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
{
int oldChunk;
int newChunk;
yaffs_Spare spare;
yaffs_Tags tags;
- __u8 buffer[YAFFS_BYTES_PER_CHUNK];
+ __u8 buffer[YAFFS_BYTES_PER_CHUNK];
// yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,block);
//T(("Collecting block %d n %d bits %x\n",block, bi->pagesInUse, bi->pageBits));
- for(chunkInBlock = 0,oldChunk = block * YAFFS_CHUNKS_PER_BLOCK;
+ for(chunkInBlock = 0,oldChunk = block * dev->nChunksPerBlock;
chunkInBlock < dev->nChunksPerBlock && yaffs_StillSomeChunkBits(dev,block);
chunkInBlock++, oldChunk++ )
{
//Scan the unlinked files looking for one to delete
list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
{
- l = list_entry(i, yaffs_Object,siblings);
- if(l->deleted)
+ if(i)
{
- return l;
+ l = list_entry(i, yaffs_Object,siblings);
+ if(l->deleted)
+ {
+ return l;
+ }
}
}
return NULL;
}
-
+#if 0
+#define YAFFS_GARBAGE_COLLECT_LOW_WATER 2
static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
{
int block;
+ int aggressive=0;
//yaffs_DoUnlinkedFileDeletion(dev);
if(dev->nErasedBlocks <= (YAFFS_RESERVED_BLOCKS + YAFFS_GARBAGE_COLLECT_LOW_WATER))
{
- dev->garbageCollectionRequired = 1;
- }
+ aggressive = 1;
+ }
- if(dev->garbageCollectionRequired)
+ if(aggressive)
{
- dev->garbageCollections++;
- dev->garbageCollectionRequired = 0;
- if(dev->blockSelectedForGC >= 0)
- {
- block = dev->blockSelectedForGC;
- }
- else
- {
- block = yaffs_FindDirtiestBlock(dev);
- }
+ block = yaffs_FindDirtiestBlock(dev,aggressive);
if(block >= 0)
{
+ dev->garbageCollections++;
return yaffs_GarbageCollectBlock(dev,block);
}
else
return YAFFS_OK;
}
+#endif
+
+// New garbage collector
+// If we're very low on erased blocks then we do aggressive garbage collection
+// otherwise we do "passive" garbage collection.
+// Aggressive gc looks further (whole array) and will accept dirtier blocks.
+// Passive gc only inspects smaller areas and will only accept cleaner blocks.
+//
+// The idea is to help clear out space in a more spread-out manner.
+// Dunno if it really does anything useful.
+//
+static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
+{
+ int block;
+ int aggressive=0;
+
+ //yaffs_DoUnlinkedFileDeletion(dev);
+
+ if(dev->nErasedBlocks <= (YAFFS_RESERVED_BLOCKS + 1))
+ {
+ aggressive = 1;
+ }
+
+ block = yaffs_FindDirtiestBlock(dev,aggressive);
+
+ if(block >= 0)
+ {
+ dev->garbageCollections++;
+ if(!aggressive)
+ {
+ dev->passiveGarbageCollections++;
+ }
+
+ T(YAFFS_TRACE_GC,(TSTR("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR),dev->nErasedBlocks,aggressive));
+
+ return yaffs_GarbageCollectBlock(dev,block);
+ }
+
+ return aggressive ? YAFFS_FAIL : YAFFS_OK;
+}
//////////////////////////// TAGS ///////////////////////////////////////
objId = in->objectId;
fSize = in->variant.fileVariant.fileSize;
- nChunks = (fSize + YAFFS_BYTES_PER_CHUNK -1)/YAFFS_BYTES_PER_CHUNK;
+ nChunks = (fSize + in->myDev->nBytesPerChunk -1)/in->myDev->nBytesPerChunk;
for(chunk = 1; chunk <= nChunks; chunk++)
{
if(chunkId <= 0) return;
dev->nDeletions++;
- block = chunkId / YAFFS_CHUNKS_PER_BLOCK;
- page = chunkId % YAFFS_CHUNKS_PER_BLOCK;
+ block = chunkId / dev->nChunksPerBlock;
+ page = chunkId % dev->nChunksPerBlock;
if(markNAND)
{
#endif
}
- retVal = yaffs_UpdateObjectHeader(in,NULL,0);
+ retVal = (yaffs_UpdateObjectHeader(in,NULL,0) >= 0)? YAFFS_OK : YAFFS_FAIL;
}
else
{
{
yaffs_Spare spare;
- yaffs_ReadChunkFromNAND(dev,blk * YAFFS_CHUNKS_PER_BLOCK,NULL,&spare,1);
+ yaffs_ReadChunkFromNAND(dev,blk * dev->nChunksPerBlock,NULL,&spare,1);
#if 1
if(yaffs_CountBits(spare.blockStatus) < 7)
{
return 1;
}
#endif
- yaffs_ReadChunkFromNAND(dev,blk * YAFFS_CHUNKS_PER_BLOCK + 1,NULL,&spare,1);
+ yaffs_ReadChunkFromNAND(dev,blk * dev->nChunksPerBlock + 1,NULL,&spare,1);
#if 1
if(yaffs_CountBits(spare.blockStatus) < 7)
// Read each chunk in the block.
- for(c = 0; c < YAFFS_CHUNKS_PER_BLOCK &&
+ for(c = 0; c < dev->nChunksPerBlock &&
state == YAFFS_BLOCK_STATE_SCANNING; c++)
{
// Read the spare area and decide what to do
- chunk = blk * YAFFS_CHUNKS_PER_BLOCK + c;
+ chunk = blk * dev->nChunksPerBlock + c;
yaffs_ReadChunkFromNAND(dev,chunk,NULL,&spare,1);
dev->allocationPage = c;
}
- dev->nFreeChunks += (YAFFS_CHUNKS_PER_BLOCK - c);
+ dev->nFreeChunks += (dev->nChunksPerBlock - c);
}
else if(tags.chunkId > 0)
{
{
struct list_head *i;
+ struct list_head *n;
+
yaffs_Object *l;
// Soft delete all the unlinked files
- list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
+ list_for_each_safe(i,n,&dev->unlinkedDir->variant.directoryVariant.children)
{
- l = list_entry(i, yaffs_Object,siblings);
- if(l->deleted)
+ if(i)
{
- yaffs_SoftDeleteFile(l);
+ l = list_entry(i, yaffs_Object,siblings);
+ if(l->deleted)
+ {
+ yaffs_SoftDeleteFile(l);
+ }
}
}
}
list_for_each(i,&directory->variant.directoryVariant.children)
{
- l = list_entry(i, yaffs_Object,siblings);
-
- // Special case for lost-n-found
- if(l->objectId == YAFFS_OBJECTID_LOSTNFOUND)
+ if(i)
{
- if(yaffs_strcmp(name,YAFFS_LOSTNFOUND_NAME) == 0)
+ l = list_entry(i, yaffs_Object,siblings);
+
+ // Special case for lost-n-found
+ if(l->objectId == YAFFS_OBJECTID_LOSTNFOUND)
{
- return l;
+ if(yaffs_strcmp(name,YAFFS_LOSTNFOUND_NAME) == 0)
+ {
+ return l;
+ }
}
- }
- else if(yaffs_SumCompare(l->sum, sum))
- {
- // Do a real check
- yaffs_GetObjectName(l,buffer,YAFFS_MAX_NAME_LENGTH);
- if(yaffs_strcmp(name,buffer) == 0)
+ else if(yaffs_SumCompare(l->sum, sum))
{
- return l;
- }
-
+ // Do a real check
+ yaffs_GetObjectName(l,buffer,YAFFS_MAX_NAME_LENGTH);
+ if(yaffs_strcmp(name,buffer) == 0)
+ {
+ return l;
+ }
+ }
}
}
list_for_each(i,&theDir->variant.directoryVariant.children)
{
- l = list_entry(i, yaffs_Object,siblings);
- if(!fn(l))
+ if(i)
{
- return YAFFS_FAIL;
+ l = list_entry(i, yaffs_Object,siblings);
+ if(l && !fn(l))
+ {
+ return YAFFS_FAIL;
+ }
}
}
// Calculate chunkGroupBits.
// We need to find the next power of 2 > than endBlock
- x = YAFFS_CHUNKS_PER_BLOCK * (dev->endBlock+1);
+ x = dev->nChunksPerBlock * (dev->endBlock+1);
for(bits = extraBits = 0; x > 1; bits++)
{
// More device initialisation
- dev->garbageCollectionRequired = 0;
dev->garbageCollections = 0;
+ dev->passiveGarbageCollections = 0;
dev->currentDirtyChecker = 0;
dev->bufferedBlock = -1;
dev->doingBufferedBlockRewrite = 0;
- dev->blockSelectedForGC = -1;
dev->nDeletedFiles = 0;
dev->nBackgroundDeletions=0;
dev->nUnlinkedFiles = 0;
int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
{
- int nFree = dev->nFreeChunks - (YAFFS_CHUNKS_PER_BLOCK * YAFFS_RESERVED_BLOCKS);
+ int nFree = dev->nFreeChunks - (dev->nChunksPerBlock * YAFFS_RESERVED_BLOCKS);
struct list_head *i;
yaffs_Object *l;
// To the free chunks add the chunks that are in the deleted unlinked files.
list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
{
- l = list_entry(i, yaffs_Object,siblings);
- if(l->deleted)
+ if(i)
{
- pending++;
- pending += l->nDataChunks;
+ l = list_entry(i, yaffs_Object,siblings);
+ if(l->deleted)
+ {
+ pending++;
+ pending += l->nDataChunks;
+ }
}
}
*
*/
-const char *yaffs_mtdif_c_version = "$Id: yaffs_mtdif.c,v 1.6 2003-01-14 23:15:32 charles Exp $";
+const char *yaffs_mtdif_c_version = "$Id: yaffs_mtdif.c,v 1.7 2003-01-17 04:19:08 charles Exp $";
#ifdef CONFIG_YAFFS_MTD_ENABLED
size_t dummy;
int retval = 0;
- loff_t addr = ((loff_t)chunkInNAND) * YAFFS_BYTES_PER_CHUNK;
+ loff_t addr = ((loff_t)chunkInNAND) * dev->nBytesPerChunk;
__u8 *spareAsBytes = (__u8 *)spare;
if(data && spare)
{
if(dev->useNANDECC)
- mtd->write_ecc(mtd,addr,YAFFS_BYTES_PER_CHUNK,&dummy,data,spareAsBytes,NAND_YAFFS_OOB);
+ mtd->write_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,spareAsBytes,NAND_YAFFS_OOB);
else
- mtd->write_ecc(mtd,addr,YAFFS_BYTES_PER_CHUNK,&dummy,data,spareAsBytes,NAND_NONE_OOB);
+ mtd->write_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,spareAsBytes,NAND_NONE_OOB);
}
else
{
#endif
if(data)
- retval = mtd->write(mtd,addr,YAFFS_BYTES_PER_CHUNK,&dummy,data);
+ retval = mtd->write(mtd,addr,dev->nBytesPerChunk,&dummy,data);
if(spare)
retval = mtd->write_oob(mtd,addr,YAFFS_BYTES_PER_SPARE,&dummy,spareAsBytes);
#ifndef CONFIG_YAFFS_USE_OLD_MTD
size_t dummy;
int retval = 0;
- loff_t addr = ((loff_t)chunkInNAND) * YAFFS_BYTES_PER_CHUNK;
+ loff_t addr = ((loff_t)chunkInNAND) * dev->nBytesPerChunk;
__u8 *spareAsBytes = (__u8 *)spare;
if(dev->useNANDECC)
{
u8 tmpSpare[ YAFFS_BYTES_PER_SPARE + (2*sizeof(int)) ];
- retval = mtd->read_ecc(mtd,addr,YAFFS_BYTES_PER_CHUNK,&dummy,data,tmpSpare,NAND_YAFFS_OOB);
+ retval = mtd->read_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,tmpSpare,NAND_YAFFS_OOB);
memcpy(spareAsBytes, tmpSpare, YAFFS_BYTES_PER_SPARE);
}
else
{
- retval = mtd->read_ecc(mtd,addr,YAFFS_BYTES_PER_CHUNK,&dummy,data,spareAsBytes,NAND_NONE_OOB);
+ retval = mtd->read_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,spareAsBytes,NAND_NONE_OOB);
}
}
else
{
#endif
if(data)
- retval = mtd->read(mtd,addr,YAFFS_BYTES_PER_CHUNK,&dummy,data);
+ retval = mtd->read(mtd,addr,dev->nBytesPerChunk,&dummy,data);
if(spare)
retval = mtd->read_oob(mtd,addr,YAFFS_BYTES_PER_SPARE,&dummy,spareAsBytes);
#ifndef CONFIG_YAFFS_USE_OLD_MTD
int nandmtd_EraseBlockInNAND(yaffs_Device *dev, int blockNumber)
{
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
- __u32 addr = ((loff_t) blockNumber) * YAFFS_BYTES_PER_BLOCK;
+ __u32 addr = ((loff_t) blockNumber) * dev->nBytesPerChunk * dev->nChunksPerBlock;
struct erase_info ei;
int retval = 0;
ei.mtd = mtd;
ei.addr = addr;
- ei.len = YAFFS_BYTES_PER_BLOCK;
+ ei.len = dev->nBytesPerChunk * dev->nChunksPerBlock;
ei.time = 1000;
ei.retries = 2;
ei.callback = nandmtd_EraseCallback;