Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ubi: Select fastmap anchor PEBs considering wear level rules

There is a risk that the fastmap anchor PEB is alternating between
just two PEBs, the current anchor and the previous anchor that was just
deleted. As the fastmap pools gets the first take on free PEBs, the
pools may leave no free PEBs to be selected as the new anchor,
resulting in the two PEBs alternating behaviour. If the anchor PEBs gets
a high erase count the PEBs will not be used by the pools but remain in
ubi->free, even more increasing the likelihood they will be used as
anchors.

Getting stuck using only a couple of PEBs continuously will result in an
uneven wear, eventually leading to failure.

To fix this:

- Choose the fastmap anchor when the most free PEBs are available. This is
during rebuilding of the fastmap pools, after the unused pool PEBs are
added to ubi->free but before the pools are populated again from the
free PEBs. Also reserve an additional second best PEB as a candidate
for the next time the fast map anchor is updated. If a better PEB is
found the next time the fast map anchor is updated, the candidate is
made available for building the pools.

- Enable anchor move within the anchor area again as it is useful for
distributing wear.

- The anchor candidate for the next fastmap update is the most suited free
PEB. Check this PEB's erase count during wear leveling. If the wear
leveling limit is exceeded, the PEB is considered unsuitable for now. As
all other non used anchor area PEBs should be even worse, free up the
used anchor area PEB with the lowest erase count.

Signed-off-by: Arne Edholm <arne.edholm@axis.com>
Signed-off-by: Richard Weinberger <richard@nod.at>

authored by

Arne Edholm and committed by
Richard Weinberger
4b68bf9a 3d77e6a8

