This patch implements bitrot checking for UBI.
ubi_wl_trigger_bitrot_check() triggers a re-read of every
PEB which is currently in use.

Signed-off-by: Richard Weinberger <[email protected]>
---
 drivers/mtd/ubi/debug.c |   3 ++
 drivers/mtd/ubi/eba.c   |   2 +-
 drivers/mtd/ubi/ubi.h   |   3 +-
 drivers/mtd/ubi/wl.c    | 115 ++++++++++++++++++++++++++++++++++++++++++++----
 4 files changed, 112 insertions(+), 11 deletions(-)

diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c
index 63cb1d7..80d7832 100644
--- a/drivers/mtd/ubi/debug.c
+++ b/drivers/mtd/ubi/debug.c
@@ -332,6 +332,9 @@ static ssize_t dfs_file_write(struct file *file, const char 
__user *user_buf,
                goto out;
        }
 
+       //XXX
+       ubi_wl_trigger_bitrot_check(ubi);
+
        if (dent == d->dfs_chk_gen)
                d->chk_gen = val;
        else if (dent == d->dfs_chk_io)
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
index 0e11671d..78109e7 100644
--- a/drivers/mtd/ubi/eba.c
+++ b/drivers/mtd/ubi/eba.c
@@ -468,7 +468,7 @@ retry:
        }
 
        if (scrub)
-               err = ubi_wl_scrub_peb(ubi, pnum);
+               err = ubi_wl_scrub_peb(ubi, pnum, 0);
 
        leb_read_unlock(ubi, vol_id, lnum);
        return err;
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 7bf4163..ad004bd 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -806,7 +806,7 @@ int ubi_wl_get_peb(struct ubi_device *ubi);
 int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum,
                   int pnum, int torture);
 int ubi_wl_flush(struct ubi_device *ubi, int vol_id, int lnum);
-int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum);
+int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum, int nested);
 int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai);
 void ubi_wl_close(struct ubi_device *ubi);
 int ubi_thread(void *u);
@@ -816,6 +816,7 @@ int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct 
ubi_wl_entry *used_e,
 int ubi_is_erase_work(struct ubi_work *wrk);
 void ubi_refill_pools(struct ubi_device *ubi);
 int ubi_ensure_anchor_pebs(struct ubi_device *ubi);
+void ubi_wl_trigger_bitrot_check(struct ubi_device *ubi);
 
 /* io.c */
 int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 20f4917..80e451f 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -325,6 +325,20 @@ static int in_wl_tree(struct ubi_wl_entry *e, struct 
rb_root *root)
        return 0;
 }
 
