Skip to content

Commit d09e9a2

Browse files
Zhihao Chengrichardweinberger
authored andcommitted
ubi: fastmap: Fix high cpu usage of ubi_bgt by making sure wl_pool not empty
There at least 6 PEBs reserved on UBI device: 1. EBA_RESERVED_PEBS[1] 2. WL_RESERVED_PEBS[1] 3. UBI_LAYOUT_VOLUME_EBS[2] 4. MIN_FASTMAP_RESERVED_PEBS[2] When all ubi volumes take all their PEBs, there are 3 (EBA_RESERVED_PEBS + WL_RESERVED_PEBS + MIN_FASTMAP_RESERVED_PEBS - MIN_FASTMAP_TAKEN_PEBS[1]) free PEBs. Since commit f9c34bb ("ubi: Fix producing anchor PEBs") and commit 4b68bf9 ("ubi: Select fastmap anchor PEBs considering wear level rules") applied, there is only 1 (3 - FASTMAP_ANCHOR_PEBS[1] - FASTMAP_NEXT_ANCHOR_PEBS[1]) free PEB to fill pool and wl_pool, after filling pool, wl_pool is always empty. So, UBI could be stuck in an infinite loop: ubi_thread system_wq wear_leveling_worker <-------------------------------------------------- get_peb_for_wl | // fm_wl_pool, used = size = 0 | schedule_work(&ubi->fm_work) | | update_fastmap_work_fn | ubi_update_fastmap | ubi_refill_pools | // ubi->free_count - ubi->beb_rsvd_pebs < 5 | // wl_pool is not filled with any PEBs | schedule_erase(old_fm_anchor) | ubi_ensure_anchor_pebs | __schedule_ubi_work(wear_leveling_worker) | | __erase_worker | ensure_wear_leveling | __schedule_ubi_work(wear_leveling_worker) -------------------------- , which cause high cpu usage of ubi_bgt: top - 12:10:42 up 5 min, 2 users, load average: 1.76, 0.68, 0.27 Tasks: 123 total, 3 running, 54 sleeping, 0 stopped, 0 zombie PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1589 root 20 0 0 0 0 R 45.0 0.0 0:38.86 ubi_bgt0d 319 root 20 0 0 0 0 I 15.2 0.0 0:15.29 kworker/0:3-eve 371 root 20 0 0 0 0 I 14.9 0.0 0:12.85 kworker/3:3-eve 20 root 20 0 0 0 0 I 11.3 0.0 0:05.33 kworker/1:0-eve 202 root 20 0 0 0 0 I 11.3 0.0 0:04.93 kworker/2:3-eve In commit 4b68bf9 ("ubi: Select fastmap anchor PEBs considering wear level rules"), there are three key changes: 1) Choose the fastmap anchor when the most free PEBs are available. 2) Enable anchor move within the anchor area again as it is useful for distributing wear. 3) Import a candidate fm anchor and check this PEB's erase count during wear leveling. If the wear leveling limit is exceeded, use the used anchor area PEB with the lowest erase count to replace it. The anchor candidate can be removed, we can check fm_anchor PEB's erase count during wear leveling. Fix it by: 1) Removing 'fm_next_anchor' and check 'fm_anchor' during wear leveling. 2) Preferentially filling one free peb into fm_wl_pool in condition of ubi->free_count > ubi->beb_rsvd_pebs, then try to reserve enough free count for fastmap non anchor pebs after the above prerequisites are met. Then, there are at least 1 PEB in pool and 1 PEB in wl_pool after calling ubi_refill_pools() with all erase works done. Fetch a reproducer in [Link]. Fixes: 4b68bf9 ("ubi: Select fastmap anchor PEBs ... rules") Link: https://bugzilla.kernel.org/show_bug.cgi?id=215407 Signed-off-by: Zhihao Cheng <[email protected]> Signed-off-by: Richard Weinberger <[email protected]>
1 parent 532aef5 commit d09e9a2

File tree

4 files changed

+57
-46
lines changed

4 files changed

+57
-46
lines changed

drivers/mtd/ubi/fastmap-wl.c

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,33 @@ struct ubi_wl_entry *ubi_wl_get_fm_peb(struct ubi_device *ubi, int anchor)
9797
return e;
9898
}
9999

