From adf8ff155a242969024595f8750f191be6b8acaf Mon Sep 17 00:00:00 2001 From: charles Date: Tue, 9 Mar 2010 04:12:00 +0000 Subject: [PATCH] Add background processing of updated directories --- yaffs_fs.c | 128 +++++++++++++++++++++++++++++++++++++++++--------- yaffs_guts.c | 56 ++++++++++++++++++++-- yaffs_guts.h | 15 +++++- yaffs_linux.h | 2 + yaffs_trace.h | 2 + 5 files changed, 176 insertions(+), 27 deletions(-) diff --git a/yaffs_fs.c b/yaffs_fs.c index 8b11f40..99de632 100644 --- a/yaffs_fs.c +++ b/yaffs_fs.c @@ -32,7 +32,7 @@ */ const char *yaffs_fs_c_version = - "$Id: yaffs_fs.c,v 1.96 2010-02-25 22:41:46 charles Exp $"; + "$Id: yaffs_fs.c,v 1.97 2010-03-09 04:12:00 charles Exp $"; extern const char *yaffs_guts_c_version; #include @@ -52,6 +52,8 @@ extern const char *yaffs_guts_c_version; #include #include +#include + #include "asm/div64.h" @@ -1779,7 +1781,7 @@ static int yaffs_statfs(struct super_block *sb, struct statfs *buf) -static void yaffs_flush_sb_inodes(struct super_block *sb) +static void yaffs_FlushInodes(struct super_block *sb) { struct inode *iptr; yaffs_Object *obj; @@ -1793,21 +1795,32 @@ static void yaffs_flush_sb_inodes(struct super_block *sb) } } -static int yaffs_do_sync_fs(struct super_block *sb) + +static void yaffs_FlushSuperBlock(struct super_block *sb, int do_checkpoint) +{ + yaffs_Device *dev = yaffs_SuperToDevice(sb); + if(!dev) + return; + + yaffs_FlushInodes(sb); + yaffs_UpdateDirtyDirectories(dev); + yaffs_FlushEntireDeviceCache(dev); + if(do_checkpoint) + yaffs_CheckpointSave(dev); +} + +static int yaffs_do_sync_fs(struct super_block *sb, int do_checkpoint) { yaffs_Device *dev = yaffs_SuperToDevice(sb); - T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ("yaffs_do_sync_fs\n")); + T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, + ("yaffs_do_sync_fs: %s %s\n", + sb->s_dirt ? "dirty" : "clean", + do_checkpoint ? "with checkpoint" : "no checkpoint")); if (sb->s_dirt) { yaffs_GrossLock(dev); - - if (dev) { - yaffs_FlushEntireDeviceCache(dev); - yaffs_flush_sb_inodes(sb); - yaffs_CheckpointSave(dev); - } - + yaffs_FlushSuperBlock(sb,do_checkpoint); yaffs_GrossUnlock(dev); sb->s_dirt = 0; @@ -1815,6 +1828,70 @@ static int yaffs_do_sync_fs(struct super_block *sb) return 0; } +/* + * yaffs background thread functions . + * yaffs_BackgroundThread() the thread function + * yaffs_BackgroundStart() launches the background thread. + * yaffs_BackgroundStop() cleans up the background thread. + * + * NB: + * The thread should only run after the yaffs is initialised + * The thread should be stopped before yaffs is unmounted. + * The thread should not do any writing while the fs is in read only. + */ + +static int yaffs_BackgroundThread(void *data) +{ + yaffs_Device *dev = (yaffs_Device *)data; + struct yaffs_LinuxContext *context = yaffs_DeviceToContext(dev); + + T(YAFFS_TRACE_BACKGROUND, + ("yaffs_background starting for dev %p\n", + (void *)dev)); + + while(context->bgRunning){ + T(YAFFS_TRACE_BACKGROUND, + ("yaffs_background\n")); + + if(kthread_should_stop()) + break; + yaffs_GrossLock(dev); + yaffs_UpdateDirtyDirectories(dev); + yaffs_GrossUnlock(dev); + msleep(500); + } + return 0; +} + +static int yaffs_BackgroundStart(yaffs_Device *dev) +{ + int retval = 0; + + struct yaffs_LinuxContext *context = yaffs_DeviceToContext(dev); + + context->bgRunning = 1; + + context->bgThread = kthread_run(yaffs_BackgroundThread,(void *)dev,"yaffs_%x",(unsigned)dev); + + if(IS_ERR(context->bgThread)){ + retval = PTR_ERR(context->bgThread); + context->bgThread = NULL; + context->bgRunning = 0; + } + return retval; +} + +static void yaffs_BackgroundStop(yaffs_Device *dev) +{ + struct yaffs_LinuxContext *ctxt = yaffs_DeviceToContext(dev); + + ctxt->bgRunning = 0; + + if( ctxt->bgThread){ + kthread_stop(ctxt->bgThread); + ctxt->bgThread = NULL; + } +} #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) static void yaffs_write_super(struct super_block *sb) @@ -1824,8 +1901,8 @@ static int yaffs_write_super(struct super_block *sb) { T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ("yaffs_write_super\n")); - if (yaffs_auto_checkpoint >= 2) - yaffs_do_sync_fs(sb); + yaffs_do_sync_fs(sb, yaffs_auto_checkpoint >= 2); + #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) return 0; #endif @@ -1840,8 +1917,7 @@ static int yaffs_sync_fs(struct super_block *sb) { T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ("yaffs_sync_fs\n")); - if (yaffs_auto_checkpoint >= 1) - yaffs_do_sync_fs(sb); + yaffs_do_sync_fs(sb,yaffs_auto_checkpoint >= 1); return 0; } @@ -1921,9 +1997,7 @@ static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data) yaffs_GrossLock(dev); - yaffs_FlushEntireDeviceCache(dev); - - yaffs_CheckpointSave(dev); + yaffs_FlushSuperBlock(sb,1); if (mtd->sync) mtd->sync(mtd); @@ -1946,13 +2020,13 @@ static void yaffs_put_super(struct super_block *sb) yaffs_GrossLock(dev); - yaffs_FlushEntireDeviceCache(dev); - - yaffs_CheckpointSave(dev); + yaffs_FlushSuperBlock(sb,1); if (yaffs_DeviceToContext(dev)->putSuperFunc) yaffs_DeviceToContext(dev)->putSuperFunc(sb); + yaffs_BackgroundStop(dev); + yaffs_Deinitialise(dev); yaffs_GrossUnlock(dev); @@ -2295,6 +2369,9 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, #ifdef CONFIG_YAFFS_DISABLE_TAGS_ECC param->noTagsECC = 1; #endif + + param->deferDirectoryUpdate = 1; + if(options.tags_ecc_overridden) param->noTagsECC = !options.tags_ecc_on; @@ -2385,6 +2462,13 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, T(YAFFS_TRACE_OS, ("yaffs_read_super: guts initialised %s\n", (err == YAFFS_OK) ? "OK" : "FAILED")); + + if(err == YAFFS_OK) + yaffs_BackgroundStart(dev); + + if(!context->bgThread) + param->deferDirectoryUpdate = 0; + /* Release lock before yaffs_get_inode() */ yaffs_GrossUnlock(dev); @@ -2653,6 +2737,8 @@ static struct { {"tracing", YAFFS_TRACE_TRACING}, {"sync", YAFFS_TRACE_SYNC}, + {"background", YAFFS_TRACE_BACKGROUND}, + {"verify", YAFFS_TRACE_VERIFY}, {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, {"verify_full", YAFFS_TRACE_VERIFY_FULL}, diff --git a/yaffs_guts.c b/yaffs_guts.c index e5b1eaf..1ad630b 100644 --- a/yaffs_guts.c +++ b/yaffs_guts.c @@ -12,7 +12,7 @@ */ const char *yaffs_guts_c_version = - "$Id: yaffs_guts.c,v 1.115 2010-03-07 23:43:34 charles Exp $"; + "$Id: yaffs_guts.c,v 1.116 2010-03-09 04:12:00 charles Exp $"; #include "yportenv.h" #include "yaffs_trace.h" @@ -2358,6 +2358,8 @@ yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev, int number, 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: @@ -4856,9 +4858,9 @@ static void yaffs_InvalidateCheckpoint(yaffs_Device *dev) 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); } @@ -5469,6 +5471,10 @@ int retVal = -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: @@ -5543,6 +5549,7 @@ static int yaffs_UnlinkWorker(yaffs_Object *obj) 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: @@ -7051,17 +7058,57 @@ static void yaffs_VerifyDirectory(yaffs_Object *directory) * 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); +} + +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); + } } static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj) @@ -7672,6 +7719,7 @@ int yaffs_GutsInitialise(yaffs_Device *dev) dev->nErasedBlocks = 0; dev->isDoingGC = 0; dev->hasPendingPrioritisedGCs = 1; /* Assume the worst for now, will get fixed on first GC */ + YINIT_LIST_HEAD(&dev->dirtyDirectories); dev->oldestDirtySequence = 0; /* Initialise temporary buffers and caches. */ diff --git a/yaffs_guts.h b/yaffs_guts.h index 3bae979..7643dfc 100644 --- a/yaffs_guts.h +++ b/yaffs_guts.h @@ -386,6 +386,7 @@ typedef struct { typedef struct { struct ylist_head children; /* list of child links */ + struct ylist_head dirty; /* Entry for list of dirty directories */ } yaffs_DirectoryStructure; typedef struct { @@ -602,8 +603,9 @@ struct yaffs_DeviceParamStruct { int useHeaderFileSize; /* Flag to determine if we should use file sizes from the header */ int disableLazyLoad; /* Disable lazy loading on this device */ int wideTnodesDisabled; /* Set to disable wide tnodes */ - - /* End of stuff that must be set before initialisation. */ + + int deferDirectoryUpdate; /* Set to defer directory updates */ + }; typedef struct yaffs_DeviceParamStruct yaffs_DeviceParam; @@ -744,6 +746,10 @@ struct yaffs_DeviceStruct { /* Block refreshing */ int refreshSkip; /* A skip down counter. Refresh happens when this gets to zero. */ + /* Dirty directory handling */ + struct ylist_head dirtyDirectories; /* List of dirty directories */ + + /* Statistcs */ int nPageWrites; int nPageReads; @@ -882,6 +888,10 @@ void yfsd_WinFileTimeNow(__u32 target[2]); void yaffs_HandleDeferedFree(yaffs_Object *obj); + +void yaffs_UpdateDirtyDirectories(yaffs_Device *dev); + + /* Debug dump */ int yaffs_DumpObject(yaffs_Object *obj); @@ -896,4 +906,5 @@ void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi); __u8 *yaffs_GetTempBuffer(yaffs_Device *dev, int lineNo); void yaffs_ReleaseTempBuffer(yaffs_Device *dev, __u8 *buffer, int lineNo); + #endif diff --git a/yaffs_linux.h b/yaffs_linux.h index 272186a..54556e0 100644 --- a/yaffs_linux.h +++ b/yaffs_linux.h @@ -23,6 +23,8 @@ struct yaffs_LinuxContext { struct ylist_head contextList; /* List of these we have mounted */ struct yaffs_DeviceStruct *dev; struct super_block * superBlock; + struct task_struct *bgThread; /* Background thread for this device */ + int bgRunning; struct semaphore grossLock; /* Gross locking semaphore */ __u8 *spareBuffer; /* For mtdif2 use. Don't know the size of the buffer * at compile time so we have to allocate it. diff --git a/yaffs_trace.h b/yaffs_trace.h index 5ddb977..68a2ffc 100644 --- a/yaffs_trace.h +++ b/yaffs_trace.h @@ -48,6 +48,8 @@ extern unsigned int yaffs_wr_attempts; #define YAFFS_TRACE_SYNC 0x00100000 +#define YAFFS_TRACE_BACKGROUND 0x00200000 + #define YAFFS_TRACE_ERROR 0x40000000 #define YAFFS_TRACE_BUG 0x80000000 #define YAFFS_TRACE_ALWAYS 0xF0000000 -- 2.30.2