Message ID | 1411886287-8947-1-git-send-email-tlinder@codeaurora.org (mailing list archive) |
---|---|
State | Not Applicable, archived |
Headers | show |
Tanya, On Sun, Sep 28, 2014 at 8:38 AM, Tanya Brokhman <tlinder@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@codeaurora.org> > Signed-off-by: Tatyana Brokhman <tlinder@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/
On 9/28/2014 11:42 AM, Richard Weinberger wrote: > Tanya, > > On Sun, Sep 28, 2014 at 8:38 AM, Tanya Brokhman <tlinder@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. :) oh, I'm sorry, missed that (its new :) ) this patch was tested thoroughly and working. I would appreciate if you could review this patch and perhaps we could improve it if required.
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