*/
//yaffs_guts.c
-const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.14 2002-12-04 19:12:43 charles Exp $";
+const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.40 2005-04-05 03:45:40 charles Exp $";
#include "yportenv.h"
#include "yaffsinterface.h"
#include "yaffs_guts.h"
+#define YAFFS_PASSIVE_GC_CHUNKS 2
-#define YAFFS_GARBAGE_COLLECT_LOW_WATER 2
-
-
-
+#if 0
+// Use Steven Hill's ECC struff instead
// External functions for ECC on data
void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc);
-
+#define yaffs_ECCCalculate(data,ecc) nand_calculate_ecc(data,ecc)
+#define yaffs_ECCCorrect(data,read_ecc,calc_ecc) nand_correct_ecc(data,read_ecc,calc_ecc)
+#else
+#include "yaffs_ecc.h"
+#endif
// countBits is a quick way of counting the number of bits in a byte.
// ie. countBits[n] holds the number of 1 bits in a byte with the value n.
static yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type);
static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj);
static int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force);
-static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId);
+static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId,int markNAND);
static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj);
static int yaffs_CheckStructures(void);
static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit);
static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND);
static int yaffs_UnlinkWorker(yaffs_Object *obj);
-
+static void yaffs_AbortHalfCreatedObject(yaffs_Object *obj);
static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1);
static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in);
static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId);
+// Chunk bitmap manipulations
+
+static __inline __u8 *yaffs_BlockBits(yaffs_Device *dev, int blk)
+{
+ if(blk < dev->startBlock || blk > dev->endBlock)
+ {
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR),blk));
+ YBUG();
+ }
+ return dev->chunkBits + (dev->chunkBitmapStride * (blk - dev->startBlock));
+}
+
+static __inline__ void yaffs_ClearChunkBits(yaffs_Device *dev,int blk)
+{
+ __u8 *blkBits = yaffs_BlockBits(dev,blk);
+
+ memset(blkBits,0,dev->chunkBitmapStride);
+}
+
+static __inline__ void yaffs_ClearChunkBit(yaffs_Device *dev,int blk,int chunk)
+{
+ __u8 *blkBits = yaffs_BlockBits(dev,blk);
+
+ blkBits[chunk/8] &= ~ (1<<(chunk & 7));
+}
+
+static __inline__ void yaffs_SetChunkBit(yaffs_Device *dev,int blk,int chunk)
+{
+ __u8 *blkBits = yaffs_BlockBits(dev,blk);
+
+ blkBits[chunk/8] |= (1<<(chunk & 7));
+}
+
+static __inline__ int yaffs_CheckChunkBit(yaffs_Device *dev,int blk,int chunk)
+{
+ __u8 *blkBits = yaffs_BlockBits(dev,blk);
+ return (blkBits[chunk/8] & (1<<(chunk & 7))) ? 1 :0;
+}
+
+static __inline__ int yaffs_StillSomeChunkBits(yaffs_Device *dev,int blk)
+{
+ __u8 *blkBits = yaffs_BlockBits(dev,blk);
+ int i;
+ for(i = 0; i < dev->chunkBitmapStride; i++)
+ {
+ if(*blkBits) return 1;
+ blkBits++;
+ }
+ return 0;
+}
+// Function to manipulate block info
static __inline__ yaffs_BlockInfo* yaffs_GetBlockInfo(yaffs_Device *dev, int blk)
{
if(blk < dev->startBlock || blk > dev->endBlock)
{
- T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: block %d is not valid" TENDSTR),blk));
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: getBlockInfo block %d is not valid" TENDSTR),blk));
YBUG();
}
return &dev->blockInfo[blk - dev->startBlock];
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;
return dev->writeChunkToNAND(dev,chunkInNAND,data,spare);
}
-struct nandspare {
- yaffs_Spare spare;
- int eccres1;
- int eccres2;
-};
+
int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,
int chunkInNAND,
int retVal;
yaffs_Spare localSpare;
-#ifndef CONFIG_YAFFS_USE_NANDECC
- __u8 calcEcc[3];
- int eccResult1,eccResult2;
-#else
- struct nandspare nspare;
-#endif
-
dev->nPageReads++;
}
-#ifndef CONFIG_YAFFS_USE_NANDECC
- retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,spare);
- if(data && doErrorCorrection)
- {
- // Hoosterman, we had a data problem on this page
- yaffs_HandleReadDataError(dev,chunkInNAND);
- }
-#else
- retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare*)&nspare);
- memcpy (spare, &nspare, sizeof(yaffs_Spare));
- if(data && doErrorCorrection)
- {
- if(nspare.eccres1>0)
- {
- T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));
- }
- else if(nspare.eccres1<0)
- {
- T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));
- }
-
- if(nspare.eccres2>0)
- {
- T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));
- }
- else if(nspare.eccres2<0)
- {
- T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));
- }
-
- if(nspare.eccres2 || nspare.eccres2)
- {
- // Hoosterman, we had a data problem on this page
- yaffs_HandleReadDataError(dev,chunkInNAND);
- }
+ if(!dev->useNANDECC)
+ {
+ retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,spare);
+ if(data && doErrorCorrection)
+ {
+ // Do ECC correction
+ //Todo handle any errors
+ int eccResult1,eccResult2;
+ __u8 calcEcc[3];
+
+ yaffs_ECCCalculate(data,calcEcc);
+ eccResult1 = yaffs_ECCCorrect (data,spare->ecc1, calcEcc);
+ yaffs_ECCCalculate(&data[256],calcEcc);
+ eccResult2 = yaffs_ECCCorrect(&data[256],spare->ecc2, calcEcc);
+
+ if(eccResult1>0)
+ {
+ T(YAFFS_TRACE_ERROR, (TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));
+ dev->eccFixed++;
+ }
+ else if(eccResult1<0)
+ {
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));
+ dev->eccUnfixed++;
+ }
+
+ if(eccResult2>0)
+ {
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));
+ dev->eccFixed++;
+ }
+ else if(eccResult2<0)
+ {
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));
+ dev->eccUnfixed++;
+ }
+ if(eccResult1 || eccResult2)
+ {
+ // Hoosterman, we had a data problem on this page
+ yaffs_HandleReadDataError(dev,chunkInNAND);
+ }
+ }
+ }
+ else
+ {
+ // Must allocate enough memory for spare+2*sizeof(int) for ecc results from device.
+ struct yaffs_NANDSpare nspare;
+ retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare*)&nspare);
+ memcpy (spare, &nspare, sizeof(yaffs_Spare));
+ if(data && doErrorCorrection)
+ {
+ if(nspare.eccres1>0)
+ {
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));
+ }
+ else if(nspare.eccres1<0)
+ {
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));
+ }
+
+ if(nspare.eccres2>0)
+ {
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));
+ }
+ else if(nspare.eccres2<0)
+ {
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));
+ }
+
+ if(nspare.eccres1 || nspare.eccres2)
+ {
+ // Hoosterman, we had a data problem on this page
+ yaffs_HandleReadDataError(dev,chunkInNAND);
+ }
+
+ }
}
-#endif
return retVal;
}
static int init = 0;
static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK];
static __u8 data[YAFFS_BYTES_PER_CHUNK];
- static __u8 spare[16];
-
-
- dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare *)spare);
-
-
+ // Might as well always allocate the larger size for dev->useNANDECC == true;
+ static __u8 spare[sizeof(struct yaffs_NANDSpare)];
+
+ dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare *)spare);
if(!init)
{
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;
// Delete the chunk
- yaffs_DeleteChunk(dev,chunkInNAND);
+ yaffs_DeleteChunk(dev,chunkInNAND,1);
}
{
while ((*bname) && (i <=YAFFS_MAX_NAME_LENGTH))
{
-#ifdef CONFIG_YAFFS_WINCE
+
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
sum += toupper(*bname) * i;
#else
sum += (*bname) * i;
void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare)
{
- nand_calculate_ecc (data , spare->ecc1);
- nand_calculate_ecc (&data[256] , spare->ecc2);
+ yaffs_ECCCalculate(data , spare->ecc1);
+ yaffs_ECCCalculate(&data[256] , spare->ecc2);
}
void yaffs_CalcTagsECC(yaffs_Tags *tags)
for(i = 0; i < nTnodes - 1; i++)
{
newTnodes[i].internal[0] = &newTnodes[i+1];
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+ newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = 1;
+#endif
}
newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+ newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = 1;
+#endif
dev->freeTnodes = newTnodes;
dev->nFreeTnodes+= nTnodes;
dev->nTnodesCreated += nTnodes;
if(dev->freeTnodes)
{
tn = dev->freeTnodes;
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+ if(tn->internal[YAFFS_NTNODES_INTERNAL] != 1)
+ {
+ // Hoosterman, this thing looks like it isn't in the list
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Tnode list bug 1" TENDSTR)));
+ }
+#endif
dev->freeTnodes = dev->freeTnodes->internal[0];
dev->nFreeTnodes--;
// zero out
// FreeTnode frees up a tnode and puts it back on the free list
static void yaffs_FreeTnode(yaffs_Device*dev, yaffs_Tnode *tn)
{
- tn->internal[0] = dev->freeTnodes;
- dev->freeTnodes = tn;
- dev->nFreeTnodes++;
+ if(tn)
+ {
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+ if(tn->internal[YAFFS_NTNODES_INTERNAL] != 0)
+ {
+ // Hoosterman, this thing looks like it is already in the list
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Tnode list bug 2" TENDSTR)));
+ }
+ tn->internal[YAFFS_NTNODES_INTERNAL] = 1;
+#endif
+ tn->internal[0] = dev->freeTnodes;
+ dev->freeTnodes = tn;
+ dev->nFreeTnodes++;
+ }
}
static void yaffs_DeinitialiseTnodes(yaffs_Device*dev)
{
// Free the list of allocated tnodes
-
+ yaffs_TnodeList *tmp;
+
while(dev->allocatedTnodeList)
{
+ tmp = dev->allocatedTnodeList->next;
+
YFREE(dev->allocatedTnodeList->tnodes);
- dev->allocatedTnodeList = dev->allocatedTnodeList->next;
+ YFREE(dev->allocatedTnodeList);
+ dev->allocatedTnodeList = tmp;
+
}
dev->freeTnodes = NULL;
}
else if(level == 0)
{
- for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0; i--) //NB Don't apply the limit here, always delete a whole level0
+ int hitLimit = 0;
+
+ for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0 && !hitLimit; i--)
{
if(tn->level0[i])
{
if(found)
{
- yaffs_DeleteChunk(in->myDev,theChunk);
+ yaffs_DeleteChunk(in->myDev,theChunk,1);
in->nDataChunks--;
if(limit)
{
*limit = *limit-1;
+ if(*limit <= 0)
+ {
+ hitLimit = 1;
+ }
}
}
}
}
- return 1;
+ return (i < 0) ? 1 : 0;
}
}
+// SoftDeleteWorker scans backwards through the tnode tree and soft deletes all the chunks in the file.
+// All soft deleting does is increment the block's softdelete count and pulls the chunk out
+// of the tnode.
+// THus, essentially this is the same as DeleteWorker except that the chunks are soft deleted.
+//
+static int yaffs_SoftDeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset)
+{
+ int i;
+ int chunkInInode;
+ int theChunk;
+ yaffs_BlockInfo *theBlock;
+ yaffs_Tags tags;
+ int found;
+ int chunkDeleted;
+ int allDone = 1;
+
+
+ if(tn)
+ {
+ if(level > 0)
+ {
+
+ for(i = YAFFS_NTNODES_INTERNAL -1; allDone && i >= 0; i--)
+ {
+ if(tn->internal[i])
+ {
+ allDone = yaffs_SoftDeleteWorker(in,tn->internal[i],level - 1,
+ (chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i);
+ if(allDone)
+ {
+ yaffs_FreeTnode(in->myDev,tn->internal[i]);
+ tn->internal[i] = NULL;
+ }
+ else
+ {
+ //Hoosterman... how could this happen.
+ }
+ }
+ }
+ return (allDone) ? 1 : 0;
+ }
+ else if(level == 0)
+ {
+
+ for(i = YAFFS_NTNODES_LEVEL0 -1; i >=0; i--)
+ {
+ if(tn->level0[i])
+ {
+ // Note this does not find the real chunk, only the chunk group.
+ // We make an assumption that a chunk group is niot larger than a block.
+ theChunk = (tn->level0[i] << in->myDev->chunkGroupBits);
+ T(YAFFS_TRACE_SCAN,(TSTR("soft delete tch %d cgb %d chunk %d" TENDSTR),
+ tn->level0[i],in->myDev->chunkGroupBits,theChunk));
+
+ theBlock = yaffs_GetBlockInfo(in->myDev, theChunk/in->myDev->nChunksPerBlock);
+ if(theBlock)
+ {
+ theBlock->softDeletions++;
+ }
+ tn->level0[i] = 0;
+ }
+
+ }
+ return 1;
+
+ }
+
+ }
+
+ return 1;
+
+}
+
+
+
+static void yaffs_SoftDeleteFile(yaffs_Object *obj)
+{
+ if(obj->deleted &&
+ obj->variantType == YAFFS_OBJECT_TYPE_FILE &&
+ !obj->softDeleted)
+ {
+ if(obj->nDataChunks <= 0)
+ {
+ // Empty file, just delete it immediately
+ yaffs_FreeTnode(obj->myDev,obj->variant.fileVariant.top);
+ obj->variant.fileVariant.top = NULL;
+ T(YAFFS_TRACE_TRACING,(TSTR("yaffs: Deleting empty file %d" TENDSTR),obj->objectId));
+ yaffs_DoGenericObjectDeletion(obj);
+ }
+ else
+ {
+ yaffs_SoftDeleteWorker(obj, obj->variant.fileVariant.top, obj->variant.fileVariant.topLevel, 0);
+ obj->softDeleted = 1;
+ }
+ }
+}
+
+
// Hook them into the free list
for(i = 0; i < nObjects - 1; i++)
{
- (yaffs_Object *)newObjects[i].siblings.next = &newObjects[i+1];
+ newObjects[i].siblings.next = (struct list_head *)(&newObjects[i+1]);
}
newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects;
yaffs_Device *dev = tn->myDev;
+#ifdef __KERNEL__
+ if(tn->myInode)
+ {
+ // We're still hooked up to a cached inode.
+ // Don't delete now, but mark for later deletion
+ tn->deferedFree = 1;
+ return;
+ }
+#endif
+
yaffs_UnhashObject(tn);
// Link into the free list.
- (yaffs_Object *)(tn->siblings.next) = dev->freeObjects;
+ tn->siblings.next = (struct list_head *)(dev->freeObjects);
dev->freeObjects = tn;
dev->nFreeObjects++;
}
+#ifdef __KERNEL__
+
+void yaffs_HandleDeferedFree(yaffs_Object *obj)
+{
+ if(obj->deferedFree)
+ {
+ yaffs_FreeObject(obj);
+ }
+}
+
+#endif
+
static void yaffs_DeinitialiseObjects(yaffs_Device *dev)
{
// Free the list of allocated Objects
+ yaffs_ObjectList *tmp;
+
while( dev->allocatedObjectList)
{
+ tmp = dev->allocatedObjectList->next;
YFREE(dev->allocatedObjectList->objects);
- dev->allocatedObjectList = dev->allocatedObjectList->next;
+ YFREE(dev->allocatedObjectList);
+
+ dev->allocatedObjectList = tmp;
}
dev->freeObjects = NULL;
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)
+ {
+#ifdef __KERNEL__
+ // Don't tell the VFS about this if it has been marked for freeing
+ if(in->deferedFree)
+ return NULL;
+#endif
+ return in;
+ }
}
}
theObject->win_ctime[1] = theObject->win_mtime[1] = theObject->win_atime[1];
#else
- theObject->st_atime = theObject->st_mtime = theObject->st_ctime = CURRENT_TIME;
+
+ theObject->st_atime = theObject->st_mtime = theObject->st_ctime = Y_CURRENT_TIME;
+
#endif
switch(type)
{
#ifdef CONFIG_YAFFS_WINCE
yfsd_WinFileTimeNow(in->win_atime);
in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0];
- in->win_ctime[1] = in->win_mtime[1] = in->win_atime[0];
+ in->win_ctime[1] = in->win_mtime[1] = in->win_atime[1];
#else
- in->st_atime = in->st_mtime = in->st_ctime = CURRENT_TIME;
+
+ in->st_atime = in->st_mtime = in->st_ctime = Y_CURRENT_TIME;
in->st_rdev = rdev;
in->st_uid = uid;
in->st_gid = gid;
break;
}
- if(yaffs_GetNumberOfFreeChunks(dev) <= 0 ||
+ if(/*yaffs_GetNumberOfFreeChunks(dev) <= 0 || */
yaffs_UpdateObjectHeader(in,name,0) < 0)
{
// Could not create the object header, fail the creation
- yaffs_UnlinkWorker(in);
+ yaffs_AbortHalfCreatedObject(in);
in = NULL;
}
yaffs_Object *obj;
int force = 0;
-#ifdef CONFIG_YAFFS_WINCE
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
// Special case for WinCE.
// While look-up is case insensitive, the name isn't.
// THerefore we might want to change x.txt to X.txt
dev->allocationBlock = -1; // force it to get a new one
//Todo we're assuming the malloc will pass.
dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo));
- if(dev->blockInfo)
+ // Set up dynamic blockinfo stuff.
+ dev->chunkBitmapStride = (dev->nChunksPerBlock+7)/8;
+ dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks);
+ if(dev->blockInfo && dev->chunkBits)
{
memset(dev->blockInfo,0,nBlocks * sizeof(yaffs_BlockInfo));
+ memset(dev->chunkBits,0,dev->chunkBitmapStride * nBlocks);
return YAFFS_OK;
}
+
return YAFFS_FAIL;
}
static void yaffs_DeinitialiseBlocks(yaffs_Device *dev)
{
YFREE(dev->blockInfo);
+ dev->blockInfo = NULL;
+ YFREE(dev->chunkBits);
+ dev->chunkBits = NULL;
}
// 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 = 100; // silly big number
+ int pagesInUse;
yaffs_BlockInfo *bi;
+
+ // If we're doing aggressive GC then we are happy to take a less-dirty block, and
+ // search further.
- for(i = dev->startBlock; i <= dev->endBlock && pagesInUse > 2 ; i++)
+ pagesInUse = (aggressive)? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1;
+ if(aggressive)
+ {
+ iterations = dev->endBlock - dev->startBlock + 1;
+ }
+ else
+ {
+ iterations = dev->endBlock - dev->startBlock + 1;
+ iterations = iterations / 16;
+ if(iterations > 200)
+ {
+ iterations = 200;
+ }
+ }
+
+ for(i = 0; i <= iterations && pagesInUse > 0 ; i++)
{
b++;
if ( b < dev->startBlock || b > dev->endBlock)
bi = yaffs_GetBlockInfo(dev,b);
if(bi->blockState == YAFFS_BLOCK_STATE_FULL &&
- bi->pagesInUse < pagesInUse)
+ (bi->pagesInUse - bi->softDeletions )< pagesInUse)
{
dirtiest = b;
- pagesInUse = bi->pagesInUse;
+ pagesInUse = (bi->pagesInUse - bi->softDeletions);
}
}
if( erasedOk )
{
+ // Clean it up...
bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
dev->nErasedBlocks++;
bi->pagesInUse = 0;
- bi->pageBits = 0;
+ bi->softDeletions = 0;
+ yaffs_ClearChunkBits(dev,blockNo);
T(YAFFS_TRACE_ERASE,(TSTR("Erased block %d" TENDSTR),blockNo));
}
static int yaffs_FindBlockForAllocation(yaffs_Device *dev)
{
int i;
+
yaffs_BlockInfo *bi;
if(dev->nErasedBlocks < 1)
{
// Hoosterman we've got a problem.
// Can't get space to gc
- T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: no space during gc" TENDSTR)));
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: no space during gc" TENDSTR)));
return -1;
}
for(i = dev->startBlock; i <= dev->endBlock; i++)
{
- bi = yaffs_GetBlockInfo(dev,i);
+ dev->allocationBlockFinder++;
+ if(dev->allocationBlockFinder <dev->startBlock || dev->allocationBlockFinder> dev->endBlock)
+ {
+ dev->allocationBlockFinder = dev->startBlock;
+ }
+
+ bi = yaffs_GetBlockInfo(dev,dev->allocationBlockFinder);
+
if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
{
bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING;
dev->nErasedBlocks--;
- return i;
+ return dev->allocationBlockFinder;
}
}
dev->allocationPage = 0;
}
- if(!useReserve && dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS)
+ if(!useReserve && dev->nErasedBlocks <= dev->nReservedBlocks)
{
// Not enough space to allocate unless we're allowed to use the reserve.
return -1;
{
bi = yaffs_GetBlockInfo(dev,dev->allocationBlock);
- retVal = (dev->allocationBlock * YAFFS_CHUNKS_PER_BLOCK) +
+ retVal = (dev->allocationBlock * dev->nChunksPerBlock) +
dev->allocationPage;
bi->pagesInUse++;
- bi->pageBits |= (1 << (dev->allocationPage));
+ yaffs_SetChunkBit(dev,dev->allocationBlock,dev->allocationPage);
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);
+ return (dev->nErasedBlocks >= dev->nReservedBlocks);
}
-int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
+static int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
{
int oldChunk;
int newChunk;
- __u32 mask;
+ int chunkInBlock;
+ int markNAND;
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);
+// yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,block);
yaffs_Object *object;
//T(("Collecting block %d n %d bits %x\n",block, bi->pagesInUse, bi->pageBits));
- for(mask = 1,oldChunk = block * YAFFS_CHUNKS_PER_BLOCK;
- mask && bi->pageBits;
- mask <<= 1, oldChunk++ )
+ for(chunkInBlock = 0,oldChunk = block * dev->nChunksPerBlock;
+ chunkInBlock < dev->nChunksPerBlock && yaffs_StillSomeChunkBits(dev,block);
+ chunkInBlock++, oldChunk++ )
{
- if(bi->pageBits & mask)
+ if(yaffs_CheckChunkBit(dev,block,chunkInBlock))
{
- // This page is in use and needs to be copied off
+ // This page is in use and might need to be copied off
- dev->nGCCopies++;
+ markNAND = 1;
//T(("copying page %x from %d to %d\n",mask,oldChunk,newChunk));
// No need to copy this, just forget about it and fix up the
// object.
- yaffs_PutChunkIntoFile(object, tags.chunkId, 0,0);
+ //yaffs_PutChunkIntoFile(object, tags.chunkId, 0,0);
object->nDataChunks--;
+
+ if(object->nDataChunks <= 0)
+ {
+ // Time to delete the file too
+ yaffs_FreeTnode(object->myDev,object->variant.fileVariant.top);
+ object->variant.fileVariant.top = NULL;
+ T(YAFFS_TRACE_TRACING,(TSTR("yaffs: About to finally delete object %d" TENDSTR),object->objectId));
+ yaffs_DoGenericObjectDeletion(object);
+ }
+ markNAND = 0;
}
else if( 0 /* Todo object && object->deleted && object->nDataChunks == 0 */)
{
// Deleted object header with no data chunks.
- // Can be discarded
+ // Can be discarded and the file deleted.
object->chunkId = 0;
- //Todo some clean up
+ yaffs_FreeTnode(object->myDev,object->variant.fileVariant.top);
+ object->variant.fileVariant.top = NULL;
+ yaffs_DoGenericObjectDeletion(object);
}
else if(object)
tags.serialNumber++;
yaffs_LoadTagsIntoSpare(&spare,&tags);
+ dev->nGCCopies++;
newChunk = yaffs_WriteNewChunkToNAND(dev, buffer, &spare,1);
{
// It's a header
object->chunkId = newChunk;
+ object->serial = tags.serialNumber;
}
else
{
}
}
- yaffs_DeleteChunk(dev,oldChunk);
+ yaffs_DeleteChunk(dev,oldChunk,markNAND);
}
}
static yaffs_Object *yaffs_FindDeletedUnlinkedFile(yaffs_Device *dev)
{
- // todo find a file to delete
+ // find a file to delete
struct list_head *i;
yaffs_Object *l;
- // To the free chunks add the chunks that are in the deleted unlinked files.
+ //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;
}
+
static void yaffs_DoUnlinkedFileDeletion(yaffs_Device *dev)
{
// This does background deletion on unlinked files.. only deleted ones.
int limit; // Number of chunks to delete in a file.
// NB this can be exceeded, but not by much.
- limit = 5;
-#if 0
- if(dev->nErasedBlocks <= (YAFFS_RESERVED_BLOCKS + YAFFS_GARBAGE_COLLECT_LOW_WATER))
- {
- limit = 50; // Doing GC soon, so dig deeper
- }
- else
- {
- limit = 5;
- }
-#endif
-
+ limit = -1;
+
delresult = yaffs_DeleteWorker(obj, obj->variant.fileVariant.top, obj->variant.fileVariant.topLevel, 0,&limit);
if(obj->nDataChunks == 0)
// Done all the deleting of data chunks.
// Now dump the header and clean up
yaffs_FreeTnode(dev,obj->variant.fileVariant.top);
+ obj->variant.fileVariant.top = NULL;
yaffs_DoGenericObjectDeletion(obj);
dev->nDeletedFiles--;
dev->nUnlinkedFiles--;
}
-
+#if 0
+#define YAFFS_GARBAGE_COLLECT_LOW_WATER 2
static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
{
int block;
+ int aggressive=0;
- yaffs_DoUnlinkedFileDeletion(dev);
+ //yaffs_DoUnlinkedFileDeletion(dev);
- if(dev->nErasedBlocks <= (YAFFS_RESERVED_BLOCKS + YAFFS_GARBAGE_COLLECT_LOW_WATER))
+ if(dev->nErasedBlocks <= (dev->nReservedBlocks + 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 <= (dev->nReservedBlocks + 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 ///////////////////////////////////////
yaffs_SpareInitialise(&spare);
-#ifndef CONFIG_YAFFS_USE_NANDECC
- if(buffer)
+ if(!dev->useNANDECC && buffer)
{
yaffs_CalcECC(buffer,&spare);
}
-#endif
yaffs_LoadTagsIntoSpare(&spare,tags);
yaffs_SpareInitialise(&spare);
-#ifndef CONFIG_YAFFS_USE_NANDECC
-
- if(buffer)
+ if(!dev->useNANDECC && buffer)
{
yaffs_CalcECC(buffer,&spare);
}
-#endif
yaffs_LoadTagsIntoSpare(&spare,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++)
{
{
//Hoosterman - how did this happen?
- T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: existing chunk < 0 in scan" TENDSTR)));
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: existing chunk < 0 in scan" TENDSTR)));
}
{
// Use new
// Delete the old one and drop through to update the tnode
- yaffs_DeleteChunk(dev,existingChunk);
+ yaffs_DeleteChunk(dev,existingChunk,1);
}
else
{
// Use existing.
// Delete the new one and return early so that the tnode isn't changed
- yaffs_DeleteChunk(dev,chunkInNAND);
+ yaffs_DeleteChunk(dev,chunkInNAND,1);
return YAFFS_OK;
}
}
}
else
{
+ memset(buffer,0,YAFFS_BYTES_PER_CHUNK);
return 0;
}
}
-static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId)
+static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId,int markNAND)
{
int block;
int page;
yaffs_BlockInfo *bi;
if(chunkId <= 0) return;
-
- block = chunkId / YAFFS_CHUNKS_PER_BLOCK;
- page = chunkId % YAFFS_CHUNKS_PER_BLOCK;
- yaffs_SpareInitialise(&spare);
- spare.pageStatus = 0; // To mark it as deleted.
+ dev->nDeletions++;
+ block = chunkId / dev->nChunksPerBlock;
+ page = chunkId % dev->nChunksPerBlock;
+
+ if(markNAND)
+ {
+ yaffs_SpareInitialise(&spare);
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+
+ //read data before write, to ensure correct ecc
+ //if we're using MTD verification under Linux
+ yaffs_ReadChunkFromNAND(dev,chunkId,NULL,&spare,0);
+#endif
+
+ spare.pageStatus = 0; // To mark it as deleted.
- yaffs_WriteChunkToNAND(dev,chunkId,NULL,&spare);
- yaffs_HandleUpdateChunk(dev,chunkId,&spare);
+ yaffs_WriteChunkToNAND(dev,chunkId,NULL,&spare);
+ yaffs_HandleUpdateChunk(dev,chunkId,&spare);
+ }
+ else
+ {
+ dev->nUnmarkedDeletions++;
+ }
+
bi = yaffs_GetBlockInfo(dev,block);
{
dev->nFreeChunks++;
- bi->pageBits &= ~(1 << page);
+ yaffs_ClearChunkBit(dev,block,page);
bi->pagesInUse--;
if(bi->pagesInUse == 0 &&
if(prevChunkId >= 0)
{
- yaffs_DeleteChunk(dev,prevChunkId);
+ yaffs_DeleteChunk(dev,prevChunkId,1);
}
// Create new chunk in NAND
- newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,bufferNew,&newTags,1);
+ newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,bufferNew,&newTags, (prevChunkId >= 0) ? 1 : 0 );
if(newChunkId >= 0)
{
if(prevChunkId >= 0)
{
- yaffs_DeleteChunk(dev,prevChunkId);
+ yaffs_DeleteChunk(dev,prevChunkId,1);
}
in->dirty = 0;
// need a very intelligent search.
-#ifdef CONFIG_YAFFS_SHORT_OP_CACHE
+
static void yaffs_FlushFilesChunkCache(yaffs_Object *obj)
int lowest;
int i;
yaffs_ChunkCache *cache;
- int chunkWritten;
+ int chunkWritten = 0;
int nBytes;
+ int nCaches = obj->myDev->nShortOpCaches;
- do{
- cache = NULL;
+ if (nCaches > 0)
+ {
+ do{
+ cache = NULL;
- // Find the dirty cache for this object with the lowest chunk id.
- for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)
- {
- if(dev->srCache[i].object == obj &&
- dev->srCache[i].dirty)
+ // Find the dirty cache for this object with the lowest chunk id.
+ for(i = 0; i < nCaches; i++)
{
- if(!cache || dev->srCache[i].chunkId < lowest)
+ if(dev->srCache[i].object == obj &&
+ dev->srCache[i].dirty)
{
- cache = &dev->srCache[i];
- lowest = cache->chunkId;
+ if(!cache || dev->srCache[i].chunkId < lowest)
+ {
+ cache = &dev->srCache[i];
+ lowest = cache->chunkId;
+ }
}
}
- }
- if(cache)
- {
- //Write it out
-
- nBytes = cache->object->variant.fileVariant.fileSize - ((cache->chunkId -1) * YAFFS_BYTES_PER_CHUNK);
-
- if(nBytes > YAFFS_BYTES_PER_CHUNK)
+ if(cache)
{
- nBytes= YAFFS_BYTES_PER_CHUNK;
- }
+ //Write it out
+
+#if 0
+ nBytes = cache->object->variant.fileVariant.fileSize - ((cache->chunkId -1) * YAFFS_BYTES_PER_CHUNK);
- chunkWritten = yaffs_WriteChunkDataToObject(cache->object,
- cache->chunkId,
- cache->data,
- nBytes,1);
+ if(nBytes > YAFFS_BYTES_PER_CHUNK)
+ {
+ nBytes= YAFFS_BYTES_PER_CHUNK;
+ }
+#endif
+ chunkWritten = yaffs_WriteChunkDataToObject(cache->object,
+ cache->chunkId,
+ cache->data,
+ cache->nBytes,1);
- cache->dirty = 0;
- }
+ cache->dirty = 0;
+ cache->object = NULL;
+ }
- } while(cache && chunkWritten > 0);
+ } while(cache && chunkWritten > 0);
- if(cache)
- {
- //Hoosterman, disk full while writing cache out.
- T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: no space during caceh write" TENDSTR)));
+ if(cache)
+ {
+ //Hoosterman, disk full while writing cache out.
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: no space during cache write" TENDSTR)));
+ }
}
}
int usage;
int theOne;
- for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)
+ if(dev->nShortOpCaches > 0)
{
- if(!dev->srCache[i].object)
+ for(i = 0; i < dev->nShortOpCaches; i++)
{
- //T(("Grabbing empty %d\n",i));
+ if(!dev->srCache[i].object)
+ {
+ //T(("Grabbing empty %d\n",i));
+
+ //printf("Grabbing empty %d\n",i);
- return &dev->srCache[i];
+ return &dev->srCache[i];
+ }
}
- }
+
+ return NULL;
- theOne = -1;
- usage = 0; // just to stop the compiler grizzling
+ theOne = -1;
+ usage = 0; // just to stop the compiler grizzling
- for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)
- {
- if(!dev->srCache[i].dirty &&
- ((dev->srCache[i].lastUse < usage && theOne >= 0)||
- theOne < 0))
+ for(i = 0; i < dev->nShortOpCaches; i++)
{
- usage = dev->srCache[i].lastUse;
- theOne = i;
+ if(!dev->srCache[i].dirty &&
+ ((dev->srCache[i].lastUse < usage && theOne >= 0)||
+ theOne < 0))
+ {
+ usage = dev->srCache[i].lastUse;
+ theOne = i;
+ }
}
- }
- //T(("Grabbing non-empty %d\n",theOne));
- return theOne >= 0 ? &dev->srCache[theOne] : NULL;
+ //T(("Grabbing non-empty %d\n",theOne));
+
+ //if(theOne >= 0) printf("Grabbed non-empty cache %d\n",theOne);
+
+ return theOne >= 0 ? &dev->srCache[theOne] : NULL;
+ }
+ else
+ {
+ return NULL;
+ }
}
yaffs_Object *theObj;
int usage;
int i;
+ int pushout;
- // Try find a non-dirty one...
+ if(dev->nShortOpCaches > 0)
+ {
+ // Try find a non-dirty one...
- cache = yaffs_GrabChunkCacheWorker(dev);
+ cache = yaffs_GrabChunkCacheWorker(dev);
- if(!cache)
- {
- // They were all dirty, find the last recently used object and flush
- // its cache, then find again.
- // NB what's here is not very accurate, we actually flush the object
- // the last recently used page.
+ if(!cache)
+ {
+ // They were all dirty, find the last recently used object and flush
+ // its cache, then find again.
+ // NB what's here is not very accurate, we actually flush the object
+ // the last recently used page.
- theObj = dev->srCache[0].object;
- usage = dev->srCache[0].lastUse;
+ theObj = dev->srCache[0].object;
+ usage = dev->srCache[0].lastUse;
+ cache = &dev->srCache[0];
+ pushout = 0;
- for(i = 1; i < YAFFS_N_CACHE_CHUNKS; i++)
- {
- if( dev->srCache[i].object &&
- dev->srCache[i].lastUse < usage)
+ for(i = 1; i < dev->nShortOpCaches; i++)
{
- usage = dev->srCache[i].lastUse;
- theObj = dev->srCache[i].object;
+ if( dev->srCache[i].object &&
+ dev->srCache[i].lastUse < usage)
+ {
+ usage = dev->srCache[i].lastUse;
+ theObj = dev->srCache[i].object;
+ cache = &dev->srCache[i];
+ pushout = i;
+ }
}
- }
- yaffs_FlushFilesChunkCache(theObj);
+ if(!cache || cache->dirty)
+ {
+
+ //printf("Dirty ");
+ yaffs_FlushFilesChunkCache(theObj);
- // Try again
- cache = yaffs_GrabChunkCacheWorker(dev);
+ // Try again
+ cache = yaffs_GrabChunkCacheWorker(dev);
+ }
+ else
+ {
+ //printf(" pushout %d\n",pushout);
+ }
+
+ }
+
+ return cache;
}
-
- return cache;
+ else
+ return NULL;
}
{
yaffs_Device *dev = obj->myDev;
int i;
-
- for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)
+ if(dev->nShortOpCaches > 0)
{
- if(dev->srCache[i].object == obj &&
- dev->srCache[i].chunkId == chunkId)
+ for(i = 0; i < dev->nShortOpCaches; i++)
{
- dev->cacheHits++;
+ if(dev->srCache[i].object == obj &&
+ dev->srCache[i].chunkId == chunkId)
+ {
+ dev->cacheHits++;
- return &dev->srCache[i];
+ return &dev->srCache[i];
+ }
}
}
-
return NULL;
}
// Mark the chunk for the least recently used algorithym
static void yaffs_UseChunkCache(yaffs_Device *dev, yaffs_ChunkCache *cache, int isAWrite)
{
- if( dev->srLastUse < 0 ||
- dev->srLastUse > 100000000)
+
+ if(dev->nShortOpCaches > 0)
{
- // Reset the cache usages
- int i;
- for(i = 1; i < YAFFS_N_CACHE_CHUNKS; i++)
+ if( dev->srLastUse < 0 ||
+ dev->srLastUse > 100000000)
{
- dev->srCache[i].lastUse = 0;
+ // Reset the cache usages
+ int i;
+ for(i = 1; i < dev->nShortOpCaches; i++)
+ {
+ dev->srCache[i].lastUse = 0;
+ }
+ dev->srLastUse = 0;
}
- dev->srLastUse = 0;
- }
- dev->srLastUse++;
+ dev->srLastUse++;
- cache->lastUse = dev->srLastUse;
+ cache->lastUse = dev->srLastUse;
- if(isAWrite)
- {
- cache->dirty = 1;
+ if(isAWrite)
+ {
+ cache->dirty = 1;
+ }
}
}
// ie the short cache for this page is no longer valid.
static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId)
{
- yaffs_ChunkCache *cache = yaffs_FindChunkCache(object,chunkId);
-
- if(cache)
+ if(object->myDev->nShortOpCaches > 0)
{
- cache->object = NULL;
+ yaffs_ChunkCache *cache = yaffs_FindChunkCache(object,chunkId);
+
+ if(cache)
+ {
+ cache->object = NULL;
+ }
}
}
int i;
yaffs_Device *dev = in->myDev;
- // Now invalidate it.
- for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)
- {
- if(dev->srCache[i].object == in)
+ if(dev->nShortOpCaches > 0)
+ {
+ // Now invalidate it.
+ for(i = 0; i < dev->nShortOpCaches; i++)
{
- dev->srCache[i].object = NULL;
+ if(dev->srCache[i].object == in)
+ {
+ dev->srCache[i].object = NULL;
+ }
}
}
}
-#else
-// Stubs for disabling short op caching
-
-static void yaffs_FlushFilesChunkCache(yaffs_Object *obj)
-{}
-
-static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device *dev)
-{
- return NULL;
-}
-
-
-static yaffs_ChunkCache *yaffs_FindChunkCache(yaffs_Device *dev, int objectId, int chunkId)
-{
- return NULL;
-}
-
-static void yaffs_UseChunkCache(yaffs_Device *dev, yaffs_ChunkCache *cache)
-{
-}
-
-static void yaffs_InvalidateChunkCache(yaffs_Object *obj, int chunkId)
-{
-}
-
-static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in)
-{
-}
-
-
-
-#endif
int nToCopy;
int n = nBytes;
int nDone = 0;
+ yaffs_ChunkCache *cache;
yaffs_Device *dev;
nToCopy = YAFFS_BYTES_PER_CHUNK - start;
}
- if(nToCopy != YAFFS_BYTES_PER_CHUNK)
+ cache = yaffs_FindChunkCache(in,chunk);
+
+ // If the chunk is already in the cache or it is less than a whole chunk
+ // then use the cache (if there is caching)
+ // else bypass the cache.
+ if( cache || nToCopy != YAFFS_BYTES_PER_CHUNK)
{
- // An incomplete start or end chunk (or maybe both start and end chunk)
-#ifdef CONFIG_YAFFS_SHORT_OP_CACHE
- yaffs_ChunkCache *cache;
- // If we can't find the data in the cache, then load it up.
- cache = yaffs_FindChunkCache(in,chunk);
- if(!cache)
+ if(dev->nShortOpCaches > 0)
{
- cache = yaffs_GrabChunkCache(in->myDev);
- cache->object = in;
- cache->chunkId = chunk;
- cache->dirty = 0;
- yaffs_ReadChunkDataFromObject(in,chunk,cache->data);
- }
+
+ // If we can't find the data in the cache, then load it up.
+
+ if(!cache)
+ {
+ cache = yaffs_GrabChunkCache(in->myDev);
+ cache->object = in;
+ cache->chunkId = chunk;
+ cache->dirty = 0;
+ yaffs_ReadChunkDataFromObject(in,chunk,cache->data);
+ cache->nBytes = 0;
+ }
- yaffs_UseChunkCache(dev,cache,0);
+ yaffs_UseChunkCache(dev,cache,0);
+
+ memcpy(buffer,&cache->data[start],nToCopy);
+ }
+ else
+ {
+ // Read into the local buffer then copy...
+ yaffs_ReadChunkDataFromObject(in,chunk,dev->localBuffer);
+ memcpy(buffer,&dev->localBuffer[start],nToCopy);
+ }
- memcpy(buffer,&cache->data[start],nToCopy);
-#else
- // Read into the local buffer then copy...
- yaffs_ReadChunkDataFromObject(in,chunk,dev->localBuffer);
- memcpy(buffer,&dev->localBuffer[start],nToCopy);
-#endif
}
else
{
if(nToCopy != YAFFS_BYTES_PER_CHUNK)
{
// An incomplete start or end chunk (or maybe both start and end chunk)
-#ifdef CONFIG_YAFFS_SHORT_OP_CACHE
- yaffs_ChunkCache *cache;
- // If we can't find the data in the cache, then load it up.
- cache = yaffs_FindChunkCache(in,chunk);
- if(!cache && yaffs_CheckSpaceForChunkCache(in->myDev))
+ if(dev->nShortOpCaches > 0)
{
- cache = yaffs_GrabChunkCache(in->myDev);
- cache->object = in;
- cache->chunkId = chunk;
- cache->dirty = 0;
- yaffs_ReadChunkDataFromObject(in,chunk,cache->data);
- }
+ yaffs_ChunkCache *cache;
+ // If we can't find the data in the cache, then load it up.
+ cache = yaffs_FindChunkCache(in,chunk);
+ if(!cache && yaffs_CheckSpaceForChunkCache(in->myDev))
+ {
+ cache = yaffs_GrabChunkCache(in->myDev);
+ cache->object = in;
+ cache->chunkId = chunk;
+ cache->dirty = 0;
+ yaffs_ReadChunkDataFromObject(in,chunk,cache->data);
+ }
- if(cache)
- {
- yaffs_UseChunkCache(dev,cache,1);
- memcpy(&cache->data[start],buffer,nToCopy);
+ if(cache)
+ {
+ yaffs_UseChunkCache(dev,cache,1);
+ memcpy(&cache->data[start],buffer,nToCopy);
+ cache->nBytes = nToWriteBack;
+ }
+ else
+ {
+ chunkWritten = -1; // fail the write
+ }
}
else
{
- chunkWritten = -1; // fail the write
- }
-#else
+ // An incomplete start or end chunk (or maybe both start and end chunk)
+ // Read into the local buffer then copy, then copy over and write back.
-
- // An incomplete start or end chunk (or maybe both start and end chunk)
- // Read into the local buffer then copy, then copy over and write back.
-
- yaffs_ReadChunkDataFromObject(in,chunk,dev->localBuffer);
+ yaffs_ReadChunkDataFromObject(in,chunk,dev->localBuffer);
- memcpy(&dev->localBuffer[start],buffer,nToCopy);
+ memcpy(&dev->localBuffer[start],buffer,nToCopy);
- chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,dev->localBuffer,nToWriteBack,0);
+ chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,dev->localBuffer,nToWriteBack,0);
- //T(("Write with readback to chunk %d %d start %d copied %d wrote back %d\n",chunk,chunkWritten,start, nToCopy, nToWriteBack));
-#endif
+ //T(("Write with readback to chunk %d %d start %d copied %d wrote back %d\n",chunk,chunkWritten,start, nToCopy, nToWriteBack));
+ }
}
else
// using yaffs_DeleteChunk
chunkId = yaffs_FindAndDeleteChunkInFile(in,i,NULL);
- if(chunkId <= 0 || chunkId >= (dev->endBlock * 32))
+ if(chunkId < (dev->startBlock * 32) || chunkId >= ((dev->endBlock+1) * 32))
{
//T(("Found daft chunkId %d for %d\n",chunkId,i));
}
else
{
in->nDataChunks--;
- yaffs_DeleteChunk(dev,chunkId);
+ yaffs_DeleteChunk(dev,chunkId,1);
}
}
int lastChunk = 1+ newSize/YAFFS_BYTES_PER_CHUNK;
// Got to read and rewrite the last chunk with its new size.
+ // NB Got to zero pad to nuke old data
yaffs_ReadChunkDataFromObject(in,lastChunk,dev->localBuffer);
-
+ memset(dev->localBuffer + sizeOfPartialChunk,0, YAFFS_BYTES_PER_CHUNK - sizeOfPartialChunk);
+
yaffs_WriteChunkDataToObject(in,lastChunk,dev->localBuffer,sizeOfPartialChunk,1);
}
// yaffs_FlushFile() updates the file's
// objectId in NAND
-int yaffs_FlushFile(yaffs_Object *in)
+int yaffs_FlushFile(yaffs_Object *in, int updateTime)
{
int retVal;
if(in->dirty)
//T(("flushing object header\n"));
yaffs_FlushFilesChunkCache(in);
-
+ if(updateTime)
+ {
#ifdef CONFIG_YAFFS_WINCE
- yfsd_WinFileTimeNow(in->win_mtime);
+ yfsd_WinFileTimeNow(in->win_mtime);
#else
- in->st_mtime = CURRENT_TIME;
+ in->st_mtime = Y_CURRENT_TIME;
#endif
+ }
- retVal = yaffs_UpdateObjectHeader(in,NULL,0);
+ retVal = (yaffs_UpdateObjectHeader(in,NULL,0) >= 0)? YAFFS_OK : YAFFS_FAIL;
}
else
{
yaffs_InvalidateWholeChunkCache(in);
yaffs_RemoveObjectFromDirectory(in);
- yaffs_DeleteChunk(in->myDev,in->chunkId);
+ yaffs_DeleteChunk(in->myDev,in->chunkId,1);
+ in->chunkId = -1;
+#if 0
#ifdef __KERNEL__
if(in->myInode)
{
in->myInode->u.generic_ip = NULL;
- in->myInode = 0;
+ in->myInode = NULL;
}
+#endif
#endif
yaffs_FreeObject(in);
return YAFFS_OK;
{
#ifdef CONFIG_YAFFS_DISABLE_BACKGROUND_DELETION
+
// Delete the file data & tnodes
yaffs_DeleteWorker(in, in->variant.fileVariant.top, in->variant.fileVariant.topLevel, 0,NULL);
int retVal;
int immediateDeletion=0;
retVal = yaffs_ChangeObjectName(in, in->myDev->unlinkedDir,NULL,0);
- if(retVal == YAFFS_OK)
+ if(1 || // Ignore the result of the change name. This will only fail
+ // if the disk was completely full (an error condition)
+ // We ignore the error so we can delete files to recover the disk
+ retVal == YAFFS_OK)
{
//in->unlinked = 1;
//in->myDev->nUnlinkedFiles++;
#ifdef __KERNEL__
if(!in->myInode)
{
+ // Might be open at present,
+ // Caught by delete_inode in yaffs_fs.c
immediateDeletion = 1;
}
-#endif
-#ifdef CONFIG_YAFFS_WINCE
+#else
if(in->inUse <= 0)
{
immediateDeletion = 1;
T(YAFFS_TRACE_TRACING,(TSTR("yaffs: immediate deletion of file %d" TENDSTR),in->objectId));
in->deleted=1;
in->myDev->nDeletedFiles++;
+ yaffs_SoftDeleteFile(in);
}
}
int yaffs_DeleteFile(yaffs_Object *in)
{
int retVal = YAFFS_OK;
- if(!in->unlinked)
+
+ if(in->nDataChunks > 0)
{
- retVal = yaffs_UnlinkFile(in);
+ // Use soft deletion
+ if(!in->unlinked)
+ {
+ retVal = yaffs_UnlinkFile(in);
+ }
+ if(retVal == YAFFS_OK &&
+ in->unlinked &&
+ !in->deleted)
+ {
+ in->deleted = 1;
+ in->myDev->nDeletedFiles++;
+ yaffs_SoftDeleteFile(in);
+ }
+ return in->deleted ? YAFFS_OK : YAFFS_FAIL;
}
- if(retVal == YAFFS_OK &&
- in->unlinked &&
- !in->deleted)
+ else
{
- in->deleted = 1;
- in->myDev->nDeletedFiles++;
+ // The file has no data chunks so we toss it immediately
+ yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);
+ in->variant.fileVariant.top = NULL;
+ yaffs_DoGenericObjectDeletion(in);
+
+ return YAFFS_OK;
}
- return in->deleted ? YAFFS_OK : YAFFS_FAIL;
}
static int yaffs_DeleteDirectory(yaffs_Object *in)
}
+static void yaffs_AbortHalfCreatedObject(yaffs_Object *obj)
+{
+ switch(obj->variantType)
+ {
+ case YAFFS_OBJECT_TYPE_FILE: yaffs_DeleteFile(obj); break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY: yaffs_DeleteDirectory(obj); break;
+ case YAFFS_OBJECT_TYPE_SYMLINK: yaffs_DeleteSymLink(obj); break;
+ case YAFFS_OBJECT_TYPE_HARDLINK: yaffs_DeleteHardLink(obj); break;
+ case YAFFS_OBJECT_TYPE_SPECIAL: yaffs_DoGenericObjectDeletion(obj); break;
+ case YAFFS_OBJECT_TYPE_UNKNOWN: break; // should not happen.
+ }
+}
+
+
static int yaffs_UnlinkWorker(yaffs_Object *obj)
{
{
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)
{
deleted = 0;
bi = yaffs_GetBlockInfo(dev,blk);
- bi->pageBits = 0;
+ yaffs_ClearChunkBits(dev,blk);
bi->pagesInUse = 0;
+ bi->softDeletions = 0;
state = YAFFS_BLOCK_STATE_SCANNING;
// 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)
{
int endpos;
// A data chunk.
- bi->pageBits |= (1 << c);
+ yaffs_SetChunkBit(dev,blk,c);
bi->pagesInUse++;
in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);
{
// chunkId == 0, so it is an ObjectHeader.
// Thus, we read in the object header and make the object
- bi->pageBits |= (1 << c);
+ yaffs_SetChunkBit(dev,blk,c);
bi->pagesInUse++;
yaffs_ReadChunkFromNAND(dev,chunk,chunkData,NULL,1);
if(((existingSerial+1) & 3) == newSerial)
{
// Use new one - destroy the exisiting one
- yaffs_DeleteChunk(dev,in->chunkId);
+ yaffs_DeleteChunk(dev,in->chunkId,1);
in->valid = 0;
}
else
{
// Use existing - destroy this one.
- yaffs_DeleteChunk(dev,chunk);
+ yaffs_DeleteChunk(dev,chunk,1);
}
}
{
// Hoosterman, another problem....
// We're trying to use a non-directory as a directory
- // Todo ... handle
- T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: attempting to use non-directory as a directory in scan" TENDSTR)));
+
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." TENDSTR)));
+ parent = dev->lostNFoundDir;
}
break;
case YAFFS_OBJECT_TYPE_HARDLINK:
in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId;
- (yaffs_Object *)(in->hardLinks.next) = hardList;
+ in->hardLinks.next = (struct list_head *)hardList;
hardList = in;
break;
case YAFFS_OBJECT_TYPE_DIRECTORY: // Do nothing
}
+ {
+ struct list_head *i;
+ struct list_head *n;
+
+ yaffs_Object *l;
+ // Soft delete all the unlinked files
+ list_for_each_safe(i,n,&dev->unlinkedDir->variant.directoryVariant.children)
+ {
+ if(i)
+ {
+ l = list_entry(i, yaffs_Object,siblings);
+ if(l->deleted)
+ {
+ yaffs_SoftDeleteFile(l);
+ }
+ }
+ }
+ }
return YAFFS_OK;
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;
+ }
}
}
if(valid & ATTR_UID) obj->st_uid = attr->ia_uid;
if(valid & ATTR_GID) obj->st_gid = attr->ia_gid;
- if(valid & ATTR_ATIME) obj->st_atime = attr->ia_atime;
- if(valid & ATTR_CTIME) obj->st_ctime = attr->ia_ctime;
- if(valid & ATTR_MTIME) obj->st_mtime = attr->ia_mtime;
+ if(valid & ATTR_ATIME) obj->st_atime = Y_TIME_CONVERT(attr->ia_atime);
+ if(valid & ATTR_CTIME) obj->st_ctime = Y_TIME_CONVERT(attr->ia_ctime);
+ if(valid & ATTR_MTIME) obj->st_mtime = Y_TIME_CONVERT(attr->ia_mtime);
+
if(valid & ATTR_SIZE) yaffs_ResizeFile(obj,attr->ia_size);
return YAFFS_OK;
}
+
int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr)
{
unsigned int valid = 0;
attr->ia_uid = obj->st_uid; valid |= ATTR_UID;
attr->ia_gid = obj->st_gid; valid |= ATTR_GID;
- attr->ia_atime = obj->st_atime; valid |= ATTR_ATIME;
- attr->ia_ctime = obj->st_ctime; valid |= ATTR_CTIME;
- attr->ia_mtime = obj->st_mtime; valid |= ATTR_MTIME;
+ Y_TIME_CONVERT(attr->ia_atime) = obj->st_atime; valid |= ATTR_ATIME;
+ Y_TIME_CONVERT(attr->ia_ctime) = obj->st_ctime; valid |= ATTR_CTIME;
+ Y_TIME_CONVERT(attr->ia_mtime) = obj->st_mtime; valid |= ATTR_MTIME;
+
attr->ia_size = yaffs_GetFileSize(obj); valid |= ATTR_SIZE;
attr->ia_valid = valid;
int extraBits;
int nBlocks;
+ if( dev->nBytesPerChunk != YAFFS_BYTES_PER_CHUNK ||
+ dev->nChunksPerBlock < 2 ||
+ dev->nReservedBlocks < 2 ||
+ dev->startBlock <= 0 ||
+ dev->endBlock <= 0 ||
+ dev->endBlock <= (dev->startBlock + dev->nReservedBlocks)
+ )
+ {
+ //these parameters must be set before stating yaffs
+ // Other parameters startBlock,
+ return YAFFS_FAIL;
+ }
+
if(!yaffs_CheckStructures())
T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs_CheckStructures failed\n" TENDSTR)));
return YAFFS_FAIL;
}
+
+ if(dev->isMounted)
+ {
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: device already mounted\n" TENDSTR)));
+ return YAFFS_FAIL;
+ }
+
+ dev->isMounted = 1;
if(dev->startBlock <= 0 ||
(dev->endBlock - dev->startBlock) < 10)
// 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++)
{
{
dev->chunkGroupBits = bits - 16;
}
+
dev->chunkGroupSize = 1 << dev->chunkGroupBits;
+
+ if(dev->nChunksPerBlock < dev->chunkGroupSize)
+ {
+ // We have a problem because the soft delete won't work if
+ // the chunk group size > chunks per block.
+ // This can be remedied by using larger "virtual blocks".
+
+ return YAFFS_FAIL;
+ }
+
+
// 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;
dev->eccUnfixed=0;
dev->tagsEccFixed=0;
dev->tagsEccUnfixed=0;
+ dev->nErasedBlocks=0;
+
+ dev->localBuffer = YMALLOC(dev->nBytesPerChunk);
+
+
- dev->localBuffer = YMALLOC(YAFFS_BYTES_PER_CHUNK);
yaffs_InitialiseBlocks(dev,nBlocks);
yaffs_InitialiseObjects(dev);
-#ifdef CONFIG_YAFFS_SHORT_OP_CACHE
+ if(dev->nShortOpCaches > 0)
{
int i;
- for(i=0; i < YAFFS_N_CACHE_CHUNKS; i++)
+
+ if(dev->nShortOpCaches > YAFFS_MAX_SHORT_OP_CACHES)
+ {
+ dev->nShortOpCaches = YAFFS_MAX_SHORT_OP_CACHES;
+ }
+
+ dev->srCache = YMALLOC( dev->nShortOpCaches * sizeof(yaffs_ChunkCache));
+
+ for(i=0; i < dev->nShortOpCaches; i++)
{
dev->srCache[i].object = NULL;
dev->srCache[i].lastUse = 0;
}
dev->srLastUse = 0;
}
-#endif
dev->cacheHits = 0;
void yaffs_Deinitialise(yaffs_Device *dev)
{
- yaffs_DeinitialiseBlocks(dev);
- yaffs_DeinitialiseTnodes(dev);
- yaffs_DeinitialiseObjects(dev);
+ if(dev->isMounted)
+ {
+
+ yaffs_DeinitialiseBlocks(dev);
+ yaffs_DeinitialiseTnodes(dev);
+ yaffs_DeinitialiseObjects(dev);
+ if(dev->nShortOpCaches > 0)
+ YFREE(dev->srCache);
+ YFREE(dev->localBuffer);
+ dev->isMounted = 0;
+ }
}
+#if 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;
nFree += l->nDataChunks;
}
}
+
+
+ // printf("___________ nFreeChunks is %d nFree is %d\n",dev->nFreeChunks,nFree);
+ if(nFree < 0) nFree = 0;
- return (nFree < 0) ? 0 : nFree;
+ return nFree;
}
+#endif
+
+int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
+{
+ int nFree;
+ int pending;
+ int b;
+ int nDirtyCacheChunks=0;
+
+ yaffs_BlockInfo *blk;
+
+ struct list_head *i;
+ yaffs_Object *l;
+
+ for(nFree = 0, b = dev->startBlock; b <= dev->endBlock; b++)
+ {
+ blk = yaffs_GetBlockInfo(dev,b);
+
+ switch(blk->blockState)
+ {
+ case YAFFS_BLOCK_STATE_EMPTY:
+ case YAFFS_BLOCK_STATE_ALLOCATING:
+ case YAFFS_BLOCK_STATE_FULL: nFree += (dev->nChunksPerBlock - blk->pagesInUse); break;
+ default: break;
+ }
+ }
+
+
+ pending = 0;
+
+ // To the free chunks add the chunks that are in the deleted unlinked files.
+ list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
+ {
+ if(i)
+ {
+ l = list_entry(i, yaffs_Object,siblings);
+ if(l->deleted)
+ {
+ pending++;
+ pending += l->nDataChunks;
+ }
+ }
+ }
+
+
+
+ //printf("___________ really free is %d, pending %d, nFree is %d\n",nFree,pending, nFree+pending);
+
+ if(nFree != dev->nFreeChunks)
+ {
+ // printf("___________Different! really free is %d, nFreeChunks %d\n",nFree dev->nFreeChunks);
+ }
+
+ nFree += pending;
+
+ // Now count the number of dirty chunks in the cache and subtract those
+
+ {
+ int i;
+ for(i = 0; i < dev->nShortOpCaches; i++)
+ {
+ if(dev->srCache[i].dirty) nDirtyCacheChunks++;
+ }
+ }
+
+ nFree -= nDirtyCacheChunks;
+
+ nFree -= ((dev->nReservedBlocks + 1) * dev->nChunksPerBlock);
+
+ if(nFree < 0) nFree = 0;
+
+ return nFree;
+
+}
+
+
/////////////////// YAFFS test code //////////////////////////////////
yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags")
yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion")
yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare")
+#ifndef CONFIG_YAFFS_TNODE_LIST_DEBUG
yaffs_CheckStruct(yaffs_Tnode,2* YAFFS_NTNODES_LEVEL0,"yaffs_Tnode")
+#endif
yaffs_CheckStruct(yaffs_ObjectHeader,512,"yaffs_ObjectHeader")
+
+
+