Loading drivers/mtd/ubi/attach.c +14 −2 Original line number Diff line number Diff line Loading @@ -834,9 +834,21 @@ struct ubi_ainf_peb *ubi_early_get_peb(struct ubi_device *ubi, int err = 0; struct ubi_ainf_peb *aeb, *tmp_aeb; if (!list_empty(&ai->free)) { aeb = list_entry(ai->free.next, struct ubi_ainf_peb, u.list); list_for_each_entry_safe(aeb, tmp_aeb, &ai->free, u.list) { list_del(&aeb->u.list); if (aeb->ec == UBI_UNKNOWN) { ubi_err(ubi, "PEB %d in freelist has unknown EC", aeb->pnum); aeb->ec = ai->mean_ec; } err = early_erase_peb(ubi, ai, aeb->pnum, aeb->ec+1); if (err) { ubi_err(ubi, "Erase failed for PEB %d in freelist", aeb->pnum); list_add(&aeb->u.list, &ai->erase); continue; } aeb->ec += 1; dbg_bld("return free PEB %d, EC %d", aeb->pnum, aeb->ec); return aeb; } Loading drivers/mtd/ubi/build.c +65 −0 Original line number Diff line number Diff line Loading @@ -125,6 +125,9 @@ struct class ubi_class = { static ssize_t dev_attribute_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t dev_attribute_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); /* UBI device attributes (correspond to files in '/<sysfs>/class/ubi/ubiX') */ static struct device_attribute dev_eraseblock_size = Loading @@ -151,6 +154,13 @@ static struct device_attribute dev_mtd_num = __ATTR(mtd_num, S_IRUGO, dev_attribute_show, NULL); static struct device_attribute dev_ro_mode = __ATTR(ro_mode, S_IRUGO, dev_attribute_show, NULL); static struct device_attribute dev_mtd_trigger_scrub = __ATTR(scrub_all, 0644, dev_attribute_show, dev_attribute_store); static struct device_attribute dev_mtd_max_scrub_sqnum = __ATTR(scrub_max_sqnum, 0444, dev_attribute_show, NULL); static struct device_attribute dev_mtd_min_scrub_sqnum = __ATTR(scrub_min_sqnum, 0444, dev_attribute_show, NULL); /** * ubi_volume_notify - send a volume change notification. Loading Loading @@ -343,6 +353,17 @@ int ubi_major2num(int major) return ubi_num; } static unsigned long long get_max_sqnum(struct ubi_device *ubi) { unsigned long long max_sqnum; spin_lock(&ubi->ltree_lock); max_sqnum = ubi->global_sqnum - 1; spin_unlock(&ubi->ltree_lock); return max_sqnum; } /* "Show" method for files in '/<sysfs>/class/ubi/ubiX/' */ static ssize_t dev_attribute_show(struct device *dev, struct device_attribute *attr, char *buf) Loading Loading @@ -389,6 +410,15 @@ static ssize_t dev_attribute_show(struct device *dev, ret = sprintf(buf, "%d\n", ubi->mtd->index); else if (attr == &dev_ro_mode) ret = sprintf(buf, "%d\n", ubi->ro_mode); else if (attr == &dev_mtd_trigger_scrub) ret = snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&ubi->scrub_work_count)); else if (attr == &dev_mtd_max_scrub_sqnum) ret = snprintf(buf, PAGE_SIZE, "%llu\n", get_max_sqnum(ubi)); else if (attr == &dev_mtd_min_scrub_sqnum) ret = snprintf(buf, PAGE_SIZE, "%llu\n", ubi_wl_scrub_get_min_sqnum(ubi)); else ret = -EINVAL; Loading @@ -409,10 +439,45 @@ static struct attribute *ubi_dev_attrs[] = { &dev_bgt_enabled.attr, &dev_mtd_num.attr, &dev_ro_mode.attr, &dev_mtd_trigger_scrub.attr, &dev_mtd_max_scrub_sqnum.attr, &dev_mtd_min_scrub_sqnum.attr, NULL }; ATTRIBUTE_GROUPS(ubi_dev); static ssize_t dev_attribute_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int ret = count; struct ubi_device *ubi; unsigned long long scrub_sqnum; ubi = container_of(dev, struct ubi_device, dev); ubi = ubi_get_device(ubi->ubi_num); if (!ubi) return -ENODEV; if (attr == &dev_mtd_trigger_scrub) { if (kstrtoull(buf, 10, &scrub_sqnum)) { ret = -EINVAL; goto out; } if (!ubi->lookuptbl) { pr_err("lookuptbl is null"); goto out; } ret = ubi_wl_scrub_all(ubi, scrub_sqnum); if (ret == 0) ret = count; } out: ubi_put_device(ubi); return ret; } static void dev_release(struct device *dev) { struct ubi_device *ubi = container_of(dev, struct ubi_device, dev); Loading drivers/mtd/ubi/io.c +12 −0 Original line number Diff line number Diff line Loading @@ -1105,6 +1105,16 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, dbg_io("write VID header to PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); /* * Re-erase the PEB before using it. This should minimize any issues * from decay of charge in this block. */ if (ubi->wl_is_inited) { err = ubi_wl_re_erase_peb(ubi, pnum); if (err) return err; } err = self_check_peb_ec_hdr(ubi, pnum); if (err) return err; Loading @@ -1123,6 +1133,8 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, ubi->vid_hdr_alsize); if (!err && ubi->wl_is_inited) ubi_wl_update_peb_sqnum(ubi, pnum, vid_hdr); return err; } Loading drivers/mtd/ubi/ubi.h +13 −0 Original line number Diff line number Diff line Loading @@ -183,6 +183,8 @@ struct ubi_vid_io_buf { * @u.list: link in the protection queue * @ec: erase counter * @pnum: physical eraseblock number * @tagged_scrub_all: if the entry is tagged for scrub all * @sqnum: The sequence number of the vol header. * * This data structure is used in the WL sub-system. Each physical eraseblock * has a corresponding &struct wl_entry object which may be kept in different Loading @@ -195,6 +197,8 @@ struct ubi_wl_entry { } u; int ec; int pnum; unsigned int tagged_scrub_all:1; unsigned long long sqnum; }; /** Loading Loading @@ -624,6 +628,9 @@ struct ubi_device { struct task_struct *bgt_thread; int thread_enabled; char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; bool scrub_in_progress; atomic_t scrub_work_count; int wl_is_inited; /* I/O sub-system's stuff */ long long flash_size; Loading Loading @@ -919,6 +926,12 @@ 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); ssize_t ubi_wl_scrub_all(struct ubi_device *ubi, unsigned long long scrub_sqnum); void ubi_wl_update_peb_sqnum(struct ubi_device *ubi, int pnum, struct ubi_vid_hdr *vid_hdr); unsigned long long ubi_wl_scrub_get_min_sqnum(struct ubi_device *ubi); int ubi_wl_re_erase_peb(struct ubi_device *ubi, int pnum); /* io.c */ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, Loading drivers/mtd/ubi/wl.c +262 −3 Original line number Diff line number Diff line Loading @@ -102,6 +102,7 @@ #include <linux/crc32.h> #include <linux/freezer.h> #include <linux/kthread.h> #include <linux/delay.h> #include "ubi.h" #include "wl.h" Loading Loading @@ -487,6 +488,42 @@ static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, return err; } int ubi_wl_re_erase_peb(struct ubi_device *ubi, int pnum) { int err; struct ubi_wl_entry *e; struct ubi_ec_hdr *ec_hdr; spin_lock(&ubi->wl_lock); e = ubi->lookuptbl[pnum]; spin_unlock(&ubi->wl_lock); dbg_wl("Re-erase PEB %d, EC %u", e->pnum, e->ec); err = self_check_ec(ubi, e->pnum, e->ec); if (err) return -EINVAL; ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS); if (!ec_hdr) return -ENOMEM; err = ubi_io_sync_erase(ubi, e->pnum, 0); if (err < 0) goto out_free; dbg_wl("re-erased PEB %d, EC %u", e->pnum, e->ec); ec_hdr->ec = cpu_to_be64(e->ec); err = ubi_io_write_ec_hdr(ubi, e->pnum, ec_hdr); if (err) goto out_free; out_free: kfree(ec_hdr); return err; } /** * serve_prot_queue - check if it is time to stop protecting PEBs. * @ubi: UBI device description object Loading Loading @@ -862,9 +899,20 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, } /* The PEB has been successfully moved */ if (scrubbing) ubi_msg(ubi, "scrubbed PEB %d (LEB %d:%d), data moved to PEB %d", if (scrubbing) { spin_lock(&ubi->wl_lock); if (e1->tagged_scrub_all) { WARN_ON(atomic_read(&ubi->scrub_work_count) <= 0); atomic_dec(&ubi->scrub_work_count); e1->tagged_scrub_all = 0; e2->tagged_scrub_all = 0; } else { ubi_msg(ubi, "scrubbed PEB %d (LEB %d:%d),data moved to PEB %d", e1->pnum, vol_id, lnum, e2->pnum); } spin_unlock(&ubi->wl_lock); } ubi_free_vid_buf(vidb); spin_lock(&ubi->wl_lock); Loading Loading @@ -1226,6 +1274,7 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum, retry: spin_lock(&ubi->wl_lock); e = ubi->lookuptbl[pnum]; e->sqnum = UBI_UNKNOWN; if (e == ubi->move_from) { /* * User is putting the physical eraseblock which was selected to Loading Loading @@ -1262,6 +1311,20 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum, } else if (in_wl_tree(e, &ubi->scrub)) { self_check_in_wl_tree(ubi, e, &ubi->scrub); rb_erase(&e->u.rb, &ubi->scrub); /* * Since this PEB has been put we dont need to worry * about it anymore */ if (e->tagged_scrub_all) { int wrk_count; wrk_count = atomic_read(&ubi->scrub_work_count); WARN_ON(wrk_count <= 0); atomic_dec(&ubi->scrub_work_count); e->tagged_scrub_all = 0; } } else if (in_wl_tree(e, &ubi->erroneous)) { self_check_in_wl_tree(ubi, e, &ubi->erroneous); rb_erase(&e->u.rb, &ubi->erroneous); Loading Loading @@ -1293,6 +1356,197 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum, return err; } /** * ubi_wl_scrub_get_min_sqnum - Return the minimum sqnum of the used/pq/scrub. * @ubi: UBI device description object * * This function returns the minimum sqnum of the PEB that are currently in use. * * Return the min sqnum if there are any used PEB's otherwise return ~(0) * */ unsigned long long ubi_wl_scrub_get_min_sqnum(struct ubi_device *ubi) { int i; struct ubi_wl_entry *e, *tmp; struct rb_node *node; unsigned long long min_sqnum = ~0; spin_lock(&ubi->wl_lock); /* Go through the pq list */ for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) { list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) { if (e->sqnum < min_sqnum) min_sqnum = e->sqnum; } } /* Go through used PEB tree */ for (node = rb_first(&ubi->used); node; node = rb_next(node)) { e = rb_entry(node, struct ubi_wl_entry, u.rb); self_check_in_wl_tree(ubi, e, &ubi->used); if (e->sqnum < min_sqnum) min_sqnum = e->sqnum; } /* Go through scrub PEB tree */ for (node = rb_first(&ubi->scrub); node; node = rb_next(node)) { e = rb_entry(node, struct ubi_wl_entry, u.rb); self_check_in_wl_tree(ubi, e, &ubi->scrub); if (e->sqnum < min_sqnum) min_sqnum = e->sqnum; } spin_unlock(&ubi->wl_lock); return min_sqnum; } /** * ubi_wl_update_peb_sqnum - Update the vol hdr sqnum of the PEB. * @pnum: The PEB number. * @vid_hdr: The vol hdr being written to the PEB. * */ void ubi_wl_update_peb_sqnum(struct ubi_device *ubi, int pnum, struct ubi_vid_hdr *vid_hdr) { struct ubi_wl_entry *e; spin_lock(&ubi->wl_lock); e = ubi->lookuptbl[pnum]; e->sqnum = be64_to_cpu(vid_hdr->sqnum); e->tagged_scrub_all = 0; spin_unlock(&ubi->wl_lock); } static int is_ubi_readonly(struct ubi_device *ubi) { int is_readonly = 0; spin_lock(&ubi->wl_lock); if (ubi->ro_mode || !ubi->thread_enabled || ubi_dbg_is_bgt_disabled(ubi)) is_readonly = 1; spin_unlock(&ubi->wl_lock); return is_readonly; } /** * ubi_wl_scan_all - Scan all PEB's * @ubi: UBI device description object * @scrub_sqnum: The max seqnum of the PEB to scrub from the used/pq lists * * This function schedules all device PEBs for scrubbing if the sqnum of the * vol hdr is less than the sqnum in the trigger. * * Return 0 in case of success, (negative) error code otherwise * */ ssize_t ubi_wl_scrub_all(struct ubi_device *ubi, unsigned long long scrub_sqnum) { struct rb_node *node; struct ubi_wl_entry *e, *tmp; int scrub_count = 0; int total_scrub_count = 0; int err, i; if (!ubi->lookuptbl) { ubi_err(ubi, "lookuptbl is null"); return -ENOENT; } if (is_ubi_readonly(ubi)) { ubi_err(ubi, "Cannot *Initiate* scrub:background thread disabled or readonly!"); return -EROFS; } /* Wait for all currently running work to be done! */ down_write(&ubi->work_sem); spin_lock(&ubi->wl_lock); ubi_msg(ubi, "Scrub triggered sqnum = %llu!", scrub_sqnum); if (ubi->scrub_in_progress) { ubi_err(ubi, "Scrub already in progress, ignoring the trigger"); spin_unlock(&ubi->wl_lock); up_write(&ubi->work_sem); /* Allow new work to start. */ return -EBUSY; } ubi->scrub_in_progress = true; /* Go through scrub PEB tree and count pending */ for (node = rb_first(&ubi->scrub); node; node = rb_next(node)) { e = rb_entry(node, struct ubi_wl_entry, u.rb); self_check_in_wl_tree(ubi, e, &ubi->scrub); e->tagged_scrub_all = 1; total_scrub_count++; } /* Move all used pebs to scrub tree */ node = rb_first(&ubi->used); while (node != NULL) { e = rb_entry(node, struct ubi_wl_entry, u.rb); self_check_in_wl_tree(ubi, e, &ubi->used); node = rb_next(node); if (e->sqnum > scrub_sqnum) continue; rb_erase(&e->u.rb, &ubi->used); wl_tree_add(e, &ubi->scrub); e->tagged_scrub_all = 1; scrub_count++; total_scrub_count++; } /* Move all protected pebs to scrub tree */ for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) { list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) { if (e->sqnum > scrub_sqnum) continue; list_del(&e->u.list); wl_tree_add(e, &ubi->scrub); e->tagged_scrub_all = 1; scrub_count++; total_scrub_count++; } } atomic_set(&ubi->scrub_work_count, total_scrub_count); spin_unlock(&ubi->wl_lock); up_write(&ubi->work_sem); /* Allow new work to start. */ /* * Technically scrubbing is the same as wear-leveling, so it is done * by the WL worker. */ err = ensure_wear_leveling(ubi, 0); if (err) { ubi_err(ubi, "Failed to start the WL worker err =%d", err); return err; } ubi_msg(ubi, "Scheduled %d PEB's for scrubbing!", scrub_count); ubi_msg(ubi, "Total PEB's for scrub = %d", total_scrub_count); /* Wait for scrub to finish */ while (atomic_read(&ubi->scrub_work_count) > 0) { /* Poll every second to check if the scrub work is done */ msleep(1000); if (is_ubi_readonly(ubi)) { ubi_err(ubi, "Cannot *Complete* scrub:background thread disabled or readonly!"); return -EROFS; } wake_up_process(ubi->bgt_thread); } spin_lock(&ubi->wl_lock); ubi->scrub_in_progress = false; spin_unlock(&ubi->wl_lock); ubi_msg(ubi, "Done scrubbing %d PEB's!", scrub_count); return 0; } /** * ubi_wl_scrub_peb - schedule a physical eraseblock for scrubbing. * @ubi: UBI device description object Loading Loading @@ -1622,6 +1876,8 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) e->pnum = aeb->pnum; e->ec = aeb->ec; e->tagged_scrub_all = 0; e->sqnum = aeb->sqnum; ubi_assert(e->ec >= 0); wl_tree_add(e, &ubi->free); Loading @@ -1644,6 +1900,8 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) e->pnum = aeb->pnum; e->ec = aeb->ec; e->tagged_scrub_all = 0; e->sqnum = aeb->sqnum; ubi->lookuptbl[e->pnum] = e; if (!aeb->scrub) { Loading Loading @@ -1724,6 +1982,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) if (err) goto out_free; ubi->wl_is_inited = 1; return 0; out_free: Loading Loading
drivers/mtd/ubi/attach.c +14 −2 Original line number Diff line number Diff line Loading @@ -834,9 +834,21 @@ struct ubi_ainf_peb *ubi_early_get_peb(struct ubi_device *ubi, int err = 0; struct ubi_ainf_peb *aeb, *tmp_aeb; if (!list_empty(&ai->free)) { aeb = list_entry(ai->free.next, struct ubi_ainf_peb, u.list); list_for_each_entry_safe(aeb, tmp_aeb, &ai->free, u.list) { list_del(&aeb->u.list); if (aeb->ec == UBI_UNKNOWN) { ubi_err(ubi, "PEB %d in freelist has unknown EC", aeb->pnum); aeb->ec = ai->mean_ec; } err = early_erase_peb(ubi, ai, aeb->pnum, aeb->ec+1); if (err) { ubi_err(ubi, "Erase failed for PEB %d in freelist", aeb->pnum); list_add(&aeb->u.list, &ai->erase); continue; } aeb->ec += 1; dbg_bld("return free PEB %d, EC %d", aeb->pnum, aeb->ec); return aeb; } Loading
drivers/mtd/ubi/build.c +65 −0 Original line number Diff line number Diff line Loading @@ -125,6 +125,9 @@ struct class ubi_class = { static ssize_t dev_attribute_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t dev_attribute_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); /* UBI device attributes (correspond to files in '/<sysfs>/class/ubi/ubiX') */ static struct device_attribute dev_eraseblock_size = Loading @@ -151,6 +154,13 @@ static struct device_attribute dev_mtd_num = __ATTR(mtd_num, S_IRUGO, dev_attribute_show, NULL); static struct device_attribute dev_ro_mode = __ATTR(ro_mode, S_IRUGO, dev_attribute_show, NULL); static struct device_attribute dev_mtd_trigger_scrub = __ATTR(scrub_all, 0644, dev_attribute_show, dev_attribute_store); static struct device_attribute dev_mtd_max_scrub_sqnum = __ATTR(scrub_max_sqnum, 0444, dev_attribute_show, NULL); static struct device_attribute dev_mtd_min_scrub_sqnum = __ATTR(scrub_min_sqnum, 0444, dev_attribute_show, NULL); /** * ubi_volume_notify - send a volume change notification. Loading Loading @@ -343,6 +353,17 @@ int ubi_major2num(int major) return ubi_num; } static unsigned long long get_max_sqnum(struct ubi_device *ubi) { unsigned long long max_sqnum; spin_lock(&ubi->ltree_lock); max_sqnum = ubi->global_sqnum - 1; spin_unlock(&ubi->ltree_lock); return max_sqnum; } /* "Show" method for files in '/<sysfs>/class/ubi/ubiX/' */ static ssize_t dev_attribute_show(struct device *dev, struct device_attribute *attr, char *buf) Loading Loading @@ -389,6 +410,15 @@ static ssize_t dev_attribute_show(struct device *dev, ret = sprintf(buf, "%d\n", ubi->mtd->index); else if (attr == &dev_ro_mode) ret = sprintf(buf, "%d\n", ubi->ro_mode); else if (attr == &dev_mtd_trigger_scrub) ret = snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&ubi->scrub_work_count)); else if (attr == &dev_mtd_max_scrub_sqnum) ret = snprintf(buf, PAGE_SIZE, "%llu\n", get_max_sqnum(ubi)); else if (attr == &dev_mtd_min_scrub_sqnum) ret = snprintf(buf, PAGE_SIZE, "%llu\n", ubi_wl_scrub_get_min_sqnum(ubi)); else ret = -EINVAL; Loading @@ -409,10 +439,45 @@ static struct attribute *ubi_dev_attrs[] = { &dev_bgt_enabled.attr, &dev_mtd_num.attr, &dev_ro_mode.attr, &dev_mtd_trigger_scrub.attr, &dev_mtd_max_scrub_sqnum.attr, &dev_mtd_min_scrub_sqnum.attr, NULL }; ATTRIBUTE_GROUPS(ubi_dev); static ssize_t dev_attribute_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int ret = count; struct ubi_device *ubi; unsigned long long scrub_sqnum; ubi = container_of(dev, struct ubi_device, dev); ubi = ubi_get_device(ubi->ubi_num); if (!ubi) return -ENODEV; if (attr == &dev_mtd_trigger_scrub) { if (kstrtoull(buf, 10, &scrub_sqnum)) { ret = -EINVAL; goto out; } if (!ubi->lookuptbl) { pr_err("lookuptbl is null"); goto out; } ret = ubi_wl_scrub_all(ubi, scrub_sqnum); if (ret == 0) ret = count; } out: ubi_put_device(ubi); return ret; } static void dev_release(struct device *dev) { struct ubi_device *ubi = container_of(dev, struct ubi_device, dev); Loading
drivers/mtd/ubi/io.c +12 −0 Original line number Diff line number Diff line Loading @@ -1105,6 +1105,16 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, dbg_io("write VID header to PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); /* * Re-erase the PEB before using it. This should minimize any issues * from decay of charge in this block. */ if (ubi->wl_is_inited) { err = ubi_wl_re_erase_peb(ubi, pnum); if (err) return err; } err = self_check_peb_ec_hdr(ubi, pnum); if (err) return err; Loading @@ -1123,6 +1133,8 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, ubi->vid_hdr_alsize); if (!err && ubi->wl_is_inited) ubi_wl_update_peb_sqnum(ubi, pnum, vid_hdr); return err; } Loading
drivers/mtd/ubi/ubi.h +13 −0 Original line number Diff line number Diff line Loading @@ -183,6 +183,8 @@ struct ubi_vid_io_buf { * @u.list: link in the protection queue * @ec: erase counter * @pnum: physical eraseblock number * @tagged_scrub_all: if the entry is tagged for scrub all * @sqnum: The sequence number of the vol header. * * This data structure is used in the WL sub-system. Each physical eraseblock * has a corresponding &struct wl_entry object which may be kept in different Loading @@ -195,6 +197,8 @@ struct ubi_wl_entry { } u; int ec; int pnum; unsigned int tagged_scrub_all:1; unsigned long long sqnum; }; /** Loading Loading @@ -624,6 +628,9 @@ struct ubi_device { struct task_struct *bgt_thread; int thread_enabled; char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; bool scrub_in_progress; atomic_t scrub_work_count; int wl_is_inited; /* I/O sub-system's stuff */ long long flash_size; Loading Loading @@ -919,6 +926,12 @@ 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); ssize_t ubi_wl_scrub_all(struct ubi_device *ubi, unsigned long long scrub_sqnum); void ubi_wl_update_peb_sqnum(struct ubi_device *ubi, int pnum, struct ubi_vid_hdr *vid_hdr); unsigned long long ubi_wl_scrub_get_min_sqnum(struct ubi_device *ubi); int ubi_wl_re_erase_peb(struct ubi_device *ubi, int pnum); /* io.c */ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, Loading
drivers/mtd/ubi/wl.c +262 −3 Original line number Diff line number Diff line Loading @@ -102,6 +102,7 @@ #include <linux/crc32.h> #include <linux/freezer.h> #include <linux/kthread.h> #include <linux/delay.h> #include "ubi.h" #include "wl.h" Loading Loading @@ -487,6 +488,42 @@ static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, return err; } int ubi_wl_re_erase_peb(struct ubi_device *ubi, int pnum) { int err; struct ubi_wl_entry *e; struct ubi_ec_hdr *ec_hdr; spin_lock(&ubi->wl_lock); e = ubi->lookuptbl[pnum]; spin_unlock(&ubi->wl_lock); dbg_wl("Re-erase PEB %d, EC %u", e->pnum, e->ec); err = self_check_ec(ubi, e->pnum, e->ec); if (err) return -EINVAL; ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS); if (!ec_hdr) return -ENOMEM; err = ubi_io_sync_erase(ubi, e->pnum, 0); if (err < 0) goto out_free; dbg_wl("re-erased PEB %d, EC %u", e->pnum, e->ec); ec_hdr->ec = cpu_to_be64(e->ec); err = ubi_io_write_ec_hdr(ubi, e->pnum, ec_hdr); if (err) goto out_free; out_free: kfree(ec_hdr); return err; } /** * serve_prot_queue - check if it is time to stop protecting PEBs. * @ubi: UBI device description object Loading Loading @@ -862,9 +899,20 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, } /* The PEB has been successfully moved */ if (scrubbing) ubi_msg(ubi, "scrubbed PEB %d (LEB %d:%d), data moved to PEB %d", if (scrubbing) { spin_lock(&ubi->wl_lock); if (e1->tagged_scrub_all) { WARN_ON(atomic_read(&ubi->scrub_work_count) <= 0); atomic_dec(&ubi->scrub_work_count); e1->tagged_scrub_all = 0; e2->tagged_scrub_all = 0; } else { ubi_msg(ubi, "scrubbed PEB %d (LEB %d:%d),data moved to PEB %d", e1->pnum, vol_id, lnum, e2->pnum); } spin_unlock(&ubi->wl_lock); } ubi_free_vid_buf(vidb); spin_lock(&ubi->wl_lock); Loading Loading @@ -1226,6 +1274,7 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum, retry: spin_lock(&ubi->wl_lock); e = ubi->lookuptbl[pnum]; e->sqnum = UBI_UNKNOWN; if (e == ubi->move_from) { /* * User is putting the physical eraseblock which was selected to Loading Loading @@ -1262,6 +1311,20 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum, } else if (in_wl_tree(e, &ubi->scrub)) { self_check_in_wl_tree(ubi, e, &ubi->scrub); rb_erase(&e->u.rb, &ubi->scrub); /* * Since this PEB has been put we dont need to worry * about it anymore */ if (e->tagged_scrub_all) { int wrk_count; wrk_count = atomic_read(&ubi->scrub_work_count); WARN_ON(wrk_count <= 0); atomic_dec(&ubi->scrub_work_count); e->tagged_scrub_all = 0; } } else if (in_wl_tree(e, &ubi->erroneous)) { self_check_in_wl_tree(ubi, e, &ubi->erroneous); rb_erase(&e->u.rb, &ubi->erroneous); Loading Loading @@ -1293,6 +1356,197 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum, return err; } /** * ubi_wl_scrub_get_min_sqnum - Return the minimum sqnum of the used/pq/scrub. * @ubi: UBI device description object * * This function returns the minimum sqnum of the PEB that are currently in use. * * Return the min sqnum if there are any used PEB's otherwise return ~(0) * */ unsigned long long ubi_wl_scrub_get_min_sqnum(struct ubi_device *ubi) { int i; struct ubi_wl_entry *e, *tmp; struct rb_node *node; unsigned long long min_sqnum = ~0; spin_lock(&ubi->wl_lock); /* Go through the pq list */ for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) { list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) { if (e->sqnum < min_sqnum) min_sqnum = e->sqnum; } } /* Go through used PEB tree */ for (node = rb_first(&ubi->used); node; node = rb_next(node)) { e = rb_entry(node, struct ubi_wl_entry, u.rb); self_check_in_wl_tree(ubi, e, &ubi->used); if (e->sqnum < min_sqnum) min_sqnum = e->sqnum; } /* Go through scrub PEB tree */ for (node = rb_first(&ubi->scrub); node; node = rb_next(node)) { e = rb_entry(node, struct ubi_wl_entry, u.rb); self_check_in_wl_tree(ubi, e, &ubi->scrub); if (e->sqnum < min_sqnum) min_sqnum = e->sqnum; } spin_unlock(&ubi->wl_lock); return min_sqnum; } /** * ubi_wl_update_peb_sqnum - Update the vol hdr sqnum of the PEB. * @pnum: The PEB number. * @vid_hdr: The vol hdr being written to the PEB. * */ void ubi_wl_update_peb_sqnum(struct ubi_device *ubi, int pnum, struct ubi_vid_hdr *vid_hdr) { struct ubi_wl_entry *e; spin_lock(&ubi->wl_lock); e = ubi->lookuptbl[pnum]; e->sqnum = be64_to_cpu(vid_hdr->sqnum); e->tagged_scrub_all = 0; spin_unlock(&ubi->wl_lock); } static int is_ubi_readonly(struct ubi_device *ubi) { int is_readonly = 0; spin_lock(&ubi->wl_lock); if (ubi->ro_mode || !ubi->thread_enabled || ubi_dbg_is_bgt_disabled(ubi)) is_readonly = 1; spin_unlock(&ubi->wl_lock); return is_readonly; } /** * ubi_wl_scan_all - Scan all PEB's * @ubi: UBI device description object * @scrub_sqnum: The max seqnum of the PEB to scrub from the used/pq lists * * This function schedules all device PEBs for scrubbing if the sqnum of the * vol hdr is less than the sqnum in the trigger. * * Return 0 in case of success, (negative) error code otherwise * */ ssize_t ubi_wl_scrub_all(struct ubi_device *ubi, unsigned long long scrub_sqnum) { struct rb_node *node; struct ubi_wl_entry *e, *tmp; int scrub_count = 0; int total_scrub_count = 0; int err, i; if (!ubi->lookuptbl) { ubi_err(ubi, "lookuptbl is null"); return -ENOENT; } if (is_ubi_readonly(ubi)) { ubi_err(ubi, "Cannot *Initiate* scrub:background thread disabled or readonly!"); return -EROFS; } /* Wait for all currently running work to be done! */ down_write(&ubi->work_sem); spin_lock(&ubi->wl_lock); ubi_msg(ubi, "Scrub triggered sqnum = %llu!", scrub_sqnum); if (ubi->scrub_in_progress) { ubi_err(ubi, "Scrub already in progress, ignoring the trigger"); spin_unlock(&ubi->wl_lock); up_write(&ubi->work_sem); /* Allow new work to start. */ return -EBUSY; } ubi->scrub_in_progress = true; /* Go through scrub PEB tree and count pending */ for (node = rb_first(&ubi->scrub); node; node = rb_next(node)) { e = rb_entry(node, struct ubi_wl_entry, u.rb); self_check_in_wl_tree(ubi, e, &ubi->scrub); e->tagged_scrub_all = 1; total_scrub_count++; } /* Move all used pebs to scrub tree */ node = rb_first(&ubi->used); while (node != NULL) { e = rb_entry(node, struct ubi_wl_entry, u.rb); self_check_in_wl_tree(ubi, e, &ubi->used); node = rb_next(node); if (e->sqnum > scrub_sqnum) continue; rb_erase(&e->u.rb, &ubi->used); wl_tree_add(e, &ubi->scrub); e->tagged_scrub_all = 1; scrub_count++; total_scrub_count++; } /* Move all protected pebs to scrub tree */ for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) { list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) { if (e->sqnum > scrub_sqnum) continue; list_del(&e->u.list); wl_tree_add(e, &ubi->scrub); e->tagged_scrub_all = 1; scrub_count++; total_scrub_count++; } } atomic_set(&ubi->scrub_work_count, total_scrub_count); spin_unlock(&ubi->wl_lock); up_write(&ubi->work_sem); /* Allow new work to start. */ /* * Technically scrubbing is the same as wear-leveling, so it is done * by the WL worker. */ err = ensure_wear_leveling(ubi, 0); if (err) { ubi_err(ubi, "Failed to start the WL worker err =%d", err); return err; } ubi_msg(ubi, "Scheduled %d PEB's for scrubbing!", scrub_count); ubi_msg(ubi, "Total PEB's for scrub = %d", total_scrub_count); /* Wait for scrub to finish */ while (atomic_read(&ubi->scrub_work_count) > 0) { /* Poll every second to check if the scrub work is done */ msleep(1000); if (is_ubi_readonly(ubi)) { ubi_err(ubi, "Cannot *Complete* scrub:background thread disabled or readonly!"); return -EROFS; } wake_up_process(ubi->bgt_thread); } spin_lock(&ubi->wl_lock); ubi->scrub_in_progress = false; spin_unlock(&ubi->wl_lock); ubi_msg(ubi, "Done scrubbing %d PEB's!", scrub_count); return 0; } /** * ubi_wl_scrub_peb - schedule a physical eraseblock for scrubbing. * @ubi: UBI device description object Loading Loading @@ -1622,6 +1876,8 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) e->pnum = aeb->pnum; e->ec = aeb->ec; e->tagged_scrub_all = 0; e->sqnum = aeb->sqnum; ubi_assert(e->ec >= 0); wl_tree_add(e, &ubi->free); Loading @@ -1644,6 +1900,8 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) e->pnum = aeb->pnum; e->ec = aeb->ec; e->tagged_scrub_all = 0; e->sqnum = aeb->sqnum; ubi->lookuptbl[e->pnum] = e; if (!aeb->scrub) { Loading Loading @@ -1724,6 +1982,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) if (err) goto out_free; ubi->wl_is_inited = 1; return 0; out_free: Loading