mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 03:01:39 -04:00
This pull request includes the following UBI/UBIFS changes:
* Powercut emulation for UBI * A huge update to UBI Fastmap * Cleanups and bugfixes all over UBI and UBIFS -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABAgAGBQJVLsBfAAoJEEtJtSqsAOnWyaYP/30jnSEu4y2agDPwdev0wnqF QRAAVWxLCzinBkElXJ0POvhdQwnxP37ACqa/mNVuTa5++N1eI8PPpcRu+EUrVmoP mSRmEBdarvNYRLmOaoBBwY4qFvmcZx9Odt2BIdXz9xV+Mk220VdRW2PBn8JfGDn4 JJziWKmq9HUKXMQY9SVkOfT5tW+xHdjRUuNwp2S5nuV4hz2rUv0ChlYqCBqvca8/ tfjtlCLBGGuFSXfa9wtdCTWlU2Hyh4sG9u72c23iGqHmwOc9ZIpBlgiz+nYZFXoo GriBDBXG5ib1p+Px+oecWPBLv/Ipgc887IJHFAz3exPmSkQQTnuCSYFsraS1zqwZ 91kOGjcF5wY9EwBbT0a1VDdOwYT9MC5gD7G71MarDt0BWEYDvPufd6XPc5qPfP/9 xtscNQZ2FNd4gWGRT5irBOqIERdsWt2cjgD0aK2gbV1p9meUBU6hlvMIOU1YmBKg TwK8o+RwhE13JWwSExqcW+FRz9Aup/W/EDRNuuUGOXFFSnSvqSwE/y3JeP7jydIf 7J3rvrKdRWObTO/uZvtx+mwU3FgNPoITCgQQtAz7jexjksLWMxVi1gF1VYULScW/ p5ewCFsuRQSeCL/M1cKO1trYARbe9YITewbrQKuL8wknQKGcXY0XiV0OpPlH0xXD LqnvNoeixq4AH7rkMQIk =nzVw -----END PGP SIGNATURE----- Merge tag 'upstream-4.1-rc1' of git://git.infradead.org/linux-ubifs Pull UBI/UBIFS updates from Richard Weinberger: "This pull request includes the following UBI/UBIFS changes: - powercut emulation for UBI - a huge update to UBI Fastmap - cleanups and bugfixes all over UBI and UBIFS" * tag 'upstream-4.1-rc1' of git://git.infradead.org/linux-ubifs: (50 commits) UBI: power cut emulation for testing UBIFS: fix output format of INUM_WATERMARK UBI: Fastmap: Fall back to scanning mode after ECC error UBI: Fastmap: Remove is_fm_block() UBI: Fastmap: Add blank line after declarations UBI: Fastmap: Remove else after return. UBI: Fastmap: Introduce may_reserve_for_fm() UBI: Fastmap: Introduce ubi_fastmap_init() UBI: Fastmap: Wire up WL accessor functions UBI: Add accessor functions for WL data structures UBI: Move fastmap specific functions out of wl.c UBI: Fastmap: Add new module parameter fm_debug UBI: Fastmap: Make self_check_eba() depend on fastmap self checking UBI: Fastmap: Add self check to detect absent PEBs UBI: Fix stale pointers in ubi->lookuptbl UBI: Fastmap: Enhance fastmap checking UBI: Add initial support for fastmap self checks UBI: Fastmap: Rework fastmap error paths UBI: Fastmap: Prepare for variable sized fastmaps UBI: Fastmap: Locking updates ...
This commit is contained in:
commit
d613896926
38 changed files with 1507 additions and 1141 deletions
|
|
@ -410,7 +410,7 @@ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb,
|
|||
second_is_newer = !second_is_newer;
|
||||
} else {
|
||||
dbg_bld("PEB %d CRC is OK", pnum);
|
||||
bitflips = !!err;
|
||||
bitflips |= !!err;
|
||||
}
|
||||
mutex_unlock(&ubi->buf_mutex);
|
||||
|
||||
|
|
@ -1301,6 +1301,30 @@ out_ech:
|
|||
return err;
|
||||
}
|
||||
|
||||
static struct ubi_attach_info *alloc_ai(void)
|
||||
{
|
||||
struct ubi_attach_info *ai;
|
||||
|
||||
ai = kzalloc(sizeof(struct ubi_attach_info), GFP_KERNEL);
|
||||
if (!ai)
|
||||
return ai;
|
||||
|
||||
INIT_LIST_HEAD(&ai->corr);
|
||||
INIT_LIST_HEAD(&ai->free);
|
||||
INIT_LIST_HEAD(&ai->erase);
|
||||
INIT_LIST_HEAD(&ai->alien);
|
||||
ai->volumes = RB_ROOT;
|
||||
ai->aeb_slab_cache = kmem_cache_create("ubi_aeb_slab_cache",
|
||||
sizeof(struct ubi_ainf_peb),
|
||||
0, 0, NULL);
|
||||
if (!ai->aeb_slab_cache) {
|
||||
kfree(ai);
|
||||
ai = NULL;
|
||||
}
|
||||
|
||||
return ai;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
|
||||
/**
|
||||
|
|
@ -1313,7 +1337,7 @@ out_ech:
|
|||
* UBI_NO_FASTMAP denotes that no fastmap was found.
|
||||
* UBI_BAD_FASTMAP denotes that the found fastmap was invalid.
|
||||
*/
|
||||
static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info *ai)
|
||||
static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info **ai)
|
||||
{
|
||||
int err, pnum, fm_anchor = -1;
|
||||
unsigned long long max_sqnum = 0;
|
||||
|
|
@ -1334,7 +1358,7 @@ static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info *ai)
|
|||
cond_resched();
|
||||
|
||||
dbg_gen("process PEB %d", pnum);
|
||||
err = scan_peb(ubi, ai, pnum, &vol_id, &sqnum);
|
||||
err = scan_peb(ubi, *ai, pnum, &vol_id, &sqnum);
|
||||
if (err < 0)
|
||||
goto out_vidh;
|
||||
|
||||
|
|
@ -1350,7 +1374,12 @@ static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info *ai)
|
|||
if (fm_anchor < 0)
|
||||
return UBI_NO_FASTMAP;
|
||||
|
||||
return ubi_scan_fastmap(ubi, ai, fm_anchor);
|
||||
destroy_ai(*ai);
|
||||
*ai = alloc_ai();
|
||||
if (!*ai)
|
||||
return -ENOMEM;
|
||||
|
||||
return ubi_scan_fastmap(ubi, *ai, fm_anchor);
|
||||
|
||||
out_vidh:
|
||||
ubi_free_vid_hdr(ubi, vidh);
|
||||
|
|
@ -1362,30 +1391,6 @@ out:
|
|||
|
||||
#endif
|
||||
|
||||
static struct ubi_attach_info *alloc_ai(const char *slab_name)
|
||||
{
|
||||
struct ubi_attach_info *ai;
|
||||
|
||||
ai = kzalloc(sizeof(struct ubi_attach_info), GFP_KERNEL);
|
||||
if (!ai)
|
||||
return ai;
|
||||
|
||||
INIT_LIST_HEAD(&ai->corr);
|
||||
INIT_LIST_HEAD(&ai->free);
|
||||
INIT_LIST_HEAD(&ai->erase);
|
||||
INIT_LIST_HEAD(&ai->alien);
|
||||
ai->volumes = RB_ROOT;
|
||||
ai->aeb_slab_cache = kmem_cache_create(slab_name,
|
||||
sizeof(struct ubi_ainf_peb),
|
||||
0, 0, NULL);
|
||||
if (!ai->aeb_slab_cache) {
|
||||
kfree(ai);
|
||||
ai = NULL;
|
||||
}
|
||||
|
||||
return ai;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_attach - attach an MTD device.
|
||||
* @ubi: UBI device descriptor
|
||||
|
|
@ -1399,7 +1404,7 @@ int ubi_attach(struct ubi_device *ubi, int force_scan)
|
|||
int err;
|
||||
struct ubi_attach_info *ai;
|
||||
|
||||
ai = alloc_ai("ubi_aeb_slab_cache");
|
||||
ai = alloc_ai();
|
||||
if (!ai)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -1413,11 +1418,11 @@ int ubi_attach(struct ubi_device *ubi, int force_scan)
|
|||
if (force_scan)
|
||||
err = scan_all(ubi, ai, 0);
|
||||
else {
|
||||
err = scan_fast(ubi, ai);
|
||||
if (err > 0) {
|
||||
err = scan_fast(ubi, &ai);
|
||||
if (err > 0 || mtd_is_eccerr(err)) {
|
||||
if (err != UBI_NO_FASTMAP) {
|
||||
destroy_ai(ai);
|
||||
ai = alloc_ai("ubi_aeb_slab_cache2");
|
||||
ai = alloc_ai();
|
||||
if (!ai)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -1453,10 +1458,10 @@ int ubi_attach(struct ubi_device *ubi, int force_scan)
|
|||
goto out_wl;
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
if (ubi->fm && ubi_dbg_chk_gen(ubi)) {
|
||||
if (ubi->fm && ubi_dbg_chk_fastmap(ubi)) {
|
||||
struct ubi_attach_info *scan_ai;
|
||||
|
||||
scan_ai = alloc_ai("ubi_ckh_aeb_slab_cache");
|
||||
scan_ai = alloc_ai();
|
||||
if (!scan_ai) {
|
||||
err = -ENOMEM;
|
||||
goto out_wl;
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ static struct mtd_dev_param __initdata mtd_dev_param[UBI_MAX_DEVICES];
|
|||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
/* UBI module parameter to enable fastmap automatically on non-fastmap images */
|
||||
static bool fm_autoconvert;
|
||||
static bool fm_debug;
|
||||
#endif
|
||||
/* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */
|
||||
struct class *ubi_class;
|
||||
|
|
@ -154,23 +155,22 @@ static struct device_attribute dev_mtd_num =
|
|||
*/
|
||||
int ubi_volume_notify(struct ubi_device *ubi, struct ubi_volume *vol, int ntype)
|
||||
{
|
||||
int ret;
|
||||
struct ubi_notification nt;
|
||||
|
||||
ubi_do_get_device_info(ubi, &nt.di);
|
||||
ubi_do_get_volume_info(ubi, vol, &nt.vi);
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
switch (ntype) {
|
||||
case UBI_VOLUME_ADDED:
|
||||
case UBI_VOLUME_REMOVED:
|
||||
case UBI_VOLUME_RESIZED:
|
||||
case UBI_VOLUME_RENAMED:
|
||||
if (ubi_update_fastmap(ubi)) {
|
||||
ubi_err(ubi, "Unable to update fastmap!");
|
||||
ubi_ro_mode(ubi);
|
||||
}
|
||||
ret = ubi_update_fastmap(ubi);
|
||||
if (ret)
|
||||
ubi_msg(ubi, "Unable to write a new fastmap: %i", ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
return blocking_notifier_call_chain(&ubi_notifiers, ntype, &nt);
|
||||
}
|
||||
|
||||
|
|
@ -950,8 +950,10 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
|
|||
if (ubi->fm_pool.max_size < UBI_FM_MIN_POOL_SIZE)
|
||||
ubi->fm_pool.max_size = UBI_FM_MIN_POOL_SIZE;
|
||||
|
||||
ubi->fm_wl_pool.max_size = UBI_FM_WL_POOL_SIZE;
|
||||
ubi->fm_wl_pool.max_size = ubi->fm_pool.max_size / 2;
|
||||
ubi->fm_disabled = !fm_autoconvert;
|
||||
if (fm_debug)
|
||||
ubi_enable_dbg_chk_fastmap(ubi);
|
||||
|
||||
if (!ubi->fm_disabled && (int)mtd_div_by_eb(ubi->mtd->size, ubi->mtd)
|
||||
<= UBI_FM_MAX_START) {
|
||||
|
|
@ -970,8 +972,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
|
|||
mutex_init(&ubi->ckvol_mutex);
|
||||
mutex_init(&ubi->device_mutex);
|
||||
spin_lock_init(&ubi->volumes_lock);
|
||||
mutex_init(&ubi->fm_mutex);
|
||||
init_rwsem(&ubi->fm_sem);
|
||||
init_rwsem(&ubi->fm_protect);
|
||||
init_rwsem(&ubi->fm_eba_sem);
|
||||
|
||||
ubi_msg(ubi, "attaching mtd%d", mtd->index);
|
||||
|
||||
|
|
@ -1115,8 +1117,11 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
|
|||
ubi_msg(ubi, "detaching mtd%d", ubi->mtd->index);
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
/* If we don't write a new fastmap at detach time we lose all
|
||||
* EC updates that have been made since the last written fastmap. */
|
||||
ubi_update_fastmap(ubi);
|
||||
* EC updates that have been made since the last written fastmap.
|
||||
* In case of fastmap debugging we omit the update to simulate an
|
||||
* unclean shutdown. */
|
||||
if (!ubi_dbg_chk_fastmap(ubi))
|
||||
ubi_update_fastmap(ubi);
|
||||
#endif
|
||||
/*
|
||||
* Before freeing anything, we have to stop the background thread to
|
||||
|
|
@ -1501,6 +1506,8 @@ MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: mtd=<name|num|pa
|
|||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
module_param(fm_autoconvert, bool, 0644);
|
||||
MODULE_PARM_DESC(fm_autoconvert, "Set this parameter to enable fastmap automatically on images without a fastmap.");
|
||||
module_param(fm_debug, bool, 0);
|
||||
MODULE_PARM_DESC(fm_debug, "Set this parameter to enable fastmap debugging by default. Warning, this will make fastmap slow!");
|
||||
#endif
|
||||
MODULE_VERSION(__stringify(UBI_VERSION));
|
||||
MODULE_DESCRIPTION("UBI - Unsorted Block Images");
|
||||
|
|
|
|||
|
|
@ -455,7 +455,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd,
|
|||
/* Validate the request */
|
||||
err = -EINVAL;
|
||||
if (req.lnum < 0 || req.lnum >= vol->reserved_pebs ||
|
||||
req.bytes < 0 || req.lnum >= vol->usable_leb_size)
|
||||
req.bytes < 0 || req.bytes > vol->usable_leb_size)
|
||||
break;
|
||||
|
||||
err = get_exclusive(desc);
|
||||
|
|
|
|||
|
|
@ -263,7 +263,7 @@ static ssize_t dfs_file_read(struct file *file, char __user *user_buf,
|
|||
struct dentry *dent = file->f_path.dentry;
|
||||
struct ubi_device *ubi;
|
||||
struct ubi_debug_info *d;
|
||||
char buf[3];
|
||||
char buf[8];
|
||||
int val;
|
||||
|
||||
ubi = ubi_get_device(ubi_num);
|
||||
|
|
@ -275,12 +275,30 @@ static ssize_t dfs_file_read(struct file *file, char __user *user_buf,
|
|||
val = d->chk_gen;
|
||||
else if (dent == d->dfs_chk_io)
|
||||
val = d->chk_io;
|
||||
else if (dent == d->dfs_chk_fastmap)
|
||||
val = d->chk_fastmap;
|
||||
else if (dent == d->dfs_disable_bgt)
|
||||
val = d->disable_bgt;
|
||||
else if (dent == d->dfs_emulate_bitflips)
|
||||
val = d->emulate_bitflips;
|
||||
else if (dent == d->dfs_emulate_io_failures)
|
||||
val = d->emulate_io_failures;
|
||||
else if (dent == d->dfs_emulate_power_cut) {
|
||||
snprintf(buf, sizeof(buf), "%u\n", d->emulate_power_cut);
|
||||
count = simple_read_from_buffer(user_buf, count, ppos,
|
||||
buf, strlen(buf));
|
||||
goto out;
|
||||
} else if (dent == d->dfs_power_cut_min) {
|
||||
snprintf(buf, sizeof(buf), "%u\n", d->power_cut_min);
|
||||
count = simple_read_from_buffer(user_buf, count, ppos,
|
||||
buf, strlen(buf));
|
||||
goto out;
|
||||
} else if (dent == d->dfs_power_cut_max) {
|
||||
snprintf(buf, sizeof(buf), "%u\n", d->power_cut_max);
|
||||
count = simple_read_from_buffer(user_buf, count, ppos,
|
||||
buf, strlen(buf));
|
||||
goto out;
|
||||
}
|
||||
else {
|
||||
count = -EINVAL;
|
||||
goto out;
|
||||
|
|
@ -309,7 +327,7 @@ static ssize_t dfs_file_write(struct file *file, const char __user *user_buf,
|
|||
struct ubi_device *ubi;
|
||||
struct ubi_debug_info *d;
|
||||
size_t buf_size;
|
||||
char buf[8];
|
||||
char buf[8] = {0};
|
||||
int val;
|
||||
|
||||
ubi = ubi_get_device(ubi_num);
|
||||
|
|
@ -323,6 +341,21 @@ static ssize_t dfs_file_write(struct file *file, const char __user *user_buf,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (dent == d->dfs_power_cut_min) {
|
||||
if (kstrtouint(buf, 0, &d->power_cut_min) != 0)
|
||||
count = -EINVAL;
|
||||
goto out;
|
||||
} else if (dent == d->dfs_power_cut_max) {
|
||||
if (kstrtouint(buf, 0, &d->power_cut_max) != 0)
|
||||
count = -EINVAL;
|
||||
goto out;
|
||||
} else if (dent == d->dfs_emulate_power_cut) {
|
||||
if (kstrtoint(buf, 0, &val) != 0)
|
||||
count = -EINVAL;
|
||||
d->emulate_power_cut = val;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (buf[0] == '1')
|
||||
val = 1;
|
||||
else if (buf[0] == '0')
|
||||
|
|
@ -336,6 +369,8 @@ static ssize_t dfs_file_write(struct file *file, const char __user *user_buf,
|
|||
d->chk_gen = val;
|
||||
else if (dent == d->dfs_chk_io)
|
||||
d->chk_io = val;
|
||||
else if (dent == d->dfs_chk_fastmap)
|
||||
d->chk_fastmap = val;
|
||||
else if (dent == d->dfs_disable_bgt)
|
||||
d->disable_bgt = val;
|
||||
else if (dent == d->dfs_emulate_bitflips)
|
||||
|
|
@ -406,6 +441,13 @@ int ubi_debugfs_init_dev(struct ubi_device *ubi)
|
|||
goto out_remove;
|
||||
d->dfs_chk_io = dent;
|
||||
|
||||
fname = "chk_fastmap";
|
||||
dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
|
||||
&dfs_fops);
|
||||
if (IS_ERR_OR_NULL(dent))
|
||||
goto out_remove;
|
||||
d->dfs_chk_fastmap = dent;
|
||||
|
||||
fname = "tst_disable_bgt";
|
||||
dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
|
||||
&dfs_fops);
|
||||
|
|
@ -427,6 +469,27 @@ int ubi_debugfs_init_dev(struct ubi_device *ubi)
|
|||
goto out_remove;
|
||||
d->dfs_emulate_io_failures = dent;
|
||||
|
||||
fname = "tst_emulate_power_cut";
|
||||
dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
|
||||
&dfs_fops);
|
||||
if (IS_ERR_OR_NULL(dent))
|
||||
goto out_remove;
|
||||
d->dfs_emulate_power_cut = dent;
|
||||
|
||||
fname = "tst_emulate_power_cut_min";
|
||||
dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
|
||||
&dfs_fops);
|
||||
if (IS_ERR_OR_NULL(dent))
|
||||
goto out_remove;
|
||||
d->dfs_power_cut_min = dent;
|
||||
|
||||
fname = "tst_emulate_power_cut_max";
|
||||
dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
|
||||
&dfs_fops);
|
||||
if (IS_ERR_OR_NULL(dent))
|
||||
goto out_remove;
|
||||
d->dfs_power_cut_max = dent;
|
||||
|
||||
return 0;
|
||||
|
||||
out_remove:
|
||||
|
|
@ -447,3 +510,36 @@ void ubi_debugfs_exit_dev(struct ubi_device *ubi)
|
|||
if (IS_ENABLED(CONFIG_DEBUG_FS))
|
||||
debugfs_remove_recursive(ubi->dbg.dfs_dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_dbg_power_cut - emulate a power cut if it is time to do so
|
||||
* @ubi: UBI device description object
|
||||
* @caller: Flags set to indicate from where the function is being called
|
||||
*
|
||||
* Returns non-zero if a power cut was emulated, zero if not.
|
||||
*/
|
||||
int ubi_dbg_power_cut(struct ubi_device *ubi, int caller)
|
||||
{
|
||||
unsigned int range;
|
||||
|
||||
if ((ubi->dbg.emulate_power_cut & caller) == 0)
|
||||
return 0;
|
||||
|
||||
if (ubi->dbg.power_cut_counter == 0) {
|
||||
ubi->dbg.power_cut_counter = ubi->dbg.power_cut_min;
|
||||
|
||||
if (ubi->dbg.power_cut_max > ubi->dbg.power_cut_min) {
|
||||
range = ubi->dbg.power_cut_max - ubi->dbg.power_cut_min;
|
||||
ubi->dbg.power_cut_counter += prandom_u32() % range;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ubi->dbg.power_cut_counter--;
|
||||
if (ubi->dbg.power_cut_counter)
|
||||
return 0;
|
||||
|
||||
ubi_msg(ubi, "XXXXXXXXXXXXXXX emulating a power cut XXXXXXXXXXXXXXXX");
|
||||
ubi_ro_mode(ubi);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,4 +127,16 @@ static inline int ubi_dbg_chk_gen(const struct ubi_device *ubi)
|
|||
{
|
||||
return ubi->dbg.chk_gen;
|
||||
}
|
||||
|
||||
static inline int ubi_dbg_chk_fastmap(const struct ubi_device *ubi)
|
||||
{
|
||||
return ubi->dbg.chk_fastmap;
|
||||
}
|
||||
|
||||
static inline void ubi_enable_dbg_chk_fastmap(struct ubi_device *ubi)
|
||||
{
|
||||
ubi->dbg.chk_fastmap = 1;
|
||||
}
|
||||
|
||||
int ubi_dbg_power_cut(struct ubi_device *ubi, int caller);
|
||||
#endif /* !__UBI_DEBUG_H__ */
|
||||
|
|
|
|||
|
|
@ -340,9 +340,9 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
|
||||
dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum);
|
||||
|
||||
down_read(&ubi->fm_sem);
|
||||
down_read(&ubi->fm_eba_sem);
|
||||
vol->eba_tbl[lnum] = UBI_LEB_UNMAPPED;
|
||||
up_read(&ubi->fm_sem);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 0);
|
||||
|
||||
out_unlock:
|
||||
|
|
@ -567,6 +567,7 @@ retry:
|
|||
new_pnum = ubi_wl_get_peb(ubi);
|
||||
if (new_pnum < 0) {
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
return new_pnum;
|
||||
}
|
||||
|
||||
|
|
@ -577,13 +578,16 @@ retry:
|
|||
if (err && err != UBI_IO_BITFLIPS) {
|
||||
if (err > 0)
|
||||
err = -EIO;
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
||||
err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr);
|
||||
if (err)
|
||||
if (err) {
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
goto write_error;
|
||||
}
|
||||
|
||||
data_size = offset + len;
|
||||
mutex_lock(&ubi->buf_mutex);
|
||||
|
|
@ -592,8 +596,10 @@ retry:
|
|||
/* Read everything before the area where the write failure happened */
|
||||
if (offset > 0) {
|
||||
err = ubi_io_read_data(ubi, ubi->peb_buf, pnum, 0, offset);
|
||||
if (err && err != UBI_IO_BITFLIPS)
|
||||
if (err && err != UBI_IO_BITFLIPS) {
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(ubi->peb_buf + offset, buf, len);
|
||||
|
|
@ -601,15 +607,15 @@ retry:
|
|||
err = ubi_io_write_data(ubi, ubi->peb_buf, new_pnum, 0, data_size);
|
||||
if (err) {
|
||||
mutex_unlock(&ubi->buf_mutex);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
goto write_error;
|
||||
}
|
||||
|
||||
mutex_unlock(&ubi->buf_mutex);
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
|
||||
down_read(&ubi->fm_sem);
|
||||
vol->eba_tbl[lnum] = new_pnum;
|
||||
up_read(&ubi->fm_sem);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1);
|
||||
|
||||
ubi_msg(ubi, "data was successfully recovered");
|
||||
|
|
@ -704,6 +710,7 @@ retry:
|
|||
if (pnum < 0) {
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
return pnum;
|
||||
}
|
||||
|
||||
|
|
@ -714,6 +721,7 @@ retry:
|
|||
if (err) {
|
||||
ubi_warn(ubi, "failed to write VID header to LEB %d:%d, PEB %d",
|
||||
vol_id, lnum, pnum);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
goto write_error;
|
||||
}
|
||||
|
||||
|
|
@ -722,13 +730,13 @@ retry:
|
|||
if (err) {
|
||||
ubi_warn(ubi, "failed to write %d bytes at offset %d of LEB %d:%d, PEB %d",
|
||||
len, offset, vol_id, lnum, pnum);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
goto write_error;
|
||||
}
|
||||
}
|
||||
|
||||
down_read(&ubi->fm_sem);
|
||||
vol->eba_tbl[lnum] = pnum;
|
||||
up_read(&ubi->fm_sem);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
|
|
@ -825,6 +833,7 @@ retry:
|
|||
if (pnum < 0) {
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
return pnum;
|
||||
}
|
||||
|
||||
|
|
@ -835,6 +844,7 @@ retry:
|
|||
if (err) {
|
||||
ubi_warn(ubi, "failed to write VID header to LEB %d:%d, PEB %d",
|
||||
vol_id, lnum, pnum);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
goto write_error;
|
||||
}
|
||||
|
||||
|
|
@ -842,13 +852,13 @@ retry:
|
|||
if (err) {
|
||||
ubi_warn(ubi, "failed to write %d bytes of data to PEB %d",
|
||||
len, pnum);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
goto write_error;
|
||||
}
|
||||
|
||||
ubi_assert(vol->eba_tbl[lnum] < 0);
|
||||
down_read(&ubi->fm_sem);
|
||||
vol->eba_tbl[lnum] = pnum;
|
||||
up_read(&ubi->fm_sem);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
|
|
@ -900,7 +910,7 @@ write_error:
|
|||
int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
int lnum, const void *buf, int len)
|
||||
{
|
||||
int err, pnum, tries = 0, vol_id = vol->vol_id;
|
||||
int err, pnum, old_pnum, tries = 0, vol_id = vol->vol_id;
|
||||
struct ubi_vid_hdr *vid_hdr;
|
||||
uint32_t crc;
|
||||
|
||||
|
|
@ -943,6 +953,7 @@ retry:
|
|||
pnum = ubi_wl_get_peb(ubi);
|
||||
if (pnum < 0) {
|
||||
err = pnum;
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
goto out_leb_unlock;
|
||||
}
|
||||
|
||||
|
|
@ -953,6 +964,7 @@ retry:
|
|||
if (err) {
|
||||
ubi_warn(ubi, "failed to write VID header to LEB %d:%d, PEB %d",
|
||||
vol_id, lnum, pnum);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
goto write_error;
|
||||
}
|
||||
|
||||
|
|
@ -960,19 +972,20 @@ retry:
|
|||
if (err) {
|
||||
ubi_warn(ubi, "failed to write %d bytes of data to PEB %d",
|
||||
len, pnum);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
goto write_error;
|
||||
}
|
||||
|
||||
if (vol->eba_tbl[lnum] >= 0) {
|
||||
err = ubi_wl_put_peb(ubi, vol_id, lnum, vol->eba_tbl[lnum], 0);
|
||||
old_pnum = vol->eba_tbl[lnum];
|
||||
vol->eba_tbl[lnum] = pnum;
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
|
||||
if (old_pnum >= 0) {
|
||||
err = ubi_wl_put_peb(ubi, vol_id, lnum, old_pnum, 0);
|
||||
if (err)
|
||||
goto out_leb_unlock;
|
||||
}
|
||||
|
||||
down_read(&ubi->fm_sem);
|
||||
vol->eba_tbl[lnum] = pnum;
|
||||
up_read(&ubi->fm_sem);
|
||||
|
||||
out_leb_unlock:
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
out_mutex:
|
||||
|
|
@ -1218,9 +1231,9 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
|||
}
|
||||
|
||||
ubi_assert(vol->eba_tbl[lnum] == from);
|
||||
down_read(&ubi->fm_sem);
|
||||
down_read(&ubi->fm_eba_sem);
|
||||
vol->eba_tbl[lnum] = to;
|
||||
up_read(&ubi->fm_sem);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
|
||||
out_unlock_buf:
|
||||
mutex_unlock(&ubi->buf_mutex);
|
||||
|
|
@ -1419,7 +1432,8 @@ int ubi_eba_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
|
|||
* during re-size.
|
||||
*/
|
||||
ubi_move_aeb_to_list(av, aeb, &ai->erase);
|
||||
vol->eba_tbl[aeb->lnum] = aeb->pnum;
|
||||
else
|
||||
vol->eba_tbl[aeb->lnum] = aeb->pnum;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
362
drivers/mtd/ubi/fastmap-wl.c
Normal file
362
drivers/mtd/ubi/fastmap-wl.c
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Linutronix GmbH
|
||||
* Copyright (c) 2014 sigma star gmbh
|
||||
* Author: Richard Weinberger <richard@nod.at>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
||||
* the GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* update_fastmap_work_fn - calls ubi_update_fastmap from a work queue
|
||||
* @wrk: the work description object
|
||||
*/
|
||||
static void update_fastmap_work_fn(struct work_struct *wrk)
|
||||
{
|
||||
struct ubi_device *ubi = container_of(wrk, struct ubi_device, fm_work);
|
||||
|
||||
ubi_update_fastmap(ubi);
|
||||
spin_lock(&ubi->wl_lock);
|
||||
ubi->fm_work_scheduled = 0;
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* find_anchor_wl_entry - find wear-leveling entry to used as anchor PEB.
|
||||
* @root: the RB-tree where to look for
|
||||
*/
|
||||
static struct ubi_wl_entry *find_anchor_wl_entry(struct rb_root *root)
|
||||
{
|
||||
struct rb_node *p;
|
||||
struct ubi_wl_entry *e, *victim = NULL;
|
||||
int max_ec = UBI_MAX_ERASECOUNTER;
|
||||
|
||||
ubi_rb_for_each_entry(p, e, root, u.rb) {
|
||||
if (e->pnum < UBI_FM_MAX_START && e->ec < max_ec) {
|
||||
victim = e;
|
||||
max_ec = e->ec;
|
||||
}
|
||||
}
|
||||
|
||||
return victim;
|
||||
}
|
||||
|
||||
/**
|
||||
* return_unused_pool_pebs - returns unused PEB to the free tree.
|
||||
* @ubi: UBI device description object
|
||||
* @pool: fastmap pool description object
|
||||
*/
|
||||
static void return_unused_pool_pebs(struct ubi_device *ubi,
|
||||
struct ubi_fm_pool *pool)
|
||||
{
|
||||
int i;
|
||||
struct ubi_wl_entry *e;
|
||||
|
||||
for (i = pool->used; i < pool->size; i++) {
|
||||
e = ubi->lookuptbl[pool->pebs[i]];
|
||||
wl_tree_add(e, &ubi->free);
|
||||
ubi->free_count++;
|
||||
}
|
||||
}
|
||||
|
||||
static int anchor_pebs_avalible(struct rb_root *root)
|
||||
{
|
||||
struct rb_node *p;
|
||||
struct ubi_wl_entry *e;
|
||||
|
||||
ubi_rb_for_each_entry(p, e, root, u.rb)
|
||||
if (e->pnum < UBI_FM_MAX_START)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_wl_get_fm_peb - find a physical erase block with a given maximal number.
|
||||
* @ubi: UBI device description object
|
||||
* @anchor: This PEB will be used as anchor PEB by fastmap
|
||||
*
|
||||
* The function returns a physical erase block with a given maximal number
|
||||
* and removes it from the wl subsystem.
|
||||
* Must be called with wl_lock held!
|
||||
*/
|
||||
struct ubi_wl_entry *ubi_wl_get_fm_peb(struct ubi_device *ubi, int anchor)
|
||||
{
|
||||
struct ubi_wl_entry *e = NULL;
|
||||
|
||||
if (!ubi->free.rb_node || (ubi->free_count - ubi->beb_rsvd_pebs < 1))
|
||||
goto out;
|
||||
|
||||
if (anchor)
|
||||
e = find_anchor_wl_entry(&ubi->free);
|
||||
else
|
||||
e = find_mean_wl_entry(ubi, &ubi->free);
|
||||
|
||||
if (!e)
|
||||
goto out;
|
||||
|
||||
self_check_in_wl_tree(ubi, e, &ubi->free);
|
||||
|
||||
/* remove it from the free list,
|
||||
* the wl subsystem does no longer know this erase block */
|
||||
rb_erase(&e->u.rb, &ubi->free);
|
||||
ubi->free_count--;
|
||||
out:
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_refill_pools - refills all fastmap PEB pools.
|
||||
* @ubi: UBI device description object
|
||||
*/
|
||||
void ubi_refill_pools(struct ubi_device *ubi)
|
||||
{
|
||||
struct ubi_fm_pool *wl_pool = &ubi->fm_wl_pool;
|
||||
struct ubi_fm_pool *pool = &ubi->fm_pool;
|
||||
struct ubi_wl_entry *e;
|
||||
int enough;
|
||||
|
||||
spin_lock(&ubi->wl_lock);
|
||||
|
||||
return_unused_pool_pebs(ubi, wl_pool);
|
||||
return_unused_pool_pebs(ubi, pool);
|
||||
|
||||
wl_pool->size = 0;
|
||||
pool->size = 0;
|
||||
|
||||
for (;;) {
|
||||
enough = 0;
|
||||
if (pool->size < pool->max_size) {
|
||||
if (!ubi->free.rb_node)
|
||||
break;
|
||||
|
||||
e = wl_get_wle(ubi);
|
||||
if (!e)
|
||||
break;
|
||||
|
||||
pool->pebs[pool->size] = e->pnum;
|
||||
pool->size++;
|
||||
} else
|
||||
enough++;
|
||||
|
||||
if (wl_pool->size < wl_pool->max_size) {
|
||||
if (!ubi->free.rb_node ||
|
||||
(ubi->free_count - ubi->beb_rsvd_pebs < 5))
|
||||
break;
|
||||
|
||||
e = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF);
|
||||
self_check_in_wl_tree(ubi, e, &ubi->free);
|
||||
rb_erase(&e->u.rb, &ubi->free);
|
||||
ubi->free_count--;
|
||||
|
||||
wl_pool->pebs[wl_pool->size] = e->pnum;
|
||||
wl_pool->size++;
|
||||
} else
|
||||
enough++;
|
||||
|
||||
if (enough == 2)
|
||||
break;
|
||||
}
|
||||
|
||||
wl_pool->used = 0;
|
||||
pool->used = 0;
|
||||
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_wl_get_peb - get a physical eraseblock.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* This function returns a physical eraseblock in case of success and a
|
||||
* negative error code in case of failure.
|
||||
* Returns with ubi->fm_eba_sem held in read mode!
|
||||
*/
|
||||
int ubi_wl_get_peb(struct ubi_device *ubi)
|
||||
{
|
||||
int ret, retried = 0;
|
||||
struct ubi_fm_pool *pool = &ubi->fm_pool;
|
||||
struct ubi_fm_pool *wl_pool = &ubi->fm_wl_pool;
|
||||
|
||||
again:
|
||||
down_read(&ubi->fm_eba_sem);
|
||||
spin_lock(&ubi->wl_lock);
|
||||
|
||||
/* We check here also for the WL pool because at this point we can
|
||||
* refill the WL pool synchronous. */
|
||||
if (pool->used == pool->size || wl_pool->used == wl_pool->size) {
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
ret = ubi_update_fastmap(ubi);
|
||||
if (ret) {
|
||||
ubi_msg(ubi, "Unable to write a new fastmap: %i", ret);
|
||||
down_read(&ubi->fm_eba_sem);
|
||||
return -ENOSPC;
|
||||
}
|
||||
down_read(&ubi->fm_eba_sem);
|
||||
spin_lock(&ubi->wl_lock);
|
||||
}
|
||||
|
||||
if (pool->used == pool->size) {
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
if (retried) {
|
||||
ubi_err(ubi, "Unable to get a free PEB from user WL pool");
|
||||
ret = -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
retried = 1;
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
goto again;
|
||||
}
|
||||
|
||||
ubi_assert(pool->used < pool->size);
|
||||
ret = pool->pebs[pool->used++];
|
||||
prot_queue_add(ubi, ubi->lookuptbl[ret]);
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* get_peb_for_wl - returns a PEB to be used internally by the WL sub-system.
|
||||
*
|
||||
* @ubi: UBI device description object
|
||||
*/
|
||||
static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi)
|
||||
{
|
||||
struct ubi_fm_pool *pool = &ubi->fm_wl_pool;
|
||||
int pnum;
|
||||
|
||||
if (pool->used == pool->size) {
|
||||
/* We cannot update the fastmap here because this
|
||||
* function is called in atomic context.
|
||||
* Let's fail here and refill/update it as soon as possible. */
|
||||
if (!ubi->fm_work_scheduled) {
|
||||
ubi->fm_work_scheduled = 1;
|
||||
schedule_work(&ubi->fm_work);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pnum = pool->pebs[pool->used++];
|
||||
return ubi->lookuptbl[pnum];
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_ensure_anchor_pebs - schedule wear-leveling to produce an anchor PEB.
|
||||
* @ubi: UBI device description object
|
||||
*/
|
||||
int ubi_ensure_anchor_pebs(struct ubi_device *ubi)
|
||||
{
|
||||
struct ubi_work *wrk;
|
||||
|
||||
spin_lock(&ubi->wl_lock);
|
||||
if (ubi->wl_scheduled) {
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
return 0;
|
||||
}
|
||||
ubi->wl_scheduled = 1;
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
|
||||
wrk = kmalloc(sizeof(struct ubi_work), GFP_NOFS);
|
||||
if (!wrk) {
|
||||
spin_lock(&ubi->wl_lock);
|
||||
ubi->wl_scheduled = 0;
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
wrk->anchor = 1;
|
||||
wrk->func = &wear_leveling_worker;
|
||||
schedule_ubi_work(ubi, wrk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_wl_put_fm_peb - returns a PEB used in a fastmap to the wear-leveling
|
||||
* sub-system.
|
||||
* see: ubi_wl_put_peb()
|
||||
*
|
||||
* @ubi: UBI device description object
|
||||
* @fm_e: physical eraseblock to return
|
||||
* @lnum: the last used logical eraseblock number for the PEB
|
||||
* @torture: if this physical eraseblock has to be tortured
|
||||
*/
|
||||
int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *fm_e,
|
||||
int lnum, int torture)
|
||||
{
|
||||
struct ubi_wl_entry *e;
|
||||
int vol_id, pnum = fm_e->pnum;
|
||||
|
||||
dbg_wl("PEB %d", pnum);
|
||||
|
||||
ubi_assert(pnum >= 0);
|
||||
ubi_assert(pnum < ubi->peb_count);
|
||||
|
||||
spin_lock(&ubi->wl_lock);
|
||||
e = ubi->lookuptbl[pnum];
|
||||
|
||||
/* This can happen if we recovered from a fastmap the very
|
||||
* first time and writing now a new one. In this case the wl system
|
||||
* has never seen any PEB used by the original fastmap.
|
||||
*/
|
||||
if (!e) {
|
||||
e = fm_e;
|
||||
ubi_assert(e->ec >= 0);
|
||||
ubi->lookuptbl[pnum] = e;
|
||||
}
|
||||
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
|
||||
vol_id = lnum ? UBI_FM_DATA_VOLUME_ID : UBI_FM_SB_VOLUME_ID;
|
||||
return schedule_erase(ubi, e, vol_id, lnum, torture);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_is_erase_work - checks whether a work is erase work.
|
||||
* @wrk: The work object to be checked
|
||||
*/
|
||||
int ubi_is_erase_work(struct ubi_work *wrk)
|
||||
{
|
||||
return wrk->func == erase_worker;
|
||||
}
|
||||
|
||||
static void ubi_fastmap_close(struct ubi_device *ubi)
|
||||
{
|
||||
int i;
|
||||
|
||||
flush_work(&ubi->fm_work);
|
||||
return_unused_pool_pebs(ubi, &ubi->fm_pool);
|
||||
return_unused_pool_pebs(ubi, &ubi->fm_wl_pool);
|
||||
|
||||
if (ubi->fm) {
|
||||
for (i = 0; i < ubi->fm->used_blocks; i++)
|
||||
kfree(ubi->fm->e[i]);
|
||||
}
|
||||
kfree(ubi->fm);
|
||||
}
|
||||
|
||||
/**
|
||||
* may_reserve_for_fm - tests whether a PEB shall be reserved for fastmap.
|
||||
* See find_mean_wl_entry()
|
||||
*
|
||||
* @ubi: UBI device description object
|
||||
* @e: physical eraseblock to return
|
||||
* @root: RB tree to test against.
|
||||
*/
|
||||
static struct ubi_wl_entry *may_reserve_for_fm(struct ubi_device *ubi,
|
||||
struct ubi_wl_entry *e,
|
||||
struct rb_root *root) {
|
||||
if (e && !ubi->fm_disabled && !ubi->fm &&
|
||||
e->pnum < UBI_FM_MAX_START)
|
||||
e = rb_entry(rb_next(root->rb_node),
|
||||
struct ubi_wl_entry, u.rb);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Linutronix GmbH
|
||||
* Copyright (c) 2014 sigma star gmbh
|
||||
* Author: Richard Weinberger <richard@nod.at>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
|
@ -16,6 +17,69 @@
|
|||
#include <linux/crc32.h>
|
||||
#include "ubi.h"
|
||||
|
||||
/**
|
||||
* init_seen - allocate memory for used for debugging.
|
||||
* @ubi: UBI device description object
|
||||
*/
|
||||
static inline int *init_seen(struct ubi_device *ubi)
|
||||
{
|
||||
int *ret;
|
||||
|
||||
if (!ubi_dbg_chk_fastmap(ubi))
|
||||
return NULL;
|
||||
|
||||
ret = kcalloc(ubi->peb_count, sizeof(int), GFP_KERNEL);
|
||||
if (!ret)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* free_seen - free the seen logic integer array.
|
||||
* @seen: integer array of @ubi->peb_count size
|
||||
*/
|
||||
static inline void free_seen(int *seen)
|
||||
{
|
||||
kfree(seen);
|
||||
}
|
||||
|
||||
/**
|
||||
* set_seen - mark a PEB as seen.
|
||||
* @ubi: UBI device description object
|
||||
* @pnum: The PEB to be makred as seen
|
||||
* @seen: integer array of @ubi->peb_count size
|
||||
*/
|
||||
static inline void set_seen(struct ubi_device *ubi, int pnum, int *seen)
|
||||
{
|
||||
if (!ubi_dbg_chk_fastmap(ubi) || !seen)
|
||||
return;
|
||||
|
||||
seen[pnum] = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* self_check_seen - check whether all PEB have been seen by fastmap.
|
||||
* @ubi: UBI device description object
|
||||
* @seen: integer array of @ubi->peb_count size
|
||||
*/
|
||||
static int self_check_seen(struct ubi_device *ubi, int *seen)
|
||||
{
|
||||
int pnum, ret = 0;
|
||||
|
||||
if (!ubi_dbg_chk_fastmap(ubi) || !seen)
|
||||
return 0;
|
||||
|
||||
for (pnum = 0; pnum < ubi->peb_count; pnum++) {
|
||||
if (!seen[pnum] && ubi->lookuptbl[pnum]) {
|
||||
ubi_err(ubi, "self-check failed for PEB %d, fastmap didn't see it", pnum);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_calc_fm_size - calculates the fastmap size in bytes for an UBI device.
|
||||
* @ubi: UBI device description object
|
||||
|
|
@ -136,14 +200,15 @@ static struct ubi_ainf_volume *add_vol(struct ubi_attach_info *ai, int vol_id,
|
|||
if (!av)
|
||||
goto out;
|
||||
|
||||
av->highest_lnum = av->leb_count = 0;
|
||||
av->highest_lnum = av->leb_count = av->used_ebs = 0;
|
||||
av->vol_id = vol_id;
|
||||
av->used_ebs = used_ebs;
|
||||
av->data_pad = data_pad;
|
||||
av->last_data_size = last_eb_bytes;
|
||||
av->compat = 0;
|
||||
av->vol_type = vol_type;
|
||||
av->root = RB_ROOT;
|
||||
if (av->vol_type == UBI_STATIC_VOLUME)
|
||||
av->used_ebs = used_ebs;
|
||||
|
||||
dbg_bld("found volume (ID %i)", vol_id);
|
||||
|
||||
|
|
@ -362,6 +427,7 @@ static void unmap_peb(struct ubi_attach_info *ai, int pnum)
|
|||
aeb = rb_entry(node2, struct ubi_ainf_peb, u.rb);
|
||||
if (aeb->pnum == pnum) {
|
||||
rb_erase(&aeb->u.rb, &av->root);
|
||||
av->leb_count--;
|
||||
kmem_cache_free(ai->aeb_slab_cache, aeb);
|
||||
return;
|
||||
}
|
||||
|
|
@ -376,7 +442,6 @@ static void unmap_peb(struct ubi_attach_info *ai, int pnum)
|
|||
* @pebs: an array of all PEB numbers in the to be scanned pool
|
||||
* @pool_size: size of the pool (number of entries in @pebs)
|
||||
* @max_sqnum: pointer to the maximal sequence number
|
||||
* @eba_orphans: list of PEBs which need to be scanned
|
||||
* @free: list of PEBs which are most likely free (and go into @ai->free)
|
||||
*
|
||||
* Returns 0 on success, if the pool is unusable UBI_BAD_FASTMAP is returned.
|
||||
|
|
@ -384,12 +449,12 @@ static void unmap_peb(struct ubi_attach_info *ai, int pnum)
|
|||
*/
|
||||
static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
||||
int *pebs, int pool_size, unsigned long long *max_sqnum,
|
||||
struct list_head *eba_orphans, struct list_head *free)
|
||||
struct list_head *free)
|
||||
{
|
||||
struct ubi_vid_hdr *vh;
|
||||
struct ubi_ec_hdr *ech;
|
||||
struct ubi_ainf_peb *new_aeb, *tmp_aeb;
|
||||
int i, pnum, err, found_orphan, ret = 0;
|
||||
struct ubi_ainf_peb *new_aeb;
|
||||
int i, pnum, err, ret = 0;
|
||||
|
||||
ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
|
||||
if (!ech)
|
||||
|
|
@ -457,18 +522,6 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
|||
if (err == UBI_IO_BITFLIPS)
|
||||
scrub = 1;
|
||||
|
||||
found_orphan = 0;
|
||||
list_for_each_entry(tmp_aeb, eba_orphans, u.list) {
|
||||
if (tmp_aeb->pnum == pnum) {
|
||||
found_orphan = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found_orphan) {
|
||||
list_del(&tmp_aeb->u.list);
|
||||
kmem_cache_free(ai->aeb_slab_cache, tmp_aeb);
|
||||
}
|
||||
|
||||
new_aeb = kmem_cache_alloc(ai->aeb_slab_cache,
|
||||
GFP_KERNEL);
|
||||
if (!new_aeb) {
|
||||
|
|
@ -543,10 +596,9 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
|
|||
struct ubi_attach_info *ai,
|
||||
struct ubi_fastmap_layout *fm)
|
||||
{
|
||||
struct list_head used, eba_orphans, free;
|
||||
struct list_head used, free;
|
||||
struct ubi_ainf_volume *av;
|
||||
struct ubi_ainf_peb *aeb, *tmp_aeb, *_tmp_aeb;
|
||||
struct ubi_ec_hdr *ech;
|
||||
struct ubi_fm_sb *fmsb;
|
||||
struct ubi_fm_hdr *fmhdr;
|
||||
struct ubi_fm_scan_pool *fmpl1, *fmpl2;
|
||||
|
|
@ -560,22 +612,8 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
|
|||
|
||||
INIT_LIST_HEAD(&used);
|
||||
INIT_LIST_HEAD(&free);
|
||||
INIT_LIST_HEAD(&eba_orphans);
|
||||
INIT_LIST_HEAD(&ai->corr);
|
||||
INIT_LIST_HEAD(&ai->free);
|
||||
INIT_LIST_HEAD(&ai->erase);
|
||||
INIT_LIST_HEAD(&ai->alien);
|
||||
ai->volumes = RB_ROOT;
|
||||
ai->min_ec = UBI_MAX_ERASECOUNTER;
|
||||
|
||||
ai->aeb_slab_cache = kmem_cache_create("ubi_ainf_peb_slab",
|
||||
sizeof(struct ubi_ainf_peb),
|
||||
0, 0, NULL);
|
||||
if (!ai->aeb_slab_cache) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fmsb = (struct ubi_fm_sb *)(fm_raw);
|
||||
ai->max_sqnum = fmsb->sqnum;
|
||||
fm_pos += sizeof(struct ubi_fm_sb);
|
||||
|
|
@ -741,28 +779,9 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
|
|||
}
|
||||
}
|
||||
|
||||
/* This can happen if a PEB is already in an EBA known
|
||||
* by this fastmap but the PEB itself is not in the used
|
||||
* list.
|
||||
* In this case the PEB can be within the fastmap pool
|
||||
* or while writing the fastmap it was in the protection
|
||||
* queue.
|
||||
*/
|
||||
if (!aeb) {
|
||||
aeb = kmem_cache_alloc(ai->aeb_slab_cache,
|
||||
GFP_KERNEL);
|
||||
if (!aeb) {
|
||||
ret = -ENOMEM;
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
aeb->lnum = j;
|
||||
aeb->pnum = be32_to_cpu(fm_eba->pnum[j]);
|
||||
aeb->ec = -1;
|
||||
aeb->scrub = aeb->copy_flag = aeb->sqnum = 0;
|
||||
list_add_tail(&aeb->u.list, &eba_orphans);
|
||||
continue;
|
||||
ubi_err(ubi, "PEB %i is in EBA but not in used list", pnum);
|
||||
goto fail_bad;
|
||||
}
|
||||
|
||||
aeb->lnum = j;
|
||||
|
|
@ -775,49 +794,13 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
|
|||
dbg_bld("inserting PEB:%i (LEB %i) to vol %i",
|
||||
aeb->pnum, aeb->lnum, av->vol_id);
|
||||
}
|
||||
|
||||
ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
|
||||
if (!ech) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &eba_orphans,
|
||||
u.list) {
|
||||
int err;
|
||||
|
||||
if (ubi_io_is_bad(ubi, tmp_aeb->pnum)) {
|
||||
ubi_err(ubi, "bad PEB in fastmap EBA orphan list");
|
||||
ret = UBI_BAD_FASTMAP;
|
||||
kfree(ech);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = ubi_io_read_ec_hdr(ubi, tmp_aeb->pnum, ech, 0);
|
||||
if (err && err != UBI_IO_BITFLIPS) {
|
||||
ubi_err(ubi, "unable to read EC header! PEB:%i err:%i",
|
||||
tmp_aeb->pnum, err);
|
||||
ret = err > 0 ? UBI_BAD_FASTMAP : err;
|
||||
kfree(ech);
|
||||
|
||||
goto fail;
|
||||
} else if (err == UBI_IO_BITFLIPS)
|
||||
tmp_aeb->scrub = 1;
|
||||
|
||||
tmp_aeb->ec = be64_to_cpu(ech->ec);
|
||||
assign_aeb_to_av(ai, tmp_aeb, av);
|
||||
}
|
||||
|
||||
kfree(ech);
|
||||
}
|
||||
|
||||
ret = scan_pool(ubi, ai, fmpl1->pebs, pool_size, &max_sqnum,
|
||||
&eba_orphans, &free);
|
||||
ret = scan_pool(ubi, ai, fmpl1->pebs, pool_size, &max_sqnum, &free);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
ret = scan_pool(ubi, ai, fmpl2->pebs, wl_pool_size, &max_sqnum,
|
||||
&eba_orphans, &free);
|
||||
ret = scan_pool(ubi, ai, fmpl2->pebs, wl_pool_size, &max_sqnum, &free);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
|
|
@ -827,8 +810,9 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
|
|||
list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &free, u.list)
|
||||
list_move_tail(&tmp_aeb->u.list, &ai->free);
|
||||
|
||||
ubi_assert(list_empty(&used));
|
||||
ubi_assert(list_empty(&eba_orphans));
|
||||
list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &used, u.list)
|
||||
list_move_tail(&tmp_aeb->u.list, &ai->erase);
|
||||
|
||||
ubi_assert(list_empty(&free));
|
||||
|
||||
/*
|
||||
|
|
@ -850,10 +834,6 @@ fail:
|
|||
list_del(&tmp_aeb->u.list);
|
||||
kmem_cache_free(ai->aeb_slab_cache, tmp_aeb);
|
||||
}
|
||||
list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &eba_orphans, u.list) {
|
||||
list_del(&tmp_aeb->u.list);
|
||||
kmem_cache_free(ai->aeb_slab_cache, tmp_aeb);
|
||||
}
|
||||
list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &free, u.list) {
|
||||
list_del(&tmp_aeb->u.list);
|
||||
kmem_cache_free(ai->aeb_slab_cache, tmp_aeb);
|
||||
|
|
@ -884,7 +864,7 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
|||
__be32 crc, tmp_crc;
|
||||
unsigned long long sqnum = 0;
|
||||
|
||||
mutex_lock(&ubi->fm_mutex);
|
||||
down_write(&ubi->fm_protect);
|
||||
memset(ubi->fm_buf, 0, ubi->fm_size);
|
||||
|
||||
fmsb = kmalloc(sizeof(*fmsb), GFP_KERNEL);
|
||||
|
|
@ -1075,7 +1055,7 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
|||
ubi_free_vid_hdr(ubi, vh);
|
||||
kfree(ech);
|
||||
out:
|
||||
mutex_unlock(&ubi->fm_mutex);
|
||||
up_write(&ubi->fm_protect);
|
||||
if (ret == UBI_BAD_FASTMAP)
|
||||
ubi_err(ubi, "Attach by fastmap failed, doing a full scan!");
|
||||
return ret;
|
||||
|
|
@ -1107,13 +1087,14 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
|
|||
struct ubi_fm_ec *fec;
|
||||
struct ubi_fm_volhdr *fvh;
|
||||
struct ubi_fm_eba *feba;
|
||||
struct rb_node *node;
|
||||
struct ubi_wl_entry *wl_e;
|
||||
struct ubi_volume *vol;
|
||||
struct ubi_vid_hdr *avhdr, *dvhdr;
|
||||
struct ubi_work *ubi_wrk;
|
||||
struct rb_node *tmp_rb;
|
||||
int ret, i, j, free_peb_count, used_peb_count, vol_count;
|
||||
int scrub_peb_count, erase_peb_count;
|
||||
int *seen_pebs = NULL;
|
||||
|
||||
fm_raw = ubi->fm_buf;
|
||||
memset(ubi->fm_buf, 0, ubi->fm_size);
|
||||
|
|
@ -1130,6 +1111,12 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
|
|||
goto out_kfree;
|
||||
}
|
||||
|
||||
seen_pebs = init_seen(ubi);
|
||||
if (IS_ERR(seen_pebs)) {
|
||||
ret = PTR_ERR(seen_pebs);
|
||||
goto out_kfree;
|
||||
}
|
||||
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
spin_lock(&ubi->wl_lock);
|
||||
|
||||
|
|
@ -1160,8 +1147,10 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
|
|||
fmpl1->size = cpu_to_be16(ubi->fm_pool.size);
|
||||
fmpl1->max_size = cpu_to_be16(ubi->fm_pool.max_size);
|
||||
|
||||
for (i = 0; i < ubi->fm_pool.size; i++)
|
||||
for (i = 0; i < ubi->fm_pool.size; i++) {
|
||||
fmpl1->pebs[i] = cpu_to_be32(ubi->fm_pool.pebs[i]);
|
||||
set_seen(ubi, ubi->fm_pool.pebs[i], seen_pebs);
|
||||
}
|
||||
|
||||
fmpl2 = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
|
||||
fm_pos += sizeof(*fmpl2);
|
||||
|
|
@ -1169,14 +1158,16 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
|
|||
fmpl2->size = cpu_to_be16(ubi->fm_wl_pool.size);
|
||||
fmpl2->max_size = cpu_to_be16(ubi->fm_wl_pool.max_size);
|
||||
|
||||
for (i = 0; i < ubi->fm_wl_pool.size; i++)
|
||||
for (i = 0; i < ubi->fm_wl_pool.size; i++) {
|
||||
fmpl2->pebs[i] = cpu_to_be32(ubi->fm_wl_pool.pebs[i]);
|
||||
set_seen(ubi, ubi->fm_wl_pool.pebs[i], seen_pebs);
|
||||
}
|
||||
|
||||
for (node = rb_first(&ubi->free); node; node = rb_next(node)) {
|
||||
wl_e = rb_entry(node, struct ubi_wl_entry, u.rb);
|
||||
ubi_for_each_free_peb(ubi, wl_e, tmp_rb) {
|
||||
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
|
||||
|
||||
fec->pnum = cpu_to_be32(wl_e->pnum);
|
||||
set_seen(ubi, wl_e->pnum, seen_pebs);
|
||||
fec->ec = cpu_to_be32(wl_e->ec);
|
||||
|
||||
free_peb_count++;
|
||||
|
|
@ -1185,11 +1176,11 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
|
|||
}
|
||||
fmh->free_peb_count = cpu_to_be32(free_peb_count);
|
||||
|
||||
for (node = rb_first(&ubi->used); node; node = rb_next(node)) {
|
||||
wl_e = rb_entry(node, struct ubi_wl_entry, u.rb);
|
||||
ubi_for_each_used_peb(ubi, wl_e, tmp_rb) {
|
||||
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
|
||||
|
||||
fec->pnum = cpu_to_be32(wl_e->pnum);
|
||||
set_seen(ubi, wl_e->pnum, seen_pebs);
|
||||
fec->ec = cpu_to_be32(wl_e->ec);
|
||||
|
||||
used_peb_count++;
|
||||
|
|
@ -1197,25 +1188,24 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
|
|||
ubi_assert(fm_pos <= ubi->fm_size);
|
||||
}
|
||||
|
||||
for (i = 0; i < UBI_PROT_QUEUE_LEN; i++) {
|
||||
list_for_each_entry(wl_e, &ubi->pq[i], u.list) {
|
||||
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
|
||||
|
||||
fec->pnum = cpu_to_be32(wl_e->pnum);
|
||||
fec->ec = cpu_to_be32(wl_e->ec);
|
||||
|
||||
used_peb_count++;
|
||||
fm_pos += sizeof(*fec);
|
||||
ubi_assert(fm_pos <= ubi->fm_size);
|
||||
}
|
||||
}
|
||||
fmh->used_peb_count = cpu_to_be32(used_peb_count);
|
||||
|
||||
for (node = rb_first(&ubi->scrub); node; node = rb_next(node)) {
|
||||
wl_e = rb_entry(node, struct ubi_wl_entry, u.rb);
|
||||
ubi_for_each_protected_peb(ubi, i, wl_e) {
|
||||
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
|
||||
|
||||
fec->pnum = cpu_to_be32(wl_e->pnum);
|
||||
set_seen(ubi, wl_e->pnum, seen_pebs);
|
||||
fec->ec = cpu_to_be32(wl_e->ec);
|
||||
|
||||
used_peb_count++;
|
||||
fm_pos += sizeof(*fec);
|
||||
ubi_assert(fm_pos <= ubi->fm_size);
|
||||
}
|
||||
fmh->used_peb_count = cpu_to_be32(used_peb_count);
|
||||
|
||||
ubi_for_each_scrub_peb(ubi, wl_e, tmp_rb) {
|
||||
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
|
||||
|
||||
fec->pnum = cpu_to_be32(wl_e->pnum);
|
||||
set_seen(ubi, wl_e->pnum, seen_pebs);
|
||||
fec->ec = cpu_to_be32(wl_e->ec);
|
||||
|
||||
scrub_peb_count++;
|
||||
|
|
@ -1233,6 +1223,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
|
|||
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
|
||||
|
||||
fec->pnum = cpu_to_be32(wl_e->pnum);
|
||||
set_seen(ubi, wl_e->pnum, seen_pebs);
|
||||
fec->ec = cpu_to_be32(wl_e->ec);
|
||||
|
||||
erase_peb_count++;
|
||||
|
|
@ -1292,6 +1283,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
|
|||
|
||||
for (i = 0; i < new_fm->used_blocks; i++) {
|
||||
fmsb->block_loc[i] = cpu_to_be32(new_fm->e[i]->pnum);
|
||||
set_seen(ubi, new_fm->e[i]->pnum, seen_pebs);
|
||||
fmsb->block_ec[i] = cpu_to_be32(new_fm->e[i]->ec);
|
||||
}
|
||||
|
||||
|
|
@ -1325,11 +1317,13 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
|
|||
ubi_assert(new_fm);
|
||||
ubi->fm = new_fm;
|
||||
|
||||
ret = self_check_seen(ubi, seen_pebs);
|
||||
dbg_bld("fastmap written!");
|
||||
|
||||
out_kfree:
|
||||
ubi_free_vid_hdr(ubi, avhdr);
|
||||
ubi_free_vid_hdr(ubi, dvhdr);
|
||||
free_seen(seen_pebs);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1384,31 +1378,87 @@ out:
|
|||
/**
|
||||
* invalidate_fastmap - destroys a fastmap.
|
||||
* @ubi: UBI device object
|
||||
* @fm: the fastmap to be destroyed
|
||||
*
|
||||
* This function ensures that upon next UBI attach a full scan
|
||||
* is issued. We need this if UBI is about to write a new fastmap
|
||||
* but is unable to do so. In this case we have two options:
|
||||
* a) Make sure that the current fastmap will not be usued upon
|
||||
* attach time and contine or b) fall back to RO mode to have the
|
||||
* current fastmap in a valid state.
|
||||
* Returns 0 on success, < 0 indicates an internal error.
|
||||
*/
|
||||
static int invalidate_fastmap(struct ubi_device *ubi,
|
||||
struct ubi_fastmap_layout *fm)
|
||||
static int invalidate_fastmap(struct ubi_device *ubi)
|
||||
{
|
||||
int ret;
|
||||
struct ubi_vid_hdr *vh;
|
||||
struct ubi_fastmap_layout *fm;
|
||||
struct ubi_wl_entry *e;
|
||||
struct ubi_vid_hdr *vh = NULL;
|
||||
|
||||
ret = erase_block(ubi, fm->e[0]->pnum);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!ubi->fm)
|
||||
return 0;
|
||||
|
||||
ubi->fm = NULL;
|
||||
|
||||
ret = -ENOMEM;
|
||||
fm = kzalloc(sizeof(*fm), GFP_KERNEL);
|
||||
if (!fm)
|
||||
goto out;
|
||||
|
||||
vh = new_fm_vhdr(ubi, UBI_FM_SB_VOLUME_ID);
|
||||
if (!vh)
|
||||
return -ENOMEM;
|
||||
goto out_free_fm;
|
||||
|
||||
/* deleting the current fastmap SB is not enough, an old SB may exist,
|
||||
* so create a (corrupted) SB such that fastmap will find it and fall
|
||||
* back to scanning mode in any case */
|
||||
ret = -ENOSPC;
|
||||
e = ubi_wl_get_fm_peb(ubi, 1);
|
||||
if (!e)
|
||||
goto out_free_fm;
|
||||
|
||||
/*
|
||||
* Create fake fastmap such that UBI will fall back
|
||||
* to scanning mode.
|
||||
*/
|
||||
vh->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
||||
ret = ubi_io_write_vid_hdr(ubi, fm->e[0]->pnum, vh);
|
||||
ret = ubi_io_write_vid_hdr(ubi, e->pnum, vh);
|
||||
if (ret < 0) {
|
||||
ubi_wl_put_fm_peb(ubi, e, 0, 0);
|
||||
goto out_free_fm;
|
||||
}
|
||||
|
||||
fm->used_blocks = 1;
|
||||
fm->e[0] = e;
|
||||
|
||||
ubi->fm = fm;
|
||||
|
||||
out:
|
||||
ubi_free_vid_hdr(ubi, vh);
|
||||
return ret;
|
||||
|
||||
out_free_fm:
|
||||
kfree(fm);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/**
|
||||
* return_fm_pebs - returns all PEBs used by a fastmap back to the
|
||||
* WL sub-system.
|
||||
* @ubi: UBI device object
|
||||
* @fm: fastmap layout object
|
||||
*/
|
||||
static void return_fm_pebs(struct ubi_device *ubi,
|
||||
struct ubi_fastmap_layout *fm)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!fm)
|
||||
return;
|
||||
|
||||
for (i = 0; i < fm->used_blocks; i++) {
|
||||
if (fm->e[i]) {
|
||||
ubi_wl_put_fm_peb(ubi, fm->e[i], i,
|
||||
fm->to_be_tortured[i]);
|
||||
fm->e[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1420,45 +1470,32 @@ static int invalidate_fastmap(struct ubi_device *ubi,
|
|||
*/
|
||||
int ubi_update_fastmap(struct ubi_device *ubi)
|
||||
{
|
||||
int ret, i;
|
||||
int ret, i, j;
|
||||
struct ubi_fastmap_layout *new_fm, *old_fm;
|
||||
struct ubi_wl_entry *tmp_e;
|
||||
|
||||
mutex_lock(&ubi->fm_mutex);
|
||||
down_write(&ubi->fm_protect);
|
||||
|
||||
ubi_refill_pools(ubi);
|
||||
|
||||
if (ubi->ro_mode || ubi->fm_disabled) {
|
||||
mutex_unlock(&ubi->fm_mutex);
|
||||
up_write(&ubi->fm_protect);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = ubi_ensure_anchor_pebs(ubi);
|
||||
if (ret) {
|
||||
mutex_unlock(&ubi->fm_mutex);
|
||||
up_write(&ubi->fm_protect);
|
||||
return ret;
|
||||
}
|
||||
|
||||
new_fm = kzalloc(sizeof(*new_fm), GFP_KERNEL);
|
||||
if (!new_fm) {
|
||||
mutex_unlock(&ubi->fm_mutex);
|
||||
up_write(&ubi->fm_protect);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
new_fm->used_blocks = ubi->fm_size / ubi->leb_size;
|
||||
|
||||
for (i = 0; i < new_fm->used_blocks; i++) {
|
||||
new_fm->e[i] = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL);
|
||||
if (!new_fm->e[i]) {
|
||||
while (i--)
|
||||
kfree(new_fm->e[i]);
|
||||
|
||||
kfree(new_fm);
|
||||
mutex_unlock(&ubi->fm_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
old_fm = ubi->fm;
|
||||
ubi->fm = NULL;
|
||||
|
||||
|
|
@ -1473,37 +1510,49 @@ int ubi_update_fastmap(struct ubi_device *ubi)
|
|||
tmp_e = ubi_wl_get_fm_peb(ubi, 0);
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
|
||||
if (!tmp_e && !old_fm) {
|
||||
int j;
|
||||
ubi_err(ubi, "could not get any free erase block");
|
||||
if (!tmp_e) {
|
||||
if (old_fm && old_fm->e[i]) {
|
||||
ret = erase_block(ubi, old_fm->e[i]->pnum);
|
||||
if (ret < 0) {
|
||||
ubi_err(ubi, "could not erase old fastmap PEB");
|
||||
|
||||
for (j = 1; j < i; j++)
|
||||
ubi_wl_put_fm_peb(ubi, new_fm->e[j], j, 0);
|
||||
for (j = 1; j < i; j++) {
|
||||
ubi_wl_put_fm_peb(ubi, new_fm->e[j],
|
||||
j, 0);
|
||||
new_fm->e[j] = NULL;
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
new_fm->e[i] = old_fm->e[i];
|
||||
old_fm->e[i] = NULL;
|
||||
} else {
|
||||
ubi_err(ubi, "could not get any free erase block");
|
||||
|
||||
ret = -ENOSPC;
|
||||
goto err;
|
||||
} else if (!tmp_e && old_fm) {
|
||||
ret = erase_block(ubi, old_fm->e[i]->pnum);
|
||||
if (ret < 0) {
|
||||
int j;
|
||||
for (j = 1; j < i; j++) {
|
||||
ubi_wl_put_fm_peb(ubi, new_fm->e[j], j, 0);
|
||||
new_fm->e[j] = NULL;
|
||||
}
|
||||
|
||||
for (j = 1; j < i; j++)
|
||||
ubi_wl_put_fm_peb(ubi, new_fm->e[j],
|
||||
j, 0);
|
||||
|
||||
ubi_err(ubi, "could not erase old fastmap PEB");
|
||||
ret = -ENOSPC;
|
||||
goto err;
|
||||
}
|
||||
|
||||
new_fm->e[i]->pnum = old_fm->e[i]->pnum;
|
||||
new_fm->e[i]->ec = old_fm->e[i]->ec;
|
||||
} else {
|
||||
new_fm->e[i]->pnum = tmp_e->pnum;
|
||||
new_fm->e[i]->ec = tmp_e->ec;
|
||||
new_fm->e[i] = tmp_e;
|
||||
|
||||
if (old_fm)
|
||||
if (old_fm && old_fm->e[i]) {
|
||||
ubi_wl_put_fm_peb(ubi, old_fm->e[i], i,
|
||||
old_fm->to_be_tortured[i]);
|
||||
old_fm->e[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Old fastmap is larger than the new one */
|
||||
if (old_fm && new_fm->used_blocks < old_fm->used_blocks) {
|
||||
for (i = new_fm->used_blocks; i < old_fm->used_blocks; i++) {
|
||||
ubi_wl_put_fm_peb(ubi, old_fm->e[i], i,
|
||||
old_fm->to_be_tortured[i]);
|
||||
old_fm->e[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1516,67 +1565,67 @@ int ubi_update_fastmap(struct ubi_device *ubi)
|
|||
if (!tmp_e) {
|
||||
ret = erase_block(ubi, old_fm->e[0]->pnum);
|
||||
if (ret < 0) {
|
||||
int i;
|
||||
ubi_err(ubi, "could not erase old anchor PEB");
|
||||
|
||||
for (i = 1; i < new_fm->used_blocks; i++)
|
||||
for (i = 1; i < new_fm->used_blocks; i++) {
|
||||
ubi_wl_put_fm_peb(ubi, new_fm->e[i],
|
||||
i, 0);
|
||||
new_fm->e[i] = NULL;
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
|
||||
new_fm->e[0]->pnum = old_fm->e[0]->pnum;
|
||||
new_fm->e[0] = old_fm->e[0];
|
||||
new_fm->e[0]->ec = ret;
|
||||
old_fm->e[0] = NULL;
|
||||
} else {
|
||||
/* we've got a new anchor PEB, return the old one */
|
||||
ubi_wl_put_fm_peb(ubi, old_fm->e[0], 0,
|
||||
old_fm->to_be_tortured[0]);
|
||||
|
||||
new_fm->e[0]->pnum = tmp_e->pnum;
|
||||
new_fm->e[0]->ec = tmp_e->ec;
|
||||
new_fm->e[0] = tmp_e;
|
||||
old_fm->e[0] = NULL;
|
||||
}
|
||||
} else {
|
||||
if (!tmp_e) {
|
||||
int i;
|
||||
ubi_err(ubi, "could not find any anchor PEB");
|
||||
|
||||
for (i = 1; i < new_fm->used_blocks; i++)
|
||||
for (i = 1; i < new_fm->used_blocks; i++) {
|
||||
ubi_wl_put_fm_peb(ubi, new_fm->e[i], i, 0);
|
||||
new_fm->e[i] = NULL;
|
||||
}
|
||||
|
||||
ret = -ENOSPC;
|
||||
goto err;
|
||||
}
|
||||
|
||||
new_fm->e[0]->pnum = tmp_e->pnum;
|
||||
new_fm->e[0]->ec = tmp_e->ec;
|
||||
new_fm->e[0] = tmp_e;
|
||||
}
|
||||
|
||||
down_write(&ubi->work_sem);
|
||||
down_write(&ubi->fm_sem);
|
||||
down_write(&ubi->fm_eba_sem);
|
||||
ret = ubi_write_fastmap(ubi, new_fm);
|
||||
up_write(&ubi->fm_sem);
|
||||
up_write(&ubi->fm_eba_sem);
|
||||
up_write(&ubi->work_sem);
|
||||
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&ubi->fm_mutex);
|
||||
up_write(&ubi->fm_protect);
|
||||
kfree(old_fm);
|
||||
return ret;
|
||||
|
||||
err:
|
||||
kfree(new_fm);
|
||||
|
||||
ubi_warn(ubi, "Unable to write new fastmap, err=%i", ret);
|
||||
|
||||
ret = 0;
|
||||
if (old_fm) {
|
||||
ret = invalidate_fastmap(ubi, old_fm);
|
||||
if (ret < 0)
|
||||
ubi_err(ubi, "Unable to invalidiate current fastmap!");
|
||||
else if (ret)
|
||||
ret = 0;
|
||||
ret = invalidate_fastmap(ubi);
|
||||
if (ret < 0) {
|
||||
ubi_err(ubi, "Unable to invalidiate current fastmap!");
|
||||
ubi_ro_mode(ubi);
|
||||
} else {
|
||||
return_fm_pebs(ubi, old_fm);
|
||||
return_fm_pebs(ubi, new_fm);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
kfree(new_fm);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -859,6 +859,9 @@ int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
if (ubi_dbg_power_cut(ubi, POWER_CUT_EC_WRITE))
|
||||
return -EROFS;
|
||||
|
||||
err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize);
|
||||
return err;
|
||||
}
|
||||
|
|
@ -1106,6 +1109,9 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
if (ubi_dbg_power_cut(ubi, POWER_CUT_VID_WRITE))
|
||||
return -EROFS;
|
||||
|
||||
p = (char *)vid_hdr - ubi->vid_hdr_shift;
|
||||
err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset,
|
||||
ubi->vid_hdr_alsize);
|
||||
|
|
|
|||
|
|
@ -403,8 +403,6 @@ struct ubi_vtbl_record {
|
|||
#define UBI_FM_MIN_POOL_SIZE 8
|
||||
#define UBI_FM_MAX_POOL_SIZE 256
|
||||
|
||||
#define UBI_FM_WL_POOL_SIZE 25
|
||||
|
||||
/**
|
||||
* struct ubi_fm_sb - UBI fastmap super block
|
||||
* @magic: fastmap super block magic number (%UBI_FM_SB_MAGIC)
|
||||
|
|
|
|||
|
|
@ -151,6 +151,17 @@ enum {
|
|||
UBI_BAD_FASTMAP,
|
||||
};
|
||||
|
||||
/*
|
||||
* Flags for emulate_power_cut in ubi_debug_info
|
||||
*
|
||||
* POWER_CUT_EC_WRITE: Emulate a power cut when writing an EC header
|
||||
* POWER_CUT_VID_WRITE: Emulate a power cut when writing a VID header
|
||||
*/
|
||||
enum {
|
||||
POWER_CUT_EC_WRITE = 0x01,
|
||||
POWER_CUT_VID_WRITE = 0x02,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_wl_entry - wear-leveling entry.
|
||||
* @u.rb: link in the corresponding (free/used) RB-tree
|
||||
|
|
@ -356,30 +367,48 @@ struct ubi_wl_entry;
|
|||
*
|
||||
* @chk_gen: if UBI general extra checks are enabled
|
||||
* @chk_io: if UBI I/O extra checks are enabled
|
||||
* @chk_fastmap: if UBI fastmap extra checks are enabled
|
||||
* @disable_bgt: disable the background task for testing purposes
|
||||
* @emulate_bitflips: emulate bit-flips for testing purposes
|
||||
* @emulate_io_failures: emulate write/erase failures for testing purposes
|
||||
* @emulate_power_cut: emulate power cut for testing purposes
|
||||
* @power_cut_counter: count down for writes left until emulated power cut
|
||||
* @power_cut_min: minimum number of writes before emulating a power cut
|
||||
* @power_cut_max: maximum number of writes until emulating a power cut
|
||||
* @dfs_dir_name: name of debugfs directory containing files of this UBI device
|
||||
* @dfs_dir: direntry object of the UBI device debugfs directory
|
||||
* @dfs_chk_gen: debugfs knob to enable UBI general extra checks
|
||||
* @dfs_chk_io: debugfs knob to enable UBI I/O extra checks
|
||||
* @dfs_chk_fastmap: debugfs knob to enable UBI fastmap extra checks
|
||||
* @dfs_disable_bgt: debugfs knob to disable the background task
|
||||
* @dfs_emulate_bitflips: debugfs knob to emulate bit-flips
|
||||
* @dfs_emulate_io_failures: debugfs knob to emulate write/erase failures
|
||||
* @dfs_emulate_power_cut: debugfs knob to emulate power cuts
|
||||
* @dfs_power_cut_min: debugfs knob for minimum writes before power cut
|
||||
* @dfs_power_cut_max: debugfs knob for maximum writes until power cut
|
||||
*/
|
||||
struct ubi_debug_info {
|
||||
unsigned int chk_gen:1;
|
||||
unsigned int chk_io:1;
|
||||
unsigned int chk_fastmap:1;
|
||||
unsigned int disable_bgt:1;
|
||||
unsigned int emulate_bitflips:1;
|
||||
unsigned int emulate_io_failures:1;
|
||||
unsigned int emulate_power_cut:2;
|
||||
unsigned int power_cut_counter;
|
||||
unsigned int power_cut_min;
|
||||
unsigned int power_cut_max;
|
||||
char dfs_dir_name[UBI_DFS_DIR_LEN + 1];
|
||||
struct dentry *dfs_dir;
|
||||
struct dentry *dfs_chk_gen;
|
||||
struct dentry *dfs_chk_io;
|
||||
struct dentry *dfs_chk_fastmap;
|
||||
struct dentry *dfs_disable_bgt;
|
||||
struct dentry *dfs_emulate_bitflips;
|
||||
struct dentry *dfs_emulate_io_failures;
|
||||
struct dentry *dfs_emulate_power_cut;
|
||||
struct dentry *dfs_power_cut_min;
|
||||
struct dentry *dfs_power_cut_max;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -426,11 +455,13 @@ struct ubi_debug_info {
|
|||
* @fm_pool: in-memory data structure of the fastmap pool
|
||||
* @fm_wl_pool: in-memory data structure of the fastmap pool used by the WL
|
||||
* sub-system
|
||||
* @fm_mutex: serializes ubi_update_fastmap() and protects @fm_buf
|
||||
* @fm_protect: serializes ubi_update_fastmap(), protects @fm_buf and makes sure
|
||||
* that critical sections cannot be interrupted by ubi_update_fastmap()
|
||||
* @fm_buf: vmalloc()'d buffer which holds the raw fastmap
|
||||
* @fm_size: fastmap size in bytes
|
||||
* @fm_sem: allows ubi_update_fastmap() to block EBA table changes
|
||||
* @fm_eba_sem: allows ubi_update_fastmap() to block EBA table changes
|
||||
* @fm_work: fastmap work queue
|
||||
* @fm_work_scheduled: non-zero if fastmap work was scheduled
|
||||
*
|
||||
* @used: RB-tree of used physical eraseblocks
|
||||
* @erroneous: RB-tree of erroneous used physical eraseblocks
|
||||
|
|
@ -442,7 +473,8 @@ struct ubi_debug_info {
|
|||
* @pq_head: protection queue head
|
||||
* @wl_lock: protects the @used, @free, @pq, @pq_head, @lookuptbl, @move_from,
|
||||
* @move_to, @move_to_put @erase_pending, @wl_scheduled, @works,
|
||||
* @erroneous, and @erroneous_peb_count fields
|
||||
* @erroneous, @erroneous_peb_count, @fm_work_scheduled, @fm_pool,
|
||||
* and @fm_wl_pool fields
|
||||
* @move_mutex: serializes eraseblock moves
|
||||
* @work_sem: used to wait for all the scheduled works to finish and prevent
|
||||
* new works from being submitted
|
||||
|
|
@ -479,7 +511,7 @@ struct ubi_debug_info {
|
|||
* @vid_hdr_offset: starting offset of the volume identifier header (might be
|
||||
* unaligned)
|
||||
* @vid_hdr_aloffset: starting offset of the VID header aligned to
|
||||
* @hdrs_min_io_size
|
||||
* @hdrs_min_io_size
|
||||
* @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset
|
||||
* @bad_allowed: whether the MTD device admits of bad physical eraseblocks or
|
||||
* not
|
||||
|
|
@ -532,11 +564,12 @@ struct ubi_device {
|
|||
struct ubi_fastmap_layout *fm;
|
||||
struct ubi_fm_pool fm_pool;
|
||||
struct ubi_fm_pool fm_wl_pool;
|
||||
struct rw_semaphore fm_sem;
|
||||
struct mutex fm_mutex;
|
||||
struct rw_semaphore fm_eba_sem;
|
||||
struct rw_semaphore fm_protect;
|
||||
void *fm_buf;
|
||||
size_t fm_size;
|
||||
struct work_struct fm_work;
|
||||
int fm_work_scheduled;
|
||||
|
||||
/* Wear-leveling sub-system's stuff */
|
||||
struct rb_root used;
|
||||
|
|
@ -868,10 +901,14 @@ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb,
|
|||
int pnum, const struct ubi_vid_hdr *vid_hdr);
|
||||
|
||||
/* fastmap.c */
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
size_t ubi_calc_fm_size(struct ubi_device *ubi);
|
||||
int ubi_update_fastmap(struct ubi_device *ubi);
|
||||
int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
||||
int fm_anchor);
|
||||
#else
|
||||
static inline int ubi_update_fastmap(struct ubi_device *ubi) { return 0; }
|
||||
#endif
|
||||
|
||||
/* block.c */
|
||||
#ifdef CONFIG_MTD_UBI_BLOCK
|
||||
|
|
@ -892,6 +929,42 @@ static inline int ubiblock_remove(struct ubi_volume_info *vi)
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ubi_for_each_free_peb - walk the UBI free RB tree.
|
||||
* @ubi: UBI device description object
|
||||
* @e: a pointer to a ubi_wl_entry to use as cursor
|
||||
* @pos: a pointer to RB-tree entry type to use as a loop counter
|
||||
*/
|
||||
#define ubi_for_each_free_peb(ubi, e, tmp_rb) \
|
||||
ubi_rb_for_each_entry((tmp_rb), (e), &(ubi)->free, u.rb)
|
||||
|
||||
/*
|
||||
* ubi_for_each_used_peb - walk the UBI used RB tree.
|
||||
* @ubi: UBI device description object
|
||||
* @e: a pointer to a ubi_wl_entry to use as cursor
|
||||
* @pos: a pointer to RB-tree entry type to use as a loop counter
|
||||
*/
|
||||
#define ubi_for_each_used_peb(ubi, e, tmp_rb) \
|
||||
ubi_rb_for_each_entry((tmp_rb), (e), &(ubi)->used, u.rb)
|
||||
|
||||
/*
|
||||
* ubi_for_each_scub_peb - walk the UBI scub RB tree.
|
||||
* @ubi: UBI device description object
|
||||
* @e: a pointer to a ubi_wl_entry to use as cursor
|
||||
* @pos: a pointer to RB-tree entry type to use as a loop counter
|
||||
*/
|
||||
#define ubi_for_each_scrub_peb(ubi, e, tmp_rb) \
|
||||
ubi_rb_for_each_entry((tmp_rb), (e), &(ubi)->scrub, u.rb)
|
||||
|
||||
/*
|
||||
* ubi_for_each_protected_peb - walk the UBI protection queue.
|
||||
* @ubi: UBI device description object
|
||||
* @i: a integer used as counter
|
||||
* @e: a pointer to a ubi_wl_entry to use as cursor
|
||||
*/
|
||||
#define ubi_for_each_protected_peb(ubi, i, e) \
|
||||
for ((i) = 0; (i) < UBI_PROT_QUEUE_LEN; (i)++) \
|
||||
list_for_each_entry((e), &(ubi->pq[(i)]), u.list)
|
||||
|
||||
/*
|
||||
* ubi_rb_for_each_entry - walk an RB-tree.
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@
|
|||
#include <linux/freezer.h>
|
||||
#include <linux/kthread.h>
|
||||
#include "ubi.h"
|
||||
#include "wl.h"
|
||||
|
||||
/* Number of physical eraseblocks reserved for wear-leveling purposes */
|
||||
#define WL_RESERVED_PEBS 1
|
||||
|
|
@ -140,42 +141,6 @@ static int self_check_in_wl_tree(const struct ubi_device *ubi,
|
|||
static int self_check_in_pq(const struct ubi_device *ubi,
|
||||
struct ubi_wl_entry *e);
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
/**
|
||||
* update_fastmap_work_fn - calls ubi_update_fastmap from a work queue
|
||||
* @wrk: the work description object
|
||||
*/
|
||||
static void update_fastmap_work_fn(struct work_struct *wrk)
|
||||
{
|
||||
struct ubi_device *ubi = container_of(wrk, struct ubi_device, fm_work);
|
||||
ubi_update_fastmap(ubi);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_ubi_is_fm_block - returns 1 if a PEB is currently used in a fastmap.
|
||||
* @ubi: UBI device description object
|
||||
* @pnum: the to be checked PEB
|
||||
*/
|
||||
static int ubi_is_fm_block(struct ubi_device *ubi, int pnum)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!ubi->fm)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ubi->fm->used_blocks; i++)
|
||||
if (ubi->fm->e[i]->pnum == pnum)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int ubi_is_fm_block(struct ubi_device *ubi, int pnum)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* wl_tree_add - add a wear-leveling entry to a WL RB-tree.
|
||||
* @e: the wear-leveling entry to add
|
||||
|
|
@ -212,6 +177,20 @@ static void wl_tree_add(struct ubi_wl_entry *e, struct rb_root *root)
|
|||
rb_insert_color(&e->u.rb, root);
|
||||
}
|
||||
|
||||
/**
|
||||
* wl_tree_destroy - destroy a wear-leveling entry.
|
||||
* @ubi: UBI device description object
|
||||
* @e: the wear-leveling entry to add
|
||||
*
|
||||
* This function destroys a wear leveling entry and removes
|
||||
* the reference from the lookup table.
|
||||
*/
|
||||
static void wl_entry_destroy(struct ubi_device *ubi, struct ubi_wl_entry *e)
|
||||
{
|
||||
ubi->lookuptbl[e->pnum] = NULL;
|
||||
kmem_cache_free(ubi_wl_entry_slab, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* do_work - do one pending work.
|
||||
* @ubi: UBI device description object
|
||||
|
|
@ -259,33 +238,6 @@ static int do_work(struct ubi_device *ubi)
|
|||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* produce_free_peb - produce a free physical eraseblock.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* This function tries to make a free PEB by means of synchronous execution of
|
||||
* pending works. This may be needed if, for example the background thread is
|
||||
* disabled. Returns zero in case of success and a negative error code in case
|
||||
* of failure.
|
||||
*/
|
||||
static int produce_free_peb(struct ubi_device *ubi)
|
||||
{
|
||||
int err;
|
||||
|
||||
while (!ubi->free.rb_node && ubi->works_count) {
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
|
||||
dbg_wl("do one work synchronously");
|
||||
err = do_work(ubi);
|
||||
|
||||
spin_lock(&ubi->wl_lock);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* in_wl_tree - check if wear-leveling entry is present in a WL RB-tree.
|
||||
* @e: the wear-leveling entry to check
|
||||
|
|
@ -409,119 +361,32 @@ static struct ubi_wl_entry *find_mean_wl_entry(struct ubi_device *ubi,
|
|||
if (last->ec - first->ec < WL_FREE_MAX_DIFF) {
|
||||
e = rb_entry(root->rb_node, struct ubi_wl_entry, u.rb);
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
/* If no fastmap has been written and this WL entry can be used
|
||||
* as anchor PEB, hold it back and return the second best
|
||||
* WL entry such that fastmap can use the anchor PEB later. */
|
||||
if (e && !ubi->fm_disabled && !ubi->fm &&
|
||||
e->pnum < UBI_FM_MAX_START)
|
||||
e = rb_entry(rb_next(root->rb_node),
|
||||
struct ubi_wl_entry, u.rb);
|
||||
#endif
|
||||
e = may_reserve_for_fm(ubi, e, root);
|
||||
} else
|
||||
e = find_wl_entry(ubi, root, WL_FREE_MAX_DIFF/2);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
/**
|
||||
* find_anchor_wl_entry - find wear-leveling entry to used as anchor PEB.
|
||||
* @root: the RB-tree where to look for
|
||||
*/
|
||||
static struct ubi_wl_entry *find_anchor_wl_entry(struct rb_root *root)
|
||||
{
|
||||
struct rb_node *p;
|
||||
struct ubi_wl_entry *e, *victim = NULL;
|
||||
int max_ec = UBI_MAX_ERASECOUNTER;
|
||||
|
||||
ubi_rb_for_each_entry(p, e, root, u.rb) {
|
||||
if (e->pnum < UBI_FM_MAX_START && e->ec < max_ec) {
|
||||
victim = e;
|
||||
max_ec = e->ec;
|
||||
}
|
||||
}
|
||||
|
||||
return victim;
|
||||
}
|
||||
|
||||
static int anchor_pebs_avalible(struct rb_root *root)
|
||||
{
|
||||
struct rb_node *p;
|
||||
struct ubi_wl_entry *e;
|
||||
|
||||
ubi_rb_for_each_entry(p, e, root, u.rb)
|
||||
if (e->pnum < UBI_FM_MAX_START)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_wl_get_fm_peb - find a physical erase block with a given maximal number.
|
||||
* @ubi: UBI device description object
|
||||
* @anchor: This PEB will be used as anchor PEB by fastmap
|
||||
*
|
||||
* The function returns a physical erase block with a given maximal number
|
||||
* and removes it from the wl subsystem.
|
||||
* Must be called with wl_lock held!
|
||||
*/
|
||||
struct ubi_wl_entry *ubi_wl_get_fm_peb(struct ubi_device *ubi, int anchor)
|
||||
{
|
||||
struct ubi_wl_entry *e = NULL;
|
||||
|
||||
if (!ubi->free.rb_node || (ubi->free_count - ubi->beb_rsvd_pebs < 1))
|
||||
goto out;
|
||||
|
||||
if (anchor)
|
||||
e = find_anchor_wl_entry(&ubi->free);
|
||||
else
|
||||
e = find_mean_wl_entry(ubi, &ubi->free);
|
||||
|
||||
if (!e)
|
||||
goto out;
|
||||
|
||||
self_check_in_wl_tree(ubi, e, &ubi->free);
|
||||
|
||||
/* remove it from the free list,
|
||||
* the wl subsystem does no longer know this erase block */
|
||||
rb_erase(&e->u.rb, &ubi->free);
|
||||
ubi->free_count--;
|
||||
out:
|
||||
return e;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* __wl_get_peb - get a physical eraseblock.
|
||||
* wl_get_wle - get a mean wl entry to be used by ubi_wl_get_peb() or
|
||||
* refill_wl_user_pool().
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* This function returns a physical eraseblock in case of success and a
|
||||
* negative error code in case of failure.
|
||||
* This function returns a a wear leveling entry in case of success and
|
||||
* NULL in case of failure.
|
||||
*/
|
||||
static int __wl_get_peb(struct ubi_device *ubi)
|
||||
static struct ubi_wl_entry *wl_get_wle(struct ubi_device *ubi)
|
||||
{
|
||||
int err;
|
||||
struct ubi_wl_entry *e;
|
||||
|
||||
retry:
|
||||
if (!ubi->free.rb_node) {
|
||||
if (ubi->works_count == 0) {
|
||||
ubi_err(ubi, "no free eraseblocks");
|
||||
ubi_assert(list_empty(&ubi->works));
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
err = produce_free_peb(ubi);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
e = find_mean_wl_entry(ubi, &ubi->free);
|
||||
if (!e) {
|
||||
ubi_err(ubi, "no free eraseblocks");
|
||||
return -ENOSPC;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self_check_in_wl_tree(ubi, e, &ubi->free);
|
||||
|
|
@ -533,174 +398,10 @@ retry:
|
|||
rb_erase(&e->u.rb, &ubi->free);
|
||||
ubi->free_count--;
|
||||
dbg_wl("PEB %d EC %d", e->pnum, e->ec);
|
||||
#ifndef CONFIG_MTD_UBI_FASTMAP
|
||||
/* We have to enqueue e only if fastmap is disabled,
|
||||
* is fastmap enabled prot_queue_add() will be called by
|
||||
* ubi_wl_get_peb() after removing e from the pool. */
|
||||
prot_queue_add(ubi, e);
|
||||
#endif
|
||||
return e->pnum;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
/**
|
||||
* return_unused_pool_pebs - returns unused PEB to the free tree.
|
||||
* @ubi: UBI device description object
|
||||
* @pool: fastmap pool description object
|
||||
*/
|
||||
static void return_unused_pool_pebs(struct ubi_device *ubi,
|
||||
struct ubi_fm_pool *pool)
|
||||
{
|
||||
int i;
|
||||
struct ubi_wl_entry *e;
|
||||
|
||||
for (i = pool->used; i < pool->size; i++) {
|
||||
e = ubi->lookuptbl[pool->pebs[i]];
|
||||
wl_tree_add(e, &ubi->free);
|
||||
ubi->free_count++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* refill_wl_pool - refills all the fastmap pool used by the
|
||||
* WL sub-system.
|
||||
* @ubi: UBI device description object
|
||||
*/
|
||||
static void refill_wl_pool(struct ubi_device *ubi)
|
||||
{
|
||||
struct ubi_wl_entry *e;
|
||||
struct ubi_fm_pool *pool = &ubi->fm_wl_pool;
|
||||
|
||||
return_unused_pool_pebs(ubi, pool);
|
||||
|
||||
for (pool->size = 0; pool->size < pool->max_size; pool->size++) {
|
||||
if (!ubi->free.rb_node ||
|
||||
(ubi->free_count - ubi->beb_rsvd_pebs < 5))
|
||||
break;
|
||||
|
||||
e = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF);
|
||||
self_check_in_wl_tree(ubi, e, &ubi->free);
|
||||
rb_erase(&e->u.rb, &ubi->free);
|
||||
ubi->free_count--;
|
||||
|
||||
pool->pebs[pool->size] = e->pnum;
|
||||
}
|
||||
pool->used = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* refill_wl_user_pool - refills all the fastmap pool used by ubi_wl_get_peb.
|
||||
* @ubi: UBI device description object
|
||||
*/
|
||||
static void refill_wl_user_pool(struct ubi_device *ubi)
|
||||
{
|
||||
struct ubi_fm_pool *pool = &ubi->fm_pool;
|
||||
|
||||
return_unused_pool_pebs(ubi, pool);
|
||||
|
||||
for (pool->size = 0; pool->size < pool->max_size; pool->size++) {
|
||||
pool->pebs[pool->size] = __wl_get_peb(ubi);
|
||||
if (pool->pebs[pool->size] < 0)
|
||||
break;
|
||||
}
|
||||
pool->used = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_refill_pools - refills all fastmap PEB pools.
|
||||
* @ubi: UBI device description object
|
||||
*/
|
||||
void ubi_refill_pools(struct ubi_device *ubi)
|
||||
{
|
||||
spin_lock(&ubi->wl_lock);
|
||||
refill_wl_pool(ubi);
|
||||
refill_wl_user_pool(ubi);
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
}
|
||||
|
||||
/* ubi_wl_get_peb - works exaclty like __wl_get_peb but keeps track of
|
||||
* the fastmap pool.
|
||||
*/
|
||||
int ubi_wl_get_peb(struct ubi_device *ubi)
|
||||
{
|
||||
int ret;
|
||||
struct ubi_fm_pool *pool = &ubi->fm_pool;
|
||||
struct ubi_fm_pool *wl_pool = &ubi->fm_wl_pool;
|
||||
|
||||
if (!pool->size || !wl_pool->size || pool->used == pool->size ||
|
||||
wl_pool->used == wl_pool->size)
|
||||
ubi_update_fastmap(ubi);
|
||||
|
||||
/* we got not a single free PEB */
|
||||
if (!pool->size)
|
||||
ret = -ENOSPC;
|
||||
else {
|
||||
spin_lock(&ubi->wl_lock);
|
||||
ret = pool->pebs[pool->used++];
|
||||
prot_queue_add(ubi, ubi->lookuptbl[ret]);
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* get_peb_for_wl - returns a PEB to be used internally by the WL sub-system.
|
||||
*
|
||||
* @ubi: UBI device description object
|
||||
*/
|
||||
static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi)
|
||||
{
|
||||
struct ubi_fm_pool *pool = &ubi->fm_wl_pool;
|
||||
int pnum;
|
||||
|
||||
if (pool->used == pool->size || !pool->size) {
|
||||
/* We cannot update the fastmap here because this
|
||||
* function is called in atomic context.
|
||||
* Let's fail here and refill/update it as soon as possible. */
|
||||
schedule_work(&ubi->fm_work);
|
||||
return NULL;
|
||||
} else {
|
||||
pnum = pool->pebs[pool->used++];
|
||||
return ubi->lookuptbl[pnum];
|
||||
}
|
||||
}
|
||||
#else
|
||||
static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi)
|
||||
{
|
||||
struct ubi_wl_entry *e;
|
||||
|
||||
e = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF);
|
||||
self_check_in_wl_tree(ubi, e, &ubi->free);
|
||||
ubi->free_count--;
|
||||
ubi_assert(ubi->free_count >= 0);
|
||||
rb_erase(&e->u.rb, &ubi->free);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
int ubi_wl_get_peb(struct ubi_device *ubi)
|
||||
{
|
||||
int peb, err;
|
||||
|
||||
spin_lock(&ubi->wl_lock);
|
||||
peb = __wl_get_peb(ubi);
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
|
||||
if (peb < 0)
|
||||
return peb;
|
||||
|
||||
err = ubi_self_check_all_ff(ubi, peb, ubi->vid_hdr_aloffset,
|
||||
ubi->peb_size - ubi->vid_hdr_aloffset);
|
||||
if (err) {
|
||||
ubi_err(ubi, "new PEB %d does not contain all 0xFF bytes",
|
||||
peb);
|
||||
return err;
|
||||
}
|
||||
|
||||
return peb;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* prot_queue_del - remove a physical eraseblock from the protection queue.
|
||||
* @ubi: UBI device description object
|
||||
|
|
@ -867,17 +568,6 @@ static void schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk)
|
|||
static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
|
||||
int shutdown);
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
/**
|
||||
* ubi_is_erase_work - checks whether a work is erase work.
|
||||
* @wrk: The work object to be checked
|
||||
*/
|
||||
int ubi_is_erase_work(struct ubi_work *wrk)
|
||||
{
|
||||
return wrk->func == erase_worker;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* schedule_erase - schedule an erase work.
|
||||
* @ubi: UBI device description object
|
||||
|
|
@ -895,7 +585,6 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
|
|||
struct ubi_work *wl_wrk;
|
||||
|
||||
ubi_assert(e);
|
||||
ubi_assert(!ubi_is_fm_block(ubi, e->pnum));
|
||||
|
||||
dbg_wl("schedule erasure of PEB %d, EC %d, torture %d",
|
||||
e->pnum, e->ec, torture);
|
||||
|
|
@ -942,51 +631,6 @@ static int do_sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
|
|||
return erase_worker(ubi, wl_wrk, 0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
/**
|
||||
* ubi_wl_put_fm_peb - returns a PEB used in a fastmap to the wear-leveling
|
||||
* sub-system.
|
||||
* see: ubi_wl_put_peb()
|
||||
*
|
||||
* @ubi: UBI device description object
|
||||
* @fm_e: physical eraseblock to return
|
||||
* @lnum: the last used logical eraseblock number for the PEB
|
||||
* @torture: if this physical eraseblock has to be tortured
|
||||
*/
|
||||
int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *fm_e,
|
||||
int lnum, int torture)
|
||||
{
|
||||
struct ubi_wl_entry *e;
|
||||
int vol_id, pnum = fm_e->pnum;
|
||||
|
||||
dbg_wl("PEB %d", pnum);
|
||||
|
||||
ubi_assert(pnum >= 0);
|
||||
ubi_assert(pnum < ubi->peb_count);
|
||||
|
||||
spin_lock(&ubi->wl_lock);
|
||||
e = ubi->lookuptbl[pnum];
|
||||
|
||||
/* This can happen if we recovered from a fastmap the very
|
||||
* first time and writing now a new one. In this case the wl system
|
||||
* has never seen any PEB used by the original fastmap.
|
||||
*/
|
||||
if (!e) {
|
||||
e = fm_e;
|
||||
ubi_assert(e->ec >= 0);
|
||||
ubi->lookuptbl[pnum] = e;
|
||||
} else {
|
||||
e->ec = fm_e->ec;
|
||||
kfree(fm_e);
|
||||
}
|
||||
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
|
||||
vol_id = lnum ? UBI_FM_DATA_VOLUME_ID : UBI_FM_SB_VOLUME_ID;
|
||||
return schedule_erase(ubi, e, vol_id, lnum, torture);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* wear_leveling_worker - wear-leveling worker function.
|
||||
* @ubi: UBI device description object
|
||||
|
|
@ -1002,7 +646,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
|
|||
int shutdown)
|
||||
{
|
||||
int err, scrubbing = 0, torture = 0, protect = 0, erroneous = 0;
|
||||
int vol_id = -1, uninitialized_var(lnum);
|
||||
int vol_id = -1, lnum = -1;
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
int anchor = wrk->anchor;
|
||||
#endif
|
||||
|
|
@ -1214,7 +858,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
|
|||
err = do_sync_erase(ubi, e1, vol_id, lnum, 0);
|
||||
if (err) {
|
||||
if (e2)
|
||||
kmem_cache_free(ubi_wl_entry_slab, e2);
|
||||
wl_entry_destroy(ubi, e2);
|
||||
goto out_ro;
|
||||
}
|
||||
|
||||
|
|
@ -1282,8 +926,8 @@ out_error:
|
|||
spin_unlock(&ubi->wl_lock);
|
||||
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
kmem_cache_free(ubi_wl_entry_slab, e1);
|
||||
kmem_cache_free(ubi_wl_entry_slab, e2);
|
||||
wl_entry_destroy(ubi, e1);
|
||||
wl_entry_destroy(ubi, e2);
|
||||
|
||||
out_ro:
|
||||
ubi_ro_mode(ubi);
|
||||
|
|
@ -1369,38 +1013,6 @@ out_unlock:
|
|||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
/**
|
||||
* ubi_ensure_anchor_pebs - schedule wear-leveling to produce an anchor PEB.
|
||||
* @ubi: UBI device description object
|
||||
*/
|
||||
int ubi_ensure_anchor_pebs(struct ubi_device *ubi)
|
||||
{
|
||||
struct ubi_work *wrk;
|
||||
|
||||
spin_lock(&ubi->wl_lock);
|
||||
if (ubi->wl_scheduled) {
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
return 0;
|
||||
}
|
||||
ubi->wl_scheduled = 1;
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
|
||||
wrk = kmalloc(sizeof(struct ubi_work), GFP_NOFS);
|
||||
if (!wrk) {
|
||||
spin_lock(&ubi->wl_lock);
|
||||
ubi->wl_scheduled = 0;
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
wrk->anchor = 1;
|
||||
wrk->func = &wear_leveling_worker;
|
||||
schedule_ubi_work(ubi, wrk);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* erase_worker - physical eraseblock erase worker function.
|
||||
* @ubi: UBI device description object
|
||||
|
|
@ -1425,15 +1037,13 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
|
|||
if (shutdown) {
|
||||
dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec);
|
||||
kfree(wl_wrk);
|
||||
kmem_cache_free(ubi_wl_entry_slab, e);
|
||||
wl_entry_destroy(ubi, e);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dbg_wl("erase PEB %d EC %d LEB %d:%d",
|
||||
pnum, e->ec, wl_wrk->vol_id, wl_wrk->lnum);
|
||||
|
||||
ubi_assert(!ubi_is_fm_block(ubi, e->pnum));
|
||||
|
||||
err = sync_erase(ubi, e, wl_wrk->torture);
|
||||
if (!err) {
|
||||
/* Fine, we've erased it successfully */
|
||||
|
|
@ -1471,7 +1081,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
|
|||
return err;
|
||||
}
|
||||
|
||||
kmem_cache_free(ubi_wl_entry_slab, e);
|
||||
wl_entry_destroy(ubi, e);
|
||||
if (err != -EIO)
|
||||
/*
|
||||
* If this is not %-EIO, we have no idea what to do. Scheduling
|
||||
|
|
@ -1563,6 +1173,8 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum,
|
|||
ubi_assert(pnum >= 0);
|
||||
ubi_assert(pnum < ubi->peb_count);
|
||||
|
||||
down_read(&ubi->fm_protect);
|
||||
|
||||
retry:
|
||||
spin_lock(&ubi->wl_lock);
|
||||
e = ubi->lookuptbl[pnum];
|
||||
|
|
@ -1593,6 +1205,7 @@ retry:
|
|||
ubi_assert(!ubi->move_to_put);
|
||||
ubi->move_to_put = 1;
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
up_read(&ubi->fm_protect);
|
||||
return 0;
|
||||
} else {
|
||||
if (in_wl_tree(e, &ubi->used)) {
|
||||
|
|
@ -1614,6 +1227,7 @@ retry:
|
|||
ubi_err(ubi, "PEB %d not found", pnum);
|
||||
ubi_ro_mode(ubi);
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
up_read(&ubi->fm_protect);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
|
@ -1627,6 +1241,7 @@ retry:
|
|||
spin_unlock(&ubi->wl_lock);
|
||||
}
|
||||
|
||||
up_read(&ubi->fm_protect);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1758,9 +1373,10 @@ int ubi_wl_flush(struct ubi_device *ubi, int vol_id, int lnum)
|
|||
|
||||
/**
|
||||
* tree_destroy - destroy an RB-tree.
|
||||
* @ubi: UBI device description object
|
||||
* @root: the root of the tree to destroy
|
||||
*/
|
||||
static void tree_destroy(struct rb_root *root)
|
||||
static void tree_destroy(struct ubi_device *ubi, struct rb_root *root)
|
||||
{
|
||||
struct rb_node *rb;
|
||||
struct ubi_wl_entry *e;
|
||||
|
|
@ -1782,7 +1398,7 @@ static void tree_destroy(struct rb_root *root)
|
|||
rb->rb_right = NULL;
|
||||
}
|
||||
|
||||
kmem_cache_free(ubi_wl_entry_slab, e);
|
||||
wl_entry_destroy(ubi, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1850,6 +1466,9 @@ int ubi_thread(void *u)
|
|||
*/
|
||||
static void shutdown_work(struct ubi_device *ubi)
|
||||
{
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
flush_work(&ubi->fm_work);
|
||||
#endif
|
||||
while (!list_empty(&ubi->works)) {
|
||||
struct ubi_work *wrk;
|
||||
|
||||
|
|
@ -1883,9 +1502,6 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
|
|||
init_rwsem(&ubi->work_sem);
|
||||
ubi->max_ec = ai->max_ec;
|
||||
INIT_LIST_HEAD(&ubi->works);
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
INIT_WORK(&ubi->fm_work, update_fastmap_work_fn);
|
||||
#endif
|
||||
|
||||
sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num);
|
||||
|
||||
|
|
@ -1907,10 +1523,9 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
|
|||
|
||||
e->pnum = aeb->pnum;
|
||||
e->ec = aeb->ec;
|
||||
ubi_assert(!ubi_is_fm_block(ubi, e->pnum));
|
||||
ubi->lookuptbl[e->pnum] = e;
|
||||
if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0)) {
|
||||
kmem_cache_free(ubi_wl_entry_slab, e);
|
||||
wl_entry_destroy(ubi, e);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
|
|
@ -1928,7 +1543,6 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
|
|||
e->pnum = aeb->pnum;
|
||||
e->ec = aeb->ec;
|
||||
ubi_assert(e->ec >= 0);
|
||||
ubi_assert(!ubi_is_fm_block(ubi, e->pnum));
|
||||
|
||||
wl_tree_add(e, &ubi->free);
|
||||
ubi->free_count++;
|
||||
|
|
@ -1966,17 +1580,20 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
|
|||
|
||||
dbg_wl("found %i PEBs", found_pebs);
|
||||
|
||||
if (ubi->fm)
|
||||
if (ubi->fm) {
|
||||
ubi_assert(ubi->good_peb_count == \
|
||||
found_pebs + ubi->fm->used_blocks);
|
||||
|
||||
for (i = 0; i < ubi->fm->used_blocks; i++) {
|
||||
e = ubi->fm->e[i];
|
||||
ubi->lookuptbl[e->pnum] = e;
|
||||
}
|
||||
}
|
||||
else
|
||||
ubi_assert(ubi->good_peb_count == found_pebs);
|
||||
|
||||
reserved_pebs = WL_RESERVED_PEBS;
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
/* Reserve enough LEBs to store two fastmaps. */
|
||||
reserved_pebs += (ubi->fm_size / ubi->leb_size) * 2;
|
||||
#endif
|
||||
ubi_fastmap_init(ubi, &reserved_pebs);
|
||||
|
||||
if (ubi->avail_pebs < reserved_pebs) {
|
||||
ubi_err(ubi, "no enough physical eraseblocks (%d, need %d)",
|
||||
|
|
@ -1998,9 +1615,9 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
|
|||
|
||||
out_free:
|
||||
shutdown_work(ubi);
|
||||
tree_destroy(&ubi->used);
|
||||
tree_destroy(&ubi->free);
|
||||
tree_destroy(&ubi->scrub);
|
||||
tree_destroy(ubi, &ubi->used);
|
||||
tree_destroy(ubi, &ubi->free);
|
||||
tree_destroy(ubi, &ubi->scrub);
|
||||
kfree(ubi->lookuptbl);
|
||||
return err;
|
||||
}
|
||||
|
|
@ -2017,7 +1634,7 @@ static void protection_queue_destroy(struct ubi_device *ubi)
|
|||
for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) {
|
||||
list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) {
|
||||
list_del(&e->u.list);
|
||||
kmem_cache_free(ubi_wl_entry_slab, e);
|
||||
wl_entry_destroy(ubi, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2029,12 +1646,13 @@ static void protection_queue_destroy(struct ubi_device *ubi)
|
|||
void ubi_wl_close(struct ubi_device *ubi)
|
||||
{
|
||||
dbg_wl("close the WL sub-system");
|
||||
ubi_fastmap_close(ubi);
|
||||
shutdown_work(ubi);
|
||||
protection_queue_destroy(ubi);
|
||||
tree_destroy(&ubi->used);
|
||||
tree_destroy(&ubi->erroneous);
|
||||
tree_destroy(&ubi->free);
|
||||
tree_destroy(&ubi->scrub);
|
||||
tree_destroy(ubi, &ubi->used);
|
||||
tree_destroy(ubi, &ubi->erroneous);
|
||||
tree_destroy(ubi, &ubi->free);
|
||||
tree_destroy(ubi, &ubi->scrub);
|
||||
kfree(ubi->lookuptbl);
|
||||
}
|
||||
|
||||
|
|
@ -2133,3 +1751,94 @@ static int self_check_in_pq(const struct ubi_device *ubi,
|
|||
dump_stack();
|
||||
return -EINVAL;
|
||||
}
|
||||
#ifndef CONFIG_MTD_UBI_FASTMAP
|
||||
static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi)
|
||||
{
|
||||
struct ubi_wl_entry *e;
|
||||
|
||||
e = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF);
|
||||
self_check_in_wl_tree(ubi, e, &ubi->free);
|
||||
ubi->free_count--;
|
||||
ubi_assert(ubi->free_count >= 0);
|
||||
rb_erase(&e->u.rb, &ubi->free);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* produce_free_peb - produce a free physical eraseblock.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* This function tries to make a free PEB by means of synchronous execution of
|
||||
* pending works. This may be needed if, for example the background thread is
|
||||
* disabled. Returns zero in case of success and a negative error code in case
|
||||
* of failure.
|
||||
*/
|
||||
static int produce_free_peb(struct ubi_device *ubi)
|
||||
{
|
||||
int err;
|
||||
|
||||
while (!ubi->free.rb_node && ubi->works_count) {
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
|
||||
dbg_wl("do one work synchronously");
|
||||
err = do_work(ubi);
|
||||
|
||||
spin_lock(&ubi->wl_lock);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_wl_get_peb - get a physical eraseblock.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* This function returns a physical eraseblock in case of success and a
|
||||
* negative error code in case of failure.
|
||||
* Returns with ubi->fm_eba_sem held in read mode!
|
||||
*/
|
||||
int ubi_wl_get_peb(struct ubi_device *ubi)
|
||||
{
|
||||
int err;
|
||||
struct ubi_wl_entry *e;
|
||||
|
||||
retry:
|
||||
down_read(&ubi->fm_eba_sem);
|
||||
spin_lock(&ubi->wl_lock);
|
||||
if (!ubi->free.rb_node) {
|
||||
if (ubi->works_count == 0) {
|
||||
ubi_err(ubi, "no free eraseblocks");
|
||||
ubi_assert(list_empty(&ubi->works));
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
err = produce_free_peb(ubi);
|
||||
if (err < 0) {
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
return err;
|
||||
}
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
goto retry;
|
||||
|
||||
}
|
||||
e = wl_get_wle(ubi);
|
||||
prot_queue_add(ubi, e);
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
|
||||
err = ubi_self_check_all_ff(ubi, e->pnum, ubi->vid_hdr_aloffset,
|
||||
ubi->peb_size - ubi->vid_hdr_aloffset);
|
||||
if (err) {
|
||||
ubi_err(ubi, "new PEB %d does not contain all 0xFF bytes", e->pnum);
|
||||
return err;
|
||||
}
|
||||
|
||||
return e->pnum;
|
||||
}
|
||||
#else
|
||||
#include "fastmap-wl.c"
|
||||
#endif
|
||||
|
|
|
|||
28
drivers/mtd/ubi/wl.h
Normal file
28
drivers/mtd/ubi/wl.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef UBI_WL_H
|
||||
#define UBI_WL_H
|
||||
#ifdef CONFIG_MTD_UBI_FASTMAP
|
||||
static int anchor_pebs_avalible(struct rb_root *root);
|
||||
static void update_fastmap_work_fn(struct work_struct *wrk);
|
||||
static struct ubi_wl_entry *find_anchor_wl_entry(struct rb_root *root);
|
||||
static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi);
|
||||
static void ubi_fastmap_close(struct ubi_device *ubi);
|
||||
static inline void ubi_fastmap_init(struct ubi_device *ubi, int *count)
|
||||
{
|
||||
/* Reserve enough LEBs to store two fastmaps. */
|
||||
*count += (ubi->fm_size / ubi->leb_size) * 2;
|
||||
INIT_WORK(&ubi->fm_work, update_fastmap_work_fn);
|
||||
}
|
||||
static struct ubi_wl_entry *may_reserve_for_fm(struct ubi_device *ubi,
|
||||
struct ubi_wl_entry *e,
|
||||
struct rb_root *root);
|
||||
#else /* !CONFIG_MTD_UBI_FASTMAP */
|
||||
static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi);
|
||||
static inline void ubi_fastmap_close(struct ubi_device *ubi) { }
|
||||
static inline void ubi_fastmap_init(struct ubi_device *ubi, int *count) { }
|
||||
static struct ubi_wl_entry *may_reserve_for_fm(struct ubi_device *ubi,
|
||||
struct ubi_wl_entry *e,
|
||||
struct rb_root *root) {
|
||||
return e;
|
||||
}
|
||||
#endif /* CONFIG_MTD_UBI_FASTMAP */
|
||||
#endif /* UBI_WL_H */
|
||||
Loading…
Reference in a new issue