yaffs: Clean up 64-bit compilation
[yaffs2.git] / yaffs_fs.c
index 0ee40e7c36bfbd69dbda58e8c8281c22b746fc92..caade8ae1f06931d9290a9286536e30e4abde38a 100644 (file)
@@ -69,6 +69,7 @@ extern const char *yaffs_guts_c_version;
 #ifdef YAFFS_COMPILE_BACKGROUND
 #include <linux/kthread.h>
 #include <linux/delay.h>
+#include <linux/freezer.h>
 #endif
 
 
@@ -1063,9 +1064,8 @@ static int yaffs_write_end(struct file *filp, struct address_space *mapping,
        addr = kva + offset_into_page;
 
        T(YAFFS_TRACE_OS,
-               ("yaffs_write_end addr %x pos %x nBytes %d\n",
-               (unsigned) addr,
-               (int)pos, copied));
+               ("yaffs_write_end addr %p pos %x nBytes %d\n",
+               addr,(unsigned)pos, copied));
 
        ret = yaffs_file_write(filp, addr, copied, &pos);
 
@@ -1938,28 +1938,57 @@ static void yaffs_FlushSuperBlock(struct super_block *sb, int do_checkpoint)
                yaffs_CheckpointSave(dev);
 }
 
-static int yaffs_do_sync_fs(struct super_block *sb, int do_checkpoint)
+
+static unsigned yaffs_bg_gc_urgency(yaffs_Device *dev)
+{
+       unsigned erasedChunks = dev->nErasedBlocks * dev->param.nChunksPerBlock;
+       struct yaffs_LinuxContext *context = yaffs_DeviceToContext(dev);
+       unsigned scatteredFree = 0; /* Free chunks not in an erased block */
+
+       if(erasedChunks < dev->nFreeChunks)
+               scatteredFree = (dev->nFreeChunks - erasedChunks);
+
+       if(!context->bgRunning)
+               return 0;
+       else if(scatteredFree < (dev->param.nChunksPerBlock * 2))
+               return 0;
+       else if(erasedChunks > dev->nFreeChunks/2)
+               return 0;
+       else if(erasedChunks > dev->nFreeChunks/4)
+               return 1;
+       else
+               return 2;
+}
+
+static int yaffs_do_sync_fs(struct super_block *sb,
+                               int request_checkpoint)
 {
 
        yaffs_Device *dev = yaffs_SuperToDevice(sb);
        unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4);
+       unsigned gc_urgent = yaffs_bg_gc_urgency(dev);
+       int do_checkpoint;
 
-       T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, 
-               ("yaffs_do_sync_fs: %s %s%s\n",
+       T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND,
+               ("yaffs_do_sync_fs: gc-urgency %d %s %s%s\n",
+               gc_urgent,
                sb->s_dirt ? "dirty" : "clean",
-               do_checkpoint ? "with checkpoint" : "no checkpoint",
+               request_checkpoint ? "checkpoint requested" : "no checkpoint",
                oneshot_checkpoint ? " one-shot" : "" ));
 
-       if (sb->s_dirt || oneshot_checkpoint) {
-               yaffs_GrossLock(dev);
-               yaffs_FlushSuperBlock(sb,do_checkpoint);
-               yaffs_GrossUnlock(dev);
+       yaffs_GrossLock(dev);
+       do_checkpoint = ((request_checkpoint && !gc_urgent) ||
+                       oneshot_checkpoint) &&
+                       !dev->isCheckpointed;
 
+       if (sb->s_dirt || do_checkpoint) {
+               yaffs_FlushSuperBlock(sb, !dev->isCheckpointed && do_checkpoint);
                sb->s_dirt = 0;
-
                if(oneshot_checkpoint)
                        yaffs_auto_checkpoint &= ~4;
        }
+       yaffs_GrossUnlock(dev);
+
        return 0;
 }
 
@@ -1976,38 +2005,100 @@ static int yaffs_do_sync_fs(struct super_block *sb, int do_checkpoint)
  */
 
 #ifdef YAFFS_COMPILE_BACKGROUND
+
+void yaffs_background_waker(unsigned long data)
+{
+       wake_up_process((struct task_struct *)data);
+}
+
 static int yaffs_BackgroundThread(void *data)
 {
        yaffs_Device *dev = (yaffs_Device *)data;
        struct yaffs_LinuxContext *context = yaffs_DeviceToContext(dev);
+       unsigned long now = jiffies;
+       unsigned long next_dir_update = now;
+       unsigned long next_gc = now;
+       unsigned long expires;
+       unsigned int urgency;
+
+       int gcResult;
+       struct timer_list timer;
 
        T(YAFFS_TRACE_BACKGROUND,
                ("yaffs_background starting for dev %p\n",
                (void *)dev));
-       
+
+       set_freezable();
+
        while(context->bgRunning){
                T(YAFFS_TRACE_BACKGROUND,
                        ("yaffs_background\n"));
 
                if(kthread_should_stop())
                        break;
+
+               if(try_to_freeze())
+                       continue;
+
                yaffs_GrossLock(dev);
-               yaffs_UpdateDirtyDirectories(dev);
+
+               now = jiffies;
+
+               if(time_after(now, next_dir_update)){
+                       yaffs_UpdateDirtyDirectories(dev);
+                       next_dir_update = now + HZ;
+               }
+
+               if(time_after(now,next_gc)){
+                       if(!dev->isCheckpointed){
+                               urgency = yaffs_bg_gc_urgency(dev);
+                               gcResult = yaffs_BackgroundGarbageCollect(dev, urgency);
+                               if(urgency > 1)
+                                       next_gc = now + HZ/20+1;
+                               else if(urgency > 0)
+                                       next_gc = now + HZ/10+1;
+                               else
+                                       next_gc = now + HZ * 2;
+                       } else /*
+                               * gc not running so set to next_dir_update
+                               * to cut down on wake ups
+                               */
+                               next_gc = next_dir_update;
+               }
                yaffs_GrossUnlock(dev);
-               msleep(500);
+#if 1
+               expires = next_dir_update;
+               if (time_before(next_gc,expires))
+                       expires = next_gc;
+               if(time_before(expires,now))
+                       expires = now + HZ;
+
+               init_timer(&timer);
+               timer.expires = expires+1;
+               timer.data = (unsigned long) current;
+               timer.function = yaffs_background_waker;
+
+                set_current_state(TASK_INTERRUPTIBLE);
+               add_timer(&timer);
+               schedule();
+               del_timer_sync(&timer);
+#else
+               msleep(10);
+#endif
        }
+
        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);
