]> git.hungrycats.org Git - linux/commitdiff
ubi: fastmap: wl: Schedule fm_work if wear-leveling pool is empty
authorZhihao Cheng <chengzhihao1@huawei.com>
Mon, 19 Aug 2024 03:26:22 +0000 (11:26 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 9 Dec 2024 09:32:55 +0000 (10:32 +0100)
[ Upstream commit c4595fe394a289927077e3da561db27811919ee0 ]

Since commit 14072ee33d5a ("ubi: fastmap: Check wl_pool for free peb
before wear leveling"), wear_leveling_worker() won't schedule fm_work
if wear-leveling pool is empty, which could temporarily disable the
wear-leveling until the fastmap is updated(eg. pool becomes empty).
Fix it by scheduling fm_work if wl_pool is empty during wear-leveing.

Fixes: 14072ee33d5a ("ubi: fastmap: Check wl_pool for free peb before wear leveling")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/mtd/ubi/fastmap-wl.c
drivers/mtd/ubi/wl.c
drivers/mtd/ubi/wl.h

index 863f571f1adb545a4991c0d22efc9889968d22f3..79733163ab7d023bbbfb402e94df1ba939867922 100644 (file)
@@ -282,14 +282,27 @@ out:
  * WL sub-system.
  *
  * @ubi: UBI device description object
+ * @need_fill: whether to fill wear-leveling pool when no PEBs are found
  */
-static struct ubi_wl_entry *next_peb_for_wl(struct ubi_device *ubi)
+static struct ubi_wl_entry *next_peb_for_wl(struct ubi_device *ubi,
+                                           bool need_fill)
 {
        struct ubi_fm_pool *pool = &ubi->fm_wl_pool;
        int pnum;
 
-       if (pool->used == pool->size)
+       if (pool->used == pool->size) {
+               if (need_fill && !ubi->fm_work_scheduled) {
+                       /*
+                        * We cannot update the fastmap here because this
+                        * function is called in atomic context.
+                        * Let's fail here and refill/update it as soon as
+                        * possible.
+                        */
+                       ubi->fm_work_scheduled = 1;
+                       schedule_work(&ubi->fm_work);
+               }
                return NULL;
+       }
 
        pnum = pool->pebs[pool->used];
        return ubi->lookuptbl[pnum];
@@ -311,7 +324,7 @@ static bool need_wear_leveling(struct ubi_device *ubi)
        if (!ubi->used.rb_node)
                return false;
 
-       e = next_peb_for_wl(ubi);
+       e = next_peb_for_wl(ubi, false);
        if (!e) {
                if (!ubi->free.rb_node)
                        return false;
index e510e2de2cfe07f957c807188efafd60d3cc4078..886d44019401a80b754caf8842de311541efdd72 100644 (file)
@@ -671,7 +671,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
        ubi_assert(!ubi->move_to_put);
 
 #ifdef CONFIG_MTD_UBI_FASTMAP
-       if (!next_peb_for_wl(ubi) ||
+       if (!next_peb_for_wl(ubi, true) ||
 #else
        if (!ubi->free.rb_node ||
 #endif
index 5ebe374a08aed9954e385cbcdabad7d974617659..1d83e552533a5d2e53b324bdc74ae5f60662b72b 100644 (file)
@@ -5,7 +5,8 @@
 static void update_fastmap_work_fn(struct work_struct *wrk);
 static struct ubi_wl_entry *find_anchor_wl_entry(struct rb_root *root);
 static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi);
-static struct ubi_wl_entry *next_peb_for_wl(struct ubi_device *ubi);
+static struct ubi_wl_entry *next_peb_for_wl(struct ubi_device *ubi,
+                                           bool need_fill);
 static bool need_wear_leveling(struct ubi_device *ubi);
 static void ubi_fastmap_close(struct ubi_device *ubi);
 static inline void ubi_fastmap_init(struct ubi_device *ubi, int *count)