*/
//yaffs_guts.c
-const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.15 2002-12-13 00:13:06 charles Exp $";
+const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.16 2003-01-14 23:15:29 charles Exp $";
#include "yportenv.h"
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];
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
+ struct yaffs_NANDSpare nspare;
dev->nPageReads++;
}
-#ifndef CONFIG_YAFFS_USE_NANDECC
+ if(!dev->useNANDECC)
+ {
retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,spare);
if(data && doErrorCorrection)
{
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);
- }
+ }
+ 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);
+ }
+
+ }
}
-#endif
return retVal;
}
// 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;
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++;
+ }
}
}
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])
+ {
+
+ theChunk = (tn->level0[i] << in->myDev->chunkGroupBits);
+ theBlock = yaffs_GetBlockInfo(in->myDev, theChunk/ YAFFS_CHUNKS_PER_BLOCK);
+ 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_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++;
}
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 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)
int i;
int dirtiest = -1;
- int pagesInUse = 100; // silly big number
+ int pagesInUse = dev->nChunksPerBlock;
yaffs_BlockInfo *bi;
for(i = dev->startBlock; i <= dev->endBlock && pagesInUse > 2 ; i++)
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++)
{
dev->allocationBlockFinder++;
- if(dev->allocationBlockFinder < dev->startBlock ||
- dev->allocationBlockFinder > dev->endBlock)
+ if(dev->allocationBlockFinder <dev->startBlock || dev->allocationBlockFinder> dev->endBlock)
{
- dev->allocationBlockFinder = dev->startBlock;
+ dev->allocationBlockFinder = dev->startBlock;
}
bi = yaffs_GetBlockInfo(dev,dev->allocationBlockFinder);
+
if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
{
bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING;
retVal = (dev->allocationBlock * YAFFS_CHUNKS_PER_BLOCK) +
dev->allocationPage;
bi->pagesInUse++;
- bi->pageBits |= (1 << (dev->allocationPage));
+ yaffs_SetChunkBit(dev,dev->allocationBlock,dev->allocationPage);
dev->allocationPage++;
{
int oldChunk;
int newChunk;
- __u32 mask;
+ int chunkInBlock;
+ int markNAND;
yaffs_Spare spare;
yaffs_Tags tags;
__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 * YAFFS_CHUNKS_PER_BLOCK;
+ 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);
}
}
- 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);
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--;
{
int block;
- yaffs_DoUnlinkedFileDeletion(dev);
+ //yaffs_DoUnlinkedFileDeletion(dev);
if(dev->nErasedBlocks <= (YAFFS_RESERVED_BLOCKS + YAFFS_GARBAGE_COLLECT_LOW_WATER))
{
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);
{
//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;
}
}
}
-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;
-
+
+ dev->nDeletions++;
block = chunkId / YAFFS_CHUNKS_PER_BLOCK;
page = chunkId % YAFFS_CHUNKS_PER_BLOCK;
- yaffs_SpareInitialise(&spare);
- spare.pageStatus = 0; // To mark it as deleted.
+ if(markNAND)
+ {
+ yaffs_SpareInitialise(&spare);
+
+ 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)
yaffs_ChunkCache *cache;
int chunkWritten;
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;
+ }
- } 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));
- return &dev->srCache[i];
+ return &dev->srCache[i];
+ }
}
- }
- 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));
+ return theOne >= 0 ? &dev->srCache[theOne] : NULL;
+ }
+ else
+ {
+ return NULL;
+ }
}
int usage;
int i;
- // 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;
- 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;
+ }
}
- }
- yaffs_FlushFilesChunkCache(theObj);
+ yaffs_FlushFilesChunkCache(theObj);
- // Try again
- cache = yaffs_GrabChunkCacheWorker(dev);
- }
+ // Try again
+ cache = yaffs_GrabChunkCacheWorker(dev);
+ }
- 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
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)
+ 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)
+ {
+ 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
else
{
in->nDataChunks--;
- yaffs_DeleteChunk(dev,chunkId);
+ yaffs_DeleteChunk(dev,chunkId,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 = CURRENT_TIME;
#endif
+ }
retVal = yaffs_UpdateObjectHeader(in,NULL,0);
}
yaffs_InvalidateWholeChunkCache(in);
yaffs_RemoveObjectFromDirectory(in);
- yaffs_DeleteChunk(in->myDev,in->chunkId);
+ yaffs_DeleteChunk(in->myDev,in->chunkId,1);
#ifdef __KERNEL__
if(in->myInode)
{
{
#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++;
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)
{
{
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;
{
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" TENDSTR)));
}
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;
+ yaffs_Object *l;
+ // Soft delete all the unlinked files
+ list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
+ {
+ l = list_entry(i, yaffs_Object,siblings);
+ if(l->deleted)
+ {
+ yaffs_SoftDeleteFile(l);
+ }
+ }
+ }
return YAFFS_OK;
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)
}
dev->chunkGroupSize = 1 << dev->chunkGroupBits;
+ // Stuff to be taken out later
+ dev->nBytesPerChunk = YAFFS_BYTES_PER_CHUNK;
+ dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK;
+
// More device initialisation
dev->garbageCollectionRequired = 0;
dev->tagsEccFixed=0;
dev->tagsEccUnfixed=0;
- dev->localBuffer = YMALLOC(YAFFS_BYTES_PER_CHUNK);
+ 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].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 0
+
int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
{
int nFree = dev->nFreeChunks - (YAFFS_CHUNKS_PER_BLOCK * YAFFS_RESERVED_BLOCKS);
nFree += l->nDataChunks;
}
}
+
+
+ printf("___________ nFreeChunks is %d nFree is %d\n",dev->nFreeChunks,nFree);
+
+ if(nFree < 0) nFree = 0;
+
+ return nFree;
+
+}
+
+#endif
+
+int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
+{
+ int nFree;
+ int pending;
+ int b;
+
+ 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)
+ {
+ 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;
+
+ if(nFree < 0) nFree = 0;
- return (nFree < 0) ? 0 : nFree;
+ return nFree;
}
+
/////////////////// YAFFS test code //////////////////////////////////
#define yaffs_CheckStruct(structure,syze, name) \
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")