[RFC/PATCH 5/5] mtd: ubi: Add sysfs entry to force all pebs' scan

Richard Weinberger richard.weinberger at gmail.com
Sun Sep 28 01:42:48 PDT 2014


Tanya,

On Sun, Sep 28, 2014 at 8:38 AM, Tanya Brokhman <tlinder at codeaurora.org> wrote:
> A given eraseblock can be read many times or not at all.
> We do not have the account of such eraseblocks that have
> not been accessed since a pre-defined threshold interval.
> By means of scanning the entire flash device it is possible to identify
> all such eraseblocks.
>
> Add a sysfs entry to force scan on all the PEBs on demand basis.
>
> The sysfs entry would be available under /sys/class/ubi/ubiX/peb_scan
>  - echo 1 to this entry would trigger the scan, ignored if in progress
>  - On reading returns the scan status (1 = Scan executing, 0 = Scan not
>    executing)

Did you see that?
http://linux-kernel.2935.n7.nabble.com/RFC-UBI-bitrot-checking-td949453.html
Maybe we can combine our work.
Although your patch seems to be in better shape than mine. :)

> Signed-off-by: Pratibhasagar V <pratibha at codeaurora.org>
> Signed-off-by: Tatyana Brokhman <tlinder at codeaurora.org>
> ---
>  drivers/mtd/ubi/build.c |  32 +++++++++--
>  drivers/mtd/ubi/ubi.h   |   3 +
>  drivers/mtd/ubi/wl.c    | 143 +++++++++++++++++++++++++++++++++++++++++++++++-
>  3 files changed, 171 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
> index 34fe23a..a7464f8 100644
> --- a/drivers/mtd/ubi/build.c
> +++ b/drivers/mtd/ubi/build.c
> @@ -154,6 +154,9 @@ static struct device_attribute dev_dt_threshold =
>  static struct device_attribute dev_rd_threshold =
>         __ATTR(rd_threshold, (S_IWUSR | S_IRUGO), dev_attribute_show,
>                    dev_attribute_store);
> +static struct device_attribute dev_mtd_trigger_scan =
> +       __ATTR(peb_scan, (S_IWUSR | S_IRUGO),
> +               dev_attribute_show, dev_attribute_store);
>
>  /**
>   * ubi_volume_notify - send a volume change notification.
> @@ -395,6 +398,8 @@ static ssize_t dev_attribute_show(struct device *dev,
>                 ret = sprintf(buf, "%d\n", ubi->dt_threshold);
>         else if (attr == &dev_rd_threshold)
>                 ret = sprintf(buf, "%d\n", ubi->rd_threshold);
> +       else if (attr == &dev_mtd_trigger_scan)
> +               ret = sprintf(buf, "%d\n", ubi->scan_in_progress);
>         else
>                 ret = -EINVAL;
>
> @@ -406,7 +411,7 @@ static ssize_t dev_attribute_store(struct device *dev,
>                            struct device_attribute *attr,
>                            const char *buf, size_t count)
>  {
> -       int value;
> +       int value, ret;
>         struct ubi_device *ubi;
>
>         ubi = container_of(dev, struct ubi_device, dev);
> @@ -414,8 +419,11 @@ static ssize_t dev_attribute_store(struct device *dev,
>         if (!ubi)
>                 return -ENODEV;
>
> -       if (kstrtos32(buf, 10, &value))
> -               return -EINVAL;
> +       ret = count;
> +       if (kstrtos32(buf, 10, &value)) {
> +               ret = -EINVAL;
> +               goto out;
> +       }
>         /* Consider triggering full scan if threshods change */
>         else if (attr == &dev_dt_threshold) {
>                 if (value < UBI_MAX_DT_THRESHOLD)
> @@ -429,9 +437,21 @@ static ssize_t dev_attribute_store(struct device *dev,
>                 else
>                         pr_err("Max supported threshold value is %d",
>                                    UBI_MAX_READCOUNTER);
> +       } else if (attr == &dev_mtd_trigger_scan) {
> +               if (value != 1) {
> +                       pr_err("Invalid input. Echo 1 to start trigger");
> +                       goto out;
> +               }
> +               if (!ubi->lookuptbl) {
> +                       pr_err("lookuptbl is null");
> +                       goto out;
> +               }
> +               ret = ubi_wl_scan_all(ubi);
>         }
>
> -       return count;
> +out:
> +       ubi_put_device(ubi);
> +       return ret;
>  }
>
>  static void dev_release(struct device *dev)
> @@ -500,6 +520,9 @@ static int ubi_sysfs_init(struct ubi_device *ubi, int *ref)
>         if (err)
>                 return err;
>         err = device_create_file(&ubi->dev, &dev_rd_threshold);
> +       if (err)
> +               return err;
> +       err = device_create_file(&ubi->dev, &dev_mtd_trigger_scan);
>         return err;
>  }
>
> @@ -509,6 +532,7 @@ static int ubi_sysfs_init(struct ubi_device *ubi, int *ref)
>   */
>  static void ubi_sysfs_close(struct ubi_device *ubi)
>  {
> +       device_remove_file(&ubi->dev, &dev_mtd_trigger_scan);
>         device_remove_file(&ubi->dev, &dev_mtd_num);
>         device_remove_file(&ubi->dev, &dev_dt_threshold);
>         device_remove_file(&ubi->dev, &dev_rd_threshold);
> diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
> index ed04de2..1079517 100644
> --- a/drivers/mtd/ubi/ubi.h
> +++ b/drivers/mtd/ubi/ubi.h
> @@ -491,6 +491,7 @@ struct ubi_debug_info {
>   *                             for more info
>   * @dt_threshold: data retention threshold. See UBI_DT_THRESHOLD
>   *                             for more info
> + * @scan_in_progress: true if scanning of device PEBs is in progress
>   *
>   * @flash_size: underlying MTD device size (in bytes)
>   * @peb_count: count of physical eraseblocks on the MTD device
> @@ -595,6 +596,7 @@ struct ubi_device {
>         char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2];
>         int rd_threshold;
>         int dt_threshold;
> +       bool scan_in_progress;
>
>
>         /* I/O sub-system's stuff */
> @@ -873,6 +875,7 @@ 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);
>  int ubi_in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root);
> +int ubi_wl_scan_all(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 a5d754f..4edbb4c 100644
> --- a/drivers/mtd/ubi/wl.c
> +++ b/drivers/mtd/ubi/wl.c
> @@ -143,6 +143,8 @@ static int self_check_in_wl_tree(const struct ubi_device *ubi,
>                                  struct ubi_wl_entry *e, struct rb_root *root);
>  static int self_check_in_pq(const struct ubi_device *ubi,
>                             struct ubi_wl_entry *e);
> +static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
> +                         int vol_id, int lnum, int torture);
>
>  #ifdef CONFIG_MTD_UBI_FASTMAP
>  /**
> @@ -555,8 +557,11 @@ retry:
>  static void return_unused_pool_pebs(struct ubi_device *ubi,
>                                     struct ubi_fm_pool *pool)
>  {
> -       int i;
> +       int i, err;
>         struct ubi_wl_entry *e;
> +       struct timeval tv;
> +
> +       do_gettimeofday(&tv);
>
>         for (i = pool->used; i < pool->size; i++) {
>                 e = ubi->lookuptbl[pool->pebs[i]];
> @@ -566,8 +571,22 @@ static void return_unused_pool_pebs(struct ubi_device *ubi,
>                         self_check_in_wl_tree(ubi, e, &ubi->scrub);
>                         rb_erase(&e->u.rb, &ubi->scrub);
>                 }
> -               wl_tree_add(e, &ubi->free);
> -               ubi->free_count++;
> +               if (e->last_erase_time + UBI_DT_THRESHOLD <
> +                        (tv.tv_sec / NUM_SEC_IN_DAY)) {
> +                       spin_unlock(&ubi->wl_lock);
> +                       err = schedule_erase(ubi, e, UBI_UNKNOWN,
> +                                        UBI_UNKNOWN, 0);
> +                       spin_lock(&ubi->wl_lock);
> +                       if (err) {
> +                               ubi_err(
> +                               "Failed to schedule erase for PEB %d (err=%d)",
> +                                       e->pnum, err);
> +                               ubi_ro_mode(ubi);
> +                       }
> +               } else {
> +                       wl_tree_add(e, &ubi->free);
> +                       ubi->free_count++;
> +               }
>         }
>  }
>
> @@ -711,6 +730,124 @@ int ubi_wl_get_peb(struct ubi_device *ubi)
>  #endif
>
>  /**
> + * ubi_wl_scan_all - Scan all PEB's
> + * @ubi: UBI device description object
> + *
> + * This function scans all device PEBs in order to locate once
> + * need scrubbing; due to read disturb threashold or last erase
> + * timestamp.
> + *
> + * Return 0 in case of sucsess, (negative) error code otherwise
> + *
> + */
> +int ubi_wl_scan_all(struct ubi_device *ubi)
> +{
> +       struct timeval tv;
> +       struct rb_node *node;
> +       struct ubi_wl_entry *wl_e, *tmp;
> +       int used_cnt, free_cnt;
> +       int err;
> +
> +       do_gettimeofday(&tv);
> +       if (!ubi->lookuptbl) {
> +               ubi_err("lookuptbl is null");
> +               return -ENOENT;
> +       }
> +
> +       spin_lock(&ubi->wl_lock);
> +       if (ubi->scan_in_progress) {
> +               ubi_err("Scan already in progress, ignoring the trigger");
> +               err = -EPERM;
> +               goto out;
> +       }
> +       ubi->scan_in_progress = true;
> +
> +       ubi_msg("Scanning all PEBs for read-disturb/erasures");
> +       /* For PEBs in free list rc=0 */
> +       free_cnt = 0;
> +       node = rb_first(&ubi->free);
> +       while (node) {
> +               wl_e = rb_entry(node, struct ubi_wl_entry, u.rb);
> +               node = rb_next(node);
> +               if (wl_e->last_erase_time + UBI_DT_THRESHOLD <
> +                        (tv.tv_sec / NUM_SEC_IN_DAY)) {
> +                       if (self_check_in_wl_tree(ubi, wl_e, &ubi->free)) {
> +                               ubi_err("PEB %d moved from free tree",
> +                                       wl_e->pnum);
> +                               err = -EAGAIN;
> +                               goto out;
> +                       }
> +                       rb_erase(&wl_e->u.rb, &ubi->free);
> +                       ubi->free_count--;
> +                       spin_unlock(&ubi->wl_lock);
> +                       err = schedule_erase(ubi, wl_e, UBI_UNKNOWN,
> +                                        UBI_UNKNOWN, 0);
> +                       spin_lock(&ubi->wl_lock);
> +                       if (err) {
> +                               ubi_err(
> +                               "Failed to schedule erase for PEB %d (err=%d)",
> +                                       wl_e->pnum, err);
> +                               ubi_ro_mode(ubi);
> +                               goto out;
> +                       }
> +                       free_cnt++;
> +               }
> +       }
> +
> +       used_cnt = 0;
> +       node = rb_first(&ubi->used);
> +       while (node) {
> +               wl_e = rb_entry(node, struct ubi_wl_entry, u.rb);
> +               node = rb_next(node);
> +               if ((wl_e->rc >= UBI_RD_THRESHOLD) ||
> +                       (wl_e->last_erase_time +
> +                        UBI_DT_THRESHOLD < (tv.tv_sec / NUM_SEC_IN_DAY))) {
> +                       spin_unlock(&ubi->wl_lock);
> +                       err = ubi_wl_scrub_peb(ubi, wl_e->pnum);
> +                       if (err)
> +                               ubi_err(
> +                               "Failed to schedule scrub for PEB %d (err=%d)",
> +                                       wl_e->pnum, err);
> +                       else
> +                               used_cnt++;
> +                       spin_lock(&ubi->wl_lock);
> +               }
> +       }
> +
> +       /* Go over protection queue */
> +       list_for_each_entry_safe(wl_e, tmp, &ubi->pq[ubi->pq_head], u.list) {
> +               if ((wl_e->rc >= UBI_RD_THRESHOLD) ||
> +                       (wl_e->last_erase_time +
> +                        UBI_DT_THRESHOLD < (tv.tv_sec / NUM_SEC_IN_DAY))) {
> +                       spin_unlock(&ubi->wl_lock);
> +                       err = ubi_wl_scrub_peb(ubi, wl_e->pnum);
> +                       if (err)
> +                               ubi_err(
> +                               "Failed to schedule scrub for PEB %d (err=%d)",
> +                                       wl_e->pnum, err);
> +                       else
> +                               used_cnt++;
> +                       spin_lock(&ubi->wl_lock);
> +               }
> +       }
> +       spin_unlock(&ubi->wl_lock);
> +       ubi_msg("Scheduled %d for erasure", free_cnt);
> +       ubi_msg("Scehduled %d for scrubbing", used_cnt);
> +       err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL);
> +       if (err)
> +               ubi_err("Failed to flush ubi wq. err = %d", err);
> +       else
> +               ubi_msg("Flashed ubi wq");
> +
> +       spin_lock(&ubi->wl_lock);
> +out:
> +       ubi->scan_in_progress = false;
> +       spin_unlock(&ubi->wl_lock);
> +       ubi_msg("Scanning all PEBs completed. err = %d", err);
> +       return err;
> +}
> +
> +/**
>   * prot_queue_del - remove a physical eraseblock from the protection queue.
>   * @ubi: UBI device description object
>   * @pnum: the physical eraseblock to remove
> --
> 1.8.5.2
> --
> QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
> of Code Aurora Forum, hosted by The Linux Foundation
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/



-- 
Thanks,
//richard



More information about the linux-mtd mailing list