+57 -25
+24 -15
drivers/mtd/ubi/fastmap-wl.c
··· 116 116 wl_pool->size = 0; 117 117 pool->size = 0; 118 118 119 + if (ubi->fm_anchor) { 120 + wl_tree_add(ubi->fm_anchor, &ubi->free); 121 + ubi->free_count++; 122 + } 123 + if (ubi->fm_next_anchor) { 124 + wl_tree_add(ubi->fm_next_anchor, &ubi->free); 125 + ubi->free_count++; 126 + } 127 + 128 + /* All available PEBs are in ubi->free, now is the time to get 129 + * the best anchor PEBs. 130 + */ 131 + ubi->fm_anchor = ubi_wl_get_fm_peb(ubi, 1); 132 + ubi->fm_next_anchor = ubi_wl_get_fm_peb(ubi, 1); 133 + 119 134 for (;;) { 120 135 enough = 0; 121 136 if (pool->size < pool->max_size) { ··· 286 271 int ubi_ensure_anchor_pebs(struct ubi_device *ubi) 287 272 { 288 273 struct ubi_work *wrk; 289 - struct ubi_wl_entry *anchor; 290 274 291 275 spin_lock(&ubi->wl_lock); 292 276 293 - /* Do we already have an anchor? */ 294 - if (ubi->fm_anchor) { 295 - spin_unlock(&ubi->wl_lock); 296 - return 0; 277 + /* Do we have a next anchor? */ 278 + if (!ubi->fm_next_anchor) { 279 + ubi->fm_next_anchor = ubi_wl_get_fm_peb(ubi, 1); 280 + if (!ubi->fm_next_anchor) 281 + /* Tell wear leveling to produce a new anchor PEB */ 282 + ubi->fm_do_produce_anchor = 1; 297 283 } 298 284 299 - /* See if we can find an anchor PEB on the list of free PEBs */ 300 - anchor = ubi_wl_get_fm_peb(ubi, 1); 301 - if (anchor) { 302 - ubi->fm_anchor = anchor; 303 - spin_unlock(&ubi->wl_lock); 304 - return 0; 305 - } 306 - 307 - /* No luck, trigger wear leveling to produce a new anchor PEB */ 308 - ubi->fm_do_produce_anchor = 1; 285 + /* Do wear leveling to get a new anchor PEB or check the 286 + * existing next anchor candidate. 287 + */ 309 288 if (ubi->wl_scheduled) { 310 289 spin_unlock(&ubi->wl_lock); 311 290 return 0;
+11
drivers/mtd/ubi/fastmap.c
··· 1220 1220 fm_pos += sizeof(*fec); 1221 1221 ubi_assert(fm_pos <= ubi->fm_size); 1222 1222 } 1223 + if (ubi->fm_next_anchor) { 1224 + fec = (struct ubi_fm_ec *)(fm_raw + fm_pos); 1225 + 1226 + fec->pnum = cpu_to_be32(ubi->fm_next_anchor->pnum); 1227 + set_seen(ubi, ubi->fm_next_anchor->pnum, seen_pebs); 1228 + fec->ec = cpu_to_be32(ubi->fm_next_anchor->ec); 1229 + 1230 + free_peb_count++; 1231 + fm_pos += sizeof(*fec); 1232 + ubi_assert(fm_pos <= ubi->fm_size); 1233 + } 1223 1234 fmh->free_peb_count = cpu_to_be32(free_peb_count); 1224 1235 1225 1236 ubi_for_each_used_peb(ubi, wl_e, tmp_rb) {
+3 -1
drivers/mtd/ubi/ubi.h
··· 491 491 * @fm_work: fastmap work queue 492 492 * @fm_work_scheduled: non-zero if fastmap work was scheduled 493 493 * @fast_attach: non-zero if UBI was attached by fastmap 494 - * @fm_anchor: The next anchor PEB to use for fastmap 494 + * @fm_anchor: The new anchor PEB used during fastmap update 495 + * @fm_next_anchor: An anchor PEB candidate for the next time fastmap is updated 495 496 * @fm_do_produce_anchor: If true produce an anchor PEB in wl 496 497 * 497 498 * @used: RB-tree of used physical eraseblocks ··· 603 602 int fm_work_scheduled; 604 603 int fast_attach; 605 604 struct ubi_wl_entry *fm_anchor; 605 + struct ubi_wl_entry *fm_next_anchor; 606 606 int fm_do_produce_anchor; 607 607 608 608 /* Wear-leveling sub-system's stuff */
+19 -9
drivers/mtd/ubi/wl.c
··· 687 687 } 688 688 689 689 #ifdef CONFIG_MTD_UBI_FASTMAP 690 + e1 = find_anchor_wl_entry(&ubi->used); 691 + if (e1 && ubi->fm_next_anchor && 692 + (ubi->fm_next_anchor->ec - e1->ec >= UBI_WL_THRESHOLD)) { 693 + ubi->fm_do_produce_anchor = 1; 694 + /* fm_next_anchor is no longer considered a good anchor 695 + * candidate. 696 + * NULL assignment also prevents multiple wear level checks 697 + * of this PEB. 698 + */ 699 + wl_tree_add(ubi->fm_next_anchor, &ubi->free); 700 + ubi->fm_next_anchor = NULL; 701 + ubi->free_count++; 702 + } 703 + 690 704 if (ubi->fm_do_produce_anchor) { 691 - e1 = find_anchor_wl_entry(&ubi->used); 692 705 if (!e1) 693 706 goto out_cancel; 694 707 e2 = get_peb_for_wl(ubi); 695 708 if (!e2) 696 - goto out_cancel; 697 - 698 - /* 699 - * Anchor move within the anchor area is useless. 700 - */ 701 - if (e2->pnum < UBI_FM_MAX_START) 702 709 goto out_cancel; 703 710 704 711 self_check_in_wl_tree(ubi, e1, &ubi->used); ··· 1086 1079 if (!err) { 1087 1080 spin_lock(&ubi->wl_lock); 1088 1081 1089 - if (!ubi->fm_anchor && e->pnum < UBI_FM_MAX_START) { 1090 - ubi->fm_anchor = e; 1082 + if (!ubi->fm_next_anchor && e->pnum < UBI_FM_MAX_START) { 1083 + /* Abort anchor production, if needed it will be 1084 + * enabled again in the wear leveling started below. 1085 + */ 1086 + ubi->fm_next_anchor = e; 1091 1087 ubi->fm_do_produce_anchor = 0; 1092 1088 } else { 1093 1089 wl_tree_add(e, &ubi->free);