/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
- * Copyright (C) 2002-2007 Aleph One Ltd.
+ * Copyright (C) 2002-2010 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-
-const char *yaffs_guts_c_version =
- "$Id: yaffs_guts.c,v 1.110 2010-02-25 22:41:46 charles Exp $";
-
#include "yportenv.h"
#include "yaffs_trace.h"
#include "yaffs_nand.h"
#include "yaffs_packedtags2.h"
+#include "yaffs_nameval.h"
+#include "yaffs_allocator.h"
+
+/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */
+#define YAFFS_GC_GOOD_ENOUGH 2
+#define YAFFS_GC_PASSIVE_THRESHOLD 4
+
+#define YAFFS_SMALL_HOLE_THRESHOLD 3
-#define YAFFS_PASSIVE_GC_CHUNKS 2
+/*
+ * Checkpoints are really no benefit on very small partitions.
+ *
+ * To save space on small partitions don't bother with checkpoints unless
+ * the partition is at least this big.
+ */
+#define YAFFS_CHECKPOINT_MIN_BLOCKS 60
#include "yaffs_ecc.h"
+/* Private structure for doing xattr modifications */
+typedef struct {
+ int set; /* If 0 then this is a deletion */
+ const char *name;
+ const void *data;
+ int size;
+ int flags;
+ int result;
+} yaffs_XAttrMod;
/* Robustification (if it ever comes about...) */
static void yaffs_RetireBlock(yaffs_Device *dev, int blockInNAND);
static void yaffs_AddObjectToDirectory(yaffs_Object *directory,
yaffs_Object *obj);
static int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name,
- int force, int isShrink, int shadows);
+ int force, int isShrink, int shadows, yaffs_XAttrMod *xop);
+static int yaffs_ApplyXMod(yaffs_Device *dev, char *buffer, yaffs_XAttrMod *xmod);
+
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_DoGenericObjectDeletion(yaffs_Object *in);
static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device *dev, int blockNo);
yaffs_FileStructure *fStruct,
__u32 chunkId);
+static int yaffs_HandleHole(yaffs_Object *obj, loff_t newSize);
static void yaffs_SkipRestOfBlock(yaffs_Device *dev);
static int yaffs_VerifyChunkWritten(yaffs_Device *dev,
int chunkInNAND,
static __u32 Shifts(__u32 x)
{
- int nShifts;
+ __u32 nShifts;
nShifts = 0;
static int yaffs_SkipVerification(yaffs_Device *dev)
{
+ dev=dev;
return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL));
}
static int yaffs_SkipFullVerification(yaffs_Device *dev)
{
+ dev=dev;
return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_FULL));
}
static int yaffs_SkipNANDVerification(yaffs_Device *dev)
{
+ dev=dev;
return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_NAND));
}
}
-
+#if 0
+/* Not being used, but don't want to throw away yet */
static int yaffs_VerifyTnodeWorker(yaffs_Object *obj, yaffs_Tnode *tn,
__u32 level, int chunkOffset)
{
}
+#endif
static void yaffs_VerifyFile(yaffs_Object *obj)
{
return chunk;
}
+
+/*
+ * Oldest Dirty Sequence Number handling.
+ */
+
+/* yaffs_CalcOldestDirtySequence()
+ * yaffs_FindOldestDirtySequence()
+ * Calculate the oldest dirty sequence number if we don't know it.
+ */
+static void yaffs_CalcOldestDirtySequence(yaffs_Device *dev)
+{
+ int i;
+ unsigned seq;
+ unsigned blockNo = 0;
+ yaffs_BlockInfo *b;
+
+ if(!dev->param.isYaffs2)
+ return;
+
+ /* Find the oldest dirty sequence number. */
+ seq = dev->sequenceNumber + 1;
+ b = dev->blockInfo;
+ for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) {
+ if (b->blockState == YAFFS_BLOCK_STATE_FULL &&
+ (b->pagesInUse - b->softDeletions) < dev->param.nChunksPerBlock &&
+ b->sequenceNumber < seq) {
+ seq = b->sequenceNumber;
+ blockNo = i;
+ }
+ b++;
+ }
+
+ if(blockNo){
+ dev->oldestDirtySequence = seq;
+ dev->oldestDirtyBlock = blockNo;
+ }
+
+}
+
+
+static void yaffs_FindOldestDirtySequence(yaffs_Device *dev)
+{
+ if(dev->param.isYaffs2 && !dev->oldestDirtySequence)
+ yaffs_CalcOldestDirtySequence(dev);
+}
+
+/*
+ * yaffs_ClearOldestDirtySequence()
+ * Called when a block is erased or marked bad. (ie. when its sequenceNumber
+ * becomes invalid). If the value matches the oldest then we clear
+ * dev->oldestDirtySequence to force its recomputation.
+ */
+static void yaffs_ClearOldestDirtySequence(yaffs_Device *dev, yaffs_BlockInfo *bi)
+{
+
+ if(!dev->param.isYaffs2)
+ return;
+
+ if(!bi || bi->sequenceNumber == dev->oldestDirtySequence){
+ dev->oldestDirtySequence = 0;
+ dev->oldestDirtyBlock = 0;
+ }
+}
+
+/*
+ * yaffs_UpdateOldestDirtySequence()
+ * Update the oldest dirty sequence number whenever we dirty a block.
+ * Only do this if the oldestDirtySequence is actually being tracked.
+ */
+static void yaffs_UpdateOldestDirtySequence(yaffs_Device *dev, unsigned blockNo, yaffs_BlockInfo *bi)
+{
+ if(dev->param.isYaffs2 && dev->oldestDirtySequence){
+ if(dev->oldestDirtySequence > bi->sequenceNumber){
+ dev->oldestDirtySequence = bi->sequenceNumber;
+ dev->oldestDirtyBlock = blockNo;
+ }
+ }
+}
+
/*
* Block retiring for handling a broken block.
*/
yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND);
yaffs_InvalidateCheckpoint(dev);
+
+ yaffs_ClearOldestDirtySequence(dev,bi);
if (yaffs_MarkBlockBad(dev, blockInNAND) != YAFFS_OK) {
if (yaffs_EraseBlockInNAND(dev, blockInNAND) != YAFFS_OK) {
const __u8 *data,
const yaffs_ExtendedTags *tags)
{
+ dev=dev;
+ chunkInNAND=chunkInNAND;
+ data=data;
+ tags=tags;
}
static void yaffs_HandleUpdateChunk(yaffs_Device *dev, int chunkInNAND,
const yaffs_ExtendedTags *tags)
{
+ dev=dev;
+ chunkInNAND=chunkInNAND;
+ tags=tags;
}
void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi)
* in the tnode.
*/
-/* yaffs_CreateTnodes creates a bunch more tnodes and
- * adds them to the tnode free list.
- * Don't use this function directly
- */
-static Y_INLINE int yaffs_CalcTnodeSize(yaffs_Device *dev)
-{
- int tnodeSize;
- /* Calculate the tnode size in bytes for variable width tnode support.
- * Must be a multiple of 32-bits */
- tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
-
- if (tnodeSize < sizeof(yaffs_Tnode))
- tnodeSize = sizeof(yaffs_Tnode);
- return tnodeSize;
-}
-static int yaffs_CreateTnodes(yaffs_Device *dev, int nTnodes)
+static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev)
{
- int i;
- int tnodeSize = yaffs_CalcTnodeSize(dev);
- yaffs_Tnode *newTnodes;
- __u8 *mem;
- yaffs_Tnode *curr;
- yaffs_Tnode *next;
- yaffs_TnodeList *tnl;
-
- if (nTnodes < 1)
- return YAFFS_OK;
-
-
- /* make these things */
-
- newTnodes = YMALLOC(nTnodes * tnodeSize);
- mem = (__u8 *)newTnodes;
-
- if (!newTnodes) {
- T(YAFFS_TRACE_ERROR,
- (TSTR("yaffs: Could not allocate Tnodes" TENDSTR)));
- return YAFFS_FAIL;
- }
-
- /* Hook them into the free list */
-#if 0
- 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] = (void *)1;
-#endif
- }
-
- newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;
-#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
- newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
-#endif
- dev->freeTnodes = newTnodes;
-#else
- /* New hookup for wide tnodes */
- for (i = 0; i < nTnodes - 1; i++) {
- curr = (yaffs_Tnode *) &mem[i * tnodeSize];
- next = (yaffs_Tnode *) &mem[(i+1) * tnodeSize];
- curr->internal[0] = next;
- }
-
- curr = (yaffs_Tnode *) &mem[(nTnodes - 1) * tnodeSize];
- curr->internal[0] = dev->freeTnodes;
- dev->freeTnodes = (yaffs_Tnode *)mem;
-
-#endif
-
-
- 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) {
- T(YAFFS_TRACE_ERROR,
- (TSTR
- ("yaffs: Could not add tnodes to management list" TENDSTR)));
- return YAFFS_FAIL;
- } else {
- tnl->tnodes = newTnodes;
- tnl->next = dev->allocatedTnodeList;
- dev->allocatedTnodeList = tnl;
+ yaffs_Tnode *tn = yaffs_AllocateRawTnode(dev);
+ if (tn){
+ memset(tn, 0, dev->tnodeSize);
+ dev->nTnodes++;
}
- T(YAFFS_TRACE_ALLOCATE, (TSTR("yaffs: Tnodes added" TENDSTR)));
-
- return YAFFS_OK;
-}
-
-/* GetTnode gets us a clean tnode. Tries to make allocate more if we run out */
-
-static yaffs_Tnode *yaffs_GetTnodeRaw(yaffs_Device *dev)
-{
- yaffs_Tnode *tn = NULL;
-
-#ifdef CONFIG_YAFFS_VALGRIND_TEST
- tn = YMALLOC(yaffs_CalcTnodeSize(dev));
- if(tn)
- dev->nTnodesCreated++;
-#else
- /* If there are none left make more */
- if (!dev->freeTnodes)
- yaffs_CreateTnodes(dev, YAFFS_ALLOCATION_NTNODES);
-
- if (dev->freeTnodes) {
- tn = dev->freeTnodes;
-#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
- if (tn->internal[YAFFS_NTNODES_INTERNAL] != (void *)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--;
- }
-#endif
dev->nCheckpointBlocksRequired = 0; /* force recalculation*/
return tn;
}
-static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev)
-{
- yaffs_Tnode *tn = yaffs_GetTnodeRaw(dev);
- int tnodeSize = yaffs_CalcTnodeSize(dev);
-
- if (tn)
- memset(tn, 0, tnodeSize);
-
- return tn;
-}
-
/* FreeTnode frees up a tnode and puts it back on the free list */
static void yaffs_FreeTnode(yaffs_Device *dev, yaffs_Tnode *tn)
{
- if (tn) {
-#ifdef CONFIG_YAFFS_VALGRIND_TEST
- YFREE(tn);
- dev->nTnodesCreated--;
-#else
-#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] = (void *)1;
-#endif
- tn->internal[0] = dev->freeTnodes;
- dev->freeTnodes = tn;
- dev->nFreeTnodes++;
-#endif
- }
+ yaffs_FreeRawTnode(dev,tn);
+ dev->nTnodes--;
dev->nCheckpointBlocksRequired = 0; /* force recalculation*/
}
-static void yaffs_DeinitialiseTnodes(yaffs_Device *dev)
+static void yaffs_DeinitialiseTnodesAndObjects(yaffs_Device *dev)
{
- /* Free the list of allocated tnodes */
- yaffs_TnodeList *tmp;
-
- while (dev->allocatedTnodeList) {
- tmp = dev->allocatedTnodeList->next;
-
- YFREE(dev->allocatedTnodeList->tnodes);
- YFREE(dev->allocatedTnodeList);
- dev->allocatedTnodeList = tmp;
-
- }
-
- dev->freeTnodes = NULL;
- dev->nFreeTnodes = 0;
- dev->nTnodesCreated = 0;
-}
-
-static void yaffs_InitialiseTnodes(yaffs_Device *dev)
-{
- dev->allocatedTnodeList = NULL;
- dev->freeTnodes = NULL;
- dev->nFreeTnodes = 0;
- dev->nTnodesCreated = 0;
+ yaffs_DeinitialiseRawTnodesAndObjects(dev);
+ dev->nObjects = 0;
+ dev->nTnodes = 0;
}
int requiredTallness;
int level = fStruct->topLevel;
+ dev=dev;
+
/* Check sane level and chunk Id */
if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL)
return NULL;
return -1;
}
-
+#if 0
+/* Experimental code not being used yet. Might speed up file deletion */
/* DeleteWorker scans backwards through the tnode tree and deletes all the
- * chunks and tnodes in the file
+ * chunks and tnodes in the file.
* Returns 1 if the tree was deleted.
* Returns 0 if it stopped early due to hitting the limit and the delete is incomplete.
*/
}
+#endif
+
static void yaffs_SoftDeleteChunk(yaffs_Device *dev, int chunk)
{
yaffs_BlockInfo *theBlock;
+ unsigned blockNo;
T(YAFFS_TRACE_DELETION, (TSTR("soft delete chunk %d" TENDSTR), chunk));
- theBlock = yaffs_GetBlockInfo(dev, chunk / dev->param.nChunksPerBlock);
+ blockNo = chunk / dev->param.nChunksPerBlock;
+ theBlock = yaffs_GetBlockInfo(dev, blockNo);
if (theBlock) {
theBlock->softDeletions++;
dev->nFreeChunks++;
+ yaffs_UpdateOldestDirtySequence(dev, blockNo, theBlock);
}
}
hasData++;
}
} else {
- int tnodeSize_u32 = yaffs_CalcTnodeSize(dev)/sizeof(__u32);
+ int tnodeSize_u32 = dev->tnodeSize/sizeof(__u32);
__u32 *map = (__u32 *)tn;
for(i = 0; !hasData && i < tnodeSize_u32; i++){
/*-------------------- End of File Structure functions.-------------------*/
-/* yaffs_CreateFreeObjects creates a bunch more objects and
- * adds them to the object free list.
- */
-static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects)
-{
- int i;
- yaffs_Object *newObjects;
- yaffs_ObjectList *list;
-
- if (nObjects < 1)
- return YAFFS_OK;
-
- /* make these things */
- newObjects = YMALLOC(nObjects * sizeof(yaffs_Object));
- list = YMALLOC(sizeof(yaffs_ObjectList));
-
- if (!newObjects || !list) {
- if (newObjects){
- YFREE(newObjects);
- newObjects = NULL;
- }
- if (list){
- YFREE(list);
- list = NULL;
- }
- 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++) {
- newObjects[i].siblings.next =
- (struct ylist_head *)(&newObjects[i + 1]);
- }
-
- newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects;
- dev->freeObjects = newObjects;
- dev->nFreeObjects += nObjects;
- dev->nObjectsCreated += nObjects;
-
- /* Now add this bunch of Objects to a list for freeing up. */
-
- list->objects = newObjects;
- list->next = dev->allocatedObjectList;
- dev->allocatedObjectList = list;
-
- return YAFFS_OK;
-}
-
/* AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out */
static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device *dev)
{
- yaffs_Object *tn = NULL;
+ yaffs_Object *obj = yaffs_AllocateRawObject(dev);
-#ifdef CONFIG_YAFFS_VALGRIND_TEST
- tn = YMALLOC(sizeof(yaffs_Object));
- if(tn)
- dev->nObjectsCreated++;
-#else
- /* If there are none left make more */
- if (!dev->freeObjects)
- yaffs_CreateFreeObjects(dev, YAFFS_ALLOCATION_NOBJECTS);
+ if (obj) {
+ dev->nObjects++;
- if (dev->freeObjects) {
- tn = dev->freeObjects;
- dev->freeObjects =
- (yaffs_Object *) (dev->freeObjects->siblings.next);
- dev->nFreeObjects--;
- }
-#endif
- if (tn) {
/* Now sweeten it up... */
- memset(tn, 0, sizeof(yaffs_Object));
- tn->beingCreated = 1;
+ memset(obj, 0, sizeof(yaffs_Object));
+ obj->beingCreated = 1;
- tn->myDev = dev;
- tn->hdrChunk = 0;
- tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN;
- YINIT_LIST_HEAD(&(tn->hardLinks));
- YINIT_LIST_HEAD(&(tn->hashLink));
- YINIT_LIST_HEAD(&tn->siblings);
+ obj->myDev = dev;
+ obj->hdrChunk = 0;
+ obj->variantType = YAFFS_OBJECT_TYPE_UNKNOWN;
+ YINIT_LIST_HEAD(&(obj->hardLinks));
+ YINIT_LIST_HEAD(&(obj->hashLink));
+ YINIT_LIST_HEAD(&obj->siblings);
/* Now make the directory sane */
if (dev->rootDir) {
- tn->parent = dev->rootDir;
- ylist_add(&(tn->siblings), &dev->rootDir->variant.directoryVariant.children);
+ obj->parent = dev->rootDir;
+ ylist_add(&(obj->siblings), &dev->rootDir->variant.directoryVariant.children);
}
/* Add it to the lost and found directory.
* check if lostNFound exists first
*/
if (dev->lostNFoundDir)
- yaffs_AddObjectToDirectory(dev->lostNFoundDir, tn);
+ yaffs_AddObjectToDirectory(dev->lostNFoundDir, obj);
- tn->beingCreated = 0;
+ obj->beingCreated = 0;
}
dev->nCheckpointBlocksRequired = 0; /* force recalculation*/
- return tn;
+ return obj;
}
static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device *dev, int number,
}
-static void yaffs_UnhashObject(yaffs_Object *tn)
+static void yaffs_UnhashObject(yaffs_Object *obj)
{
int bucket;
- yaffs_Device *dev = tn->myDev;
+ yaffs_Device *dev = obj->myDev;
/* If it is still linked into the bucket list, free from the list */
- if (!ylist_empty(&tn->hashLink)) {
- ylist_del_init(&tn->hashLink);
- bucket = yaffs_HashFunction(tn->objectId);
+ if (!ylist_empty(&obj->hashLink)) {
+ ylist_del_init(&obj->hashLink);
+ bucket = yaffs_HashFunction(obj->objectId);
dev->objectBucket[bucket].count--;
}
}
/* FreeObject frees up a Object and puts it back on the free list */
-static void yaffs_FreeObject(yaffs_Object *tn)
+static void yaffs_FreeObject(yaffs_Object *obj)
{
- yaffs_Device *dev = tn->myDev;
+ yaffs_Device *dev = obj->myDev;
- T(YAFFS_TRACE_OS, (TSTR("FreeObject %p inode %p"TENDSTR), tn, tn->myInode));
+ T(YAFFS_TRACE_OS, (TSTR("FreeObject %p inode %p"TENDSTR), obj, obj->myInode));
- if (!tn)
+ if (!obj)
YBUG();
- if (tn->parent)
+ if (obj->parent)
YBUG();
- if (!ylist_empty(&tn->siblings))
+ if (!ylist_empty(&obj->siblings))
YBUG();
- if (tn->myInode) {
+ if (obj->myInode) {
/* We're still hooked up to a cached inode.
* Don't delete now, but mark for later deletion
*/
- tn->deferedFree = 1;
+ obj->deferedFree = 1;
return;
}
- yaffs_UnhashObject(tn);
-
-#ifdef CONFIG_YAFFS_VALGRIND_TEST
- YFREE(tn);
- dev->nObjectsCreated--;
- tn = NULL;
-#else
- /* Link into the free list. */
- tn->siblings.next = (struct ylist_head *)(dev->freeObjects);
- dev->freeObjects = tn;
- dev->nFreeObjects++;
-#endif
+ yaffs_UnhashObject(obj);
+
+ yaffs_FreeRawObject(dev,obj);
+ dev->nObjects--;
dev->nCheckpointBlocksRequired = 0; /* force recalculation*/
}
yaffs_FreeObject(obj);
}
-
-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);
- YFREE(dev->allocatedObjectList);
-
- dev->allocatedObjectList = tmp;
- }
-
- dev->freeObjects = NULL;
- dev->nFreeObjects = 0;
- dev->nObjectsCreated = 0;
-}
-
-static void yaffs_InitialiseObjects(yaffs_Device *dev)
+static void yaffs_InitialiseTnodesAndObjects(yaffs_Device *dev)
{
int i;
- dev->allocatedObjectList = NULL;
- dev->freeObjects = NULL;
- dev->nFreeObjects = 0;
+ dev->nObjects = 0;
+ dev->nTnodes = 0;
+
+ yaffs_InitialiseRawTnodesAndObjects(dev);
for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {
YINIT_LIST_HEAD(&dev->objectBucket[i].list);
static int yaffs_FindNiceObjectBucket(yaffs_Device *dev)
{
- static int x;
int i;
int l = 999;
int lowest = 999999;
- /* First let's see if we can find one that's empty. */
- for (i = 0; i < 10 && lowest > 0; i++) {
- x++;
- x %= YAFFS_NOBJECT_BUCKETS;
- if (dev->objectBucket[x].count < lowest) {
- lowest = dev->objectBucket[x].count;
- l = x;
- }
-
- }
-
- /* If we didn't find an empty list, then try
- * looking a bit further for a short one
+ /* Search for the shortest list or one that
+ * isn't too long.
*/
- for (i = 0; i < 10 && lowest > 3; i++) {
- x++;
- x %= YAFFS_NOBJECT_BUCKETS;
- if (dev->objectBucket[x].count < lowest) {
- lowest = dev->objectBucket[x].count;
- l = x;
+ for (i = 0; i < 10 && lowest > 4; i++) {
+ dev->bucketFinder++;
+ dev->bucketFinder %= YAFFS_NOBJECT_BUCKETS;
+ if (dev->objectBucket[dev->bucketFinder].count < lowest) {
+ lowest = dev->objectBucket[dev->bucketFinder].count;
+ l = dev->bucketFinder;
}
}
case YAFFS_OBJECT_TYPE_DIRECTORY:
YINIT_LIST_HEAD(&theObject->variant.directoryVariant.
children);
+ YINIT_LIST_HEAD(&theObject->variant.directoryVariant.
+ dirty);
break;
case YAFFS_OBJECT_TYPE_SYMLINK:
case YAFFS_OBJECT_TYPE_HARDLINK:
break;
}
- if (yaffs_UpdateObjectHeader(in, name, 0, 0, 0) < 0) {
+ if (yaffs_UpdateObjectHeader(in, name, 0, 0, 0, NULL) < 0) {
/* Could not create the object header, fail the creation */
yaffs_DeleteObject(in);
in = NULL;
obj->unlinked = 1;
/* If it is a deletion then we mark it as a shrink for gc purposes. */
- if (yaffs_UpdateObjectHeader(obj, newName, 0, deleteOp, shadows) >= 0)
+ if (yaffs_UpdateObjectHeader(obj, newName, 0, deleteOp, shadows, NULL) >= 0)
return YAFFS_OK;
}
* Note we must disable gc otherwise it can mess up the shadowing.
*
*/
- dev->isDoingGC=1;
+ dev->gcDisable=1;
yaffs_ChangeObjectName(obj, newDir, newName, force,
existingTarget->objectId);
existingTarget->isShadowed = 1;
yaffs_UnlinkObject(existingTarget);
- dev->isDoingGC=0;
+ dev->gcDisable=0;
}
result = yaffs_ChangeObjectName(obj, newDir, newName, 1, 0);
static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device *dev,
yaffs_BlockInfo *bi)
{
- int i;
- __u32 seq;
- yaffs_BlockInfo *b;
if (!dev->param.isYaffs2)
return 1; /* disqualification only applies to yaffs2. */
if (!bi->hasShrinkHeader)
return 1; /* can gc */
- /* Find the oldest dirty sequence number if we don't know it and save it
- * so we don't have to keep recomputing it.
- */
- if (!dev->oldestDirtySequence) {
- seq = dev->sequenceNumber;
-
- for (i = dev->internalStartBlock; i <= dev->internalEndBlock;
- i++) {
- b = yaffs_GetBlockInfo(dev, i);
- if (b->blockState == YAFFS_BLOCK_STATE_FULL &&
- (b->pagesInUse - b->softDeletions) <
- dev->param.nChunksPerBlock && b->sequenceNumber < seq) {
- seq = b->sequenceNumber;
- }
- }
- dev->oldestDirtySequence = seq;
- }
+ yaffs_FindOldestDirtySequence(dev);
/* Can't do gc of this block if there are any blocks older than this one that have
* discarded pages.
if(dev->refreshSkip > dev->param.refreshPeriod)
dev->refreshSkip = dev->param.refreshPeriod;
- if(dev->refreshSkip > 0){
- dev->refreshSkip--;
+ if(dev->refreshSkip > 0)
return oldest;
- }
/*
* Refresh skip is now zero.
*/
dev->refreshSkip = dev->param.refreshPeriod;
dev->refreshCount++;
-
+ bi = dev->blockInfo;
for (b = dev->internalStartBlock; b <=dev->internalEndBlock; b++){
- bi = yaffs_GetBlockInfo(dev, b);
-
-
if (bi->blockState == YAFFS_BLOCK_STATE_FULL){
if(oldest < 1 ||
oldestSequence = bi->sequenceNumber;
}
}
+ bi++;
}
if (oldest > 0) {
return oldest;
}
-/*
- * FindDiretiestBlock is used to select the dirtiest block (or close enough)
- * for garbage collection.
- */
-
-static int yaffs_FindBlockForGarbageCollection(yaffs_Device *dev,
- int aggressive)
-{
- int b = dev->currentDirtyChecker;
-
- int i;
- int iterations;
- int dirtiest = -1;
- int pagesInUse = 0;
- int prioritised = 0;
- yaffs_BlockInfo *bi;
- int pendingPrioritisedExist = 0;
-
- /* First let's see if we need to grab a prioritised block */
- if (dev->hasPendingPrioritisedGCs) {
- for (i = dev->internalStartBlock; i < dev->internalEndBlock && !prioritised; i++) {
-
- bi = yaffs_GetBlockInfo(dev, i);
- /* yaffs_VerifyBlock(dev,bi,i); */
-
- if (bi->gcPrioritise) {
- pendingPrioritisedExist = 1;
- if (bi->blockState == YAFFS_BLOCK_STATE_FULL &&
- yaffs_BlockNotDisqualifiedFromGC(dev, bi)) {
- pagesInUse = (bi->pagesInUse - bi->softDeletions);
- dirtiest = i;
- prioritised = 1;
- aggressive = 1; /* Fool the non-aggressive skip logiv below */
- }
- }
- }
-
- if (!pendingPrioritisedExist) /* None found, so we can clear this */
- dev->hasPendingPrioritisedGCs = 0;
- }
-
- /* If we're doing aggressive GC then we are happy to take a less-dirty block, and
- * search harder.
- * else (we're doing a leasurely gc), then we only bother to do this if the
- * block has only a few pages in use.
- */
-
- dev->nonAggressiveSkip--;
-
- if (!aggressive && (dev->nonAggressiveSkip > 0))
- return -1;
-
- if (!prioritised)
- pagesInUse =
- (aggressive) ? dev->param.nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1;
-
- if (aggressive)
- iterations =
- dev->internalEndBlock - dev->internalStartBlock + 1;
- else {
- iterations =
- dev->internalEndBlock - dev->internalStartBlock + 1;
- iterations = iterations / 16;
- if (iterations > 200)
- iterations = 200;
- }
-
- for (i = 0; i <= iterations && pagesInUse > 0 && !prioritised; i++) {
- b++;
- if (b < dev->internalStartBlock || b > dev->internalEndBlock)
- b = dev->internalStartBlock;
-
- if (b < dev->internalStartBlock || b > dev->internalEndBlock) {
- 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 - bi->softDeletions) < pagesInUse &&
- yaffs_BlockNotDisqualifiedFromGC(dev, bi)) {
- dirtiest = b;
- pagesInUse = (bi->pagesInUse - bi->softDeletions);
- }
- }
-
- dev->currentDirtyChecker = b;
-
- if (dirtiest > 0) {
- T(YAFFS_TRACE_GC,
- (TSTR("GC Selected block %d with %d free, prioritised:%d" TENDSTR), dirtiest,
- dev->param.nChunksPerBlock - pagesInUse, prioritised));
- }
-
- dev->oldestDirtySequence = 0;
-
- if (dirtiest > 0)
- dev->nonAggressiveSkip = 4;
-
- return dirtiest;
-}
static void yaffs_BlockBecameDirty(yaffs_Device *dev, int blockNo)
{
(TSTR("yaffs_BlockBecameDirty block %d state %d %s"TENDSTR),
blockNo, bi->blockState, (bi->needsRetiring) ? "needs retiring" : ""));
+ yaffs_ClearOldestDirtySequence(dev,bi);
+
bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
+ /* If this is the block being garbage collected then stop gc'ing this block */
+ if(blockNo == dev->gcBlock)
+ dev->gcBlock = 0;
+
+ /* If this block is currently the best candidate for gc then drop as a candidate */
+ if(blockNo == dev->gcDirtiest){
+ dev->gcDirtiest = 0;
+ dev->gcPagesInUse = 0;
+ }
+
if (!bi->needsRetiring) {
yaffs_InvalidateCheckpoint(dev);
erasedOk = yaffs_EraseBlockInNAND(dev, blockNo);
+static int yaffs_CheckpointRequired(yaffs_Device *dev)
+{
+ int nblocks = dev->internalEndBlock - dev->internalStartBlock + 1 ;
+ return dev->param.isYaffs2 &&
+ !dev->param.skipCheckpointWrite &&
+ (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS);
+}
static int yaffs_CalcCheckpointBlocksRequired(yaffs_Device *dev)
{
if (!dev->nCheckpointBlocksRequired &&
- dev->param.isYaffs2) {
+ yaffs_CheckpointRequired(dev)){
/* Not a valid value so recalculate */
int nBytes = 0;
int nBlocks;
int devBlocks = (dev->param.endBlock - dev->param.startBlock + 1);
- int tnodeSize = yaffs_CalcTnodeSize(dev);
nBytes += sizeof(yaffs_CheckpointValidity);
nBytes += sizeof(yaffs_CheckpointDevice);
nBytes += devBlocks * sizeof(yaffs_BlockInfo);
nBytes += devBlocks * dev->chunkBitmapStride;
- nBytes += (sizeof(yaffs_CheckpointObject) + sizeof(__u32)) * (dev->nObjectsCreated - dev->nFreeObjects);
- nBytes += (tnodeSize + sizeof(__u32)) * (dev->nTnodesCreated - dev->nFreeTnodes);
+ nBytes += (sizeof(yaffs_CheckpointObject) + sizeof(__u32)) * (dev->nObjects);
+ nBytes += (dev->tnodeSize + sizeof(__u32)) * (dev->nTnodes);
nBytes += sizeof(yaffs_CheckpointValidity);
nBytes += sizeof(__u32); /* checksum*/
* Check if there's space to allocate...
* Thinks.... do we need top make this ths same as yaffs_GetFreeChunks()?
*/
-static int yaffs_CheckSpaceForAllocation(yaffs_Device *dev)
+static int yaffs_CheckSpaceForAllocation(yaffs_Device *dev, int nChunks)
{
int reservedChunks;
int reservedBlocks = dev->param.nReservedBlocks;
reservedChunks = ((reservedBlocks + checkpointBlocks) * dev->param.nChunksPerBlock);
- return (dev->nFreeChunks > reservedChunks);
+ return (dev->nFreeChunks > (reservedChunks + nChunks));
}
static int yaffs_AllocateChunk(yaffs_Device *dev, int useReserve,
dev->allocationPage = 0;
}
- if (!useReserve && !yaffs_CheckSpaceForAllocation(dev)) {
+ if (!useReserve && !yaffs_CheckSpaceForAllocation(dev, 1)) {
/* Not enough space to allocate unless we're allowed to use the reserve. */
return -1;
}
bi->hasShrinkHeader = 0; /* clear the flag so that the block can erase */
- /* Take off the number of soft deleted entries because
- * they're going to get really deleted during GC.
- */
- if(dev->gcChunk == 0) /* first time through for this block */
- dev->nFreeChunks -= bi->softDeletions;
-
- dev->isDoingGC = 1;
+ dev->gcDisable = 1;
if (isCheckpointBlock ||
!yaffs_StillSomeChunkBits(dev, block)) {
yaffs_VerifyBlock(dev, bi, block);
- maxCopies = (wholeBlock) ? dev->param.nChunksPerBlock : 10;
+ maxCopies = (wholeBlock) ? dev->param.nChunksPerBlock : 5;
oldChunk = block * dev->param.nChunksPerBlock + dev->gcChunk;
for (/* init already done */;
* No need to copy this, just forget about it and
* fix up the object.
*/
+
+ /* Free chunks already includes softdeleted chunks.
+ * How ever this chunk is going to soon be really deleted
+ * which will increment free chunks.
+ * We have to decrement free chunks so this works out properly.
+ */
+ dev->nFreeChunks--;
object->nDataChunks--;
yaffs_ObjectHeader *oh;
oh = (yaffs_ObjectHeader *)buffer;
+
oh->isShrink = 0;
tags.extraIsShrinkHeader = 0;
+
oh->shadowsObject = 0;
oh->inbandShadowsObject = 0;
- if(object->variantType == YAFFS_OBJECT_TYPE_FILE)
- oh->fileSize = object->variant.fileVariant.fileSize;
tags.extraShadows = 0;
+ /* Update file size */
+ if(object->variantType == YAFFS_OBJECT_TYPE_FILE){
+ oh->fileSize = object->variant.fileVariant.fileSize;
+ tags.extraFileLength = oh->fileSize;
+ }
+
yaffs_VerifyObjectHeader(object, oh, &tags, 1);
newChunk =
yaffs_WriteNewChunkWithTagsToNAND(dev,(__u8 *) oh, &tags, 1);
- /* If the gc completed then clear the current gcBlock so that we find another. */
- if (bi->blockState != YAFFS_BLOCK_STATE_COLLECTING) {
+ if (bi->blockState == YAFFS_BLOCK_STATE_COLLECTING) {
+ /*
+ * The gc did not complete. Set block state back to FULL
+ * because checkpointing does not restore gc.
+ */
+ bi->blockState = YAFFS_BLOCK_STATE_FULL;
+ } else {
+ /* The gc completed. */
chunksAfter = yaffs_GetErasedChunks(dev);
if (chunksBefore >= chunksAfter) {
T(YAFFS_TRACE_GC,
("gc did not increase free chunks before %d after %d"
TENDSTR), chunksBefore, chunksAfter));
}
- dev->gcBlock = -1;
+ dev->gcBlock = 0;
dev->gcChunk = 0;
}
- dev->isDoingGC = 0;
+ dev->gcDisable = 0;
return retVal;
}
+/*
+ * FindBlockForgarbageCollection is used to select the dirtiest block (or close enough)
+ * for garbage collection.
+ */
+
+static unsigned yaffs_FindBlockForGarbageCollection(yaffs_Device *dev,
+ int aggressive,
+ int background)
+{
+ int i;
+ int iterations;
+ unsigned selected = 0;
+ int prioritised = 0;
+ int prioritisedExists = 0;
+ yaffs_BlockInfo *bi;
+ int threshold;
+
+ /* First let's see if we need to grab a prioritised block */
+ if (dev->hasPendingPrioritisedGCs && !aggressive) {
+ dev->gcDirtiest = 0;
+ bi = dev->blockInfo;
+ for (i = dev->internalStartBlock;
+ i <= dev->internalEndBlock && !selected;
+ i++) {
+
+ if (bi->gcPrioritise) {
+ prioritisedExists = 1;
+ if (bi->blockState == YAFFS_BLOCK_STATE_FULL &&
+ yaffs_BlockNotDisqualifiedFromGC(dev, bi)) {
+ selected = i;
+ prioritised = 1;
+ }
+ }
+ bi++;
+ }
+
+ /*
+ * If there is a prioritised block and none was selected then
+ * this happened because there is at least one old dirty block gumming
+ * up the works. Let's gc the oldest dirty block.
+ */
+
+ if(prioritisedExists &&
+ !selected &&
+ dev->oldestDirtyBlock > 0)
+ selected = dev->oldestDirtyBlock;
+
+ if (!prioritisedExists) /* None found, so we can clear this */
+ dev->hasPendingPrioritisedGCs = 0;
+ }
+
+ /* If we're doing aggressive GC then we are happy to take a less-dirty block, and
+ * search harder.
+ * else (we're doing a leasurely gc), then we only bother to do this if the
+ * block has only a few pages in use.
+ */
+
+ if (!selected){
+ int pagesUsed;
+ int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
+ if (aggressive){
+ threshold = dev->param.nChunksPerBlock;
+ iterations = nBlocks;
+ } else {
+ int maxThreshold = dev->param.nChunksPerBlock/2;
+ threshold = background ?
+ (dev->gcNotDone + 2) * 2 : 0;
+ if(threshold <YAFFS_GC_PASSIVE_THRESHOLD)
+ threshold = YAFFS_GC_PASSIVE_THRESHOLD;
+ if(threshold > maxThreshold)
+ threshold = maxThreshold;
+
+ iterations = nBlocks / 16 + 1;
+ if (iterations > 100)
+ iterations = 100;
+ }
+
+ for (i = 0;
+ i < iterations &&
+ (dev->gcDirtiest < 1 ||
+ dev->gcPagesInUse > YAFFS_GC_GOOD_ENOUGH);
+ i++) {
+ dev->gcBlockFinder++;
+ if (dev->gcBlockFinder < dev->internalStartBlock ||
+ dev->gcBlockFinder > dev->internalEndBlock)
+ dev->gcBlockFinder = dev->internalStartBlock;
+
+ bi = yaffs_GetBlockInfo(dev, dev->gcBlockFinder);
+
+ pagesUsed = bi->pagesInUse - bi->softDeletions;
+
+ if (bi->blockState == YAFFS_BLOCK_STATE_FULL &&
+ pagesUsed < dev->param.nChunksPerBlock &&
+ (dev->gcDirtiest < 1 || pagesUsed < dev->gcPagesInUse) &&
+ yaffs_BlockNotDisqualifiedFromGC(dev, bi)) {
+ dev->gcDirtiest = dev->gcBlockFinder;
+ dev->gcPagesInUse = pagesUsed;
+ }
+ }
+
+ if(dev->gcDirtiest > 0 && dev->gcPagesInUse <= threshold)
+ selected = dev->gcDirtiest;
+ }
+
+ /*
+ * If nothing has been selected for a while, try selecting the oldest dirty
+ * because that's gumming up the works.
+ */
+
+ if(!selected && dev->param.isYaffs2 &&
+ dev->gcNotDone >= ( background ? 10 : 20)){
+ yaffs_FindOldestDirtySequence(dev);
+ if(dev->oldestDirtyBlock > 0) {
+ selected = dev->oldestDirtyBlock;
+ dev->gcDirtiest = selected;
+ dev->oldestDirtyGCs++;
+ bi = yaffs_GetBlockInfo(dev, selected);
+ dev->gcPagesInUse = bi->pagesInUse - bi->softDeletions;
+ } else
+ dev->gcNotDone = 0;
+ }
+
+ if(selected){
+ T(YAFFS_TRACE_GC,
+ (TSTR("GC Selected block %d with %d free, prioritised:%d" TENDSTR),
+ selected,
+ dev->param.nChunksPerBlock - dev->gcPagesInUse,
+ prioritised));
+
+ if(background)
+ dev->backgroundGCs++;
+ dev->gcDirtiest = 0;
+ dev->gcPagesInUse = 0;
+ dev->gcNotDone = 0;
+ if(dev->refreshSkip > 0)
+ dev->refreshSkip--;
+ } else{
+ dev->gcNotDone++;
+ T(YAFFS_TRACE_GC,
+ (TSTR("GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s" TENDSTR),
+ dev->gcBlockFinder, dev->gcNotDone,
+ threshold,
+ dev->gcDirtiest, dev->gcPagesInUse,
+ dev->oldestDirtyBlock,
+ background ? " bg" : ""));
+ }
+
+ return selected;
+}
+
/* New garbage collector
* If we're very low on erased blocks then we do aggressive garbage collection
* otherwise we do "leasurely" garbage collection.
* 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)
+static int yaffs_CheckGarbageCollection(yaffs_Device *dev, int background)
{
- int block;
- int aggressive;
+ int aggressive = 0;
int gcOk = YAFFS_OK;
int maxTries = 0;
+ int minErased;
+ int erasedChunks;
+
int checkpointBlockAdjust;
- if (dev->isDoingGC) {
+ if(dev->param.gcControl &&
+ (dev->param.gcControl(dev) & 1) == 0)
+ return YAFFS_OK;
+
+ if (dev->gcDisable) {
/* Bail out so we don't get recursive gc */
return YAFFS_OK;
}
/* This loop should pass the first time.
- * We'll only see looping here if the erase of the collected block fails.
+ * We'll only see looping here if the collection does not increase space.
*/
do {
if (checkpointBlockAdjust < 0)
checkpointBlockAdjust = 0;
+ minErased = dev->param.nReservedBlocks + checkpointBlockAdjust + 1;
+ erasedChunks = dev->nErasedBlocks * dev->param.nChunksPerBlock;
+
/* If we need a block soon then do aggressive gc.*/
- if (dev->nErasedBlocks < (dev->param.nReservedBlocks + checkpointBlockAdjust + 2))
+ if (dev->nErasedBlocks < minErased)
aggressive = 1;
- else
- aggressive = 0;
+ else {
+ if(dev->gcSkip > 20)
+ dev->gcSkip = 20;
+ if(erasedChunks < dev->nFreeChunks/2 ||
+ dev->gcSkip < 1 ||
+ background)
+ aggressive = 0;
+ else {
+ dev->gcSkip--;
+ break;
+ }
+ }
+
+ dev->gcSkip = 5;
/* If we don't already have a block being gc'd then see if we should start another */
dev->gcChunk = 0;
}
if (dev->gcBlock < 1) {
- dev->gcBlock = yaffs_FindBlockForGarbageCollection(dev, aggressive);
+ dev->gcBlock = yaffs_FindBlockForGarbageCollection(dev, aggressive, background);
dev->gcChunk = 0;
}
- block = dev->gcBlock;
-
- if (block > 0) {
- dev->garbageCollections++;
+ if (dev->gcBlock > 0) {
+ dev->allGCs++;
if (!aggressive)
- dev->passiveGarbageCollections++;
+ dev->passiveGCs++;
T(YAFFS_TRACE_GC,
(TSTR
("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR),
dev->nErasedBlocks, aggressive));
- gcOk = yaffs_GarbageCollectBlock(dev, block, aggressive);
+ gcOk = yaffs_GarbageCollectBlock(dev, dev->gcBlock, aggressive);
}
- if (dev->nErasedBlocks < (dev->param.nReservedBlocks) && block > 0) {
+ if (dev->nErasedBlocks < (dev->param.nReservedBlocks) && dev->gcBlock > 0) {
T(YAFFS_TRACE_GC,
(TSTR
("yaffs: GC !!!no reclaim!!! erasedBlocks %d after try %d block %d"
- TENDSTR), dev->nErasedBlocks, maxTries, block));
+ TENDSTR), dev->nErasedBlocks, maxTries, dev->gcBlock));
}
} while ((dev->nErasedBlocks < dev->param.nReservedBlocks) &&
- (block > 0) &&
+ (dev->gcBlock > 0) &&
(maxTries < 2));
return aggressive ? gcOk : YAFFS_OK;
}
+/*
+ * yaffs_BackgroundGarbageCollect()
+ * Garbage collects. Intended to be called from a background thread.
+ * Returns non-zero if at least half the free chunks are erased.
+ */
+int yaffs_BackgroundGarbageCollect(yaffs_Device *dev, unsigned urgency)
+{
+ int erasedChunks = dev->nErasedBlocks * dev->param.nChunksPerBlock;
+
+ T(YAFFS_TRACE_BACKGROUND, (TSTR("Background gc %u" TENDSTR),urgency));
+
+ yaffs_CheckGarbageCollection(dev, 1);
+ return erasedChunks > dev->nFreeChunks/2;
+}
+
/*------------------------- TAGS --------------------------------*/
static int yaffs_TagsMatch(const yaffs_ExtendedTags *tags, int objectId,
chunkId));
bi = yaffs_GetBlockInfo(dev, block);
+
+ yaffs_UpdateOldestDirtySequence(dev, block, bi);
T(YAFFS_TRACE_DELETION,
(TSTR("line %d delete of chunk %d" TENDSTR), lyn, chunkId));
yaffs_Device *dev = in->myDev;
- yaffs_CheckGarbageCollection(dev);
+ yaffs_CheckGarbageCollection(dev,0);
/* Get the previous chunk at this location in the file if it exists.
* If it does not exist then put a zero into the tree. This creates
* the tnode now, rather than later when it is harder to clean up.
*/
prevChunkId = yaffs_FindChunkInFile(in, chunkInInode, &prevTags);
- if(prevChunkId <= 0 &&
- !yaffs_PutChunkIntoFile(in, chunkInInode, 0, 0)){
- }
+ if(prevChunkId < 1 &&
+ !yaffs_PutChunkIntoFile(in, chunkInInode, 0, 0))
+ return 0;
/* Set up new tags */
yaffs_InitialiseTags(&newTags);
YBUG();
}
- /*
- * If there isn't already a chunk there then do a dummy
- * insert to make sue we have the desired tnode structure.
- */
- if(prevChunkId < 1 &&
- yaffs_PutChunkIntoFile(in, chunkInInode, 0, 0) != YAFFS_OK)
- return -1;
newChunkId =
yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags,
* If name is not NULL, then that new name is used.
*/
int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name, int force,
- int isShrink, int shadows)
+ int isShrink, int shadows, yaffs_XAttrMod *xmod)
{
yaffs_BlockInfo *bi;
if (!in->fake ||
in == dev->rootDir || /* The rootDir should also be saved */
- force) {
+ force || xmod) {
- yaffs_CheckGarbageCollection(dev);
+ yaffs_CheckGarbageCollection(dev,0);
yaffs_CheckObjectDetailsLoaded(in);
buffer = yaffs_GetTempBuffer(in->myDev, __LINE__);
yaffs_VerifyObjectHeader(in, oh, &oldTags, 0);
memcpy(oldName, oh->name, sizeof(oh->name));
- }
-
- memset(buffer, 0xFF, dev->nDataBytesPerChunk);
+ memset(buffer, 0xFF, sizeof(yaffs_ObjectHeader));
+ } else
+ memset(buffer, 0xFF, dev->nDataBytesPerChunk);
oh->type = in->variantType;
oh->yst_mode = in->yst_mode;
break;
}
+ /* process any xattrib modifications */
+ if(xmod)
+ yaffs_ApplyXMod(dev, (char *)buffer, xmod);
+
+
/* Tags */
yaffs_InitialiseTags(&newTags);
in->serial++;
cp->nUnlinkedFiles = dev->nUnlinkedFiles;
cp->nBackgroundDeletions = dev->nBackgroundDeletions;
cp->sequenceNumber = dev->sequenceNumber;
- cp->oldestDirtySequence = dev->oldestDirtySequence;
}
dev->nUnlinkedFiles = cp->nUnlinkedFiles;
dev->nBackgroundDeletions = cp->nBackgroundDeletions;
dev->sequenceNumber = cp->sequenceNumber;
- dev->oldestDirtySequence = cp->oldestDirtySequence;
}
int i;
yaffs_Device *dev = in->myDev;
int ok = 1;
- int tnodeSize = yaffs_CalcTnodeSize(dev);
if (tn) {
if (level > 0) {
__u32 baseOffset = chunkOffset << YAFFS_TNODES_LEVEL0_BITS;
ok = (yaffs_CheckpointWrite(dev, &baseOffset, sizeof(baseOffset)) == sizeof(baseOffset));
if (ok)
- ok = (yaffs_CheckpointWrite(dev, tn, tnodeSize) == tnodeSize);
+ ok = (yaffs_CheckpointWrite(dev, tn, dev->tnodeSize) == dev->tnodeSize);
}
}
yaffs_FileStructure *fileStructPtr = &obj->variant.fileVariant;
yaffs_Tnode *tn;
int nread = 0;
- int tnodeSize = yaffs_CalcTnodeSize(dev);
ok = (yaffs_CheckpointRead(dev, &baseChunk, sizeof(baseChunk)) == sizeof(baseChunk));
/* Read level 0 tnode */
- tn = yaffs_GetTnodeRaw(dev);
- if (tn)
- ok = (yaffs_CheckpointRead(dev, tn, tnodeSize) == tnodeSize);
- else
+ tn = yaffs_GetTnode(dev);
+ if (tn){
+ ok = (yaffs_CheckpointRead(dev, tn, dev->tnodeSize) == dev->tnodeSize);
+ } else
ok = 0;
if (tn && ok)
{
int ok = 1;
- if (dev->param.skipCheckpointWrite || !dev->param.isYaffs2) {
+ if (!yaffs_CheckpointRequired(dev)) {
T(YAFFS_TRACE_CHECKPOINT, (TSTR("skipping checkpoint write" TENDSTR)));
ok = 0;
}
dev->blocksInCheckpoint > 0) {
dev->isCheckpointed = 0;
yaffs_CheckpointInvalidateStream(dev);
- if (dev->param.markSuperBlockDirty)
- dev->param.markSuperBlockDirty(dev);
}
+ if (dev->param.markSuperBlockDirty)
+ dev->param.markSuperBlockDirty(dev);
}
return nDone;
}
-int yaffs_WriteDataToFile(yaffs_Object *in, const __u8 *buffer, loff_t offset,
+int yaffs_DoWriteDataToFile(yaffs_Object *in, const __u8 *buffer, loff_t offset,
int nBytes, int writeThrough)
{
dev = in->myDev;
while (n > 0 && chunkWritten >= 0) {
- /* chunk = offset / dev->nDataBytesPerChunk + 1; */
- /* start = offset % dev->nDataBytesPerChunk; */
yaffs_AddrToChunk(dev, offset, &chunk, &start);
if (chunk * dev->nDataBytesPerChunk + start != offset ||
TENDSTR),
(int)offset, chunk, start));
}
- chunk++;
+ chunk++; /* File pos to chunk in file offset */
/* OK now check for the curveball where the start and end are in
* the same chunk.
cache = yaffs_FindChunkCache(in, chunk);
if (!cache
- && yaffs_CheckSpaceForAllocation(in->
- myDev)) {
- cache = yaffs_GrabChunkCache(in->myDev);
+ && yaffs_CheckSpaceForAllocation(dev, 1)) {
+ cache = yaffs_GrabChunkCache(dev);
cache->object = in;
cache->chunkId = chunk;
cache->dirty = 0;
cache->locked = 0;
yaffs_ReadChunkDataFromObject(in, chunk,
- cache->
- data);
+ cache->data);
} else if (cache &&
!cache->dirty &&
- !yaffs_CheckSpaceForAllocation(in->myDev)) {
+ !yaffs_CheckSpaceForAllocation(dev, 1)) {
/* Drop the cache if it was a read cache item and
* no space check has been made for it.
*/
return nDone;
}
+int yaffs_WriteDataToFile(yaffs_Object *in, const __u8 *buffer, loff_t offset,
+ int nBytes, int writeThrough)
+{
+ yaffs_HandleHole(in,offset);
+ return yaffs_DoWriteDataToFile(in,buffer,offset,nBytes,writeThrough);
+}
+
+
/* ---------------------- File resizing stuff ------------------ */
}
-int yaffs_ResizeFile(yaffs_Object *in, loff_t newSize)
-{
- int oldFileSize = in->variant.fileVariant.fileSize;
- __u32 newSizeOfPartialChunk;
+static void yaffs_ResizeDown( yaffs_Object *obj, loff_t newSize)
+{
int newFullChunks;
-
- yaffs_Device *dev = in->myDev;
+ __u32 newSizeOfPartialChunk;
+ yaffs_Device *dev = obj->myDev;
yaffs_AddrToChunk(dev, newSize, &newFullChunks, &newSizeOfPartialChunk);
- yaffs_FlushFilesChunkCache(in);
- yaffs_InvalidateWholeChunkCache(in);
+ yaffs_PruneResizedChunks(obj, newSize);
- yaffs_CheckGarbageCollection(dev);
+ if (newSizeOfPartialChunk != 0) {
+ int lastChunk = 1 + newFullChunks;
+ __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__);
- if (in->variantType != YAFFS_OBJECT_TYPE_FILE)
- return YAFFS_FAIL;
+ /* Got to read and rewrite the last chunk with its new size and zero pad */
+ yaffs_ReadChunkDataFromObject(obj, lastChunk, localBuffer);
+ memset(localBuffer + newSizeOfPartialChunk, 0,
+ dev->nDataBytesPerChunk - newSizeOfPartialChunk);
- if (newSize == oldFileSize)
- return YAFFS_OK;
+ yaffs_WriteChunkDataToObject(obj, lastChunk, localBuffer,
+ newSizeOfPartialChunk, 1);
+
+ yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__);
+ }
+
+ obj->variant.fileVariant.fileSize = newSize;
+
+ yaffs_PruneFileStructure(dev, &obj->variant.fileVariant);
+}
- if (newSize < oldFileSize) {
- yaffs_PruneResizedChunks(in, newSize);
+static int yaffs_HandleHole(yaffs_Object *obj, loff_t newSize)
+{
+ /* if newsSize > oldFileSize.
+ * We're going to be writing a hole.
+ * If the hole is small then write zeros otherwise write a start of hole marker.
+ */
+
+
+ loff_t oldFileSize;
+ int increase;
+ int smallHole ;
+ int result = YAFFS_OK;
+ yaffs_Device *dev = NULL;
+
+ __u8 *localBuffer = NULL;
+
+ int smallIncreaseOk = 0;
+
+ if(!obj)
+ return YAFFS_FAIL;
- if (newSizeOfPartialChunk != 0) {
- int lastChunk = 1 + newFullChunks;
+ if(obj->variantType != YAFFS_OBJECT_TYPE_FILE)
+ return YAFFS_FAIL;
+
+ dev = obj->myDev;
+
+ /* Bail out if not yaffs2 mode */
+ if(!dev->param.isYaffs2)
+ return YAFFS_OK;
- __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__);
+ oldFileSize = obj->variant.fileVariant.fileSize;
- /* Got to read and rewrite the last chunk with its new size and zero pad */
- yaffs_ReadChunkDataFromObject(in, lastChunk,
- localBuffer);
+ if (newSize <= oldFileSize)
+ return YAFFS_OK;
- memset(localBuffer + newSizeOfPartialChunk, 0,
- dev->nDataBytesPerChunk - newSizeOfPartialChunk);
+ increase = newSize - oldFileSize;
- yaffs_WriteChunkDataToObject(in, lastChunk, localBuffer,
- newSizeOfPartialChunk, 1);
+ if(increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->nDataBytesPerChunk &&
+ yaffs_CheckSpaceForAllocation(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1))
+ smallHole = 1;
+ else
+ smallHole = 0;
- yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__);
+ if(smallHole)
+ localBuffer= yaffs_GetTempBuffer(dev, __LINE__);
+
+ if(localBuffer){
+ /* fill hole with zero bytes */
+ int pos = oldFileSize;
+ int thisWrite;
+ int written;
+ memset(localBuffer,0,dev->nDataBytesPerChunk);
+ smallIncreaseOk = 1;
+
+ while(increase > 0 && smallIncreaseOk){
+ thisWrite = increase;
+ if(thisWrite > dev->nDataBytesPerChunk)
+ thisWrite = dev->nDataBytesPerChunk;
+ written = yaffs_DoWriteDataToFile(obj,localBuffer,pos,thisWrite,0);
+ if(written == thisWrite){
+ pos += thisWrite;
+ increase -= thisWrite;
+ } else
+ smallIncreaseOk = 0;
}
- in->variant.fileVariant.fileSize = newSize;
+ yaffs_ReleaseTempBuffer(dev,localBuffer,__LINE__);
- yaffs_PruneFileStructure(dev, &in->variant.fileVariant);
- } else {
- /* newsSize > oldFileSize */
- in->variant.fileVariant.fileSize = newSize;
+ /* If we were out of space then reverse any chunks we've added */
+ if(!smallIncreaseOk)
+ yaffs_ResizeDown(obj, oldFileSize);
+ }
+
+ if (!smallIncreaseOk &&
+ obj->parent &&
+ obj->parent->objectId != YAFFS_OBJECTID_UNLINKED &&
+ obj->parent->objectId != YAFFS_OBJECTID_DELETED){
+ /* Write a hole start header with the old file size */
+ yaffs_UpdateObjectHeader(obj, NULL, 0, 1, 0, NULL);
}
+ return result;
+
+}
+
+int yaffs_ResizeFile(yaffs_Object *in, loff_t newSize)
+{
+ yaffs_Device *dev = in->myDev;
+ int oldFileSize = in->variant.fileVariant.fileSize;
+
+ yaffs_FlushFilesChunkCache(in);
+ yaffs_InvalidateWholeChunkCache(in);
+
+ yaffs_CheckGarbageCollection(dev,0);
+
+ if (in->variantType != YAFFS_OBJECT_TYPE_FILE)
+ return YAFFS_FAIL;
+
+ if (newSize == oldFileSize)
+ return YAFFS_OK;
+
+ if(newSize > oldFileSize){
+ yaffs_HandleHole(in,newSize);
+ in->variant.fileVariant.fileSize = newSize;
+ } else {
+ /* newSize < oldFileSize */
+ yaffs_ResizeDown(in, newSize);
+ }
/* Write a new object header to reflect the resize.
* show we've shrunk the file, if need be
!in->isShadowed &&
in->parent->objectId != YAFFS_OBJECTID_UNLINKED &&
in->parent->objectId != YAFFS_OBJECTID_DELETED)
- yaffs_UpdateObjectHeader(in, NULL, 0,
- (newSize < oldFileSize) ? 1 : 0, 0);
+ yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0, NULL);
+
return YAFFS_OK;
}
#endif
}
- retVal = (yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0) >=
+ retVal = (yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0, NULL) >=
0) ? YAFFS_OK : YAFFS_FAIL;
}
} else {
int retVal;
int immediateDeletion = 0;
+ yaffs_Device *dev = in->myDev;
if (!in->myInode)
immediateDeletion = 1;
in->objectId));
in->deleted = 1;
in->myDev->nDeletedFiles++;
- if (1 || in->myDev->param.isYaffs2)
+ if (dev->param.disableSoftDelete || dev->param.isYaffs2)
yaffs_ResizeFile(in, 0);
yaffs_SoftDeleteFile(in);
} else {
int yaffs_DeleteFile(yaffs_Object *in)
{
int retVal = YAFFS_OK;
- int deleted = in->deleted;
+ int deleted; /* Need to cache value on stack if in is freed */
+ yaffs_Device *dev = in->myDev;
- yaffs_ResizeFile(in, 0);
+ if (dev->param.disableSoftDelete || dev->param.isYaffs2)
+ yaffs_ResizeFile(in, 0);
if (in->nDataChunks > 0) {
/* Use soft deletion if there is data in the file.
if (!in->unlinked)
retVal = yaffs_UnlinkFileIfNeeded(in);
+ deleted = in->deleted;
+
if (retVal == YAFFS_OK && in->unlinked && !in->deleted) {
in->deleted = 1;
deleted = 1;
retVal = yaffs_DeleteFile(obj);
break;
case YAFFS_OBJECT_TYPE_DIRECTORY:
+ if(!ylist_empty(&obj->variant.directoryVariant.dirty)){
+ T(YAFFS_TRACE_BACKGROUND, (TSTR("Remove object %d from dirty directories" TENDSTR),obj->objectId));
+ ylist_del_init(&obj->variant.directoryVariant.dirty);
+ }
return yaffs_DeleteDirectory(obj);
break;
case YAFFS_OBJECT_TYPE_SYMLINK:
return yaffs_DeleteFile(obj);
break;
case YAFFS_OBJECT_TYPE_DIRECTORY:
+ ylist_del_init(&obj->variant.directoryVariant.dirty);
return yaffs_DeleteDirectory(obj);
break;
case YAFFS_OBJECT_TYPE_SYMLINK:
dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER;
/* Scan all the blocks to determine their state */
+ bi = dev->blockInfo;
for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) {
- bi = yaffs_GetBlockInfo(dev, blk);
yaffs_ClearChunkBits(dev, blk);
bi->pagesInUse = 0;
bi->softDeletions = 0;
dev->nErasedBlocks++;
dev->nFreeChunks += dev->param.nChunksPerBlock;
}
+ bi++;
}
startIterator = dev->internalStartBlock;
obj = yaffs_FindObjectByNumber(dev, fixer->objectId);
if (obj)
- yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0);
+ yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0, NULL);
YFREE(fixer);
}
chunkData = yaffs_GetTempBuffer(dev, __LINE__);
/* Scan all the blocks to determine their state */
+ bi = dev->blockInfo;
for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) {
- bi = yaffs_GetBlockInfo(dev, blk);
yaffs_ClearChunkBits(dev, blk);
bi->pagesInUse = 0;
bi->softDeletions = 0;
}
}
+ bi++;
}
T(YAFFS_TRACE_SCAN,
* the current allocation block.
*/
- T(YAFFS_TRACE_ALWAYS,
+ T(YAFFS_TRACE_SCAN,
(TSTR("Partially written block %d detected" TENDSTR),
blk));
}
* create dir/a : update dir's mtime/ctime
* rm dir/a: update dir's mtime/ctime
* modify dir/a: don't update dir's mtimme/ctime
+ *
+ * This can be handled immediately or defered. Defering helps reduce the number
+ * of updates when many files in a directory are changed within a brief period.
+ *
+ * If the directory updating is defered then yaffs_UpdateDirtyDirecories must be
+ * called periodically.
*/
static void yaffs_UpdateParent(yaffs_Object *obj)
{
+ yaffs_Device *dev;
if(!obj)
return;
+ dev = obj->myDev;
obj->dirty = 1;
obj->yst_mtime = obj->yst_ctime = Y_CURRENT_TIME;
+ if(dev->param.deferDirectoryUpdate){
+ struct ylist_head *link = &obj->variant.directoryVariant.dirty;
+
+ if(ylist_empty(link)){
+ ylist_add(link,&dev->dirtyDirectories);
+ T(YAFFS_TRACE_BACKGROUND, (TSTR("Added object %d to dirty directories" TENDSTR),obj->objectId));
+ }
- yaffs_UpdateObjectHeader(obj,NULL,0,0,0);
+ } else
+ yaffs_UpdateObjectHeader(obj, NULL, 0, 0, 0, NULL);
+}
+
+void yaffs_UpdateDirtyDirectories(yaffs_Device *dev)
+{
+ struct ylist_head *link;
+ yaffs_Object *obj;
+ yaffs_DirectoryStructure *dS;
+ yaffs_ObjectVariant *oV;
+
+ T(YAFFS_TRACE_BACKGROUND, (TSTR("Update dirty directories" TENDSTR)));
+
+ while(!ylist_empty(&dev->dirtyDirectories)){
+ link = dev->dirtyDirectories.next;
+ ylist_del_init(link);
+
+ dS=ylist_entry(link,yaffs_DirectoryStructure,dirty);
+ oV = ylist_entry(dS,yaffs_ObjectVariant,directoryVariant);
+ obj = ylist_entry(oV,yaffs_Object,variant);
+
+ T(YAFFS_TRACE_BACKGROUND, (TSTR("Update directory %d" TENDSTR), obj->objectId));
+
+ if(obj->dirty)
+ yaffs_UpdateObjectHeader(obj, NULL, 0, 0, 0, NULL);
+ }
}
static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj)
if (valid & ATTR_SIZE)
yaffs_ResizeFile(obj, attr->ia_size);
- yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0);
+ yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0, NULL);
return YAFFS_OK;
#endif
+
+static int yaffs_DoXMod(yaffs_Object *obj, int set, const char *name, const void *value, int size, int flags)
+{
+ yaffs_XAttrMod xmod;
+
+ int result;
+
+ xmod.set = set;
+ xmod.name = name;
+ xmod.data = value;
+ xmod.size = size;
+ xmod.flags = flags;
+ xmod.result = -ENOSPC;
+
+ result = yaffs_UpdateObjectHeader(obj, NULL, 0, 0, 0, &xmod);
+
+ if(result > 0)
+ return xmod.result;
+ else
+ return -ENOSPC;
+}
+
+static int yaffs_ApplyXMod(yaffs_Device *dev, char *buffer, yaffs_XAttrMod *xmod)
+{
+ int retval = 0;
+ int x_offs = sizeof(yaffs_ObjectHeader);
+ int x_size = dev->nDataBytesPerChunk - sizeof(yaffs_ObjectHeader);
+
+ char * x_buffer = buffer + x_offs;
+
+ if(xmod->set)
+ retval = nval_set(x_buffer, x_size, xmod->name, xmod->data, xmod->size, xmod->flags);
+ else
+ retval = nval_del(x_buffer, x_size, xmod->name);
+
+ xmod->result = retval;
+
+ return retval;
+}
+
+static int yaffs_DoXFetch(yaffs_Object *obj, const char *name, void *value, int size)
+{
+ char *buffer = NULL;
+ int result;
+ yaffs_ExtendedTags tags;
+ yaffs_Device *dev = obj->myDev;
+ int x_offs = sizeof(yaffs_ObjectHeader);
+ int x_size = dev->nDataBytesPerChunk - sizeof(yaffs_ObjectHeader);
+
+ __u8 * x_buffer;
+
+ int retval = 0;
+
+ if(obj->hdrChunk < 1)
+ return -ENOENT;
+
+ buffer = yaffs_GetTempBuffer(dev, __LINE__);
+ if(!buffer)
+ return -ENOMEM;
+
+ result = yaffs_ReadChunkWithTagsFromNAND(dev,obj->hdrChunk, buffer, &tags);
+
+ if(result != YAFFS_OK)
+ retval = -ENOENT;
+ else{
+ x_buffer = buffer + x_offs;
+
+ if(name)
+ retval = nval_get(x_buffer, x_size, name, value, size);
+ else
+ retval = nval_list(x_buffer, x_size, value,size);
+ }
+ yaffs_ReleaseTempBuffer(dev,buffer,__LINE__);
+ return retval;
+}
+
+int yaffs_SetXAttribute(yaffs_Object *obj, const char *name, const void * value, int size, int flags)
+{
+ return yaffs_DoXMod(obj, 1, name, value, size, flags);
+}
+
+int yaffs_RemoveXAttribute(yaffs_Object *obj, const char *name)
+{
+ return yaffs_DoXMod(obj, 0, name, NULL, 0, 0);
+}
+
+int yaffs_GetXAttribute(yaffs_Object *obj, const char *name, void *value, int size)
+{
+ return yaffs_DoXFetch(obj, name, value, size);
+}
+
+int yaffs_ListXAttributes(yaffs_Object *obj, char *buffer, int size)
+{
+ return yaffs_DoXFetch(obj, NULL, buffer,size);
+}
+
+
+
#if 0
int yaffs_DumpObject(yaffs_Object *obj)
{
dev->chunkOffset = 0;
dev->nFreeChunks = 0;
- dev->gcBlock = -1;
+ dev->gcBlock = 0;
if (dev->param.startBlock == 0) {
dev->internalStartBlock = dev->param.startBlock + 1;
else
dev->chunkGroupBits = bits - dev->tnodeWidth;
+ dev->tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
+ if(dev->tnodeSize < sizeof(yaffs_Tnode))
+ dev->tnodeSize = sizeof(yaffs_Tnode);
dev->chunkGroupSize = 1 << dev->chunkGroupBits;
/* OK, we've finished verifying the device, lets continue with initialisation */
/* More device initialisation */
- dev->garbageCollections = 0;
- dev->passiveGarbageCollections = 0;
- dev->currentDirtyChecker = 0;
+ dev->allGCs = 0;
+ dev->passiveGCs = 0;
+ dev->oldestDirtyGCs = 0;
+ dev->backgroundGCs = 0;
+ dev->gcBlockFinder = 0;
dev->bufferedBlock = -1;
dev->doingBufferedBlockRewrite = 0;
dev->nDeletedFiles = 0;
dev->tagsEccUnfixed = 0;
dev->nErasureFailures = 0;
dev->nErasedBlocks = 0;
- dev->isDoingGC = 0;
+ dev->gcDisable= 0;
dev->hasPendingPrioritisedGCs = 1; /* Assume the worst for now, will get fixed on first GC */
+ YINIT_LIST_HEAD(&dev->dirtyDirectories);
+ dev->oldestDirtySequence = 0;
+ dev->oldestDirtyBlock = 0;
/* Initialise temporary buffers and caches. */
if (!yaffs_InitialiseTempBuffers(dev))
if (!init_failed && !yaffs_InitialiseBlocks(dev))
init_failed = 1;
- yaffs_InitialiseTnodes(dev);
- yaffs_InitialiseObjects(dev);
+ yaffs_InitialiseTnodesAndObjects(dev);
if (!init_failed && !yaffs_CreateInitialDirectories(dev))
init_failed = 1;
* and scan backwards.
*/
yaffs_DeinitialiseBlocks(dev);
- yaffs_DeinitialiseTnodes(dev);
- yaffs_DeinitialiseObjects(dev);
+ yaffs_DeinitialiseTnodesAndObjects(dev);
dev->nErasedBlocks = 0;
dev->nFreeChunks = 0;
dev->nDeletedFiles = 0;
dev->nUnlinkedFiles = 0;
dev->nBackgroundDeletions = 0;
- dev->oldestDirtySequence = 0;
if (!init_failed && !yaffs_InitialiseBlocks(dev))
init_failed = 1;
- yaffs_InitialiseTnodes(dev);
- yaffs_InitialiseObjects(dev);
+ yaffs_InitialiseTnodesAndObjects(dev);
if (!init_failed && !yaffs_CreateInitialDirectories(dev))
init_failed = 1;
int i;
yaffs_DeinitialiseBlocks(dev);
- yaffs_DeinitialiseTnodes(dev);
- yaffs_DeinitialiseObjects(dev);
+ yaffs_DeinitialiseTnodesAndObjects(dev);
if (dev->param.nShortOpCaches > 0 &&
dev->srCache) {
static int yaffs_CountFreeChunks(yaffs_Device *dev)
{
- int nFree;
+ int nFree=0;
int b;
yaffs_BlockInfo *blk;
- for (nFree = 0, b = dev->internalStartBlock; b <= dev->internalEndBlock;
- b++) {
- blk = yaffs_GetBlockInfo(dev, b);
-
+ blk = dev->blockInfo;
+ for (b = dev->internalStartBlock; b <= dev->internalEndBlock; b++) {
switch (blk->blockState) {
case YAFFS_BLOCK_STATE_EMPTY:
case YAFFS_BLOCK_STATE_ALLOCATING:
default:
break;
}
+ blk++;
}
return nFree;