diff options
author | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2008-12-05 14:37:02 +0300 |
---|---|---|
committer | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2008-12-05 14:46:50 +0300 |
commit | 6fa6f5bbc3a2ad833a3d4b798140602004f70f5a (patch) | |
tree | 96aa4b8b9f672c098f12db1d359d8e942c8a80aa /drivers/mtd/ubi/wl.c | |
parent | 3c98b0a043f25fa44b289c2f35b9d6ad1d859ac9 (diff) | |
download | linux-6fa6f5bbc3a2ad833a3d4b798140602004f70f5a.tar.xz |
UBI: handle write errors in WL worker
When a PEB is moved and a write error happens, UBI switches
to R/O mode, which is wrong, because we just copy the data
and may select a different PEB and re-try this. This patch
fixes WL worker's behavior.
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Diffstat (limited to 'drivers/mtd/ubi/wl.c')
-rw-r--r-- | drivers/mtd/ubi/wl.c | 32 |
1 files changed, 20 insertions, 12 deletions
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 442099d76ec9..abf65ea414e7 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -738,7 +738,7 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, int cancel) { - int err, scrubbing = 0; + int err, scrubbing = 0, torture = 0; struct ubi_wl_prot_entry *uninitialized_var(pe); struct ubi_wl_entry *e1, *e2; struct ubi_vid_hdr *vid_hdr; @@ -842,20 +842,26 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr); if (err) { - + if (err == -EAGAIN) + goto out_not_moved; if (err < 0) goto out_error; - if (err == 1) + if (err == 2) { + /* Target PEB write error, torture it */ + torture = 1; goto out_not_moved; + } /* - * For some reason the LEB was not moved - it might be because - * the volume is being deleted. We should prevent this PEB from - * being selected for wear-levelling movement for some "time", - * so put it to the protection tree. + * The LEB has not been moved because the volume is being + * deleted or the PEB has been put meanwhile. We should prevent + * this PEB from being selected for wear-leveling movement + * again, so put it to the protection tree. */ - dbg_wl("cancelled moving PEB %d", e1->pnum); + dbg_wl("canceled moving PEB %d", e1->pnum); + ubi_assert(err == 1); + pe = kmalloc(sizeof(struct ubi_wl_prot_entry), GFP_NOFS); if (!pe) { err = -ENOMEM; @@ -920,9 +926,10 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, /* * For some reasons the LEB was not moved, might be an error, might be * something else. @e1 was not changed, so return it back. @e2 might - * be changed, schedule it for erasure. + * have been changed, schedule it for erasure. */ out_not_moved: + dbg_wl("canceled moving PEB %d", e1->pnum); ubi_free_vid_hdr(ubi, vid_hdr); vid_hdr = NULL; spin_lock(&ubi->wl_lock); @@ -930,12 +937,13 @@ out_not_moved: wl_tree_add(e1, &ubi->scrub); else wl_tree_add(e1, &ubi->used); + ubi_assert(!ubi->move_to_put); ubi->move_from = ubi->move_to = NULL; - ubi->move_to_put = ubi->wl_scheduled = 0; + ubi->wl_scheduled = 0; spin_unlock(&ubi->wl_lock); e1 = NULL; - err = schedule_erase(ubi, e2, 0); + err = schedule_erase(ubi, e2, torture); if (err) goto out_error; @@ -1324,7 +1332,7 @@ int ubi_wl_flush(struct ubi_device *ubi) up_write(&ubi->work_sem); /* - * And in case last was the WL worker and it cancelled the LEB + * And in case last was the WL worker and it canceled the LEB * movement, flush again. */ while (ubi->works_count) { |