Add background processing of updated directories
authorcharles <charles>
Tue, 9 Mar 2010 04:12:00 +0000 (04:12 +0000)
committercharles <charles>
Tue, 9 Mar 2010 04:12:00 +0000 (04:12 +0000)
yaffs_fs.c
yaffs_guts.c
yaffs_guts.h
yaffs_linux.h
yaffs_trace.h

index 8b11f4034c4896c54d6099d8256027c77b521f86..99de632bbbc3cccccf8007eb9b6b524e77bd52db 100644 (file)
@@ -32,7 +32,7 @@
  */
 
 const char *yaffs_fs_c_version =
  */
 
 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 <linux/version.h>
 extern const char *yaffs_guts_c_version;
 
 #include <linux/version.h>
@@ -52,6 +52,8 @@ extern const char *yaffs_guts_c_version;
 #include <linux/string.h>
 #include <linux/ctype.h>
 
 #include <linux/string.h>
 #include <linux/ctype.h>
 
+#include <linux/kthread.h>
+
 #include "asm/div64.h"
 
 
 #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;
 {
        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);
 {
 
        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 (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;
                yaffs_GrossUnlock(dev);
 
                sb->s_dirt = 0;
@@ -1815,6 +1828,70 @@ static int yaffs_do_sync_fs(struct super_block *sb)
        return 0;
 }
 
        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)
 
 #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"));
 {
 
        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
 #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"));
 
 {
        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;
 }
 
        return 0;
 }
@@ -1921,9 +1997,7 @@ static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data)
 
                yaffs_GrossLock(dev);
 
 
                yaffs_GrossLock(dev);
 
-               yaffs_FlushEntireDeviceCache(dev);
-
-               yaffs_CheckpointSave(dev);
+               yaffs_FlushSuperBlock(sb,1);
 
                if (mtd->sync)
                        mtd->sync(mtd);
 
                if (mtd->sync)
                        mtd->sync(mtd);
@@ -1946,13 +2020,13 @@ static void yaffs_put_super(struct super_block *sb)
 
        yaffs_GrossLock(dev);
 
 
        yaffs_GrossLock(dev);
 
-       yaffs_FlushEntireDeviceCache(dev);
-
-       yaffs_CheckpointSave(dev);
+       yaffs_FlushSuperBlock(sb,1);
 
        if (yaffs_DeviceToContext(dev)->putSuperFunc)
                yaffs_DeviceToContext(dev)->putSuperFunc(sb);
 
 
        if (yaffs_DeviceToContext(dev)->putSuperFunc)
                yaffs_DeviceToContext(dev)->putSuperFunc(sb);
 
+       yaffs_BackgroundStop(dev);
+
        yaffs_Deinitialise(dev);
 
        yaffs_GrossUnlock(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
 #ifdef CONFIG_YAFFS_DISABLE_TAGS_ECC
        param->noTagsECC = 1;
 #endif
+
+       param->deferDirectoryUpdate = 1;
+
        if(options.tags_ecc_overridden)
                param->noTagsECC = !options.tags_ecc_on;
 
        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"));
        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);
 
        /* Release lock before yaffs_get_inode() */
        yaffs_GrossUnlock(dev);
@@ -2653,6 +2737,8 @@ static struct {
        {"tracing", YAFFS_TRACE_TRACING},
        {"sync", YAFFS_TRACE_SYNC},
 
        {"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},
        {"verify", YAFFS_TRACE_VERIFY},
        {"verify_nand", YAFFS_TRACE_VERIFY_NAND},
        {"verify_full", YAFFS_TRACE_VERIFY_FULL},
index e5b1eaf185dd410014dde4b69bf04aceb68585c7..1ad630b8a9f4e3c26cc3527717cea0fc9c109bdb 100644 (file)
@@ -12,7 +12,7 @@
  */
 
 const char *yaffs_guts_c_version =
  */
 
 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"
 
 #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);
                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;
                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);
                        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:
                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_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:
                        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:
                        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
  *   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)
 {
  */
  
 static void yaffs_UpdateParent(yaffs_Object *obj)
 {
+       yaffs_Device *dev;
        if(!obj)
                return;
 
        if(!obj)
                return;
 
+       dev = obj->myDev;
        obj->dirty = 1;
        obj->yst_mtime = obj->yst_ctime = Y_CURRENT_TIME;
        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)
 }
 
 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 */
        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. */
        dev->oldestDirtySequence = 0;
 
        /* Initialise temporary buffers and caches. */
index 3bae979134dfcd3fc7bb66f12d4bb6c78f65c39f..7643dfc6d2805d9201ed29059275738ef0022ac5 100644 (file)
@@ -386,6 +386,7 @@ typedef struct {
 
 typedef struct {
        struct ylist_head children;     /* list of child links */
 
 typedef struct {
        struct ylist_head children;     /* list of child links */
+       struct ylist_head dirty;        /* Entry for list of dirty directories */
 } yaffs_DirectoryStructure;
 
 typedef struct {
 } 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 */
        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;
 };
 
 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. */
 
        /* 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;
        /* Statistcs */
        int nPageWrites;
        int nPageReads;
@@ -882,6 +888,10 @@ void yfsd_WinFileTimeNow(__u32 target[2]);
 
 void yaffs_HandleDeferedFree(yaffs_Object *obj);
 
 
 void yaffs_HandleDeferedFree(yaffs_Object *obj);
 
+
+void yaffs_UpdateDirtyDirectories(yaffs_Device *dev);
+
+
 /* Debug dump  */
 int yaffs_DumpObject(yaffs_Object *obj);
 
 /* 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);
 
 __u8 *yaffs_GetTempBuffer(yaffs_Device *dev, int lineNo);
 void yaffs_ReleaseTempBuffer(yaffs_Device *dev, __u8 *buffer, int lineNo);
 
+
 #endif
 #endif
index 272186aeb0d7f26f8283116aab2b850bb2115eea..54556e07dbbc23ff1e050fbad954bbabbb6b1f61 100644 (file)
@@ -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 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.
         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.
index 5ddb9775ca64c907e2c06dee1c02f9eb0b0eef9e..68a2ffc9dbc8efcba86902113ac18903d7b687ec 100644 (file)
@@ -48,6 +48,8 @@ extern unsigned int yaffs_wr_attempts;
 
 #define YAFFS_TRACE_SYNC               0x00100000
 
 
 #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
 #define YAFFS_TRACE_ERROR              0x40000000
 #define YAFFS_TRACE_BUG                        0x80000000
 #define YAFFS_TRACE_ALWAYS             0xF0000000