mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-25 22:23:14 -04:00
Merge "ubi: Add scrub_all support to UBI"
This commit is contained in:
commit
d70a1a1959
7 changed files with 422 additions and 38 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, ¶m->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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Reference in a new issue