100+
/*
101+
* has_enough_free_count - whether ubi has enough free pebs to fill fm pools
102+
* @ubi: UBI device description object
103+
* @is_wl_pool: whether UBI is filling wear leveling pool
104+
*
105+
* This helper function checks whether there are enough free pebs (deducted
106+
* by fastmap pebs) to fill fm_pool and fm_wl_pool, above rule works after
107+
* there is at least one of free pebs is filled into fm_wl_pool.
108+
* For wear leveling pool, UBI should also reserve free pebs for bad pebs
109+
* handling, because there maybe no enough free pebs for user volumes after
110+
* producing new bad pebs.
111+
*/
112+
static bool has_enough_free_count(struct ubi_device *ubi, bool is_wl_pool)
113+
{
114+
int fm_used = 0; // fastmap non anchor pebs.
115+
int beb_rsvd_pebs;
116+
117+
if (!ubi->free.rb_node)
118+
return false;
119+
120+
beb_rsvd_pebs = is_wl_pool ? ubi->beb_rsvd_pebs : 0;
121+
if (ubi->fm_wl_pool.size > 0 && !(ubi->ro_mode || ubi->fm_disabled))
122+
fm_used = ubi->fm_size / ubi->leb_size - 1;
123+
124+
return ubi->free_count - beb_rsvd_pebs > fm_used;
125+
}
126+
100127
/**
101128
* ubi_refill_pools - refills all fastmap PEB pools.
102129
* @ubi: UBI device description object
@@ -120,21 +147,17 @@ void ubi_refill_pools(struct ubi_device *ubi)
120147
wl_tree_add(ubi->fm_anchor, &ubi->free);
121148
ubi->free_count++;
122149
}
123-
if (ubi->fm_next_anchor) {
124-
wl_tree_add(ubi->fm_next_anchor, &ubi->free);
125-
ubi->free_count++;
126-
}
127150

128-
/* All available PEBs are in ubi->free, now is the time to get
151+
/*
152+
* All available PEBs are in ubi->free, now is the time to get
129153
* the best anchor PEBs.
130154
*/
131155
ubi->fm_anchor = ubi_wl_get_fm_peb(ubi, 1);
132-
ubi->fm_next_anchor = ubi_wl_get_fm_peb(ubi, 1);
133156

