*/
//yaffs_guts.c
-const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.10 2002-09-27 20:50:50 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);
#define yaffs_CheckFileSanity(in)
#endif
-#ifdef CONFIG_YAFFS_SHORT_OP_CACHE
-static void yaffs_InvalidateChunkCache(yaffs_Object *in);
-#define yaffs_INVALIDATECHUNKCACHE(in) yaffs_InvalidateChunkCache(in)
-#else
-#define yaffs_INVALIDATECHUNKCACHE(in)
-#endif
+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: 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 * dev->nChunksPerBlock)
+ {
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d is not valid" TENDSTR),chunkInNAND));
+ return YAFFS_FAIL;
+ }
+
dev->nPageWrites++;
return dev->writeChunkToNAND(dev,chunkInNAND,data,spare);
}
-int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare,int doErrorCorrection)
+
+int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,
+ int chunkInNAND,
+ __u8 *data,
+ yaffs_Spare *spare,
+ int doErrorCorrection)
{
int retVal;
- __u8 calcEcc[3];
yaffs_Spare localSpare;
- int eccResult1,eccResult2;
-
+
dev->nPageReads++;
+
+
+
if(!spare && data)
{
// If we don't have a real spare, then we use a local one.
spare = &localSpare;
}
- retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,spare);
- if(data && doErrorCorrection)
+
+ if(!dev->useNANDECC)
{
- // Do ECC correction
- //Todo handle any errors
- nand_calculate_ecc(data,calcEcc);
- eccResult1 = nand_correct_data (data,spare->ecc1, calcEcc);
- nand_calculate_ecc(&data[256],calcEcc);
- eccResult2 = nand_correct_data (&data[256],spare->ecc2, calcEcc);
-
- if(eccResult1>0)
- {
- T((TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));
- dev->eccFixed++;
- }
- else if(eccResult1<0)
- {
- T((TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));
- dev->eccUnfixed++;
- }
-
- if(eccResult2>0)
- {
- T((TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));
- dev->eccFixed++;
- }
- else if(eccResult2<0)
- {
- T((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);
- }
+ 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);
+ }
+
+ }
}
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)
{
#endif
if(!writeOk)
{
- T((TSTR("**>> yaffs chunk %d was not erased" TENDSTR),chunk));
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d was not erased" TENDSTR),chunk));
}
else
{
#ifndef CONFIG_YAFFS_DISABLE_WRITE_VERIFY
if(!yaffs_VerifyCompare(data,rbData,spare,&rbSpare))
- {
+ {
// Didn't verify
- T((TSTR("**>> yaffs write verify failed on chunk %d" TENDSTR), chunk));
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs write verify failed on chunk %d" TENDSTR), chunk));
writeOk = 0;
}
if(attempts > 1)
{
- T((TSTR("**>> yaffs write required %d attempts" TENDSTR),attempts));
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs write required %d attempts" TENDSTR),attempts));
dev->nRetriedWrites+= (attempts - 1);
}
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;
- T((TSTR("**>>Block %d marked for retirement" TENDSTR),blockInNAND));
+ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>>Block %d marked for retirement" TENDSTR),blockInNAND));
//TODO
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);
}
__u8 *bname = (__u8 *)name;
if(bname)
{
- while (*bname)
+ while ((*bname) && (i <=YAFFS_MAX_NAME_LENGTH))
{
+
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
+ sum += toupper(*bname) * i;
+#else
sum += (*bname) * i;
+#endif
i++;
bname++;
}
return sum;
}
+void yaffs_SetObjectName(yaffs_Object *obj, const char *name)
+{
+#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
+ if(name && strlen(name) <= YAFFS_SHORT_NAME_LENGTH)
+ {
+ strcpy(obj->shortName,name);
+ }
+ else
+ {
+ obj->shortName[0]='\0';
+ }
+#endif
+ obj->sum = yaffs_CalcNameSum(name);
+}
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)
if (!newTnodes)
{
- YALERT("Could not malloc tnodes");
+ T(YAFFS_TRACE_ERROR,(TSTR("yaffs: Could not allocate Tnodes"TENDSTR)));
return YAFFS_FAIL;
}
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;
// Now add this bunch of tnodes to a list for freeing up.
-
+ // NB If we can't add this to the management list it isn't fatal
+ // but it just means we can't free this bunch of tnodes later.
tnl = YMALLOC(sizeof(yaffs_TnodeList));
if(!tnl)
{
- YALERT("Could not add tnodes to management list");
+ T(YAFFS_TRACE_ERROR,(TSTR("yaffs: Could not add tnodes to management list" TENDSTR)));
+
}
else
{
}
- YINFO("Tnodes created");
+ T(YAFFS_TRACE_ALLOCATE,(TSTR("yaffs: Tnodes added" TENDSTR)));
return YAFFS_OK;
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;
}
+#if 0
void yaffs_TnodeTest(yaffs_Device *dev)
{
}
}
+#endif
+
////////////////// END OF TNODE MANIPULATION ///////////////////////////
// Check sane level and chunk Id
if(level < 0 || level > YAFFS_TNODES_MAX_LEVEL)
{
- char str[50];
- sprintf(str,"Bad level %d",level);
- YALERT(str);
+// char str[50];
+// sprintf(str,"Bad level %d",level);
+// YALERT(str);
return NULL;
}
if(chunkId > YAFFS_MAX_CHUNK_ID)
{
- char str[50];
- sprintf(str,"Bad chunkId %d",chunkId);
- YALERT(str);
+// char str[50];
+// sprintf(str,"Bad chunkId %d",chunkId);
+// YALERT(str);
return NULL;
}
yaffs_Tnode *tn;
int requiredTallness;
+ int i;
+ int l;
- __u32 i;
- __u32 l;
-
+ __u32 x;
+
//T((TSTR("AddOrFind topLevel=%d, chunk=%d"),fStruct->topLevel,chunkId));
// Check sane level and page Id
if(fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL)
{
- char str[50];
- sprintf(str,"Bad level %d",fStruct->topLevel);
- YALERT(str);
+// char str[50];
+// sprintf(str,"Bad level %d",fStruct->topLevel);
+// YALERT(str);
return NULL;
}
if(chunkId > YAFFS_MAX_CHUNK_ID)
{
- char str[50];
- sprintf(str,"Bad chunkId %d",chunkId);
- YALERT(str);
+// char str[50];
+// sprintf(str,"Bad chunkId %d",chunkId);
+// YALERT(str);
return NULL;
}
// First check we're tall enough (ie enough topLevel)
- i = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS);
+ x = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS);
requiredTallness = 0;
- while(i)
+ while(x)
{
- i >>= YAFFS_TNODES_INTERNAL_BITS;
+ x >>= YAFFS_TNODES_INTERNAL_BITS;
requiredTallness++;
}
}
else
{
- YALERT("No more tnodes");
+ T(YAFFS_TRACE_ERROR,(TSTR("yaffs: no more tnodes" TENDSTR)));
}
}
tn = fStruct->top;
while (l > 0 && tn)
{
- i = (chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS + (l-1) * YAFFS_TNODES_INTERNAL_BITS)) &
+ x = (chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS + (l-1) * YAFFS_TNODES_INTERNAL_BITS)) &
YAFFS_TNODES_INTERNAL_MASK;
//T((TSTR(" [%d:%d]"),l,i));
- if(!tn->internal[i])
+ if(!tn->internal[x])
{
//T((TSTR(" added")));
- tn->internal[i] = yaffs_GetTnode(dev);
+ tn->internal[x] = yaffs_GetTnode(dev);
}
- tn = tn->internal[i];
+ tn = tn->internal[x];
l--;
}
}
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;
+ }
+ }
+}
+
+
+
/////////////////////// End of File Structure functions. /////////////////
// yaffs_CreateFreeObjects creates a bunch more objects and
if (!newObjects)
{
- YALERT("Could not allocate more objects");
+ T(YAFFS_TRACE_ALLOCATE,(TSTR("yaffs: Could not allocate more objects" TENDSTR)));
return YAFFS_FAIL;
}
// 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;
list = YMALLOC(sizeof(yaffs_ObjectList));
if(!list)
{
- YALERT("Could not add Objects to management list");
+ T(YAFFS_TRACE_ALLOCATE,(TSTR("Could not add objects to management list" TENDSTR)));
}
else
{
}
- YINFO("Objects created");
-
return YAFFS_OK;
}
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;
int found = 0;
struct list_head *i;
- int n = bucket;
+ __u32 n = (__u32)bucket;
//yaffs_CheckObjectHashSanity();
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;
}
if(!list_empty(&in->hashLink))
{
- YINFO("!!!");
+ //YINFO("!!!");
}
-
+
list_add(&in->hashLink,&dev->objectBucket[bucket].list);
dev->objectBucket[bucket].count++;
}
-yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,int number)
+yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,__u32 number)
{
int bucket = yaffs_HashFunction(number);
struct list_head *i;
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->objectId = number;
yaffs_HashObject(theObject);
theObject->variantType = type;
- theObject->st_atime = theObject->st_mtime = theObject->st_ctime = CURRENT_TIME;
+#ifdef CONFIG_YAFFS_WINCE
+ yfsd_WinFileTimeNow(theObject->win_atime);
+ theObject->win_ctime[0] = theObject->win_mtime[0] = theObject->win_atime[0];
+ theObject->win_ctime[1] = theObject->win_mtime[1] = theObject->win_atime[1];
+
+#else
+
+ theObject->st_atime = theObject->st_mtime = theObject->st_ctime = Y_CURRENT_TIME;
+#endif
switch(type)
{
case YAFFS_OBJECT_TYPE_FILE:
in->variantType = type;
in->st_mode = mode;
+
+#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[1];
+
+#else
+
+ in->st_atime = in->st_mtime = in->st_ctime = Y_CURRENT_TIME;
in->st_rdev = rdev;
in->st_uid = uid;
in->st_gid = gid;
- in->st_atime = in->st_mtime = in->st_ctime = CURRENT_TIME;
-
+#endif
in->nDataChunks = 0;
- in->sum = yaffs_CalcNameSum(name);
+ yaffs_SetObjectName(in,name);
in->dirty = 1;
yaffs_AddObjectToDirectory(parent,in);
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;
}
}
-static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const char *newName)
+static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const char *newName,int force)
{
int unlinkOp;
// If the object is a file going into the unlinked directory, then it is OK to just stuff it in since
// duplicate names are allowed.
// Otherwise only proceed if the new name does not exist and if we're putting it into a directory.
- if( unlinkOp||
- (!yaffs_FindObjectByName(newDir,newName) &&
- newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY))
+ if( (unlinkOp||
+ force ||
+ !yaffs_FindObjectByName(newDir,newName)) &&
+ newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
{
- obj->sum = yaffs_CalcNameSum(newName);
+ yaffs_SetObjectName(obj,newName);
obj->dirty = 1;
yaffs_AddObjectToDirectory(newDir,obj);
return YAFFS_FAIL;
}
+
+
int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName)
{
yaffs_Object *obj;
+ int force = 0;
+
+#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
+ if(oldDir == newDir && _stricmp(oldName,newName) == 0)
+ {
+ force = 1;
+ }
+#endif
obj = yaffs_FindObjectByName(oldDir,oldName);
if(obj && obj->renameAllowed)
{
- return yaffs_ChangeObjectName(obj,newDir,newName);
+ return yaffs_ChangeObjectName(obj,newDir,newName,force);
}
return YAFFS_FAIL;
}
if(countEm != dev->objectBucket[bucket].count)
{
- YALERT("Inode hash inconsistency");
+ T(YAFFS_TRACE_ERROR,(TSTR("Inode hash inconsistency" TENDSTR)));
ok = YAFFS_FAIL;
}
}
return ok;
}
+#if 0
void yaffs_ObjectTest(yaffs_Device *dev)
{
yaffs_Object *in[1000];
}
-
+#endif
/////////////////////////// Block Management and Page Allocation ///////////////////
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->endBlock)
+ if ( b < dev->startBlock || b > dev->endBlock)
{
b = dev->startBlock;
}
+
+ if(b < dev->startBlock || b > dev->endBlock)
+ {
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> Block %d is not valid" TENDSTR),b));
+ YBUG();
+ }
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);
}
}
erasedOk = yaffs_EraseBlockInNAND(dev,blockNo);
if(!erasedOk)
{
- T((TSTR("**>> Erasure failed %d" TENDSTR),blockNo));
+ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Erasure failed %d" TENDSTR),blockNo));
}
}
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((TSTR("Erased block %d" TENDSTR),blockNo));
+ T(YAFFS_TRACE_ERASE,(TSTR("Erased block %d" TENDSTR),blockNo));
}
else
{
yaffs_RetireBlock(dev,blockNo);
- T((TSTR("**>> Block %d retired" TENDSTR),blockNo));
+ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Block %d retired" 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 tragedy: no space during gc" TENDSTR)));
+
return -1;
}
for(i = dev->startBlock; i <= dev->endBlock; i++)
{
- bi = yaffs_GetBlockInfo(dev,i);
- if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
+ dev->allocationBlockFinder++;
+ if(dev->allocationBlockFinder <dev->startBlock || dev->allocationBlockFinder> dev->endBlock)
{
- bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING;
- dev->nErasedBlocks--;
- return i;
+ 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 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;
return retVal;
}
- T((TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR)));
+ T(YAFFS_TRACE_ERROR,(TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR)));
return -1;
}
+// To determine if we have enough space we just look at the
+// number of erased blocks.
+// The cache is allowed to use reserved blocks.
+
+static int yaffs_CheckSpaceForChunkCache(yaffs_Device *dev)
+{
+ 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));
yaffs_ReadChunkFromNAND(dev,oldChunk,buffer, &spare,1);
yaffs_GetTagsFromSpare(dev,&spare,&tags);
-
- tags.serialNumber++;
- yaffs_LoadTagsIntoSpare(&spare,&tags);
-#if 0
- newChunk = yaffs_AllocatePage(dev,1);
- if(newChunk < 0)
+ object = yaffs_FindObjectByNumber(dev,tags.objectId);
+
+ if(object && object->deleted && tags.chunkId != 0)
{
- return YAFFS_FAIL;
+ // Data chunk in a deleted file, throw it away
+ // It's a deleted data chunk,
+ // No need to copy this, just forget about it and fix up the
+ // object.
+
+ //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;
}
-
- yaffs_WriteChunkToNAND(dev,newChunk, buffer, &spare);
-
-#else
- newChunk = yaffs_WriteNewChunkToNAND(dev, buffer, &spare,1);
-#endif
- if(newChunk < 0)
+ else if( 0 /* Todo object && object->deleted && object->nDataChunks == 0 */)
{
- return YAFFS_FAIL;
+ // Deleted object header with no data chunks.
+ // Can be discarded and the file deleted.
+ object->chunkId = 0;
+ yaffs_FreeTnode(object->myDev,object->variant.fileVariant.top);
+ object->variant.fileVariant.top = NULL;
+ yaffs_DoGenericObjectDeletion(object);
+
}
+ else if(object)
+ {
+ // It's either a data chunk in a live file or
+ // an ObjectHeader, so we're interested in it.
+ // NB Need to keep the ObjectHeaders of deleted files
+ // until the whole file has been deleted off
+ tags.serialNumber++;
+ yaffs_LoadTagsIntoSpare(&spare,&tags);
- object = yaffs_FindObjectByNumber(dev,tags.objectId);
+ dev->nGCCopies++;
+
+ newChunk = yaffs_WriteNewChunkToNAND(dev, buffer, &spare,1);
- // Ok, now fix up the Tnodes etc.
+ if(newChunk < 0)
+ {
+ return YAFFS_FAIL;
+ }
- if(tags.chunkId == 0)
- {
- // It's a header
- object->chunkId = newChunk;
- }
- else
- {
- // It's a data chunk
- yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0);
-
+ // Ok, now fix up the Tnodes etc.
+
+ if(tags.chunkId == 0)
+ {
+ // It's a header
+ object->chunkId = newChunk;
+ object->serial = tags.serialNumber;
+ }
+ else
+ {
+ // It's a data chunk
+ yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0);
+ }
}
- 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.
if(dev->unlinkedDeletion)
{
yaffs_Object *obj = dev->unlinkedDeletion;
- int limit;
int delresult;
- limit = 50; // Max number of chunks to delete in a file. NB this can be exceeded, but not by much.
+ int limit; // Number of chunks to delete in a file.
+ // NB this can be exceeded, but not by much.
+
+ 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);
-
- if(buffer)
+ if(!dev->useNANDECC && buffer)
{
yaffs_CalcECC(buffer,&spare);
}
yaffs_SpareInitialise(&spare);
-
- if(buffer)
+ if(!dev->useNANDECC && buffer)
{
yaffs_CalcECC(buffer,&spare);
}
}
-#if YAFFS_PARANOID
+#ifdef YAFFS_PARANOID
static int yaffs_CheckFileSanity(yaffs_Object *in)
{
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(existingChunk <=0)
{
//Hoosterman - how did this happen?
- // todo
+
+ 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);
+ }
+ else
+ {
+ dev->nUnmarkedDeletions++;
+ }
- yaffs_WriteChunkToNAND(dev,chunkId,NULL,&spare);
- yaffs_HandleUpdateChunk(dev,chunkId,&spare);
bi = yaffs_GetBlockInfo(dev,block);
{
dev->nFreeChunks++;
- bi->pageBits &= ~(1 << page);
+ yaffs_ClearChunkBit(dev,block,page);
bi->pagesInUse--;
if(bi->pagesInUse == 0 &&
yaffs_CalcTagsECC(&newTags);
-
- #if 0
- // Create new chunk in NAND
- newChunkId = yaffs_AllocatePage(dev,useReserve);
-
-
- if(newChunkId >= 0)
- {
-
-
- yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,buffer,&newTags);
-
- yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId);
-
-
- if(prevChunkId >= 0)
- {
- yaffs_DeleteChunk(dev,prevChunkId);
-
- }
-
- yaffs_CheckFileSanity(in);
-
- return newChunkId;
- }
-
-
- return -1;
-#else
-
newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags,useReserve);
if(newChunkId >= 0)
{
if(prevChunkId >= 0)
{
- yaffs_DeleteChunk(dev,prevChunkId);
+ yaffs_DeleteChunk(dev,prevChunkId,1);
}
}
return newChunkId;
-#endif
+
yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bufferNew;
yaffs_ObjectHeader *ohOld = (yaffs_ObjectHeader *)bufferOld;
- yaffs_INVALIDATECHUNKCACHE(in);
if(!in->fake || force)
{
// Header data
oh->type = in->variantType;
-
+
oh->st_mode = in->st_mode;
+
+#ifdef CONFIG_YAFFS_WINCE
+ oh->win_atime[0] = in->win_atime[0];
+ oh->win_ctime[0] = in->win_ctime[0];
+ oh->win_mtime[0] = in->win_mtime[0];
+ oh->win_atime[1] = in->win_atime[1];
+ oh->win_ctime[1] = in->win_ctime[1];
+ oh->win_mtime[1] = in->win_mtime[1];
+#else
oh->st_uid = in->st_uid;
oh->st_gid = in->st_gid;
oh->st_atime = in->st_atime;
oh->st_mtime = in->st_mtime;
oh->st_ctime = in->st_ctime;
oh->st_rdev = in->st_rdev;
-
+#endif
if(in->parent)
{
oh->parentObjectId = in->parent->objectId;
oh->parentObjectId = 0;
}
- oh->sum = in->sum;
+ //oh->sum = in->sum;
if(name && *name)
{
memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1);
yaffs_CalcTagsECC(&newTags);
-
-
-#if 0
- // Create new chunk in NAND
- newChunkId = yaffs_AllocatePage(dev,1);
-
- if(newChunkId >= 0)
- {
- yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,bufferNew,&newTags);
-
- in->chunkId = newChunkId;
-
- if(prevChunkId >= 0)
- {
- yaffs_DeleteChunk(dev,prevChunkId);
- }
-
- in->dirty = 0;
- return newChunkId;
- }
-
- return -1;
-#else
// 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;
return newChunkId;
-#endif
}
return 0;
}
-/////////////////////// Short Read Cache ////////////////////////////////
-// In many siturations where there is no high level buffering a lot of
-// reads might be short sequential reads. eg. scanning a jpeg file.
-// In these cases, a shoprt read cache can provide a huge perfomance benefit
+/////////////////////// Short Operations Cache ////////////////////////////////
+// In many siturations where there is no high level buffering (eg WinCE) a lot of
+// reads might be short sequential reads, and a lot of writes may be short
+// sequential writes. eg. scanning/writing a jpeg file.
+// In these cases, a short read/write cache can provide a huge perfomance benefit
// with dumb-as-a-rock code.
-// There are a limited number (~10) of cache chunks per device
+// There are a limited number (~10) of cache chunks per device so that we don't
+// need a very intelligent search.
-#ifdef CONFIG_YAFFS_SHORT_OP_CACHE
-// Invalidate all the cache pages associated with this object
-// Do this whenever ther file is modified... dumb as a rock remember!
-static void yaffs_InvalidateChunkCache(yaffs_Object *in)
+
+
+static void yaffs_FlushFilesChunkCache(yaffs_Object *obj)
{
+ yaffs_Device *dev = obj->myDev;
+ int lowest;
int i;
- yaffs_Device *dev = in->myDev;
- int id = in->objectId;
+ yaffs_ChunkCache *cache;
+ int chunkWritten = 0;
+ int nBytes;
+ int nCaches = obj->myDev->nShortOpCaches;
- for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)
+ if (nCaches > 0)
{
- if(dev->srCache[i].objectId == id)
+ do{
+ cache = NULL;
+
+ // Find the dirty cache for this object with the lowest chunk id.
+ for(i = 0; i < nCaches; i++)
+ {
+ if(dev->srCache[i].object == obj &&
+ dev->srCache[i].dirty)
+ {
+ if(!cache || dev->srCache[i].chunkId < lowest)
+ {
+ cache = &dev->srCache[i];
+ lowest = cache->chunkId;
+ }
+ }
+ }
+
+ if(cache)
+ {
+ //Write it out
+
+#if 0
+ nBytes = cache->object->variant.fileVariant.fileSize - ((cache->chunkId -1) * YAFFS_BYTES_PER_CHUNK);
+
+ 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->object = NULL;
+ }
+
+ } while(cache && chunkWritten > 0);
+
+ if(cache)
{
- dev->srCache[i].objectId = 0;
+ //Hoosterman, disk full while writing cache out.
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: no space during cache write" TENDSTR)));
+
}
- }
+ }
+
}
// Grab us a chunk for use.
// First look for an empty one.
-// Then look for the least recently used one.
-static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device *dev)
+// Then look for the least recently used non-dirty one.
+// Then look for the least recently used dirty one...., flush and look again.
+static yaffs_ChunkCache *yaffs_GrabChunkCacheWorker(yaffs_Device *dev)
{
int i;
int usage;
int theOne;
- for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)
+ if(dev->nShortOpCaches > 0)
{
- if(dev->srCache[i].objectId == 0)
+ 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;
- usage = dev->srCache[i].lastUse;
- theOne = 0;
+ theOne = -1;
+ usage = 0; // just to stop the compiler grizzling
- for(i = 1; i < YAFFS_N_CACHE_CHUNKS; i++)
- {
- if(dev->srCache[i].lastUse < usage)
+ 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));
+
+ //if(theOne >= 0) printf("Grabbed non-empty cache %d\n",theOne);
+
+ return theOne >= 0 ? &dev->srCache[theOne] : NULL;
+ }
+ else
+ {
+ return NULL;
}
- //T(("Grabbing non-empty %d\n",theOne));
- return &dev->srCache[theOne];
+}
+
+
+static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device *dev)
+{
+ yaffs_ChunkCache *cache;
+ yaffs_Object *theObj;
+ int usage;
+ int i;
+ int pushout;
+
+ if(dev->nShortOpCaches > 0)
+ {
+ // Try find a non-dirty one...
+
+ 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.
+
+ theObj = dev->srCache[0].object;
+ usage = dev->srCache[0].lastUse;
+ cache = &dev->srCache[0];
+ pushout = 0;
+ for(i = 1; i < dev->nShortOpCaches; i++)
+ {
+ 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;
+ }
+ }
+
+ if(!cache || cache->dirty)
+ {
+
+ //printf("Dirty ");
+ yaffs_FlushFilesChunkCache(theObj);
+
+ // Try again
+ cache = yaffs_GrabChunkCacheWorker(dev);
+ }
+ else
+ {
+ //printf(" pushout %d\n",pushout);
+ }
+
+ }
+
+ return cache;
+ }
+ else
+ return NULL;
+
}
// Find a cached chunk
-static yaffs_ChunkCache *yaffs_FindChunkCache(yaffs_Device *dev, int objectId, int chunkId)
+static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object *obj, int chunkId)
{
+ yaffs_Device *dev = obj->myDev;
int i;
-;
-
- for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)
+ if(dev->nShortOpCaches > 0)
{
- if(dev->srCache[i].objectId == objectId &&
- 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)
+static void yaffs_UseChunkCache(yaffs_Device *dev, yaffs_ChunkCache *cache, int isAWrite)
{
- if( dev->srLastUse < 0 ||
- dev->srLastUse > 1000000)
+
+ 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;
+ }
+ }
}
+// Invalidate a single cache page.
+// Do this when a whole page gets written,
+// ie the short cache for this page is no longer valid.
+static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId)
+{
+ if(object->myDev->nShortOpCaches > 0)
+ {
+ yaffs_ChunkCache *cache = yaffs_FindChunkCache(object,chunkId);
+
+ if(cache)
+ {
+ cache->object = NULL;
+ }
+ }
+}
+
+
+// Invalidate all the cache pages associated with this object
+// Do this whenever ther file is deleted or resized.
+static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in)
+{
+ int i;
+ yaffs_Device *dev = in->myDev;
+
+ if(dev->nShortOpCaches > 0)
+ {
+ // Now invalidate it.
+ for(i = 0; i < dev->nShortOpCaches; i++)
+ {
+ if(dev->srCache[i].object == in)
+ {
+ dev->srCache[i].object = NULL;
+ }
+ }
+ }
+}
-#endif
int yaffs_ReadDataFromFile(yaffs_Object *in, __u8 * buffer, __u32 offset, int nBytes)
{
-// yaffs_Device *dev = in->myDev;
-
-#ifdef CONFIG_YAFFS_SHORT_OP_CACHE
- yaffs_ChunkCache *cache;
-#endif
-
- __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];
int chunk;
int start;
int nToCopy;
int n = nBytes;
int nDone = 0;
+ yaffs_ChunkCache *cache;
+
+ yaffs_Device *dev;
+
+ dev = in->myDev;
while(n > 0)
{
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
- // If we can't find the data in the cache, then load it up.
- cache = yaffs_FindChunkCache(in->myDev,in->objectId,chunk);
- if(!cache)
+ if(dev->nShortOpCaches > 0)
{
- cache = yaffs_GrabChunkCache(in->myDev);
- cache->objectId = in->objectId;
- cache->chunkId = chunk;
- 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(in->myDev,cache);
+ 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,localBuffer);
- memcpy(buffer,&localBuffer[start],nToCopy);
-#endif
}
else
{
-#ifdef WIN32
+#ifdef CONFIG_YAFFS_WINCE
+
// Under WinCE can't do direct transfer. Need to use a local buffer.
// This is because we otherwise screw up WinCE's memory mapper
- yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);
- memcpy(buffer,localBuffer,YAFFS_BYTES_PER_CHUNK);
+ yaffs_ReadChunkDataFromObject(in,chunk,dev->localBuffer);
+ memcpy(buffer,dev->localBuffer,YAFFS_BYTES_PER_CHUNK);
#else
// A full chunk. Read directly into the supplied buffer.
yaffs_ReadChunkDataFromObject(in,chunk,buffer);
}
+
int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, int nBytes)
{
- __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];
int chunk;
int start;
int n = nBytes;
int nDone = 0;
int nToWriteBack;
- int endOfWrite = offset+nBytes;
+ int startOfWrite = offset;
int chunkWritten = 0;
+ int nBytesRead;
+
+ yaffs_Device *dev;
+
+ dev = in->myDev;
- yaffs_INVALIDATECHUNKCACHE(in);
while(n > 0 && chunkWritten >= 0)
{
// OK now check for the curveball where the start and end are in
// the same chunk.
+
if( (start + n) < YAFFS_BYTES_PER_CHUNK)
{
nToCopy = n;
- nToWriteBack = (start + n);
+
+ // Now folks, to calculate how many bytes to write back....
+ // If we're overwriting and not writing to then end of file then
+ // we need to write back as much as was there before.
+
+ nBytesRead = in->variant.fileVariant.fileSize - ((chunk -1) * YAFFS_BYTES_PER_CHUNK);
+
+ if(nBytesRead > YAFFS_BYTES_PER_CHUNK)
+ {
+ nBytesRead = YAFFS_BYTES_PER_CHUNK;
+ }
+
+ nToWriteBack = (nBytesRead > (start + n)) ? nBytesRead : (start +n);
+
}
else
{
if(nToCopy != YAFFS_BYTES_PER_CHUNK)
{
// 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.
+ if(dev->nShortOpCaches > 0)
+ {
+ 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);
+ cache->nBytes = nToWriteBack;
+ }
+ 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.
- yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);
+ yaffs_ReadChunkDataFromObject(in,chunk,dev->localBuffer);
- memcpy(&localBuffer[start],buffer,nToCopy);
+ memcpy(&dev->localBuffer[start],buffer,nToCopy);
- chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,nToWriteBack,0);
+ chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,dev->localBuffer,nToWriteBack,0);
- //T(("Write with readback to chunk %d %d\n",chunk,chunkWritten));
+ //T(("Write with readback to chunk %d %d start %d copied %d wrote back %d\n",chunk,chunkWritten,start, nToCopy, nToWriteBack));
+ }
}
else
{
-#ifdef WIN32
+#ifdef CONFIG_YAFFS_WINCE
// Under WinCE can't do direct transfer. Need to use a local buffer.
// This is because we otherwise screw up WinCE's memory mapper
- memcpy(localBuffer,buffer,YAFFS_BYTES_PER_CHUNK);
- chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,YAFFS_BYTES_PER_CHUNK,0);
+ memcpy(dev->localBuffer,buffer,YAFFS_BYTES_PER_CHUNK);
+ chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,dev->localBuffer,YAFFS_BYTES_PER_CHUNK,0);
#else
// A full chunk. Write directly from the supplied buffer.
chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,buffer,YAFFS_BYTES_PER_CHUNK,0);
#endif
+ // Since we've overwritten the cached data, we better invalidate it.
+ yaffs_InvalidateChunkCache(in,chunk);
//T(("Write to chunk %d %d\n",chunk,chunkWritten));
}
// Update file object
- if(endOfWrite > in->variant.fileVariant.fileSize)
+ if((startOfWrite + nDone) > in->variant.fileVariant.fileSize)
{
- in->variant.fileVariant.fileSize = endOfWrite;
+ in->variant.fileVariant.fileSize = (startOfWrite + nDone);
}
in->dirty = 1;
- /*in->st_mtime = CURRENT_TIME; only update in flush*/
return nDone;
}
yaffs_Device *dev = in->myDev;
- __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];
- yaffs_INVALIDATECHUNKCACHE(in);
+ yaffs_FlushFilesChunkCache(in);
+ yaffs_InvalidateWholeChunkCache(in);
if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
{
// 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.
- yaffs_ReadChunkDataFromObject(in,lastChunk,localBuffer);
-
- yaffs_WriteChunkDataToObject(in,lastChunk,localBuffer,sizeOfPartialChunk,1);
+ // 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"));
-
- in->st_mtime = CURRENT_TIME;
+
+ yaffs_FlushFilesChunkCache(in);
+ if(updateTime)
+ {
+#ifdef CONFIG_YAFFS_WINCE
+ yfsd_WinFileTimeNow(in->win_mtime);
+#else
+ 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
{
static int yaffs_DoGenericObjectDeletion(yaffs_Object *in)
{
- yaffs_INVALIDATECHUNKCACHE(in);
+
+ // First off, invalidate the file's data in the cache, without flushing.
+ yaffs_InvalidateWholeChunkCache(in);
+
yaffs_RemoveObjectFromDirectory(in);
- yaffs_DeleteChunk(in->myDev,in->chunkId);
-#if __KERNEL__
+ 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
-#if 0
- yaffs_ResizeFile(in,0);
-#else
- yaffs_DeleteWorker(in, in->variant.fileVariant.top, in->variant.fileVariant.topLevel, 0,NULL);
-#endif
+ yaffs_DeleteWorker(in, in->variant.fileVariant.top, in->variant.fileVariant.topLevel, 0,NULL);
+
yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);
#else
int retVal;
int immediateDeletion=0;
- retVal = yaffs_ChangeObjectName(in, in->myDev->unlinkedDir,NULL);
- if(retVal == YAFFS_OK)
+ retVal = yaffs_ChangeObjectName(in, in->myDev->unlinkedDir,NULL,0);
+ 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++;
//in->renameAllowed = 0;
#ifdef __KERNEL__
- if(in->myInode)
+ if(!in->myInode)
{
+ // Might be open at present,
+ // Caught by delete_inode in yaffs_fs.c
immediateDeletion = 1;
}
-#endif
-#if WIN32
+#else
if(in->inUse <= 0)
{
immediateDeletion = 1;
if(immediateDeletion)
{
- T((TSTR("yaffs: immediate deletion of file %d" TENDSTR),in->objectId));
+ 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)
{
}
else if(!list_empty(&obj->hardLinks))
{
-#if 0
- // Curve ball: We're unlinking an object that has a hardlink.
- // Therefore we can't really delete the object.
- // Instead, we do the following:
- // - Select a hardlink.
- // - Re-type a hardlink as the equivalent object and populate the fields, including the
- // objectId. Updating the object id is important so that all the hardlinks do not need
- // to be rewritten.
- // - Update the equivalet object pointers.
- // - Delete all object.
-
- yaffs_Object *hl;
- struct list_head *i;
-
-
- yaffs_RemoveObjectFromDirectory(obj);
-
-
-
- hl = list_entry(obj->hardLinks.next, yaffs_Object,hardLinks);
-
- hl->dirty = 1;
- hl->st_mode = obj->st_mode;
- hl->st_uid = obj->st_uid;
- hl->st_gid = obj->st_gid;
- hl->st_atime = obj->st_atime;
- hl->st_mtime = obj->st_mtime;
- hl->st_ctime = obj->st_ctime;
- hl->st_rdev = obj->st_rdev;
-
- hl->variantType = obj->variantType;
-
- switch(hl->variantType)
- {
- case YAFFS_OBJECT_TYPE_FILE:
- case YAFFS_OBJECT_TYPE_SYMLINK:
- case YAFFS_OBJECT_TYPE_SPECIAL:
- // These types are OK to just copy across.
- hl->variant = obj->variant;
- break;
- case YAFFS_OBJECT_TYPE_DIRECTORY:
- // Fix the list up
- list_add(&hl->variant.directoryVariant.children,
- &obj->variant.directoryVariant.children);
- list_del(&obj->variant.directoryVariant.children);
-
- // Now change all the directory children to point to the new parent.
- list_for_each(i,&hl->variant.directoryVariant.children)
- {
- list_entry(i,yaffs_Object,siblings)->parent = hl;
- }
- break;
-
- case YAFFS_OBJECT_TYPE_HARDLINK:
- case YAFFS_OBJECT_TYPE_UNKNOWN:
- // Should not be either of these types.
- }
-
- // Now fix up the hardlink chain
- list_del(&obj->hardLinks);
-
- list_for_each(i,&hl->hardLinks)
- {
- list_entry(i,yaffs_Object,hardLinks)->variant.hardLinkVariant.equivalentObject = hl;
- list_entry(i,yaffs_Object,hardLinks)->variant.hardLinkVariant.equivalentObjectId = hl->objectId;
- }
-
- // Now fix up the hash links.
- yaffs_UnhashObject(hl);
- hl->objectId = obj->objectId;
- yaffs_HashObject(hl);
-
- // Update the hardlink which has become an object
- yaffs_UpdateObjectHeader(hl,NULL,0);
-
- // Finally throw away the deleted object
- yaffs_DeleteChunk(obj->myDev,obj->chunkId);
- yaffs_FreeObject(obj);
-
- return YAFFS_OK;
-#else
// Curve ball: We're unlinking an object that has a hardlink.
//
// This problem arises because we are not strictly following
yaffs_GetObjectName(hl,name,YAFFS_MAX_NAME_LENGTH+1);
- retVal = yaffs_ChangeObjectName(obj, hl->parent, name);
+ retVal = yaffs_ChangeObjectName(obj, hl->parent, name,0);
if(retVal == YAFFS_OK)
{
retVal = yaffs_DoGenericObjectDeletion(hl);
}
return retVal;
-
-#endif
-
}
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)
{
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;
if(yaffs_IsBlockBad(dev,blk))
{
state = YAFFS_BLOCK_STATE_DEAD;
- T((TSTR("block %d is bad" TENDSTR),blk));
+ T(YAFFS_TRACE_BAD_BLOCKS,(TSTR("block %d is bad" TENDSTR),blk));
}
// 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);
else
{
// this is the block being allocated from
- T((TSTR(" Allocating from %d %d" TENDSTR),blk,c));
+ T(YAFFS_TRACE_SCAN,(TSTR(" Allocating from %d %d" TENDSTR),blk,c));
state = YAFFS_BLOCK_STATE_ALLOCATING;
dev->allocationBlock = blk;
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);
}
}
in->variantType = oh->type;
in->st_mode = oh->st_mode;
+#ifdef CONFIG_YAFFS_WINCE
+ in->win_atime[0] = oh->win_atime[0];
+ in->win_ctime[0] = oh->win_ctime[0];
+ in->win_mtime[0] = oh->win_mtime[0];
+ in->win_atime[1] = oh->win_atime[1];
+ in->win_ctime[1] = oh->win_ctime[1];
+ in->win_mtime[1] = oh->win_mtime[1];
+#else
in->st_uid = oh->st_uid;
in->st_gid = oh->st_gid;
in->st_atime = oh->st_atime;
in->st_mtime = oh->st_mtime;
in->st_ctime = oh->st_ctime;
in->st_rdev = oh->st_rdev;
+#endif
in->chunkId = chunk;
}
in->variantType = oh->type;
in->st_mode = oh->st_mode;
+#ifdef CONFIG_YAFFS_WINCE
+ in->win_atime[0] = oh->win_atime[0];
+ in->win_ctime[0] = oh->win_ctime[0];
+ in->win_mtime[0] = oh->win_mtime[0];
+ in->win_atime[1] = oh->win_atime[1];
+ in->win_ctime[1] = oh->win_ctime[1];
+ in->win_mtime[1] = oh->win_mtime[1];
+#else
in->st_uid = oh->st_uid;
in->st_gid = oh->st_gid;
in->st_atime = oh->st_atime;
in->st_mtime = oh->st_mtime;
in->st_ctime = oh->st_ctime;
in->st_rdev = oh->st_rdev;
+#endif
in->chunkId = chunk;
- in->sum = oh->sum;
+ yaffs_SetObjectName(in,oh->name);
in->dirty = 0;
// directory stuff...
{
// Hoosterman, another problem....
// We're trying to use a non-directory as a directory
- // Todo ... handle
+
+ 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;
+
}
yaffs_AddObjectToDirectory(parent,in);
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;
+ }
}
}
strncpy(name,locName,buffSize - 1);
}
+#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
+ else if(obj->shortName[0])
+ {
+ strcpy(name,obj->shortName);
+ }
+#endif
else
{
__u8 buffer[YAFFS_BYTES_PER_CHUNK];
}
}
+#ifndef CONFIG_YAFFS_WINCE
int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr)
{
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;
}
-
+#endif
int yaffs_DumpObject(yaffs_Object *obj)
{
yaffs_GetObjectName(obj,name,256);
- YPRINTF(("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d type %d size %d\n",
+ T(YAFFS_TRACE_ALWAYS,(TSTR("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d type %d size %d\n" TENDSTR),
obj->objectId,yaffs_GetObjectInode(obj), name, obj->dirty, obj->valid, obj->serial,
obj->sum, obj->chunkId, yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj)));
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())
{
- YPRINTF(("yaffs_CheckStructures failed\n"));
+ 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)
{
- YPRINTF(("startBlock %d or endBlock %d invalid\n",
+ T(YAFFS_TRACE_ALWAYS,(TSTR("startBlock %d or endBlock %d invalid\n" TENDSTR),
dev->startBlock, dev->endBlock));
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++)
{
{
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);
+
+
+
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].objectId = 0;
+ dev->srCache[i].object = NULL;
dev->srCache[i].lastUse = 0;
+ dev->srCache[i].dirty = 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 //////////////////////////////////
#define yaffs_CheckStruct(structure,syze, name) \
if(sizeof(structure) != syze) \
- { YPRINTF(("%s should be %d but is %d\n",name,syze,sizeof(structure))); \
+ { \
+ T(YAFFS_TRACE_ALWAYS,(TSTR("%s should be %d but is %d\n" TENDSTR),name,syze,sizeof(structure))); \
return YAFFS_FAIL; \
}
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")
return YAFFS_OK;
}
+#if 0
void yaffs_GutsTest(yaffs_Device *dev)
{
if(yaffs_CheckStructures() != YAFFS_OK)
{
- YPRINTF(("One or more structures malformed-- aborting\n"));
+ T(YAFFS_TRACE_ALWAYS,(TSTR("One or more structures malformed-- aborting\n" TENDSTR)));
return;
}
- else
- {
- YPRINTF(("Structures OK\n"));
- }
yaffs_TnodeTest(dev);
yaffs_ObjectTest(dev);
}
+#endif
+
+
+
+