Merge "ubi: Add scrub_all support to UBI"

This commit is contained in:
qctecmdr 2022-01-10 09:43:09 -08:00 • committed by Gerrit - the friendly Code Review server
commit d70a1a1959
7 changed files with 422 additions and 38 deletions

View file

@ -821,9 +821,22 @@ 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;
}

View file

@ -121,7 +121,7 @@ static int __init ubiblock_set_param(const char *val,
return -EINVAL;
}
strcpy(buf, val);
strlcpy(buf, val, sizeof(buf));
/* Get rid of the final newline */
if (buf[len - 1] == '\n')
@ -141,12 +141,12 @@ static int __init ubiblock_set_param(const char *val,
ret = kstrtoint(tokens[1], 10, &param->vol_id);
if (ret < 0) {
param->vol_id = -1;
strcpy(param->name, tokens[1]);
strlcpy(param->name, tokens[1], UBIBLOCK_PARAM_LEN+1);
}
} else {
/* One parameter: must be device path */
strcpy(param->name, tokens[0]);
strlcpy(param->name, tokens[0], UBIBLOCK_PARAM_LEN+1);
param->ubi_num = -1;
param->vol_id = -1;
}
@ -412,7 +412,8 @@ int ubiblock_create(struct ubi_volume_info *vi)
goto out_put_disk;
}
gd->private_data = dev;
sprintf(gd->disk_name, "ubiblock%d_%d", dev->ubi_num, dev->vol_id);
scnprintf(gd->disk_name, DISK_NAME_LEN, "ubiblock%d_%d",
dev->ubi_num, dev->vol_id);
set_capacity(gd, disk_capacity);
dev->gd = gd;

View file

@ -95,7 +95,7 @@ static DEFINE_SPINLOCK(ubi_devices_lock);
static ssize_t version_show(struct class *class, struct class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", UBI_VERSION);
return scnprintf(buf, sizeof(int), "%d\n", UBI_VERSION);
}
static CLASS_ATTR_RO(version);
@ -114,6 +114,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 =
@ -140,6 +143,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.
@ -332,6 +342,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)
@ -355,29 +376,50 @@ static ssize_t dev_attribute_show(struct device *dev,
return -ENODEV;
if (attr == &dev_eraseblock_size)
ret = sprintf(buf, "%d\n", ubi->leb_size);
ret = scnprintf(buf, sizeof(int), "%d\n",
ubi->leb_size);
else if (attr == &dev_avail_eraseblocks)
ret = sprintf(buf, "%d\n", ubi->avail_pebs);
ret = scnprintf(buf, sizeof(int), "%d\n",
ubi->avail_pebs);
else if (attr == &dev_total_eraseblocks)
ret = sprintf(buf, "%d\n", ubi->good_peb_count);
ret = scnprintf(buf, sizeof(int), "%d\n",
ubi->good_peb_count);
else if (attr == &dev_volumes_count)
ret = sprintf(buf, "%d\n", ubi->vol_count - UBI_INT_VOL_COUNT);
ret = scnprintf(buf, sizeof(int), "%d\n",
ubi->vol_count - UBI_INT_VOL_COUNT);
else if (attr == &dev_max_ec)
ret = sprintf(buf, "%d\n", ubi->max_ec);
ret = scnprintf(buf, sizeof(int), "%d\n",
ubi->max_ec);
else if (attr == &dev_reserved_for_bad)
ret = sprintf(buf, "%d\n", ubi->beb_rsvd_pebs);
ret = scnprintf(buf, sizeof(int), "%d\n",
ubi->beb_rsvd_pebs);
else if (attr == &dev_bad_peb_count)
ret = sprintf(buf, "%d\n", ubi->bad_peb_count);
ret = scnprintf(buf, sizeof(int), "%d\n",
ubi->bad_peb_count);
else if (attr == &dev_max_vol_count)
ret = sprintf(buf, "%d\n", ubi->vtbl_slots);
ret = scnprintf(buf, sizeof(int), "%d\n",
ubi->vtbl_slots);
else if (attr == &dev_min_io_size)
ret = sprintf(buf, "%d\n", ubi->min_io_size);
ret = scnprintf(buf, sizeof(int), "%d\n",
ubi->min_io_size);
else if (attr == &dev_bgt_enabled)
ret = sprintf(buf, "%d\n", ubi->thread_enabled);
ret = scnprintf(buf, sizeof(int), "%d\n",
ubi->thread_enabled);
else if (attr == &dev_mtd_num)
ret = sprintf(buf, "%d\n", ubi->mtd->index);
ret = scnprintf(buf, sizeof(int), "%d\n",
ubi->mtd->index);
else if (attr == &dev_ro_mode)
ret = sprintf(buf, "%d\n", ubi->ro_mode);
ret = scnprintf(buf, sizeof(int), "%d\n",
ubi->ro_mode);
else if (attr == &dev_mtd_trigger_scrub)
ret = scnprintf(buf, sizeof(int), "%d\n",
atomic_read(&ubi->scrub_work_count));
else if (attr == &dev_mtd_max_scrub_sqnum)
ret = scnprintf(buf, sizeof(unsigned long long), "%llu\n",
get_max_sqnum(ubi));
else if (attr == &dev_mtd_min_scrub_sqnum)
ret = scnprintf(buf, sizeof(unsigned long long), "%llu\n",
ubi_wl_scrub_get_min_sqnum(ubi));
else
ret = -EINVAL;
@ -398,10 +440,46 @@ 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 = 0;
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\n");
ret = -ENOENT;
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);
@ -438,7 +516,8 @@ static int uif_init(struct ubi_device *ubi)
int i, err;
dev_t dev;
sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num);
scnprintf(ubi->ubi_name, sizeof(UBI_NAME_STR) + 5,
UBI_NAME_STR "%d", ubi->ubi_num);
/*
* Major numbers for the UBI character devices are allocated
@ -1376,7 +1455,7 @@ static int ubi_mtd_param_parse(const char *val, const struct kernel_param *kp)
return 0;
}
strcpy(buf, val);
strlcpy(buf, val, sizeof(buf));
/* Get rid of the final newline */
if (buf[len - 1] == '\n')
@ -1391,7 +1470,7 @@ static int ubi_mtd_param_parse(const char *val, const struct kernel_param *kp)
}
p = &mtd_dev_param[mtd_devs];
strcpy(&p->name[0], tokens[0]);
strlcpy(&p->name[0], tokens[0], MTD_PARAM_LEN_MAX);
token = tokens[1];
if (token) {

View file

@ -1056,6 +1056,15 @@ 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;
@ -1074,6 +1083,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;
}

