changeset 372:952d0cdd1cbc

Add background processing of updated directories
author charles <charles>
date Tue, 09 Mar 2010 04:12:00 +0000
parents 5cc599caef44
children 59862f4bbd87
files yaffs_fs.c yaffs_guts.c yaffs_guts.h yaffs_linux.h yaffs_trace.h
diffstat 5 files changed, 178 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- 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 <linux/version.h>
@@ -52,6 +52,8 @@ extern const char *yaffs_guts_c_version;
 #include <linux/string.h>
 #include <linux/ctype.h>
 
+#include <linux/kthread.h>
+
 #include "asm/div64.h"
 
 
@@ -1779,7 +1781,7 @@ static int yaffs_statfs(struct super_blo
 
 
 
-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
 	}
 }
 
-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
 	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 supe
 {
 
 	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_bl
 {
 	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
 
 		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
 
 	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_interna
 #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_interna
 	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},
--- 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(yaff
 		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(y
 			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_Obje
 			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_
  *   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;
-
-	yaffs_UpdateObjectHeader(obj,NULL,0,0,0);
+	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));
+		}
+
+	} 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 *d
 	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. */
--- 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
 __u8 *yaffs_GetTempBuffer(yaffs_Device *dev, int lineNo);
 void yaffs_ReleaseTempBuffer(yaffs_Device *dev, __u8 *buffer, int lineNo);
 
+
 #endif
--- 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.
--- 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