+       context->bgThread = kthread_run(yaffs_BackgroundThread,
+                               (void *)dev,"yaffs-bg");
 
        if(IS_ERR(context->bgThread)){
                retval = PTR_ERR(context->bgThread);
@@ -2051,9 +2142,13 @@ static void yaffs_write_super(struct super_block *sb)
 static int yaffs_write_super(struct super_block *sb)
 #endif
 {
+       unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2);
 
-       T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ("yaffs_write_super\n"));
-       yaffs_do_sync_fs(sb, yaffs_auto_checkpoint >= 2);
+       T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND,
+               ("yaffs_write_super%s\n",
+               request_checkpoint ? " checkpt" : ""));
+
+       yaffs_do_sync_fs(sb, request_checkpoint);
 
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
        return 0;
@@ -2067,9 +2162,13 @@ static int yaffs_sync_fs(struct super_block *sb, int wait)
 static int yaffs_sync_fs(struct super_block *sb)
 #endif
 {
-       T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ("yaffs_sync_fs\n"));
+       unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1);
+
+       T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC,
+               ("yaffs_sync_fs%s\n",
+               request_checkpoint ? " checkpt" : ""));
 
-       yaffs_do_sync_fs(sb,yaffs_auto_checkpoint >= 1);
+       yaffs_do_sync_fs(sb, request_checkpoint);
 
        return 0;
 }
@@ -2173,6 +2272,10 @@ static void yaffs_put_super(struct super_block *sb)
 
        T(YAFFS_TRACE_OS, ("yaffs_put_super\n"));
 
+       T(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, ("Shutting down yaffs background thread\n"));
+       yaffs_BackgroundStop(dev);
+       T(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, ("yaffs background thread shut down\n"));
+
        yaffs_GrossLock(dev);
 
        yaffs_FlushSuperBlock(sb,1);
@@ -2180,7 +2283,6 @@ static void yaffs_put_super(struct super_block *sb)
        if (yaffs_DeviceToContext(dev)->putSuperFunc)
                yaffs_DeviceToContext(dev)->putSuperFunc(sb);
 
-       yaffs_BackgroundStop(dev);
 
        yaffs_Deinitialise(dev);
 
@@ -2545,7 +2647,7 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion,
 #ifdef CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING
        param->refreshPeriod = 0;
 #else
-       param->refreshPeriod = 10000;
+       param->refreshPeriod = 100;
 #endif
 
        if(options.empty_lost_and_found_overridden)
@@ -2806,8 +2908,10 @@ static char *yaffs_dump_dev_part1(char *buf, yaffs_Device * dev)
        buf += sprintf(buf, "nPageReads......... %u\n", dev->nPageReads);
        buf += sprintf(buf, "nBlockErasures..... %u\n", dev->nBlockErasures);
        buf += sprintf(buf, "nGCCopies.......... %u\n", dev->nGCCopies);
-       buf += sprintf(buf, "garbageCollections. %u\n", dev->garbageCollections);
-       buf += sprintf(buf, "passiveGCs......... %u\n", dev->passiveGarbageCollections);
+       buf += sprintf(buf, "allGCs............. %u\n", dev->allGCs);
+       buf += sprintf(buf, "passiveGCs......... %u\n", dev->passiveGCs);
+       buf += sprintf(buf, "oldestDirtyGCs..... %u\n", dev->oldestDirtyGCs);
+       buf += sprintf(buf, "backgroundGCs...... %u\n", dev->backgroundGCs);
        buf += sprintf(buf, "nRetriedWrites..... %u\n", dev->nRetriedWrites);
        buf += sprintf(buf, "nRetireBlocks...... %u\n", dev->nRetiredBlocks);
        buf += sprintf(buf, "eccFixed........... %u\n", dev->eccFixed);
@@ -2900,8 +3004,9 @@ static int yaffs_debug_proc_read(char *page,
                nTnodes = dev->nTnodesCreated - dev->nFreeTnodes;
                
                
-               buf += sprintf(buf,"%d, %d, %d, %d, %d\n", 
+               buf += sprintf(buf,"%d, %d, %d, %u, %u, %d, %d\n",
                                n, dev->nFreeChunks, erasedChunks,
+                               dev->backgroundGCs, dev->oldestDirtyGCs,
                                nObjects, nTnodes);
        }
        up(&yaffs_context_lock);
@@ -3133,6 +3238,7 @@ static void __exit exit_yaffs_fs(void)
                               " removing. \n"));
 
        remove_proc_entry("yaffs", YPROC_ROOT);
+       remove_proc_entry("yaffs_debug", YPROC_ROOT);
 
        fsinst = fs_to_install;