View file

@ -170,6 +170,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
@ -182,6 +184,8 @@ struct ubi_wl_entry {
} u;
int ec;
int pnum;
unsigned int tagged_scrub_all:1;
unsigned long long sqnum;
};
/**
@ -625,6 +629,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;
@ -919,7 +926,12 @@ 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_bitflip_check(struct ubi_device *ubi, int pnum, int force_scrub);
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,
int len);

View file

@ -54,7 +54,7 @@ static struct device_attribute attr_vol_upd_marker =
static ssize_t vol_attribute_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
int ret, size;
struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
struct ubi_device *ubi;
@ -73,27 +73,36 @@ static ssize_t vol_attribute_show(struct device *dev,
spin_unlock(&ubi->volumes_lock);
if (attr == &attr_vol_reserved_ebs)
ret = sprintf(buf, "%d\n", vol->reserved_pebs);
ret = scnprintf(buf, sizeof(int), "%d\n", vol->reserved_pebs);
else if (attr == &attr_vol_type) {
const char *tp;
if (vol->vol_type == UBI_DYNAMIC_VOLUME)
if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
tp = "dynamic";
else
size = 8;
} else {
tp = "static";
ret = sprintf(buf, "%s\n", tp);
size = 7;
}
ret = scnprintf(buf, size, "%s\n", tp);
} else if (attr == &attr_vol_name)
ret = sprintf(buf, "%s\n", vol->name);
ret = scnprintf(buf, vol->name_len + 1, "%s\n",
vol->name);
else if (attr == &attr_vol_corrupted)
ret = sprintf(buf, "%d\n", vol->corrupted);
ret = scnprintf(buf, sizeof(int), "%d\n",
vol->corrupted);
else if (attr == &attr_vol_alignment)
ret = sprintf(buf, "%d\n", vol->alignment);
ret = scnprintf(buf, sizeof(int), "%d\n",
vol->alignment);
else if (attr == &attr_vol_usable_eb_size)
ret = sprintf(buf, "%d\n", vol->usable_leb_size);
ret = scnprintf(buf, sizeof(int), "%d\n",
vol->usable_leb_size);
else if (attr == &attr_vol_data_bytes)
ret = sprintf(buf, "%lld\n", vol->used_bytes);
ret = scnprintf(buf, sizeof(unsigned long long), "%lld\n",
vol->used_bytes);
else if (attr == &attr_vol_upd_marker)
ret = sprintf(buf, "%d\n", vol->upd_marker);
ret = scnprintf(buf, sizeof(int), "%d\n",
vol->upd_marker);
else
/* This must be a bug */
ret = -EINVAL;

View file

@ -89,6 +89,7 @@
#include <linux/crc32.h>
#include <linux/freezer.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include "ubi.h"
#include "wl.h"
@ -488,6 +489,42 @@ out_free:
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
@ -863,9 +900,19 @@ 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",
e1->pnum, vol_id, lnum, e2->pnum);
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);
@ -1233,6 +1280,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
@ -1269,6 +1317,20 @@ retry:
} 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);
@ -1300,6 +1362,197 @@ retry:
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
@ -1753,7 +2006,8 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
ubi->max_ec = ai->max_ec;
INIT_LIST_HEAD(&ubi->works);
sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num);
scnprintf(ubi->bgt_name, sizeof(UBI_BGT_NAME_PATTERN)+2,
UBI_BGT_NAME_PATTERN, ubi->ubi_num);
err = -ENOMEM;
ubi->lookuptbl = kcalloc(ubi->peb_count, sizeof(void *), GFP_KERNEL);
@ -1786,6 +2040,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);
@ -1808,6 +2064,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) {
@ -1891,6 +2149,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
#ifdef CONFIG_MTD_UBI_FASTMAP
ubi_ensure_anchor_pebs(ubi);
#endif
ubi->wl_is_inited = 1;
return 0;
out_free: