summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--yaffs_fs.c128
-rw-r--r--yaffs_guts.c56
-rw-r--r--yaffs_guts.h15
-rw-r--r--yaffs_linux.h2
-rw-r--r--yaffs_trace.h2
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 <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_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