134157
for (;;) {
135158
enough = 0;
136159
if (pool->size < pool->max_size) {
137-
if (!ubi->free.rb_node)
160+
if (!has_enough_free_count(ubi, false))
138161
break;
139162

140163
e = wl_get_wle(ubi);
@@ -147,8 +170,7 @@ void ubi_refill_pools(struct ubi_device *ubi)
147170
enough++;
148171

149172
if (wl_pool->size < wl_pool->max_size) {
150-
if (!ubi->free.rb_node ||
151-
(ubi->free_count - ubi->beb_rsvd_pebs < 5))
173+
if (!has_enough_free_count(ubi, true))
152174
break;
153175

154176
e = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF);
@@ -286,20 +308,26 @@ static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi)
286308
int ubi_ensure_anchor_pebs(struct ubi_device *ubi)
287309
{
288310
struct ubi_work *wrk;
311+
struct ubi_wl_entry *anchor;
289312

290313
spin_lock(&ubi->wl_lock);
291314

292-
/* Do we have a next anchor? */
293-
if (!ubi->fm_next_anchor) {
294-
ubi->fm_next_anchor = ubi_wl_get_fm_peb(ubi, 1);
295-
if (!ubi->fm_next_anchor)
296-
/* Tell wear leveling to produce a new anchor PEB */
297-
ubi->fm_do_produce_anchor = 1;
315+
/* Do we already have an anchor? */
316+
if (ubi->fm_anchor) {
317+
spin_unlock(&ubi->wl_lock);
318+
return 0;
298319
}
299320

300-
/* Do wear leveling to get a new anchor PEB or check the
301-
* existing next anchor candidate.
302-
*/
321+
/* See if we can find an anchor PEB on the list of free PEBs */
322+
anchor = ubi_wl_get_fm_peb(ubi, 1);
323+
if (anchor) {
324+
ubi->fm_anchor = anchor;
325+
spin_unlock(&ubi->wl_lock);
326+
return 0;
327+
}
328+
329+
ubi->fm_do_produce_anchor = 1;
330+
/* No luck, trigger wear leveling to produce a new anchor PEB. */
303331
if (ubi->wl_scheduled) {
304332
spin_unlock(&ubi->wl_lock);
305333
return 0;
@@ -381,11 +409,6 @@ static void ubi_fastmap_close(struct ubi_device *ubi)
381409
ubi->fm_anchor = NULL;
382410
}
383411

384-
if (ubi->fm_next_anchor) {
385-
return_unused_peb(ubi, ubi->fm_next_anchor);
386-
ubi->fm_next_anchor = NULL;
387-
}
388-
389412
if (ubi->fm) {
390413
for (i = 0; i < ubi->fm->used_blocks; i++)
391414
kfree(ubi->fm->e[i]);

drivers/mtd/ubi/fastmap.c

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,17 +1230,6 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
12301230
fm_pos += sizeof(*fec);
12311231
ubi_assert(fm_pos <= ubi->fm_size);
12321232
}
1233-
if (ubi->fm_next_anchor) {
1234-
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
1235-
1236-
fec->pnum = cpu_to_be32(ubi->fm_next_anchor->pnum);
1237-
set_seen(ubi, ubi->fm_next_anchor->pnum, seen_pebs);
1238-
fec->ec = cpu_to_be32(ubi->fm_next_anchor->ec);
1239-
1240-
free_peb_count++;
1241-
fm_pos += sizeof(*fec);
1242-
ubi_assert(fm_pos <= ubi->fm_size);
1243-
}
12441233
fmh->free_peb_count = cpu_to_be32(free_peb_count);
12451234

12461235
ubi_for_each_used_peb(ubi, wl_e, tmp_rb) {

drivers/mtd/ubi/ubi.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -489,8 +489,7 @@ struct ubi_debug_info {
489489
* @fm_work: fastmap work queue
490490
* @fm_work_scheduled: non-zero if fastmap work was scheduled
491491
* @fast_attach: non-zero if UBI was attached by fastmap
492-
* @fm_anchor: The new anchor PEB used during fastmap update
493-
* @fm_next_anchor: An anchor PEB candidate for the next time fastmap is updated
492+
* @fm_anchor: The next anchor PEB to use for fastmap
494493
* @fm_do_produce_anchor: If true produce an anchor PEB in wl
495494
*
496495
* @used: RB-tree of used physical eraseblocks
@@ -601,7 +600,6 @@ struct ubi_device {
601600
int fm_work_scheduled;
602601
int fast_attach;
603602
struct ubi_wl_entry *fm_anchor;
604-
struct ubi_wl_entry *fm_next_anchor;
605603
int fm_do_produce_anchor;
606604

607605
/* Wear-leveling sub-system's stuff */

drivers/mtd/ubi/wl.c

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -689,16 +689,16 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
689689

690690
#ifdef CONFIG_MTD_UBI_FASTMAP
691691
e1 = find_anchor_wl_entry(&ubi->used);
692-
if (e1 && ubi->fm_next_anchor &&
693-
(ubi->fm_next_anchor->ec - e1->ec >= UBI_WL_THRESHOLD)) {
692+
if (e1 && ubi->fm_anchor &&
693+
(ubi->fm_anchor->ec - e1->ec >= UBI_WL_THRESHOLD)) {
694694
ubi->fm_do_produce_anchor = 1;
695-
/* fm_next_anchor is no longer considered a good anchor
696-
* candidate.
695+
/*
696+
* fm_anchor is no longer considered a good anchor.
697697
* NULL assignment also prevents multiple wear level checks
698698
* of this PEB.
699699
*/
700-
wl_tree_add(ubi->fm_next_anchor, &ubi->free);
701-
ubi->fm_next_anchor = NULL;
700+
wl_tree_add(ubi->fm_anchor, &ubi->free);
701+
ubi->fm_anchor = NULL;
702702
ubi->free_count++;
703703
}
704704

@@ -1085,12 +1085,13 @@ static int __erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk)
10851085
if (!err) {
10861086
spin_lock(&ubi->wl_lock);
10871087

1088-
if (!ubi->fm_disabled && !ubi->fm_next_anchor &&
1088+
if (!ubi->fm_disabled && !ubi->fm_anchor &&
10891089
e->pnum < UBI_FM_MAX_START) {
1090-
/* Abort anchor production, if needed it will be
1090+
/*
1091+
* Abort anchor production, if needed it will be
10911092
* enabled again in the wear leveling started below.
10921093
*/
1093-
ubi->fm_next_anchor = e;
1094+
ubi->fm_anchor = e;
10941095
ubi->fm_do_produce_anchor = 0;
10951096
} else {
10961097
wl_tree_add(e, &ubi->free);

0 commit comments

Comments
 (0)