From 8fb870df5a1f261294b833dd807bcba3bacface6 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 6 Oct 2007 15:12:58 -0400 Subject: [PATCH] [JFFS2] Trigger garbage collection when very_dirty_list size becomes excessive 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 --- fs/jffs2/build.c | 10 ++++++++++ fs/jffs2/jffs2_fs_sb.h | 2 ++ fs/jffs2/nodemgmt.c | 14 ++++++++++++-- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 0ca2fff2617f..8c27c12816ba 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -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) diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index ae99cd7fd43b..3a2197f3c812 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -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; diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 5b49bff364b4..1b79534e9d48 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -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; }