diff options
author | David Woodhouse <dwmw2@infradead.org> | 2007-10-06 15:12:58 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2007-10-06 15:12:58 -0400 |
commit | 8fb870df5a1f261294b833dd807bcba3bacface6 (patch) | |
tree | 2e6018f0256feca906797b248603202962302fa6 /fs/jffs2 | |
parent | 49defc015ff58fda46a3afa3462dfdfa69bc8401 (diff) |
[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 <dwmw2@infradead.org>
Diffstat (limited to 'fs/jffs2')
-rw-r--r-- | fs/jffs2/build.c | 10 | ||||
-rw-r--r-- | fs/jffs2/jffs2_fs_sb.h | 2 | ||||
-rw-r--r-- | 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) | |||
285 | than actually making progress? */ | 285 | than actually making progress? */ |
286 | c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2; | 286 | c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2; |
287 | 287 | ||
288 | /* What number of 'very dirty' eraseblocks do we allow before we | ||
289 | trigger the GC thread even if we don't _need_ the space. When we | ||
290 | can't mark nodes obsolete on the medium, the old dirty nodes cause | ||
291 | performance problems because we have to inspect and discard them. */ | ||
292 | c->vdirty_blocks_gctrigger = c->resv_blocks_gcmerge; | ||
293 | if (jffs2_can_mark_obsolete(c)) | ||
294 | c->vdirty_blocks_gctrigger *= 10; | ||
295 | |||
288 | /* If there's less than this amount of dirty space, don't bother | 296 | /* If there's less than this amount of dirty space, don't bother |
289 | trying to GC to make more space. It'll be a fruitless task */ | 297 | trying to GC to make more space. It'll be a fruitless task */ |
290 | c->nospc_dirty_size = c->sector_size + (c->flash_size / 100); | 298 | 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) | |||
303 | c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024); | 311 | c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024); |
304 | dbg_fsbuild("Amount of dirty space required to GC: %d bytes\n", | 312 | dbg_fsbuild("Amount of dirty space required to GC: %d bytes\n", |
305 | c->nospc_dirty_size); | 313 | c->nospc_dirty_size); |
314 | dbg_fsbuild("Very dirty blocks before GC triggered: %d\n", | ||
315 | c->vdirty_blocks_gctrigger); | ||
306 | } | 316 | } |
307 | 317 | ||
308 | int jffs2_do_mount_fs(struct jffs2_sb_info *c) | 318 | 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 { | |||
69 | uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */ | 69 | uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */ |
70 | uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */ | 70 | uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */ |
71 | uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */ | 71 | uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */ |
72 | /* Number of 'very dirty' blocks before we trigger immediate GC */ | ||
73 | uint8_t vdirty_blocks_gctrigger; | ||
72 | 74 | ||
73 | uint32_t nospc_dirty_size; | 75 | uint32_t nospc_dirty_size; |
74 | 76 | ||
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) | |||
722 | { | 722 | { |
723 | int ret = 0; | 723 | int ret = 0; |
724 | uint32_t dirty; | 724 | uint32_t dirty; |
725 | int nr_very_dirty = 0; | ||
726 | struct jffs2_eraseblock *jeb; | ||
725 | 727 | ||
726 | if (c->unchecked_size) { | 728 | if (c->unchecked_size) { |
727 | D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n", | 729 | 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) | |||
743 | (dirty > c->nospc_dirty_size)) | 745 | (dirty > c->nospc_dirty_size)) |
744 | ret = 1; | 746 | ret = 1; |
745 | 747 | ||
746 | D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", | 748 | list_for_each_entry(jeb, &c->very_dirty_list, list) { |
747 | c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no")); | 749 | nr_very_dirty++; |
750 | if (nr_very_dirty == c->vdirty_blocks_gctrigger) { | ||
751 | ret = 1; | ||
752 | D1(break); | ||
753 | } | ||
754 | } | ||
755 | |||
756 | 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", | ||
757 | c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, nr_very_dirty, ret?"yes":"no")); | ||
748 | 758 | ||
749 | return ret; | 759 | return ret; |
750 | } | 760 | } |