diff options
author | Zhihao Cheng <chengzhihao1@huawei.com> | 2023-08-28 09:38:42 +0300 |
---|---|---|
committer | Richard Weinberger <richard@nod.at> | 2023-10-29 00:07:54 +0300 |
commit | 761893bd490b039258fda893c2a15fa59334c820 (patch) | |
tree | 098ecc0109750de81cbb973bde7af72ff1c170b2 /drivers/mtd/ubi/wl.c | |
parent | eada823e6a6fb856548f6e0c7a343cd5c41bb9d3 (diff) | |
download | linux-761893bd490b039258fda893c2a15fa59334c820.tar.xz |
ubi: fastmap: Get wl PEB even ec beyonds the 'max' if free PEBs are run out
This is the part 2 to fix cyclically reusing single fastmap data PEBs.
Consider one situation, if there are four free PEBs for fm_anchor, pool,
wl_pool and fastmap data PEB with erase counter 100, 100, 100, 5096
(ubi->beb_rsvd_pebs is 0). PEB with erase counter 5096 is always picked
for fastmap data according to the realization of find_wl_entry(), since
fastmap data PEB is not scheduled for wl, finally there are two PEBs
(fm data) with great erase counter than other PEBS.
Get wl PEB even its erase counter exceeds the 'max' in find_wl_entry()
when free PEBs are run out after filling pools and fm data. Then the PEB
with biggest erase conter is taken as wl PEB, it can be scheduled for wl.
Fixes: dbb7d2a88d2a ("UBI: Add fastmap core")
Link: https://bugzilla.kernel.org/show_bug.cgi?id=217787
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
Diffstat (limited to 'drivers/mtd/ubi/wl.c')
-rw-r--r-- | drivers/mtd/ubi/wl.c | 16 |
1 files changed, 10 insertions, 6 deletions
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 40a1c306b8af..a357f3d27f2f 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -317,12 +317,14 @@ static void prot_queue_add(struct ubi_device *ubi, struct ubi_wl_entry *e) * @ubi: UBI device description object * @root: the RB-tree where to look for * @diff: maximum possible difference from the smallest erase counter + * @pick_max: pick PEB even its erase counter beyonds 'min_ec + @diff' * * This function looks for a wear leveling entry with erase counter closest to * min + @diff, where min is the smallest erase counter. */ static struct ubi_wl_entry *find_wl_entry(struct ubi_device *ubi, - struct rb_root *root, int diff) + struct rb_root *root, int diff, + int pick_max) { struct rb_node *p; struct ubi_wl_entry *e; @@ -336,9 +338,11 @@ static struct ubi_wl_entry *find_wl_entry(struct ubi_device *ubi, struct ubi_wl_entry *e1; e1 = rb_entry(p, struct ubi_wl_entry, u.rb); - if (e1->ec >= max) + if (e1->ec >= max) { + if (pick_max) + e = e1; p = p->rb_left; - else { + } else { p = p->rb_right; e = e1; } @@ -375,7 +379,7 @@ static struct ubi_wl_entry *find_mean_wl_entry(struct ubi_device *ubi, */ e = may_reserve_for_fm(ubi, e, root); } else - e = find_wl_entry(ubi, root, WL_FREE_MAX_DIFF/2); + e = find_wl_entry(ubi, root, WL_FREE_MAX_DIFF/2, 0); return e; } @@ -1048,7 +1052,7 @@ static int ensure_wear_leveling(struct ubi_device *ubi, int nested) * %UBI_WL_THRESHOLD. */ e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, u.rb); - e2 = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF); + e2 = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF, 0); if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) goto out_unlock; @@ -2079,7 +2083,7 @@ static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi) { struct ubi_wl_entry *e; - e = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF); + e = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF, 0); self_check_in_wl_tree(ubi, e, &ubi->free); ubi->free_count--; ubi_assert(ubi->free_count >= 0); |