+static inline int in_pg(const struct ubi_device *ubi, struct ubi_wl_entry *e)
+{
+       struct ubi_wl_entry *p;
+       int i;
+
+       for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i)
+               list_for_each_entry(p, &ubi->pq[i], u.list)
+                       if (p == e)
+                               return 1;
+
+       return 0;
+}
+
+
 /**
  * prot_queue_add - add physical eraseblock to the protection queue.
  * @ubi: UBI device description object
@@ -914,6 +928,93 @@ static int schedule_erase(struct ubi_device *ubi, struct 
ubi_wl_entry *e,
 }
 
 /**
+ * bitrot_check_worker - physical eraseblock bitrot check worker function.
+ * @ubi: UBI device description object
+ * @wl_wrk: the work object
+ * @shutdown: non-zero if the worker has to free memory and exit
+ *
+ * This function read a physical eraseblock and schedules scrubbing if
+ * bit flips are detected.
+ */
+static int bitrot_check_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
+                        int shutdown)
+{
+       struct ubi_wl_entry *e = wl_wrk->e;
+       int err;
+
+       kfree(wl_wrk);
+       if (shutdown) {
+               dbg_wl("cancel bitrot check of PEB %d", e->pnum);
+               kmem_cache_free(ubi_wl_entry_slab, e);
+               return 0;
+       }
+
+       if (!in_wl_tree(e, &ubi->used) && !in_pg(ubi, e)) {
+               dbg_wl("PEB %d not in used tree nor in protection queue", 
e->pnum);
+               return 0;
+       }
+
+       mutex_lock(&ubi->buf_mutex);
+       err = ubi_io_read(ubi, ubi->peb_buf, e->pnum, 0, ubi->peb_size);
+       mutex_unlock(&ubi->buf_mutex);
+       if (err == UBI_IO_BITFLIPS)
+               err = ubi_wl_scrub_peb(ubi, e->pnum, 1);
+       else
+               /* Ignore read errors as we return only work related errors.
+                * Read errors will be logged by ubi_io_read(). */
+               err = 0;
+
+       return err;
+}
+
+/**
+ * schedule_bitrot_check - schedule a bitrot check work.
+ * @ubi: UBI device description object
+ * @e: the WL entry of the physical eraseblock to check
+ *
+ * This function returns zero in case of success and a %-ENOMEM in case of
+ * failure.
+ */
+static int schedule_bitrot_check(struct ubi_device *ubi,
+                                struct ubi_wl_entry *e)
+{
+       struct ubi_work *wl_wrk;
+
+       ubi_assert(e);
+
+       dbg_wl("schedule bitrot check of PEB %d", e->pnum);
+
+       wl_wrk = kmalloc(sizeof(struct ubi_work), GFP_NOFS);
+       if (!wl_wrk)
+               return -ENOMEM;
+
+       wl_wrk->func = &bitrot_check_worker;
+       wl_wrk->e = e;
+
+       schedule_ubi_work(ubi, wl_wrk);
+       return 0;
+}
+
+/**
+ * ubi_wl_trigger_bitrot_check - triggers a re-read of all physical erase
+ * blocks in use.
+ * @ubi: UBI device description object
+ */
+void ubi_wl_trigger_bitrot_check(struct ubi_device *ubi)
+{
+       int i;
+       struct ubi_wl_entry *e;
+
+       for (i = 0; i < ubi->peb_count; i++) {
+               spin_lock(&ubi->wl_lock);
+               e = ubi->lookuptbl[i];
+               spin_unlock(&ubi->wl_lock);
+               if (e)
+                       schedule_bitrot_check(ubi, e);
+       }
+}
+
+/**
  * do_sync_erase - run the erase worker synchronously.
  * @ubi: UBI device description object
  * @e: the WL entry of the physical eraseblock to erase
@@ -1634,13 +1735,14 @@ retry:
  * ubi_wl_scrub_peb - schedule a physical eraseblock for scrubbing.
  * @ubi: UBI device description object
  * @pnum: the physical eraseblock to schedule
+ * @nested: set to non-zero if this function is called from UBI worker
  *
  * If a bit-flip in a physical eraseblock is detected, this physical eraseblock
  * needs scrubbing. This function schedules a physical eraseblock for
  * scrubbing which is done in background. This function returns zero in case of
  * success and a negative error code in case of failure.
  */
-int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum)
+int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum, int nested)
 {
        struct ubi_wl_entry *e;
 
@@ -1690,7 +1792,7 @@ retry:
         * Technically scrubbing is the same as wear-leveling, so it is done
         * by the WL worker.
         */
-       return ensure_wear_leveling(ubi, 0);
+       return ensure_wear_leveling(ubi, nested);
 }
 
 /**
@@ -2117,16 +2219,11 @@ static int self_check_in_wl_tree(const struct 
ubi_device *ubi,
 static int self_check_in_pq(const struct ubi_device *ubi,
                            struct ubi_wl_entry *e)
 {
-       struct ubi_wl_entry *p;
-       int i;
-
        if (!ubi_dbg_chk_gen(ubi))
                return 0;
 
-       for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i)
-               list_for_each_entry(p, &ubi->pq[i], u.list)
-                       if (p == e)
-                               return 0;
+       if (!in_pg(ubi, e))
+               return 0;
 
        ubi_err("self-check failed for PEB %d, EC %d, Protect queue",
                e->pnum, e->ec);
-- 
1.8.4.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to