From 91f8026603d4443d1b24ee3552c5a58682bbae27 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 2 Feb 2010 14:43:10 -0800 Subject: JFFS2: avoid using C++ keyword `new' in userspace-visible header Addresses http://bugzilla.kernel.org/show_bug.cgi?id=14995 Reported-by: R. Diez Signed-off-by: Andrew Morton Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- fs/jffs2/fs.c | 10 +++++----- fs/jffs2/nodelist.h | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 3451a81b2142..86e0821fc989 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -313,8 +313,8 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) case S_IFBLK: case S_IFCHR: /* Read the device numbers from the media */ - if (f->metadata->size != sizeof(jdev.old) && - f->metadata->size != sizeof(jdev.new)) { + if (f->metadata->size != sizeof(jdev.old_id) && + f->metadata->size != sizeof(jdev.new_id)) { printk(KERN_NOTICE "Device node has strange size %d\n", f->metadata->size); goto error_io; } @@ -325,10 +325,10 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino); goto error; } - if (f->metadata->size == sizeof(jdev.old)) - rdev = old_decode_dev(je16_to_cpu(jdev.old)); + if (f->metadata->size == sizeof(jdev.old_id)) + rdev = old_decode_dev(je16_to_cpu(jdev.old_id)); else - rdev = new_decode_dev(je32_to_cpu(jdev.new)); + rdev = new_decode_dev(je32_to_cpu(jdev.new_id)); case S_IFSOCK: case S_IFIFO: diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index 507ed6ec1847..36d7a849ee2c 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -312,11 +312,11 @@ static inline int jffs2_blocks_use_vmalloc(struct jffs2_sb_info *c) static inline int jffs2_encode_dev(union jffs2_device_node *jdev, dev_t rdev) { if (old_valid_dev(rdev)) { - jdev->old = cpu_to_je16(old_encode_dev(rdev)); - return sizeof(jdev->old); + jdev->old_id = cpu_to_je16(old_encode_dev(rdev)); + return sizeof(jdev->old_id); } else { - jdev->new = cpu_to_je32(new_encode_dev(rdev)); - return sizeof(jdev->new); + jdev->new_id = cpu_to_je32(new_encode_dev(rdev)); + return sizeof(jdev->new_id); } } -- cgit v1.2.3 From 9957abea31aed5783d6ca7175cce553045c0eb19 Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Wed, 19 May 2010 16:32:52 +0100 Subject: jffs2: Add 'work_done' return value from jffs2_erase_pending_blocks() We're about to start calling this from the jffs2_garbage_collect_pass(), and we'll want to know whether it actually did anything or not. Signed-off-by: Joakim Tjernlund Signed-off-by: David Woodhouse --- fs/jffs2/erase.c | 5 ++++- fs/jffs2/nodelist.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index b47679be118a..b2d2b6a6e03e 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -103,9 +103,10 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, jffs2_erase_failed(c, jeb, bad_offset); } -void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) +int jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) { struct jffs2_eraseblock *jeb; + int work_done = 0; mutex_lock(&c->erase_free_sem); @@ -121,6 +122,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) mutex_unlock(&c->erase_free_sem); jffs2_mark_erased_block(c, jeb); + work_done++; if (!--count) { D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n")); goto done; @@ -157,6 +159,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) mutex_unlock(&c->erase_free_sem); done: D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); + return work_done; } static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index 36d7a849ee2c..a881a42f19e3 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -464,7 +464,7 @@ int jffs2_scan_dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb int jffs2_do_mount_fs(struct jffs2_sb_info *c); /* erase.c */ -void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count); +int jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count); void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); #ifdef CONFIG_JFFS2_FS_WRITEBUFFER -- cgit v1.2.3 From 0717bf8411bb673dd2369aaa096f7396446b38f5 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 19 May 2010 16:37:13 +0100 Subject: jffs2: Erase pending blocks in GC pass, avoid invalid -EIO return jffs2_garbage_collect_pass() would previously return -EAGAIN if it couldn't find anything to garbage collect from, and there were blocks on the erase_pending_list. If the blocks were actually in the process of being erased, though, then they wouldn't be on that list. Check for nr_erasing_blocks being non-zero instead. Fix jffs2_reserve_space() to wait for the in-progress erases to complete, when jffs2_garbage_collect_pass() returns -EAGAIN. And fix jffs2_erase_succeeded() to actually wake up the erase_wait wq that jffs2_reserve_space() is now using. Signed-off-by: David Woodhouse --- fs/jffs2/erase.c | 1 + fs/jffs2/gc.c | 15 ++++++++++++++- fs/jffs2/nodemgmt.c | 18 +++++++++++++++--- 3 files changed, 30 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index b2d2b6a6e03e..563c857ca544 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -172,6 +172,7 @@ static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblo mutex_unlock(&c->erase_free_sem); /* Ensure that kupdated calls us again to mark them clean */ jffs2_erase_pending_trigger(c); + wake_up(&c->erase_wait); } static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset) diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 3b6f2fa12cff..1ea4a843a430 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -214,6 +214,19 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) return ret; } + /* If there are any blocks which need erasing, erase them now */ + if (!list_empty(&c->erase_complete_list) || + !list_empty(&c->erase_pending_list)) { + spin_unlock(&c->erase_completion_lock); + D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() erasing pending blocks\n")); + if (jffs2_erase_pending_blocks(c, 1)) { + mutex_unlock(&c->alloc_sem); + return 0; + } + D1(printk(KERN_DEBUG "No progress from erasing blocks; doing GC anyway\n")); + spin_lock(&c->erase_completion_lock); + } + /* First, work out which block we're garbage-collecting */ jeb = c->gcblock; @@ -222,7 +235,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) if (!jeb) { /* Couldn't find a free block. But maybe we can just erase one and make 'progress'? */ - if (!list_empty(&c->erase_pending_list)) { + if (c->nr_erasing_blocks) { spin_unlock(&c->erase_completion_lock); mutex_unlock(&c->alloc_sem); return -EAGAIN; diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 191359dde4e1..08e2c69fc147 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -116,9 +116,21 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ret = jffs2_garbage_collect_pass(c); - if (ret == -EAGAIN) - jffs2_erase_pending_blocks(c, 1); - else if (ret) + if (ret == -EAGAIN) { + spin_lock(&c->erase_completion_lock); + if (c->nr_erasing_blocks && + list_empty(&c->erase_pending_list) && + list_empty(&c->erase_complete_list)) { + DECLARE_WAITQUEUE(wait, current); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&c->erase_wait, &wait); + D1(printk(KERN_DEBUG "%s waiting for erase to complete\n", __func__)); + spin_unlock(&c->erase_completion_lock); + + schedule(); + } else + spin_unlock(&c->erase_completion_lock); + } else if (ret) return ret; cond_resched(); -- cgit v1.2.3 From d6ce171069635f05737935adc813b4d48d71a583 Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Wed, 19 May 2010 16:55:40 +0100 Subject: jffs2: Wake GC thread when there are blocks to be erased Now that we trigger block erases from jffs2_garbage_collect_pass(), adjust jffs2_thread_should_wake() to return 1 when there are blocks to erase. Signed-off-by: Joakim Tjernlund Signed-off-by: David Woodhouse --- fs/jffs2/nodemgmt.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'fs') diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 08e2c69fc147..5ab5c8521cdf 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -744,6 +744,10 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c) int nr_very_dirty = 0; struct jffs2_eraseblock *jeb; + if (!list_empty(&c->erase_complete_list) || + !list_empty(&c->erase_pending_list)) + return 1; + if (c->unchecked_size) { D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n", c->unchecked_size, c->checked_ino)); -- cgit v1.2.3 From acb64a43e4503fbea9faf123f2403da7af8831eb Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 19 May 2010 17:00:10 +0100 Subject: jffs2: Require jffs2_garbage_collect_trigger() to be called with lock held We're about to call this from a bunch of places which already hold c->erase_completion_lock, so add an assertion and change its existing callers to do the same. Signed-off-by: David Woodhouse --- fs/jffs2/background.c | 3 +-- fs/jffs2/nodemgmt.c | 2 ++ fs/jffs2/super.c | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index 3ff50da94789..55f1dde2fa8b 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c @@ -23,10 +23,9 @@ static int jffs2_garbage_collect_thread(void *); void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) { - spin_lock(&c->erase_completion_lock); + assert_spin_locked(&c->erase_completion_lock); if (c->gc_task && jffs2_thread_should_wake(c)) send_sig(SIGHUP, c->gc_task, 1); - spin_unlock(&c->erase_completion_lock); } /* This must only ever be called when no GC thread is currently running */ diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 5ab5c8521cdf..dd2d920d3325 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -481,7 +481,9 @@ struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c, void jffs2_complete_reservation(struct jffs2_sb_info *c) { D1(printk(KERN_DEBUG "jffs2_complete_reservation()\n")); + spin_lock(&c->erase_completion_lock); jffs2_garbage_collect_trigger(c); + spin_unlock(&c->erase_completion_lock); mutex_unlock(&c->alloc_sem); } diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 9a80e8e595d0..12cc967c5c03 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -63,7 +63,9 @@ static void jffs2_write_super(struct super_block *sb) if (!(sb->s_flags & MS_RDONLY)) { D1(printk(KERN_DEBUG "jffs2_write_super()\n")); + spin_lock(&c->erase_completion_lock); jffs2_garbage_collect_trigger(c); + spin_unlock(&c->erase_completion_lock); jffs2_erase_pending_blocks(c, 0); jffs2_flush_wbuf_gc(c, 0); } -- cgit v1.2.3 From ae3b6ba06c8ed399ef920724ee8136e540878294 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 19 May 2010 17:05:14 +0100 Subject: jffs2: Use jffs2_garbage_collect_trigger() to trigger pending erases This is now done in a GC pass; we don't need to trigger kupdated to do it. Signed-off-by: David Woodhouse --- fs/jffs2/erase.c | 6 +++--- fs/jffs2/gc.c | 2 +- fs/jffs2/nodemgmt.c | 4 ++-- fs/jffs2/scan.c | 4 +++- fs/jffs2/wbuf.c | 6 +++--- 5 files changed, 12 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index 563c857ca544..6286ad9b00f7 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -168,10 +168,10 @@ static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblo mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move_tail(&jeb->list, &c->erase_complete_list); + /* Wake the GC thread to mark them clean */ + jffs2_garbage_collect_trigger(c); spin_unlock(&c->erase_completion_lock); mutex_unlock(&c->erase_free_sem); - /* Ensure that kupdated calls us again to mark them clean */ - jffs2_erase_pending_trigger(c); wake_up(&c->erase_wait); } @@ -491,9 +491,9 @@ filebad: refile: /* Stick it back on the list from whence it came and come back later */ - jffs2_erase_pending_trigger(c); mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); + jffs2_garbage_collect_trigger(c); list_move(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); mutex_unlock(&c->erase_free_sem); diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 1ea4a843a430..f5e96bd656e8 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -448,7 +448,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) list_add_tail(&c->gcblock->list, &c->erase_pending_list); c->gcblock = NULL; c->nr_erasing_blocks++; - jffs2_erase_pending_trigger(c); + jffs2_garbage_collect_trigger(c); } spin_unlock(&c->erase_completion_lock); diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index dd2d920d3325..694aa5b03505 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -229,7 +229,7 @@ static int jffs2_find_nextblock(struct jffs2_sb_info *c) ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list); list_move_tail(&ejeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; - jffs2_erase_pending_trigger(c); + jffs2_garbage_collect_trigger(c); D1(printk(KERN_DEBUG "jffs2_find_nextblock: Triggering erase of erasable block at 0x%08x\n", ejeb->offset)); } @@ -625,7 +625,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); list_add_tail(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; - jffs2_erase_pending_trigger(c); + jffs2_garbage_collect_trigger(c); } else { /* Sometimes, however, we leave it elsewhere so it doesn't get immediately reused, and we spread the load a bit. */ diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 696686cc206e..46f870d1cc36 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -260,7 +260,9 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) ret = -EIO; goto out; } - jffs2_erase_pending_trigger(c); + spin_lock(&c->erase_completion_lock); + jffs2_garbage_collect_trigger(c); + spin_unlock(&c->erase_completion_lock); } ret = 0; out: diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 5ef7bac265e5..be114beca13e 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -121,7 +121,7 @@ static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c) D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); list_add_tail(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; - jffs2_erase_pending_trigger(c); + jffs2_garbage_collect_trigger(c); } else { /* Sometimes, however, we leave it elsewhere so it doesn't get immediately reused, and we spread the load a bit. */ @@ -152,7 +152,7 @@ static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset)); list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; - jffs2_erase_pending_trigger(c); + jffs2_garbage_collect_trigger(c); } if (!jffs2_prealloc_raw_node_refs(c, jeb, 1)) { @@ -543,7 +543,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset)); list_move(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; - jffs2_erase_pending_trigger(c); + jffs2_garbage_collect_trigger(c); } jffs2_dbg_acct_sanity_check_nolock(c, jeb); -- cgit v1.2.3 From 64a5c2eb82d2a626116fde8a0ea3bb39a3f4f233 Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Wed, 19 May 2010 17:13:19 +0100 Subject: jffs2: Rename jffs2_erase_pending_trigger() to jffs2_dirty_trigger() Now that we do erases from GC and trigger the GC thread to do them instead of using kupdated, this function is misnamed. It's only used for triggering wbuf flush on NAND flash now. Rename it accordingly. Signed-off-by: Joakim Tjernlund Signed-off-by: David Woodhouse --- fs/jffs2/os-linux.h | 3 +-- fs/jffs2/wbuf.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index a7f03b7ebcb3..035a767f958b 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -140,8 +140,7 @@ void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c); #endif /* WRITEBUFFER */ -/* erase.c */ -static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) +static inline void jffs2_dirty_trigger(struct jffs2_sb_info *c) { OFNI_BS_2SFFJ(c)->s_dirt = 1; } diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index be114beca13e..07ee1546b2fa 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -84,7 +84,7 @@ static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino) struct jffs2_inodirty *new; /* Mark the superblock dirty so that kupdated will flush... */ - jffs2_erase_pending_trigger(c); + jffs2_dirty_trigger(c); if (jffs2_wbuf_pending_for_ino(c, ino)) return; -- cgit v1.2.3 From 9723152ad1efb844445e63f052e6d39e85e11649 Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Wed, 19 May 2010 17:16:11 +0100 Subject: jffs2: Stop triggering block erases from jffs2_write_super() This is the culmination of this sequence of patches. By moving the block erasing from jffs2_write_super() into the GC code, we avoid huge latencies on unmount where it waits for _all_ pending blocks to be erased, and we allow better control for time-critical tasks by stopping the GC thread. Signed-off-by: Joakim Tjernlund Signed-off-by: David Woodhouse --- fs/jffs2/super.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 12cc967c5c03..511e2d609d12 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -63,10 +63,6 @@ static void jffs2_write_super(struct super_block *sb) if (!(sb->s_flags & MS_RDONLY)) { D1(printk(KERN_DEBUG "jffs2_write_super()\n")); - spin_lock(&c->erase_completion_lock); - jffs2_garbage_collect_trigger(c); - spin_unlock(&c->erase_completion_lock); - jffs2_erase_pending_blocks(c, 0); jffs2_flush_wbuf_gc(c, 0); } -- cgit v1.2.3