[JFFS2] Trigger garbage collection when very_dirty_list size becomes excessive
authorDavid Woodhouse <dwmw2@infradead.org>
Sat, 6 Oct 2007 19:12:58 +0000 (15:12 -0400)
committerDavid Woodhouse <dwmw2@infradead.org>
Sat, 6 Oct 2007 19:12:58 +0000 (15:12 -0400)
With huge amounts of free space, we weren't bothering to GC for while a
while, and pathological numbers of obsolete nodes were accumulating,
seriously affecting performance on NAND flash (OLPC trac #3978)

Signed-off-by: David Woodhouse <dwmw2@infradead.org>
fs/jffs2/build.c
fs/jffs2/jffs2_fs_sb.h
fs/jffs2/nodemgmt.c

index 0ca2fff2617fa4e679860d9358a0edb1414b49d4..8c27c12816ba4ea131d4d7deb78b828a59fd9b4d 100644 (file)
@@ -285,6 +285,14 @@ static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
           than actually making progress? */
        c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2;
 
+       /* What number of 'very dirty' eraseblocks do we allow before we
+          trigger the GC thread even if we don't _need_ the space. When we
+          can't mark nodes obsolete on the medium, the old dirty nodes cause
+          performance problems because we have to inspect and discard them. */
+       c->vdirty_blocks_gctrigger = c->resv_blocks_gcmerge;
+       if (jffs2_can_mark_obsolete(c))
+               c->vdirty_blocks_gctrigger *= 10;
+
        /* If there's less than this amount of dirty space, don't bother
           trying to GC to make more space. It'll be a fruitless task */
        c->nospc_dirty_size = c->sector_size + (c->flash_size / 100);
@@ -303,6 +311,8 @@ static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
                  c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024);
        dbg_fsbuild("Amount of dirty space required to GC: %d bytes\n",
                  c->nospc_dirty_size);
+       dbg_fsbuild("Very dirty blocks before GC triggered: %d\n",
+                 c->vdirty_blocks_gctrigger);
 }
 
 int jffs2_do_mount_fs(struct jffs2_sb_info *c)
index ae99cd7fd43b2843645e26c1b78ea393571c12aa..3a2197f3c812727a06fafdbb552fa9218e2058f7 100644 (file)
@@ -69,6 +69,8 @@ struct jffs2_sb_info {
        uint8_t resv_blocks_gctrigger;  /* ... wake up the GC thread */
        uint8_t resv_blocks_gcbad;      /* ... pick a block from the bad_list to GC */
        uint8_t resv_blocks_gcmerge;    /* ... merge pages when garbage collecting */
+       /* Number of 'very dirty' blocks before we trigger immediate GC */
+       uint8_t vdirty_blocks_gctrigger;
 
        uint32_t nospc_dirty_size;
 
index 5b49bff364b4e239d5682b4b937948479d20f9d0..1b79534e9d4850991cbf2a035f60fd7225c743e7 100644 (file)
@@ -722,6 +722,8 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c)
 {
        int ret = 0;
        uint32_t dirty;
+       int nr_very_dirty = 0;
+       struct jffs2_eraseblock *jeb;
 
        if (c->unchecked_size) {
                D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
@@ -743,8 +745,16 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c)
                        (dirty > c->nospc_dirty_size))
                ret = 1;
 
-       D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n",
-                 c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no"));
+       list_for_each_entry(jeb, &c->very_dirty_list, list) {
+               nr_very_dirty++;
+               if (nr_very_dirty == c->vdirty_blocks_gctrigger) {
+                       ret = 1;
+                       D1(break);
+               }
+       }
+
+       D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x, vdirty_blocks %d: %s\n",
+                 c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, nr_very_dirty, ret?"yes":"no"));
 
        return ret;
 }