JFFS2 appears to "freeze" during erase
Radoslaw Bisewski
radbis at googlemail.com
Mon Oct 2 09:40:41 EDT 2006
I'm sending this message once again - this time in plain text format.
Could someone review these changes before putting them into the MTD?
With current desing erase_free_sem is locked every time the flash
block is being erased. For NOR flashes - ~1 second is needed to erase
single flash block. In the worst case scenario erase_free_sem may be
locked for a couple of seconds when the number of blocks is being
erased (e.g. after large file was removed). When erase_free_sem is
locked all read/write operations for given JFFS2 partition are locked
too - in effect from time to time access to the JFFS2 partition is
locked for a number of seconds. This fix makes critical section in
flash erasing procedure shorter - now erase_free_sem is locked around
erase_completion_lock spinlock only.
Regards
R.
On 9/29/06, Joakim Tjernlund <joakim.tjernlund at transmode.se> wrote:
> >
> >
> >
> > On 9/21/06, Joakim Tjernlund <Joakim.Tjernlund at transmode.se > wrote:
> >
> > Radoslaw Bisewski wrote:
> > > This bug is caused by the use of erase_free_sem
> > semaphore. Block erasing
> > > function jffs2_erase_block is called with
> > erase_free_sem locked, i.e.
> > > erase_free_sem will be locked for the time needed to
> > erase flash block (
> > > ~1 s for typical NOR chip), in the worst scenario
> > access to file system
> > > may be blocked for a numer of seconds when the number
> > of blocks is being
> > > erased. Workaround for this bug is simple -
> > erase_free_sem cannot be
> > > locked when jffs2_erase_block function is called. See
> > attached patch.
> > >
> > Me again
> >
> > I think there is a bug in jffs2_erase_pending_blocks()
> > after applying your patch:
> > spin_unlock(&c->erase_completion_lock);
> > done:
> > D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks
> > completed\n"));
> >
> > up(&c->erase_free_sem);
> >
> > the up(&c->erase_free_sem); needs to move to before done:
> >
> > Jocke
> >
> >
> >
> > Joakim,
> >
> > yes, you are right. attached is the corrected version.
> >
> > Regards
> > R.
> >
>
> Why isn't this patch in MTD yet?
> David, can you please comment?
>
> Jcoke
>
-------------- next part --------------
--- erase.c 2006-09-21 10:21:48.000000000 +0200
+++ erase-fixed.c 2006-09-21 14:24:23.000000000 +0200
@@ -53,6 +53,7 @@
instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
if (!instr) {
printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
+ down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
list_del(&jeb->list);
list_add(&jeb->list, &c->erase_pending_list);
@@ -60,6 +61,7 @@
c->dirty_size += c->sector_size;
jeb->dirty_size = c->sector_size;
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
return;
}
@@ -86,13 +88,15 @@
if (ret == -ENOMEM || ret == -EAGAIN) {
/* Erase failed immediately. Refile it on the list */
D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret));
- spin_lock(&c->erase_completion_lock);
+ down(&c->erase_free_sem);
+ spin_lock(&c->erase_completion_lock);
list_del(&jeb->list);
list_add(&jeb->list, &c->erase_pending_list);
c->erasing_size -= c->sector_size;
c->dirty_size += c->sector_size;
jeb->dirty_size = c->sector_size;
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
return;
}
@@ -119,7 +123,8 @@
jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
list_del(&jeb->list);
spin_unlock(&c->erase_completion_lock);
- jffs2_mark_erased_block(c, jeb);
+ up(&c->erase_free_sem);
+ jffs2_mark_erased_block(c, jeb);
if (!--count) {
D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n"));
@@ -139,6 +144,7 @@
jffs2_free_all_node_refs(c, jeb);
list_add(&jeb->list, &c->erasing_list);
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
jffs2_erase_block(c, jeb);
@@ -148,14 +154,15 @@
/* Be nice */
cond_resched();
+
+ down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
}
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
done:
D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
-
- up(&c->erase_free_sem);
}
static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
@@ -425,6 +432,8 @@
jeb->wasted_size = 0;
}
+ down(&c->erase_free_sem);
+
spin_lock(&c->erase_completion_lock);
c->erasing_size -= c->sector_size;
c->free_size += jeb->free_size;
@@ -438,22 +447,28 @@
c->nr_free_blocks++;
spin_unlock(&c->erase_completion_lock);
wake_up(&c->erase_wait);
+ up(&c->erase_free_sem);
+
return;
filebad:
+ down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
/* Stick it on a list (any list) so erase_failed can take it
right off again. Silly, but shouldn't happen often. */
list_add(&jeb->list, &c->erasing_list);
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
jffs2_erase_failed(c, jeb, bad_offset);
return;
refile:
/* Stick it back on the list from whence it came and come back later */
jffs2_erase_pending_trigger(c);
+ down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
list_add(&jeb->list, &c->erase_complete_list);
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
return;
}
More information about the linux-mtd
mailing list