ubi: fastmap: Fix missed ec updating after erasing old fastmap data block
authorZhihao Cheng <chengzhihao1@huawei.com>
Mon, 28 Aug 2023 06:38:34 +0000 (14:38 +0800)
committerRichard Weinberger <richard@nod.at>
Sat, 28 Oct 2023 20:30:01 +0000 (22:30 +0200)
After running fsstress on ubifs for a long time, UBI(16384 blocks,
fastmap takes 2 blocks) has an erase block with different erase
counters displayed from two views:

From ubiscan view: PEB 8031 has erase counter 31581
=========================================================
from              to     count      min      avg      max
---------------------------------------------------------
0        ..        9:        0        0        0        0
10       ..       99:        0        0        0        0
100      ..      999:    16383      290      315      781
1000     ..     9999:        0        0        0        0
10000    ..    99999:        1    31581    31581    31581
100000   ..      inf:        0        0        0        0
---------------------------------------------------------
Total               :    16384      290      317    31581

From detailed_erase_block_info view: PEB 8031 has erase counter 7
physical_block_number   erase_count
8030                    421
8031                    7   # mem info is different from disk info
8032                    434
8033                    425
8034                    431

Following process missed updating erase counter in wl_entry(in memory):
ubi_update_fastmap
 for (i = 1; i < new_fm->used_blocks; i++) // update fastmap data
  if (!tmp_e)
   if (old_fm && old_fm->e[i])
    erase_block(ubi, old_fm->e[i]->pnum)
     ret = ubi_io_sync_erase(ubi, pnum, 0)
     ec = be64_to_cpu(ec_hdr->ec)
     ec += ret
     ec_hdr->ec = cpu_to_be64(ec)
     ubi_io_write_ec_hdr(ubi, pnum, ec_hdr)  // ec is updated on flash
   // ec is not updated in old_fm->e[i] (in memory)

Fix it by passing wl_enter into erase_block() and updating erase
counter in erase_block().

Fixes: dbb7d2a88d2a ("UBI: Add fastmap core")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
drivers/mtd/ubi/fastmap.c

index 28c8151a0725d5b5c9abbc31bdab3e48fad3253b..f8c230acc55e744e8fad5d200217bd31aac69f52 100644 (file)
@@ -1392,11 +1392,12 @@ out:
 /**
  * erase_block - Manually erase a PEB.
  * @ubi: UBI device object
- * @pnum: PEB to be erased
+ * @e: the physical eraseblock to erase
  *
- * Returns the new EC value on success, < 0 indicates an internal error.
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
  */
-static int erase_block(struct ubi_device *ubi, int pnum)
+static int erase_block(struct ubi_device *ubi, struct ubi_wl_entry *e)
 {
        int ret;
        struct ubi_ec_hdr *ec_hdr;
@@ -1406,7 +1407,7 @@ static int erase_block(struct ubi_device *ubi, int pnum)
        if (!ec_hdr)
                return -ENOMEM;
 
-       ret = ubi_io_read_ec_hdr(ubi, pnum, ec_hdr, 0);
+       ret = ubi_io_read_ec_hdr(ubi, e->pnum, ec_hdr, 0);
        if (ret < 0)
                goto out;
        else if (ret && ret != UBI_IO_BITFLIPS) {
@@ -1414,7 +1415,7 @@ static int erase_block(struct ubi_device *ubi, int pnum)
                goto out;
        }
 
-       ret = ubi_io_sync_erase(ubi, pnum, 0);
+       ret = ubi_io_sync_erase(ubi, e->pnum, 0);
        if (ret < 0)
                goto out;
 
@@ -1426,11 +1427,16 @@ static int erase_block(struct ubi_device *ubi, int pnum)
        }
 
        ec_hdr->ec = cpu_to_be64(ec);
-       ret = ubi_io_write_ec_hdr(ubi, pnum, ec_hdr);
+       ret = ubi_io_write_ec_hdr(ubi, e->pnum, ec_hdr);
        if (ret < 0)
                goto out;
 
-       ret = ec;
+       e->ec = ec;
+       spin_lock(&ubi->wl_lock);
+       if (e->ec > ubi->max_ec)
+               ubi->max_ec = e->ec;
+       spin_unlock(&ubi->wl_lock);
+
 out:
        kfree(ec_hdr);
        return ret;
@@ -1576,7 +1582,7 @@ int ubi_update_fastmap(struct ubi_device *ubi)
 
                if (!tmp_e) {
                        if (old_fm && old_fm->e[i]) {
-                               ret = erase_block(ubi, old_fm->e[i]->pnum);
+                               ret = erase_block(ubi, old_fm->e[i]);
                                if (ret < 0) {
                                        ubi_err(ubi, "could not erase old fastmap PEB");
 
@@ -1628,7 +1634,7 @@ int ubi_update_fastmap(struct ubi_device *ubi)
        if (old_fm) {
                /* no fresh anchor PEB was found, reuse the old one */
                if (!tmp_e) {
-                       ret = erase_block(ubi, old_fm->e[0]->pnum);
+                       ret = erase_block(ubi, old_fm->e[0]);
                        if (ret < 0) {
                                ubi_err(ubi, "could not erase old anchor PEB");
 
@@ -1640,7 +1646,6 @@ int ubi_update_fastmap(struct ubi_device *ubi)
                                goto err;
                        }
                        new_fm->e[0] = old_fm->e[0];
-                       new_fm->e[0]->ec = ret;
                        old_fm->e[0] = NULL;
                } else {
                        /* we've got a new anchor PEB, return the old one */