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) Signed-off-by: Pratibhasagar V <pratibha@xxxxxxxxxxxxxx> Signed-off-by: Tatyana Brokhman <tlinder@xxxxxxxxxxxxxx> --- 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 -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html