This series brings a massive stabilization update for UBI Fastmap.
During the last weeks many issues have been identified and fixed.
Both power-cut and excessive run-time tests uncovered bugs which
got addressed. No on-disk layout change was needed.
Besides of plain bug fixes this series introduces also much better Fastmap
self checks. These can be enabled by the UBI debugfs knob "chk_fastmap".
It will enable checks which ensure that every PEB is known to Fastmap
while writing a fastmap to flash and will run an expensive check while attaching.
To have Fastmap checking enabled by default a new UBI module parameter was
added, "fm_debug". This parameter is useful for the attach self check. Upon
attach time no debugfs is available and therefore the check cannot be enabled.
The attach self check will load the fastmap data structure and will issue a full
scan to verify it. Therefore, don't enable it in production, it will make
attachment by Fastmap slow.
Power-cut related issues have been found by my power-cut emulation patches for UBI.
These patches are currently under internal review/polishing and will be published
next week.
Patch 29 to 35 are a massive cleanup as requested by Artem. It moves all Fastmap
specific code out of wl.c. Some Fastmap functions are very closely related to the WL
sub-system, these function are now in fastmap-wl.c located. The amount of Fastmap
specific #ifdefs have been reduced to a minimum.
After this series went mainline I'll create a list of stable patches and make sure
that all Fastmap fixes go into -stable.
Enjoy!
//richard
git://git.kernel.org/pub/scm/linux/kernel/git/rw/misc.git fastmap_upgrade1
[PATCH 01/35] UBI: Add initial support for fastmap self checks
[PATCH 02/35] UBI: Fix stale pointers in ubi->lookuptbl
[PATCH 03/35] UBI: Fastmap: Add self check to detect absent PEBs
[PATCH 04/35] UBI: Fastmap: Care about the protection queue
[PATCH 05/35] UBI: Fastmap: Ensure that only one fastmap work is
[PATCH 06/35] UBI: Fastmap: Ensure that all fastmap work is done upon
[PATCH 07/35] UBI: Fastmap: Fix races in ubi_wl_get_peb()
[PATCH 08/35] UBI: Split __wl_get_peb()
[PATCH 09/35] UBI: Fastmap: Make ubi_refill_pools() fair
[PATCH 10/35] UBI: Fastmap: Fix memory leaks while closing the WL
[PATCH 11/35] UBI: Fastmap: Don't allocate new ubi_wl_entry objects
[PATCH 12/35] UBI: Fastmap: Notify user in case of an
[PATCH 13/35] UBI: Fastmap: Wrap fastmap specific function in a ifdef
[PATCH 14/35] UBI: Fastmap: Fix fastmap usage in ubi_volume_notify()
[PATCH 15/35] UBI: Fastmap: Enhance fastmap checking
[PATCH 16/35] UBI: Fastmap: Fix memory leak while attaching
[PATCH 17/35] UBI: Remove alloc_ai() slab name from parameter list
[PATCH 18/35] UBI: Fastmap: Fix race in ubi_eba_atomic_leb_change()
[PATCH 19/35] UBI: Fastmap: Remove bogus ubi_assert()
[PATCH 20/35] UBI: Fastmap: Remove eba_orphans logic
[PATCH 21/35] UBI: Fastmap: Switch to ro mode if invalidate_fastmap()
[PATCH 22/35] UBI: Fastmap: Make WL pool size 50% of user pool size
[PATCH 23/35] UBI: Fastmap: Add new module parameter fm_debug
[PATCH 24/35] UBI: Fastmap: Fix leb_count unbalance
[PATCH 25/35] UBI: Fastmap: Fix race after ubi_wl_get_peb()
[PATCH 26/35] UBI: Fastmap: Set used_ebs only for static volumes
[PATCH 27/35] UBI: Fastmap: Locking updates
[PATCH 28/35] UBI: Fastmap: Make self_check_eba() depend on fastmap
[PATCH 29/35] UBI: Move fastmap specific functions out of wl.c
[PATCH 30/35] UBI: Add accessor functions for WL data structures
[PATCH 31/35] UBI: Fastmap: Wire up WL accessor functions
[PATCH 32/35] UBI: Fastmap: Introduce ubi_fastmap_init()
[PATCH 33/35] UBI: Fastmap: Introduce may_reserve_for_fm()
[PATCH 34/35] UBI: Fastmap: Remove else after return.
[PATCH 35/35] UBI: Fastmap: Add blank line after declarations
...otherwise the deferred work might run after datastructures
got freed and corrupt memory.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/wl.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index bd2e8d5..a06e31e 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -2047,6 +2047,9 @@ static void protection_queue_destroy(struct ubi_device *ubi)
void ubi_wl_close(struct ubi_device *ubi)
{
dbg_wl("close the WL sub-system");
+#ifdef CONFIG_MTD_UBI_FASTMAP
+ flush_work(&ubi->fm_work);
+#endif
shutdown_work(ubi);
protection_queue_destroy(ubi);
tree_destroy(&ubi->used);
--
1.8.4.5
If a LEB is unmapped we have to decrement leb_count as well.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/fastmap.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index 79b7387..af490c3 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -426,6 +426,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;
}
--
1.8.4.5
If fm_debug is set fastmap debugging is enabled by default.
This is useful if one wants to debug fastmap on an UBI device
with serves the rootfs.
The the UBI attach mechanism runs long before debugfs can be mounted
and chk_fastmap set.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/build.c | 5 +++++
drivers/mtd/ubi/debug.h | 5 +++++
2 files changed, 10 insertions(+)
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 666b2a0..a047bb4 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -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;
@@ -949,6 +950,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
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) {
@@ -1495,6 +1498,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");
diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h
index e99ef37..c6ea443 100644
--- a/drivers/mtd/ubi/debug.h
+++ b/drivers/mtd/ubi/debug.h
@@ -132,4 +132,9 @@ 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;
+}
#endif /* !__UBI_DEBUG_H__ */
--
1.8.4.5
Another checkpatch complaint:
WARNING: Missing a blank line after declarations
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/fastmap-wl.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/mtd/ubi/fastmap-wl.c b/drivers/mtd/ubi/fastmap-wl.c
index 828ea04..54e31a2 100644
--- a/drivers/mtd/ubi/fastmap-wl.c
+++ b/drivers/mtd/ubi/fastmap-wl.c
@@ -21,6 +21,7 @@
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;
--
1.8.4.5
...and kill another #ifdef.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/fastmap-wl.c | 19 +++++++++++++++++++
drivers/mtd/ubi/wl.c | 7 +------
drivers/mtd/ubi/wl.h | 8 ++++++++
3 files changed, 28 insertions(+), 6 deletions(-)
diff --git a/drivers/mtd/ubi/fastmap-wl.c b/drivers/mtd/ubi/fastmap-wl.c
index 06ed22e..f7e7ff6 100644
--- a/drivers/mtd/ubi/fastmap-wl.c
+++ b/drivers/mtd/ubi/fastmap-wl.c
@@ -352,3 +352,22 @@ static void ubi_fastmap_close(struct ubi_device *ubi)
}
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;
+}
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index ecb7402..de60d1d 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -347,15 +347,10 @@ 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);
diff --git a/drivers/mtd/ubi/wl.h b/drivers/mtd/ubi/wl.h
index 4046ccf..fbc2d8e 100644
--- a/drivers/mtd/ubi/wl.h
+++ b/drivers/mtd/ubi/wl.h
@@ -13,6 +13,9 @@ static inline void ubi_fastmap_init(struct ubi_device *ubi, int *count)
*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 int is_fm_block(struct ubi_device *ubi, int pnum)
@@ -21,5 +24,10 @@ static inline int is_fm_block(struct ubi_device *ubi, int pnum)
}
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 */
--
1.8.4.5
checkpatch.pl complains:
WARNING: else is not generally useful after a break or return
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/fastmap-wl.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/mtd/ubi/fastmap-wl.c b/drivers/mtd/ubi/fastmap-wl.c
index f7e7ff6..828ea04 100644
--- a/drivers/mtd/ubi/fastmap-wl.c
+++ b/drivers/mtd/ubi/fastmap-wl.c
@@ -253,10 +253,10 @@ static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi)
schedule_work(&ubi->fm_work);
}
return NULL;
- } else {
- pnum = pool->pebs[pool->used++];
- return ubi->lookuptbl[pnum];
}
+
+ pnum = pool->pebs[pool->used++];
+ return ubi->lookuptbl[pnum];
}
/**
--
1.8.4.5
Fastmap is tightly connected to the WL sub-system, many fastmap-specific
functionslive in wl.c.
To get rid of most #ifdefs in wl.c move this functions into a new file
and include it into wl.c
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/fastmap-wl.c | 354 ++++++++++++++++++++++++++++
drivers/mtd/ubi/wl.c | 547 ++++++++-----------------------------------
drivers/mtd/ubi/wl.h | 18 ++
3 files changed, 470 insertions(+), 449 deletions(-)
create mode 100644 drivers/mtd/ubi/fastmap-wl.c
create mode 100644 drivers/mtd/ubi/wl.h
diff --git a/drivers/mtd/ubi/fastmap-wl.c b/drivers/mtd/ubi/fastmap-wl.c
new file mode 100644
index 0000000..06ed22e
--- /dev/null
+++ b/drivers/mtd/ubi/fastmap-wl.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2012 Linutronix GmbH
+ * Copyright (c) 2014 sigma star gmbh
+ * Author: Richard Weinberger <[email protected]>
+ *
+ * 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);
+}
+
+/**
+ * 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 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;
+}
+
+/**
+ * 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) {
+ 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);
+ if (!pool->size || !wl_pool->size || 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("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 || !pool->size) {
+ spin_unlock(&ubi->wl_lock);
+ if (retried) {
+ ubi_err("Unable to get a free PEB from user WL pool");
+ ret = -ENOSPC;
+ goto out;
+ }
+ retried = 1;
+ goto again;
+ }
+
+ 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 || !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;
+ } else {
+ 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);
+}
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index ea3c79e..40d89a3 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -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,45 +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);
- spin_lock(&ubi->wl_lock);
- ubi->fm_work_scheduled = 0;
- spin_unlock(&ubi->wl_lock);
-}
-
-/**
- * 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
@@ -263,33 +225,6 @@ static int do_work(struct ubi_device *ubi)
}
/**
- * 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
* @root: the root of the tree
@@ -427,80 +362,12 @@ static struct ubi_wl_entry *find_mean_wl_entry(struct ubi_device *ubi,
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.
+ * wl_get_wle - Find a free PEB
* @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.
- * @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 free PEB and removes it from the free RB tree.
+ * In case of no free PEBs it returns NULL.
*/
static struct ubi_wl_entry *wl_get_wle(struct ubi_device *ubi)
{
@@ -525,212 +392,6 @@ static struct ubi_wl_entry *wl_get_wle(struct ubi_device *ubi)
return e;
}
-static int wl_get_peb(struct ubi_device *ubi)
-{
- int err;
- struct ubi_wl_entry *e;
-
-retry:
- if (!ubi->free.rb_node) {
- if (ubi->works_count == 0) {
- ubi_err("no free eraseblocks");
- ubi_assert(list_empty(&ubi->works));
- return -ENOSPC;
- }
-
- err = produce_free_peb(ubi);
- if (err < 0)
- return err;
- goto retry;
-
- }
-
- e = wl_get_wle(ubi);
- prot_queue_add(ubi, e);
-
- 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++;
- }
-}
-
-/**
- * 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) {
- 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 - works exaclty like __wl_get_peb but keeps track of
- * the fastmap pool.
- * 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);
- if (!pool->size || !wl_pool->size || 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("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 || !pool->size) {
- spin_unlock(&ubi->wl_lock);
- if (retried) {
- ubi_err("Unable to get a free PEB from user WL pool");
- ret = -ENOSPC;
- goto out;
- }
- retried = 1;
- goto again;
- }
-
- 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 || !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;
- } 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("new PEB %d does not contain all 0xFF bytes", peb);
- return err;
- }
-
- down_read(&ubi->fm_eba_sem);
- return peb;
-}
-#endif
-
/**
* prot_queue_del - remove a physical eraseblock from the protection queue.
* @ubi: UBI device description object
@@ -897,17 +558,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
@@ -925,7 +575,7 @@ 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));
+ ubi_assert(!is_fm_block(ubi, e->pnum));
dbg_wl("schedule erasure of PEB %d, EC %d, torture %d",
e->pnum, e->ec, torture);
@@ -972,48 +622,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;
- }
-
- 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
@@ -1407,38 +1015,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
@@ -1471,7 +1047,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
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));
+ ubi_assert(!is_fm_block(ubi, e->pnum));
err = sync_erase(ubi, e, wl_wrk->torture);
if (!err) {
@@ -1951,7 +1527,7 @@ 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_assert(!is_fm_block(ubi, e->pnum));
ubi->lookuptbl[e->pnum] = e;
if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0)) {
ubi->lookuptbl[e->pnum] = NULL;
@@ -1973,7 +1549,7 @@ 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));
+ ubi_assert(!is_fm_block(ubi, e->pnum));
wl_tree_add(e, &ubi->free);
ubi->free_count++;
@@ -2073,23 +1649,6 @@ static void protection_queue_destroy(struct ubi_device *ubi)
}
}
-static void ubi_fastmap_close(struct ubi_device *ubi)
-{
-#ifdef CONFIG_MTD_UBI_FASTMAP
- 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);
-#endif
-}
-
/**
* ubi_wl_close - close the wear-leveling sub-system.
* @ubi: UBI device description object
@@ -2202,3 +1761,93 @@ 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:
+ spin_lock(&ubi->wl_lock);
+ if (!ubi->free.rb_node) {
+ if (ubi->works_count == 0) {
+ ubi_err("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);
+ 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("new PEB %d does not contain all 0xFF bytes", e->pnum);
+ return err;
+ }
+
+ down_read(&ubi->fm_eba_sem);
+ return e->pnum;
+}
+#else
+#include "fastmap-wl.c"
+#endif
diff --git a/drivers/mtd/ubi/wl.h b/drivers/mtd/ubi/wl.h
new file mode 100644
index 0000000..db86814
--- /dev/null
+++ b/drivers/mtd/ubi/wl.h
@@ -0,0 +1,18 @@
+#ifndef UBI_WL_H
+#define UBI_WL_H
+#ifdef CONFIG_MTD_UBI_FASTMAP
+static int is_fm_block(struct ubi_device *ubi, int pnum);
+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);
+#else /* !CONFIG_MTD_UBI_FASTMAP */
+static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi);
+static inline int is_fm_block(struct ubi_device *ubi, int pnum)
+{
+ return 0;
+}
+static inline void ubi_fastmap_close(struct ubi_device *ubi) { }
+#endif /* CONFIG_MTD_UBI_FASTMAP */
+#endif /* UBI_WL_H */
--
1.8.4.5
...and kill another #ifdef in wl.c. :-)
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/wl.c | 8 +-------
drivers/mtd/ubi/wl.h | 7 +++++++
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 40d89a3..ecb7402 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -1503,9 +1503,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);
@@ -1600,10 +1597,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
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("no enough physical eraseblocks (%d, need %d)",
diff --git a/drivers/mtd/ubi/wl.h b/drivers/mtd/ubi/wl.h
index db86814..4046ccf 100644
--- a/drivers/mtd/ubi/wl.h
+++ b/drivers/mtd/ubi/wl.h
@@ -7,6 +7,12 @@ 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);
+}
#else /* !CONFIG_MTD_UBI_FASTMAP */
static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi);
static inline int is_fm_block(struct ubi_device *ubi, int pnum)
@@ -14,5 +20,6 @@ static inline int is_fm_block(struct ubi_device *ubi, int pnum)
return 0;
}
static inline void ubi_fastmap_close(struct ubi_device *ubi) { }
+static inline void ubi_fastmap_init(struct ubi_device *ubi, int *count) { }
#endif /* CONFIG_MTD_UBI_FASTMAP */
#endif /* UBI_WL_H */
--
1.8.4.5
Use the new WL accessor functions in fastmap.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/fastmap.c | 30 ++++++++++++------------------
1 file changed, 12 insertions(+), 18 deletions(-)
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index 9a8cf45..344f1db 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -1096,8 +1096,7 @@ 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_wl_entry *wl_e, *tmp_wl_e;
struct ubi_volume *vol;
struct ubi_vid_hdr *avhdr, *dvhdr;
struct ubi_work *ubi_wrk;
@@ -1172,8 +1171,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
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_wl_e) {
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
fec->pnum = cpu_to_be32(wl_e->pnum);
@@ -1186,8 +1184,7 @@ 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_wl_e) {
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
fec->pnum = cpu_to_be32(wl_e->pnum);
@@ -1199,23 +1196,20 @@ 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);
+ 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);
+ 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);
- }
+ 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_scrub_peb(ubi, wl_e, tmp_wl_e) {
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
fec->pnum = cpu_to_be32(wl_e->pnum);
--
1.8.4.5
Fastmap need access to various WL data structures as
fastmap tightly depends on WL.
To make the access less invasive add accessor functions.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/ubi.h | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 9a37fe3..e159aa1 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -895,6 +895,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
+ * @tmp_e: a pointer to a ubi_wl_entry to use as temporary storage
+ */
+#define ubi_for_each_free_peb(ubi, e, tmp_e) \
+ rbtree_postorder_for_each_entry_safe((e), (tmp_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
+ * @tmp_e: a pointer to a ubi_wl_entry to use as temporary storage
+ */
+#define ubi_for_each_used_peb(ubi, e, tmp_e) \
+ rbtree_postorder_for_each_entry_safe((e), (tmp_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
+ * @tmp_e: a pointer to a ubi_wl_entry to use as temporary storage
+ */
+#define ubi_for_each_scrub_peb(ubi, e, tmp_e) \
+ rbtree_postorder_for_each_entry_safe((e), (tmp_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.
--
1.8.4.5
a) Rename ubi->fm_sem to ubi->fm_eba_sem as this semaphore
protects EBA changes.
b) Turn ubi->fm_mutex into a rw semaphore. It will still serialize
fastmap writes but also ensures that ubi_wl_put_peb() is not
interrupted by a fastmap write. We use a rw semaphore to allow
ubi_wl_put_peb() still to be executed in parallel if no fastmap
write is happening.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/build.c | 4 ++--
drivers/mtd/ubi/eba.c | 44 ++++++++++++++++++++++----------------------
drivers/mtd/ubi/fastmap.c | 18 +++++++++---------
drivers/mtd/ubi/ubi.h | 9 +++++----
drivers/mtd/ubi/wl.c | 17 +++++++++++------
5 files changed, 49 insertions(+), 43 deletions(-)
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index a047bb4..813143a 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -969,8 +969,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("attaching mtd%d to ubi%d", mtd->index, ubi_num);
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
index 2f41e53..1d0b453 100644
--- a/drivers/mtd/ubi/eba.c
+++ b/drivers/mtd/ubi/eba.c
@@ -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:
@@ -510,7 +510,7 @@ retry:
new_pnum = ubi_wl_get_peb(ubi);
if (new_pnum < 0) {
ubi_free_vid_hdr(ubi, vid_hdr);
- up_read(&ubi->fm_sem);
+ up_read(&ubi->fm_eba_sem);
return new_pnum;
}
@@ -520,14 +520,14 @@ retry:
if (err && err != UBI_IO_BITFLIPS) {
if (err > 0)
err = -EIO;
- up_read(&ubi->fm_sem);
+ 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) {
- up_read(&ubi->fm_sem);
+ up_read(&ubi->fm_eba_sem);
goto write_error;
}
@@ -539,7 +539,7 @@ retry:
if (offset > 0) {
err = ubi_io_read_data(ubi, ubi->peb_buf, pnum, 0, offset);
if (err && err != UBI_IO_BITFLIPS) {
- up_read(&ubi->fm_sem);
+ up_read(&ubi->fm_eba_sem);
goto out_unlock;
}
}
@@ -549,7 +549,7 @@ 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_sem);
+ up_read(&ubi->fm_eba_sem);
goto write_error;
}
@@ -557,7 +557,7 @@ retry:
ubi_free_vid_hdr(ubi, vid_hdr);
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("data was successfully recovered");
@@ -652,7 +652,7 @@ retry:
if (pnum < 0) {
ubi_free_vid_hdr(ubi, vid_hdr);
leb_write_unlock(ubi, vol_id, lnum);
- up_read(&ubi->fm_sem);
+ up_read(&ubi->fm_eba_sem);
return pnum;
}
@@ -663,7 +663,7 @@ retry:
if (err) {
ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
vol_id, lnum, pnum);
- up_read(&ubi->fm_sem);
+ up_read(&ubi->fm_eba_sem);
goto write_error;
}
@@ -672,13 +672,13 @@ retry:
if (err) {
ubi_warn("failed to write %d bytes at offset %d of LEB %d:%d, PEB %d",
len, offset, vol_id, lnum, pnum);
- up_read(&ubi->fm_sem);
+ up_read(&ubi->fm_eba_sem);
goto write_error;
}
}
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);
@@ -775,7 +775,7 @@ retry:
if (pnum < 0) {
ubi_free_vid_hdr(ubi, vid_hdr);
leb_write_unlock(ubi, vol_id, lnum);
- up_read(&ubi->fm_sem);
+ up_read(&ubi->fm_eba_sem);
return pnum;
}
@@ -786,7 +786,7 @@ retry:
if (err) {
ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
vol_id, lnum, pnum);
- up_read(&ubi->fm_sem);
+ up_read(&ubi->fm_eba_sem);
goto write_error;
}
@@ -794,13 +794,13 @@ retry:
if (err) {
ubi_warn("failed to write %d bytes of data to PEB %d",
len, pnum);
- up_read(&ubi->fm_sem);
+ up_read(&ubi->fm_eba_sem);
goto write_error;
}
ubi_assert(vol->eba_tbl[lnum] < 0);
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);
@@ -895,7 +895,7 @@ retry:
pnum = ubi_wl_get_peb(ubi);
if (pnum < 0) {
err = pnum;
- up_read(&ubi->fm_sem);
+ up_read(&ubi->fm_eba_sem);
goto out_leb_unlock;
}
@@ -906,7 +906,7 @@ retry:
if (err) {
ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
vol_id, lnum, pnum);
- up_read(&ubi->fm_sem);
+ up_read(&ubi->fm_eba_sem);
goto write_error;
}
@@ -914,13 +914,13 @@ retry:
if (err) {
ubi_warn("failed to write %d bytes of data to PEB %d",
len, pnum);
- up_read(&ubi->fm_sem);
+ up_read(&ubi->fm_eba_sem);
goto write_error;
}
old_pnum = vol->eba_tbl[lnum];
vol->eba_tbl[lnum] = pnum;
- up_read(&ubi->fm_sem);
+ up_read(&ubi->fm_eba_sem);
if (old_pnum >= 0) {
err = ubi_wl_put_peb(ubi, vol_id, lnum, old_pnum, 0);
@@ -1173,9 +1173,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);
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index e563366..9a8cf45 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -874,7 +874,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);
@@ -1064,7 +1064,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("Attach by fastmap failed, doing a full scan!");
return ret;
@@ -1432,24 +1432,24 @@ int ubi_update_fastmap(struct ubi_device *ubi)
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;
}
@@ -1539,16 +1539,16 @@ int ubi_update_fastmap(struct ubi_device *ubi)
}
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;
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 0392366..9a37fe3 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -425,10 +425,11 @@ 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
*
@@ -532,8 +533,8 @@ 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;
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index fbe9b5c..ea3c79e 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -628,7 +628,7 @@ void ubi_refill_pools(struct ubi_device *ubi)
/* ubi_wl_get_peb - works exaclty like __wl_get_peb but keeps track of
* the fastmap pool.
- * Returns with ubi->fm_sem held in read mode!
+ * Returns with ubi->fm_eba_sem held in read mode!
*/
int ubi_wl_get_peb(struct ubi_device *ubi)
{
@@ -637,19 +637,19 @@ int ubi_wl_get_peb(struct ubi_device *ubi)
struct ubi_fm_pool *wl_pool = &ubi->fm_wl_pool;
again:
- down_read(&ubi->fm_sem);
+ down_read(&ubi->fm_eba_sem);
spin_lock(&ubi->wl_lock);
if (!pool->size || !wl_pool->size || pool->used >= pool->size ||
wl_pool->used >= wl_pool->size) {
spin_unlock(&ubi->wl_lock);
- up_read(&ubi->fm_sem);
+ up_read(&ubi->fm_eba_sem);
ret = ubi_update_fastmap(ubi);
if (ret) {
ubi_msg("Unable to write a new fastmap: %i", ret);
- down_read(&ubi->fm_sem);
+ down_read(&ubi->fm_eba_sem);
return -ENOSPC;
}
- down_read(&ubi->fm_sem);
+ down_read(&ubi->fm_eba_sem);
spin_lock(&ubi->wl_lock);
}
@@ -726,7 +726,7 @@ int ubi_wl_get_peb(struct ubi_device *ubi)
return err;
}
- down_read(&ubi->fm_sem);
+ down_read(&ubi->fm_eba_sem);
return peb;
}
#endif
@@ -1602,6 +1602,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];
@@ -1632,6 +1634,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)) {
@@ -1653,6 +1656,7 @@ retry:
ubi_err("PEB %d not found", pnum);
ubi_ro_mode(ubi);
spin_unlock(&ubi->wl_lock);
+ up_read(&ubi->fm_protect);
return err;
}
}
@@ -1666,6 +1670,7 @@ retry:
spin_unlock(&ubi->wl_lock);
}
+ up_read(&ubi->fm_protect);
return err;
}
--
1.8.4.5
...instead of generic self checking.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/attach.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c
index 9364348..e36fd2e 100644
--- a/drivers/mtd/ubi/attach.c
+++ b/drivers/mtd/ubi/attach.c
@@ -1454,7 +1454,7 @@ 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();
--
1.8.4.5
ubi_wl_get_peb() returns a fresh PEB which can be used by
user of UBI. Due to the pool logic fastmap will correctly
map this PEB upon attach time because it will be scanned.
If a new fastmap is written (due to heavy parallel io)
while the before the fresh PEB is assigned to the EBA table
it will not be scanned as it is no longer in the pool.
So, the race window exists between ubi_wl_get_peb()
and the EBA table assignment.
We have to make sure that no new fastmap can be written
while that.
To ensure that ubi_wl_get_peb() will grab ubi->fm_sem in read mode
and the user of ubi_wl_get_peb() has to release it after the PEB
got assigned.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/eba.c | 24 ++++++++++++++++++------
drivers/mtd/ubi/wl.c | 6 ++++++
2 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
index 04abe7f..2f41e53 100644
--- a/drivers/mtd/ubi/eba.c
+++ b/drivers/mtd/ubi/eba.c
@@ -510,6 +510,7 @@ retry:
new_pnum = ubi_wl_get_peb(ubi);
if (new_pnum < 0) {
ubi_free_vid_hdr(ubi, vid_hdr);
+ up_read(&ubi->fm_sem);
return new_pnum;
}
@@ -519,13 +520,16 @@ retry:
if (err && err != UBI_IO_BITFLIPS) {
if (err > 0)
err = -EIO;
+ up_read(&ubi->fm_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_sem);
goto write_error;
+ }
data_size = offset + len;
mutex_lock(&ubi->buf_mutex);
@@ -534,8 +538,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_sem);
goto out_unlock;
+ }
}
memcpy(ubi->peb_buf + offset, buf, len);
@@ -543,13 +549,13 @@ 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_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);
ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1);
@@ -646,6 +652,7 @@ retry:
if (pnum < 0) {
ubi_free_vid_hdr(ubi, vid_hdr);
leb_write_unlock(ubi, vol_id, lnum);
+ up_read(&ubi->fm_sem);
return pnum;
}
@@ -656,6 +663,7 @@ retry:
if (err) {
ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
vol_id, lnum, pnum);
+ up_read(&ubi->fm_sem);
goto write_error;
}
@@ -664,11 +672,11 @@ retry:
if (err) {
ubi_warn("failed to write %d bytes at offset %d of LEB %d:%d, PEB %d",
len, offset, vol_id, lnum, pnum);
+ up_read(&ubi->fm_sem);
goto write_error;
}
}
- down_read(&ubi->fm_sem);
vol->eba_tbl[lnum] = pnum;
up_read(&ubi->fm_sem);
@@ -767,6 +775,7 @@ retry:
if (pnum < 0) {
ubi_free_vid_hdr(ubi, vid_hdr);
leb_write_unlock(ubi, vol_id, lnum);
+ up_read(&ubi->fm_sem);
return pnum;
}
@@ -777,6 +786,7 @@ retry:
if (err) {
ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
vol_id, lnum, pnum);
+ up_read(&ubi->fm_sem);
goto write_error;
}
@@ -784,11 +794,11 @@ retry:
if (err) {
ubi_warn("failed to write %d bytes of data to PEB %d",
len, pnum);
+ up_read(&ubi->fm_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);
@@ -885,6 +895,7 @@ retry:
pnum = ubi_wl_get_peb(ubi);
if (pnum < 0) {
err = pnum;
+ up_read(&ubi->fm_sem);
goto out_leb_unlock;
}
@@ -895,6 +906,7 @@ retry:
if (err) {
ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
vol_id, lnum, pnum);
+ up_read(&ubi->fm_sem);
goto write_error;
}
@@ -902,10 +914,10 @@ retry:
if (err) {
ubi_warn("failed to write %d bytes of data to PEB %d",
len, pnum);
+ up_read(&ubi->fm_sem);
goto write_error;
}
- down_read(&ubi->fm_sem);
old_pnum = vol->eba_tbl[lnum];
vol->eba_tbl[lnum] = pnum;
up_read(&ubi->fm_sem);
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 44ebefd..fbe9b5c 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -628,6 +628,7 @@ void ubi_refill_pools(struct ubi_device *ubi)
/* ubi_wl_get_peb - works exaclty like __wl_get_peb but keeps track of
* the fastmap pool.
+ * Returns with ubi->fm_sem held in read mode!
*/
int ubi_wl_get_peb(struct ubi_device *ubi)
{
@@ -636,15 +637,19 @@ int ubi_wl_get_peb(struct ubi_device *ubi)
struct ubi_fm_pool *wl_pool = &ubi->fm_wl_pool;
again:
+ down_read(&ubi->fm_sem);
spin_lock(&ubi->wl_lock);
if (!pool->size || !wl_pool->size || pool->used >= pool->size ||
wl_pool->used >= wl_pool->size) {
spin_unlock(&ubi->wl_lock);
+ up_read(&ubi->fm_sem);
ret = ubi_update_fastmap(ubi);
if (ret) {
ubi_msg("Unable to write a new fastmap: %i", ret);
+ down_read(&ubi->fm_sem);
return -ENOSPC;
}
+ down_read(&ubi->fm_sem);
spin_lock(&ubi->wl_lock);
}
@@ -721,6 +726,7 @@ int ubi_wl_get_peb(struct ubi_device *ubi)
return err;
}
+ down_read(&ubi->fm_sem);
return peb;
}
#endif
--
1.8.4.5
If we set it for dynamic ones we might confuse various self checks.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/fastmap.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index af490c3..e563366 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -200,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);
--
1.8.4.5
Don't update the fastmap upon detach if fastmap checking is enabled.
This is poor men's power cut testing feature. :-)
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/build.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 941110d..3712f88 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -1110,8 +1110,11 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
ubi_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num);
#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
--
1.8.4.5
Don't use a fixed size for the WL pool.
Make it instead 50% of the user pool.
We don't make it 100% as it is not as heavily used as the user pool.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/build.c | 2 +-
drivers/mtd/ubi/ubi-media.h | 2 --
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 3712f88..666b2a0 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -947,7 +947,7 @@ 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 (!ubi->fm_disabled && (int)mtd_div_by_eb(ubi->mtd->size, ubi->mtd)
diff --git a/drivers/mtd/ubi/ubi-media.h b/drivers/mtd/ubi/ubi-media.h
index ac2b24d..d0d072e 100644
--- a/drivers/mtd/ubi/ubi-media.h
+++ b/drivers/mtd/ubi/ubi-media.h
@@ -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)
--
1.8.4.5
This logic is in vain as we treat protected PEBs also as used, so this
case must not happen.
If a PEB is found which is in the EBA table but not known as used
has to be issued as fatal error.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/fastmap.c | 80 ++++++-----------------------------------------
1 file changed, 9 insertions(+), 71 deletions(-)
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index 98d141e..ad3655c 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -440,7 +440,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.
@@ -448,12 +447,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)
@@ -521,18 +520,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) {
@@ -607,7 +594,7 @@ 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;
@@ -624,7 +611,6 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
INIT_LIST_HEAD(&used);
INIT_LIST_HEAD(&free);
- INIT_LIST_HEAD(&eba_orphans);
ai->min_ec = UBI_MAX_ERASECOUNTER;
fmsb = (struct ubi_fm_sb *)(fm_raw);
@@ -793,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("PEB %i is in EBA but not in used list", pnum);
+ goto fail_bad;
}
aeb->lnum = j;
@@ -831,45 +798,17 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
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("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("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);
+ goto fail_bad;
}
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;
@@ -882,7 +821,6 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
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(&eba_orphans));
ubi_assert(list_empty(&free));
/*
--
1.8.4.5
There is no need to switch to ro mode if ubi_update_fastmap() fails.
Also get rid of the ifdef.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/build.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 6e30a3c..941110d 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -154,23 +154,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("Unable to update fastmap!");
- ubi_ro_mode(ubi);
- }
+ ret = ubi_update_fastmap(ubi);
+ if (ret)
+ ubi_msg("Unable to write a new fastmap: %i", ret);
}
-#endif
+
return blocking_notifier_call_chain(&ubi_notifiers, ntype, &nt);
}
--
1.8.4.5
We have to switch to ro mode to guarantee that upon next UBI attach
all data is consistent.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/fastmap.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index ad3655c..79b7387 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -842,10 +842,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);
@@ -1562,8 +1558,10 @@ err:
ret = 0;
if (old_fm) {
ret = invalidate_fastmap(ubi, old_fm);
- if (ret < 0)
+ if (ret < 0) {
ubi_err("Unable to invalidiate current fastmap!");
+ ubi_ro_mode(ubi);
+ }
else if (ret)
ret = 0;
}
--
1.8.4.5
This function a) requests a new PEB, b) writes data to it,
c) returns the old PEB and d) registers the new PEB in the EBA table.
For the non-fastmap care this works perfectly fine and is powercut safe.
Is fastmap enabled this can lead to issues.
If a new fastmap is written between a) and c) the freshly requested PEB
is no longer in a pool and will not be scanned upon attaching.
If now a powercut happens between c) and d) the freshly requested PEB
will not be scanned and the old one got already scheduled for erase.
After attaching the EBA table will point to a erased PEB.
Fix this issue by swapping steps c) and d).
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/eba.c | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
index 2402d3b..04abe7f 100644
--- a/drivers/mtd/ubi/eba.c
+++ b/drivers/mtd/ubi/eba.c
@@ -842,7 +842,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;
@@ -905,16 +905,17 @@ retry:
goto write_error;
}
- if (vol->eba_tbl[lnum] >= 0) {
- err = ubi_wl_put_peb(ubi, vol_id, lnum, vol->eba_tbl[lnum], 0);
- if (err)
- goto out_leb_unlock;
- }
-
down_read(&ubi->fm_sem);
+ old_pnum = vol->eba_tbl[lnum];
vol->eba_tbl[lnum] = pnum;
up_read(&ubi->fm_sem);
+ if (old_pnum >= 0) {
+ err = ubi_wl_put_peb(ubi, vol_id, lnum, old_pnum, 0);
+ if (err)
+ goto out_leb_unlock;
+ }
+
out_leb_unlock:
leb_write_unlock(ubi, vol_id, lnum);
out_mutex:
--
1.8.4.5
It is legal to have PEB left in the used list.
This can happen if UBI copies a PEB and a powercut happens
between writing a new fastmap and adding this PEB into the EBA table.
In this case the old PEB will be used.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/fastmap.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index 1654f95..98d141e 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -879,7 +879,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));
+ 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(&eba_orphans));
ubi_assert(list_empty(&free));
--
1.8.4.5
There is always exactly one ubi_attach_info object allocated,
therefore we don't have to care about the name.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/attach.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c
index d1072bc..9364348 100644
--- a/drivers/mtd/ubi/attach.c
+++ b/drivers/mtd/ubi/attach.c
@@ -1297,7 +1297,7 @@ out_ech:
return err;
}
-static struct ubi_attach_info *alloc_ai(const char *slab_name)
+static struct ubi_attach_info *alloc_ai(void)
{
struct ubi_attach_info *ai;
@@ -1310,7 +1310,7 @@ static struct ubi_attach_info *alloc_ai(const char *slab_name)
INIT_LIST_HEAD(&ai->erase);
INIT_LIST_HEAD(&ai->alien);
ai->volumes = RB_ROOT;
- ai->aeb_slab_cache = kmem_cache_create(slab_name,
+ ai->aeb_slab_cache = kmem_cache_create("ubi_aeb_slab_cache",
sizeof(struct ubi_ainf_peb),
0, 0, NULL);
if (!ai->aeb_slab_cache) {
@@ -1371,7 +1371,7 @@ static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info **ai)
return UBI_NO_FASTMAP;
destroy_ai(*ai);
- *ai = alloc_ai("ubi_aeb_slab_cache");
+ *ai = alloc_ai();
if (!*ai)
return -ENOMEM;
@@ -1400,7 +1400,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;
@@ -1418,7 +1418,7 @@ int ubi_attach(struct ubi_device *ubi, int force_scan)
if (err > 0) {
if (err != UBI_NO_FASTMAP) {
destroy_ai(ai);
- ai = alloc_ai("ubi_aeb_slab_cache2");
+ ai = alloc_ai();
if (!ai)
return -ENOMEM;
@@ -1457,7 +1457,7 @@ int ubi_attach(struct ubi_device *ubi, int force_scan)
if (ubi->fm && ubi_dbg_chk_gen(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;
--
1.8.4.5
Currently we leak a few ubi_ainf_pebs while attaching.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/attach.c | 61 +++++++++++++++++++++++++----------------------
drivers/mtd/ubi/fastmap.c | 13 ----------
2 files changed, 33 insertions(+), 41 deletions(-)
diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c
index 6f27d9a..d1072bc 100644
--- a/drivers/mtd/ubi/attach.c
+++ b/drivers/mtd/ubi/attach.c
@@ -1297,6 +1297,30 @@ out_ech:
return err;
}
+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;
+}
+
#ifdef CONFIG_MTD_UBI_FASTMAP
/**
@@ -1309,7 +1333,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;
@@ -1330,7 +1354,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;
@@ -1346,7 +1370,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("ubi_aeb_slab_cache");
+ if (!*ai)
+ return -ENOMEM;
+
+ return ubi_scan_fastmap(ubi, *ai, fm_anchor);
out_vidh:
ubi_free_vid_hdr(ubi, vidh);
@@ -1358,30 +1387,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
@@ -1409,7 +1414,7 @@ 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);
+ err = scan_fast(ubi, &ai);
if (err > 0) {
if (err != UBI_NO_FASTMAP) {
destroy_ai(ai);
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index 09a97ad..1654f95 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -625,21 +625,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);
--
1.8.4.5
There is no need to allocate new ones every time, we can reuse
the existing ones.
This makes the code cleaner and more easy to follow.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/fastmap.c | 31 +++++--------------------------
drivers/mtd/ubi/wl.c | 11 +++++++----
2 files changed, 12 insertions(+), 30 deletions(-)
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index 8cce6f8..09a97ad 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -1529,19 +1529,6 @@ int ubi_update_fastmap(struct ubi_device *ubi)
}
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;
@@ -1577,12 +1564,9 @@ int ubi_update_fastmap(struct ubi_device *ubi)
ubi_err("could not erase old fastmap PEB");
goto err;
}
-
- new_fm->e[i]->pnum = old_fm->e[i]->pnum;
- new_fm->e[i]->ec = old_fm->e[i]->ec;
+ new_fm->e[i] = old_fm->e[i];
} 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)
ubi_wl_put_fm_peb(ubi, old_fm->e[i], i,
@@ -1607,16 +1591,13 @@ int ubi_update_fastmap(struct ubi_device *ubi)
i, 0);
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;
} 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;
}
} else {
if (!tmp_e) {
@@ -1629,9 +1610,7 @@ int ubi_update_fastmap(struct ubi_device *ubi)
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);
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 73ff40a..ac19f75 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -995,9 +995,6 @@ int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *fm_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);
@@ -1999,9 +1996,15 @@ 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);
--
1.8.4.5
Add a ubi_fastmap_close() to free all resources used by fastmap
at WL shutdown.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/wl.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 0639353..73ff40a 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -2055,6 +2055,23 @@ static void protection_queue_destroy(struct ubi_device *ubi)
}
}
+static void ubi_fastmap_close(struct ubi_device *ubi)
+{
+#ifdef CONFIG_MTD_UBI_FASTMAP
+ 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);
+#endif
+}
+
/**
* ubi_wl_close - close the wear-leveling sub-system.
* @ubi: UBI device description object
@@ -2062,9 +2079,7 @@ static void protection_queue_destroy(struct ubi_device *ubi)
void ubi_wl_close(struct ubi_device *ubi)
{
dbg_wl("close the WL sub-system");
-#ifdef CONFIG_MTD_UBI_FASTMAP
- flush_work(&ubi->fm_work);
-#endif
+ ubi_fastmap_close(ubi);
shutdown_work(ubi);
protection_queue_destroy(ubi);
tree_destroy(&ubi->used);
--
1.8.4.5
If ubi_update_fastmap() fails notify the user.
This is not a hard error as ubi_update_fastmap() makes sure upon failure that
the current on-flash fastmap will no be used upon next UBI attach.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/wl.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index ac19f75..44ebefd 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -640,7 +640,11 @@ again:
if (!pool->size || !wl_pool->size || pool->used >= pool->size ||
wl_pool->used >= wl_pool->size) {
spin_unlock(&ubi->wl_lock);
- ubi_update_fastmap(ubi);
+ ret = ubi_update_fastmap(ubi);
+ if (ret) {
+ ubi_msg("Unable to write a new fastmap: %i", ret);
+ return -ENOSPC;
+ }
spin_lock(&ubi->wl_lock);
}
--
1.8.4.5
...such that we can implement NOP variants of some functions.
This will help to reduce fastmap specific ifdefs in other c files.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/ubi.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 5d2e737..0392366 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -866,10 +866,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
--
1.8.4.5
Using this debugfs knob fastmap self checks can be controlled.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/debug.c | 11 +++++++++++
drivers/mtd/ubi/debug.h | 5 +++++
drivers/mtd/ubi/ubi.h | 4 ++++
3 files changed, 20 insertions(+)
diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c
index 63cb1d7..ea9be20 100644
--- a/drivers/mtd/ubi/debug.c
+++ b/drivers/mtd/ubi/debug.c
@@ -275,6 +275,8 @@ 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)
@@ -336,6 +338,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 +410,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);
diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h
index cba89fc..e99ef37 100644
--- a/drivers/mtd/ubi/debug.h
+++ b/drivers/mtd/ubi/debug.h
@@ -127,4 +127,9 @@ 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;
+}
#endif /* !__UBI_DEBUG_H__ */
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 320fc38..f80a726 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -352,6 +352,7 @@ 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
@@ -359,6 +360,7 @@ struct ubi_wl_entry;
* @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
@@ -366,6 +368,7 @@ struct ubi_wl_entry;
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;
@@ -373,6 +376,7 @@ struct ubi_debug_info {
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;
--
1.8.4.5
Currently ubi_refill_pools() first fills the first and then
the second one.
If only very few free PEBs are available the second pool can get
zero PEBs.
Change ubi_refill_pools() to distribute free PEBs fair between
all pools.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/wl.c | 73 ++++++++++++++++++++++++++--------------------------
1 file changed, 36 insertions(+), 37 deletions(-)
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 95bb12b..0639353 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -571,59 +571,58 @@ static void return_unused_pool_pebs(struct ubi_device *ubi,
}
/**
- * refill_wl_pool - refills all the fastmap pool used by the
- * WL sub-system.
+ * ubi_refill_pools - refills all fastmap PEB pools.
* @ubi: UBI device description object
*/
-static void refill_wl_pool(struct ubi_device *ubi)
+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;
- struct ubi_fm_pool *pool = &ubi->fm_wl_pool;
+ int enough;
+ spin_lock(&ubi->wl_lock);
+
+ return_unused_pool_pebs(ubi, 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;
+ wl_pool->size = 0;
+ pool->size = 0;
- 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--;
+ for (;;) {
+ enough = 0;
+ if (pool->size < pool->max_size) {
+ e = wl_get_wle(ubi);
+ if (!e)
+ break;
- pool->pebs[pool->size] = e->pnum;
- }
- pool->used = 0;
-}
+ pool->pebs[pool->size] = e->pnum;
+ pool->size++;
+ } else
+ enough++;
-/**
- * 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;
+ if (wl_pool->size < wl_pool->max_size) {
+ if (!ubi->free.rb_node ||
+ (ubi->free_count - ubi->beb_rsvd_pebs < 5))
+ break;
- return_unused_pool_pebs(ubi, pool);
+ 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++;
- 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)
+ if (enough == 2)
break;
}
+
+ wl_pool->used = 0;
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);
}
--
1.8.4.5
Make it two functions, wl_get_wle() and wl_get_peb().
wl_get_peb() works exactly like __wl_get_peb() but wl_get_wle()
does not call produce_free_peb().
While refilling the fastmap user pool we cannot release ubi->wl_lock
as produce_free_peb() does.
Hence the fastmap logic uses now wl_get_wle().
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/wl.c | 48 +++++++++++++++++++++++++++---------------------
1 file changed, 27 insertions(+), 21 deletions(-)
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 5f8d2ff..95bb12b 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -502,7 +502,30 @@ out:
* This function returns a physical eraseblock in case of success and a
* negative error code 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)
+{
+ struct ubi_wl_entry *e;
+
+ e = find_mean_wl_entry(ubi, &ubi->free);
+ if (!e) {
+ ubi_err("no free eraseblocks");
+ return NULL;
+ }
+
+ self_check_in_wl_tree(ubi, e, &ubi->free);
+
+ /*
+ * Move the physical eraseblock to the protection queue where it will
+ * be protected from being moved for some time.
+ */
+ rb_erase(&e->u.rb, &ubi->free);
+ ubi->free_count--;
+ dbg_wl("PEB %d EC %d", e->pnum, e->ec);
+
+ return e;
+}
+
+static int wl_get_peb(struct ubi_device *ubi)
{
int err;
struct ubi_wl_entry *e;
@@ -519,29 +542,12 @@ retry:
if (err < 0)
return err;
goto retry;
- }
- e = find_mean_wl_entry(ubi, &ubi->free);
- if (!e) {
- ubi_err("no free eraseblocks");
- return -ENOSPC;
}
- self_check_in_wl_tree(ubi, e, &ubi->free);
-
- /*
- * Move the physical eraseblock to the protection queue where it will
- * be protected from being moved for some time.
- */
- 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. */
+ e = wl_get_wle(ubi);
prot_queue_add(ubi, e);
-#endif
+
return e->pnum;
}
@@ -699,7 +705,7 @@ int ubi_wl_get_peb(struct ubi_device *ubi)
int peb, err;
spin_lock(&ubi->wl_lock);
- peb = __wl_get_peb(ubi);
+ peb = wl_get_peb(ubi);
spin_unlock(&ubi->wl_lock);
if (peb < 0)
--
1.8.4.5
If the WL pool runs out of PEBs we schedule a fastmap write
to refill it as soon as possible.
Ensure that only one at a time is scheduled otherwise we might end in
a fastmap write storm because writing the fastmap can schedule another
write if bitflips are detected.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/ubi.h | 2 ++
drivers/mtd/ubi/wl.c | 8 +++++++-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index f80a726..5d2e737 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -430,6 +430,7 @@ struct ubi_debug_info {
* @fm_size: fastmap size in bytes
* @fm_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
@@ -536,6 +537,7 @@ struct ubi_device {
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;
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index a12c54b..bd2e8d5 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -149,6 +149,9 @@ 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);
}
/**
@@ -657,7 +660,10 @@ static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi)
/* 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);
+ if (!ubi->fm_work_scheduled) {
+ ubi->fm_work_scheduled = 1;
+ schedule_work(&ubi->fm_work);
+ }
return NULL;
} else {
pnum = pool->pebs[pool->used++];
--
1.8.4.5
This self check allows Fastmap to detect absent PEBs while
writing a new fastmap to the MTD device.
It will help to find implementation issues in Fastmap.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/fastmap.c | 86 +++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 84 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index cfd5b5e..adccc1f 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Linutronix GmbH
+ * Copyright (c) 2014 sigma star gmbh
* Author: Richard Weinberger <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
@@ -17,6 +18,69 @@
#include "ubi.h"
/**
+ * init_seen - allocate the seen logic integer array
+ * @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 = kzalloc(sizeof(int) * ubi->peb_count, 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 to be marked PEB
+ * @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("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
*/
@@ -1114,6 +1178,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
struct ubi_work *ubi_wrk;
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 +1195,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 +1231,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 +1242,17 @@ 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);
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++;
@@ -1190,6 +1266,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);
used_peb_count++;
@@ -1203,6 +1280,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);
scrub_peb_count++;
@@ -1220,6 +1298,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++;
@@ -1279,6 +1358,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);
}
@@ -1312,11 +1392,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;
}
--
1.8.4.5
ubi_wl_get_peb() has two problems, it reads the pool
size and usage counters without any protection.
While reading one value would be perfectly fine it reads multiple
values and compares them. This is racy and can lead to incorrect
pool handling.
Furthermore ubi_update_fastmap() is called without wl_lock held,
before incrementing the used counter it needs to be checked again.
It could happen that another thread consumed all PEBs from the
pool and the counter goes beyond ->size.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/wl.c | 30 ++++++++++++++++++++----------
1 file changed, 20 insertions(+), 10 deletions(-)
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index a06e31e..5f8d2ff 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -626,24 +626,34 @@ void ubi_refill_pools(struct ubi_device *ubi)
*/
int ubi_wl_get_peb(struct ubi_device *ubi)
{
- int ret;
+ int ret, retried = 0;
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)
+again:
+ spin_lock(&ubi->wl_lock);
+ if (!pool->size || !wl_pool->size || pool->used >= pool->size ||
+ wl_pool->used >= wl_pool->size) {
+ spin_unlock(&ubi->wl_lock);
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]);
+ }
+
+ if (pool->used >= pool->size || !pool->size) {
spin_unlock(&ubi->wl_lock);
+ if (retried) {
+ ubi_err("Unable to get a free PEB from user WL pool");
+ ret = -ENOSPC;
+ goto out;
+ }
+ retried = 1;
+ goto again;
}
+ ret = pool->pebs[pool->used++];
+ prot_queue_add(ubi, ubi->lookuptbl[ret]);
+ spin_unlock(&ubi->wl_lock);
+out:
return ret;
}
--
1.8.4.5
Fastmap can miss a PEB if it is in the protection queue
and not jet in the used tree.
Treat every protected PEB as used.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/fastmap.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index adccc1f..8cce6f8 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -1273,6 +1273,20 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
fm_pos += sizeof(*fec);
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);
+ 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);
for (node = rb_first(&ubi->scrub); node; node = rb_next(node)) {
--
1.8.4.5
In some error paths the WL sub-system gives up on a PEB
and frees it's ubi_wl_entry struct but does not set
the entry in ubi->lookuptbl to NULL.
Fastmap can stumble over such a stale pointer as it uses
ubi->lookuptbl to find all PEBs.
Fix this by setting the pointers to free'd ubi_wl_entry to NULL.
Signed-off-by: Richard Weinberger <[email protected]>
---
drivers/mtd/ubi/wl.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 6654f191..a12c54b 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -1212,9 +1212,12 @@ 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) {
+ ubi->lookuptbl[e1->pnum] = NULL;
kmem_cache_free(ubi_wl_entry_slab, e1);
- if (e2)
+ if (e2) {
+ ubi->lookuptbl[e2->pnum] = NULL;
kmem_cache_free(ubi_wl_entry_slab, e2);
+ }
goto out_ro;
}
@@ -1227,6 +1230,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
e2->pnum, vol_id, lnum);
err = do_sync_erase(ubi, e2, vol_id, lnum, 0);
if (err) {
+ ubi->lookuptbl[e2->pnum] = NULL;
kmem_cache_free(ubi_wl_entry_slab, e2);
goto out_ro;
}
@@ -1266,6 +1270,7 @@ out_not_moved:
ubi_free_vid_hdr(ubi, vid_hdr);
err = do_sync_erase(ubi, e2, vol_id, lnum, torture);
if (err) {
+ ubi->lookuptbl[e2->pnum] = NULL;
kmem_cache_free(ubi_wl_entry_slab, e2);
goto out_ro;
}
@@ -1285,6 +1290,8 @@ out_error:
spin_unlock(&ubi->wl_lock);
ubi_free_vid_hdr(ubi, vid_hdr);
+ ubi->lookuptbl[e1->pnum] = NULL;
+ ubi->lookuptbl[e2->pnum] = NULL;
kmem_cache_free(ubi_wl_entry_slab, e1);
kmem_cache_free(ubi_wl_entry_slab, e2);
@@ -1428,6 +1435,7 @@ 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);
+ ubi->lookuptbl[e->pnum] = NULL;
kmem_cache_free(ubi_wl_entry_slab, e);
return 0;
}
@@ -1474,6 +1482,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
return err;
}
+ ubi->lookuptbl[e->pnum] = NULL;
kmem_cache_free(ubi_wl_entry_slab, e);
if (err != -EIO)
/*
@@ -1912,6 +1921,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
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)) {
+ ubi->lookuptbl[e->pnum] = NULL;
kmem_cache_free(ubi_wl_entry_slab, e);
goto out_free;
}
--
1.8.4.5
On Wed, 2014-10-29 at 13:45 +0100, Richard Weinberger wrote:
> + ubi->lookuptbl[e2->pnum] = NULL;
> kmem_cache_free(ubi_wl_entry_slab, e2);
Since these 2 must always go together, they really deserve a separate
helper function.
On Wed, 2014-10-29 at 13:45 +0100, Richard Weinberger wrote:
> This self check allows Fastmap to detect absent PEBs while
> writing a new fastmap to the MTD device.
> It will help to find implementation issues in Fastmap.
>
> Signed-off-by: Richard Weinberger <[email protected]>
> ---
> drivers/mtd/ubi/fastmap.c | 86 +++++++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 84 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
> index cfd5b5e..adccc1f 100644
> --- a/drivers/mtd/ubi/fastmap.c
> +++ b/drivers/mtd/ubi/fastmap.c
> @@ -1,5 +1,6 @@
> /*
> * Copyright (c) 2012 Linutronix GmbH
> + * Copyright (c) 2014 sigma star gmbh
> * Author: Richard Weinberger <[email protected]>
> *
> * This program is free software; you can redistribute it and/or modify
> @@ -17,6 +18,69 @@
> #include "ubi.h"
>
> /**
> + * init_seen - allocate the seen logic integer array
How about
init_seen - allocate memory for used for debugging.
And I think there should be a dot at the end of the title comment. Not
that it is very important, but I tried to follow this rule everywhere.
> +/**
> + * 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);
> +}
Just do not introduce a function for this. And the commentary does not
make it any more clear. Or if you are going to add more stuff to this
function later - rephrase the comment please.
> +/**
> + * set_seen - mark a PEB as seen
> + * @ubi: UBI device description object
> + * @pnum: the to be marked PEB
> + * @seen: integer array of @ubi->peb_count size
> + */
The dot at the end of the title line. And the return code could be
documented too.
"The to be marked PEB" is understandable, but not well-said :-)
Not sure if this adds much value, but I am trying to be consistent.
> +/**
> + * 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
> + */
Similar nit-picks.
> +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("self-check failed for PEB %d, fastmap didn't see it", pnum);
Didn't Tanya add the 'ubi' parameter in the printing functions?
> int scrub_peb_count, erase_peb_count;
> + int *seen_pebs = NULL;
Is the initialization really needed?
On Wed, 2014-10-29 at 13:45 +0100, Richard Weinberger wrote:
> 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);
> }
Why is the 'update_fastmap_work_fn()' function in wl.c? Can we move it
to fastmap.c?
On Wed, 2014-10-29 at 13:45 +0100, Richard Weinberger wrote:
> ...otherwise the deferred work might run after datastructures
> got freed and corrupt memory.
>
> Signed-off-by: Richard Weinberger <[email protected]>
> ---
> drivers/mtd/ubi/wl.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
> index bd2e8d5..a06e31e 100644
> --- a/drivers/mtd/ubi/wl.c
> +++ b/drivers/mtd/ubi/wl.c
> @@ -2047,6 +2047,9 @@ static void protection_queue_destroy(struct ubi_device *ubi)
> void ubi_wl_close(struct ubi_device *ubi)
> {
> dbg_wl("close the WL sub-system");
> +#ifdef CONFIG_MTD_UBI_FASTMAP
> + flush_work(&ubi->fm_work);
> +#endif
I see in patch 35 you introduced the init function. Could you please
introduce fastmap init/close functions earlier, and add this line to the
fastmap close function instead of adding yet another ifdef to wl.c.
Am 05.11.2014 um 16:42 schrieb Artem Bityutskiy:
> On Wed, 2014-10-29 at 13:45 +0100, Richard Weinberger wrote:
>> 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);
>> }
>
> Why is the 'update_fastmap_work_fn()' function in wl.c? Can we move it
> to fastmap.c?
>
Please see "[PATCH 29/35] UBI: Move fastmap specific functions out of wl.c".
Thanks,
//richard
Am 05.11.2014 um 16:45 schrieb Artem Bityutskiy:
> On Wed, 2014-10-29 at 13:45 +0100, Richard Weinberger wrote:
>> ...otherwise the deferred work might run after datastructures
>> got freed and corrupt memory.
>>
>> Signed-off-by: Richard Weinberger <[email protected]>
>> ---
>> drivers/mtd/ubi/wl.c | 3 +++
>> 1 file changed, 3 insertions(+)
>>
>> diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
>> index bd2e8d5..a06e31e 100644
>> --- a/drivers/mtd/ubi/wl.c
>> +++ b/drivers/mtd/ubi/wl.c
>> @@ -2047,6 +2047,9 @@ static void protection_queue_destroy(struct ubi_device *ubi)
>> void ubi_wl_close(struct ubi_device *ubi)
>> {
>> dbg_wl("close the WL sub-system");
>> +#ifdef CONFIG_MTD_UBI_FASTMAP
>> + flush_work(&ubi->fm_work);
>> +#endif
>
> I see in patch 35 you introduced the init function. Could you please
> introduce fastmap init/close functions earlier, and add this line to the
> fastmap close function instead of adding yet another ifdef to wl.c.
If you want I can rebase.
I did all fixes first and then the cleanups.
Thanks,
//richard
On Wed, 2014-10-29 at 13:45 +0100, Richard Weinberger wrote:
> ubi_wl_get_peb() has two problems, it reads the pool
> size and usage counters without any protection.
> While reading one value would be perfectly fine it reads multiple
> values and compares them. This is racy and can lead to incorrect
> pool handling.
> Furthermore ubi_update_fastmap() is called without wl_lock held,
> before incrementing the used counter it needs to be checked again.
> It could happen that another thread consumed all PEBs from the
> pool and the counter goes beyond ->size.
So wl_lock protects the 'pool->*' variables? Could you please add this
information to ubi.h. Namely, in the huge comment above 'struct device'
we document each lock, and we list the variables the lock protects.
> - if (!pool->size || !wl_pool->size || pool->used == pool->size ||
> - wl_pool->used == wl_pool->size)
> +again:
> + spin_lock(&ubi->wl_lock);
Is it possible to add a little comment here which translates the
condition below into English?
> + if (!pool->size || !wl_pool->size || pool->used >= pool->size ||
> + wl_pool->used >= wl_pool->size) {
> + spin_unlock(&ubi->wl_lock);
> ubi_update_fastmap(ubi);
> -
On Wed, 2014-11-05 at 16:49 +0100, Richard Weinberger wrote:
> If you want I can rebase.
That would be very helpful.
> I did all fixes first and then the cleanups.
Well, the first patch adds debugging stuff - not exactly a fix ;-)
Am 05.11.2014 um 16:23 schrieb Artem Bityutskiy:
> On Wed, 2014-10-29 at 13:45 +0100, Richard Weinberger wrote:
>> This self check allows Fastmap to detect absent PEBs while
>> writing a new fastmap to the MTD device.
>> It will help to find implementation issues in Fastmap.
>>
>> Signed-off-by: Richard Weinberger <[email protected]>
>> ---
>> drivers/mtd/ubi/fastmap.c | 86 +++++++++++++++++++++++++++++++++++++++++++++--
>> 1 file changed, 84 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
>> index cfd5b5e..adccc1f 100644
>> --- a/drivers/mtd/ubi/fastmap.c
>> +++ b/drivers/mtd/ubi/fastmap.c
>> @@ -1,5 +1,6 @@
>> /*
>> * Copyright (c) 2012 Linutronix GmbH
>> + * Copyright (c) 2014 sigma star gmbh
>> * Author: Richard Weinberger <[email protected]>
>> *
>> * This program is free software; you can redistribute it and/or modify
>> @@ -17,6 +18,69 @@
>> #include "ubi.h"
>>
>> /**
>> + * init_seen - allocate the seen logic integer array
>
> How about
>
> init_seen - allocate memory for used for debugging.
>
>
> And I think there should be a dot at the end of the title comment. Not
> that it is very important, but I tried to follow this rule everywhere.
>
>> +/**
>> + * 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);
>> +}
>
> Just do not introduce a function for this. And the commentary does not
> make it any more clear. Or if you are going to add more stuff to this
> function later - rephrase the comment please.
Why? I did that to have alloc and free balanced.
I.e. to not have a naked kfree(). Always when I see a kfree() somewhere
I'd like to see the k*alloc().
>> +/**
>> + * set_seen - mark a PEB as seen
>> + * @ubi: UBI device description object
>> + * @pnum: the to be marked PEB
>> + * @seen: integer array of @ubi->peb_count size
>> + */
>
> The dot at the end of the title line. And the return code could be
> documented too.
I need a script for this. ;-)
> "The to be marked PEB" is understandable, but not well-said :-)
>
> Not sure if this adds much value, but I am trying to be consistent.
>
>> +/**
>> + * 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
>> + */
>
> Similar nit-picks.
>
>> +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("self-check failed for PEB %d, fastmap didn't see it", pnum);
>
> Didn't Tanya add the 'ubi' parameter in the printing functions?
At the time I wrote this patch Tanya's work was not mainline...
>> int scrub_peb_count, erase_peb_count;
>> + int *seen_pebs = NULL;
>
> Is the initialization really needed?
>
Correct, we can drop it.
Thanks,
//richard
Am 05.11.2014 um 16:54 schrieb Artem Bityutskiy:
> On Wed, 2014-11-05 at 16:49 +0100, Richard Weinberger wrote:
>> If you want I can rebase.
>
> That would be very helpful.
>
>> I did all fixes first and then the cleanups.
>
> Well, the first patch adds debugging stuff - not exactly a fix ;-)
Let's name it robustness stuff. ;)
Seriously, I did first all fixes and the code cleanups later to make
it easy for others to backport these fixes.
Thanks,
//richard
On Wed, 2014-11-05 at 16:56 +0100, Richard Weinberger wrote:
> I did that to have alloc and free balanced.
> I.e. to not have a naked kfree(). Always when I see a kfree()
> somewhere
> I'd like to see the k*alloc().
OK. Please, just make the comment simpler then. Say that you are freeing
the debugging buffer or something.
Am 05.11.2014 um 17:01 schrieb Artem Bityutskiy:
> On Wed, 2014-11-05 at 16:56 +0100, Richard Weinberger wrote:
>> I did that to have alloc and free balanced.
>> I.e. to not have a naked kfree(). Always when I see a kfree()
>> somewhere
>> I'd like to see the k*alloc().
>
> OK. Please, just make the comment simpler then. Say that you are freeing
> the debugging buffer or something.
Will do!
Thanks,
//richard
On Wed, 2014-11-05 at 16:59 +0100, Richard Weinberger wrote:
> Am 05.11.2014 um 16:54 schrieb Artem Bityutskiy:
> > On Wed, 2014-11-05 at 16:49 +0100, Richard Weinberger wrote:
> >> If you want I can rebase.
> >
> > That would be very helpful.
> >
> >> I did all fixes first and then the cleanups.
> >
> > Well, the first patch adds debugging stuff - not exactly a fix ;-)
>
> Let's name it robustness stuff. ;)
> Seriously, I did first all fixes and the code cleanups later to make
> it easy for others to backport these fixes.
Less back-porting efforts is a good goal. But then the robustness patch
should also come later, I think.
On Wed, 2014-10-29 at 13:45 +0100, Richard Weinberger wrote:
> Make it two functions, wl_get_wle() and wl_get_peb().
> wl_get_peb() works exactly like __wl_get_peb() but wl_get_wle()
> does not call produce_free_peb().
> While refilling the fastmap user pool we cannot release ubi->wl_lock
> as produce_free_peb() does.
> Hence the fastmap logic uses now wl_get_wle().
>
> Signed-off-by: Richard Weinberger <[email protected]>
Please, re-consider naming.
The convention is that non-static function names start with ubi_wl_*.
Static function names start with _*.
Also, please add a comment above the functions explaining what it does.
Artem.
Am 06.11.2014 um 08:51 schrieb Artem Bityutskiy:
> On Wed, 2014-10-29 at 13:45 +0100, Richard Weinberger wrote:
>> Make it two functions, wl_get_wle() and wl_get_peb().
>> wl_get_peb() works exactly like __wl_get_peb() but wl_get_wle()
>> does not call produce_free_peb().
>> While refilling the fastmap user pool we cannot release ubi->wl_lock
>> as produce_free_peb() does.
>> Hence the fastmap logic uses now wl_get_wle().
>>
>> Signed-off-by: Richard Weinberger <[email protected]>
>
> Please, re-consider naming.
>
> The convention is that non-static function names start with ubi_wl_*.
> Static function names start with _*.
>
> Also, please add a comment above the functions explaining what it does.
On my TODO list is already a big "enhance comments" item. :)
I'll send an updates series soon.
Thanks,
//richard
On Wed, 2014-11-05 at 17:05 +0100, Richard Weinberger wrote:
> Am 05.11.2014 um 17:01 schrieb Artem Bityutskiy:
> > On Wed, 2014-11-05 at 16:56 +0100, Richard Weinberger wrote:
> >> I did that to have alloc and free balanced.
> >> I.e. to not have a naked kfree(). Always when I see a kfree()
> >> somewhere
> >> I'd like to see the k*alloc().
> >
> > OK. Please, just make the comment simpler then. Say that you are freeing
> > the debugging buffer or something.
>
> Will do!
I suggest you to prepare a short series of patches and send this out. I
think I can process them faster then. When you send 35 patches I tend to
postpone the review because it looks like a a lot of work which needs a
lot of time. Small series is easier to deal with.
So instead of re-sending 35 patches, just send 3 or for good patches,
I'll pick them much faster, I think.
Artem.
On 10/29/2014 2:45 PM, Richard Weinberger wrote:
> Using this debugfs knob fastmap self checks can be controlled.
>
Reviewed-by: Tanya Brokhman <[email protected]>
> Signed-off-by: Richard Weinberger <[email protected]>
> ---
> drivers/mtd/ubi/debug.c | 11 +++++++++++
> drivers/mtd/ubi/debug.h | 5 +++++
> drivers/mtd/ubi/ubi.h | 4 ++++
> 3 files changed, 20 insertions(+)
>
> diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c
> index 63cb1d7..ea9be20 100644
> --- a/drivers/mtd/ubi/debug.c
> +++ b/drivers/mtd/ubi/debug.c
> @@ -275,6 +275,8 @@ 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)
> @@ -336,6 +338,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 +410,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);
> diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h
> index cba89fc..e99ef37 100644
> --- a/drivers/mtd/ubi/debug.h
> +++ b/drivers/mtd/ubi/debug.h
> @@ -127,4 +127,9 @@ 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;
> +}
> #endif /* !__UBI_DEBUG_H__ */
> diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
> index 320fc38..f80a726 100644
> --- a/drivers/mtd/ubi/ubi.h
> +++ b/drivers/mtd/ubi/ubi.h
> @@ -352,6 +352,7 @@ 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
> @@ -359,6 +360,7 @@ struct ubi_wl_entry;
> * @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
> @@ -366,6 +368,7 @@ struct ubi_wl_entry;
> 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;
> @@ -373,6 +376,7 @@ struct ubi_debug_info {
> 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;
>
Thanks,
Tanya Brokhman
--
Qualcomm Israel, on behalf of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
Hi Richard
On 10/29/2014 2:45 PM, Richard Weinberger wrote:
> In some error paths the WL sub-system gives up on a PEB
> and frees it's ubi_wl_entry struct but does not set
> the entry in ubi->lookuptbl to NULL.
> Fastmap can stumble over such a stale pointer as it uses
> ubi->lookuptbl to find all PEBs.
>
> Fix this by setting the pointers to free'd ubi_wl_entry to NULL.
There are 2 more places:
tree_destroy() and protection_queue_destroy() where ubi_wl_entry is
released. Both functions used on power down so all should be good as is,
just wanted to make sure you didn't add ubi->lookuptbl[e2->pnum] = NULL
there on purpose.
Thanks,
Tanya Brokhman
--
Qualcomm Israel, on behalf of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
Forum, a Linux Foundation Collaborative Project
Tanya,
Am 04.12.2014 um 12:00 schrieb Tanya Brokhman:
> Hi Richard
>
> On 10/29/2014 2:45 PM, Richard Weinberger wrote:
>> In some error paths the WL sub-system gives up on a PEB
>> and frees it's ubi_wl_entry struct but does not set
>> the entry in ubi->lookuptbl to NULL.
>> Fastmap can stumble over such a stale pointer as it uses
>> ubi->lookuptbl to find all PEBs.
>>
>> Fix this by setting the pointers to free'd ubi_wl_entry to NULL.
>
> There are 2 more places:
> tree_destroy() and protection_queue_destroy() where ubi_wl_entry is released. Both functions used on power down so all should be good as is, just wanted to make sure you didn't add
> ubi->lookuptbl[e2->pnum] = NULL there on purpose.
I fear you're looking at the old patch series.
Please have a look at:
"[PATCH 3/6] UBI: Fix stale pointers in ubi->lookuptbl" sent on 30.11.2014.
Thanks,
//richard
On 12/4/2014 1:04 PM, Richard Weinberger wrote:
> Tanya,
>
> Am 04.12.2014 um 12:00 schrieb Tanya Brokhman:
>> Hi Richard
>>
>> On 10/29/2014 2:45 PM, Richard Weinberger wrote:
>>> In some error paths the WL sub-system gives up on a PEB
>>> and frees it's ubi_wl_entry struct but does not set
>>> the entry in ubi->lookuptbl to NULL.
>>> Fastmap can stumble over such a stale pointer as it uses
>>> ubi->lookuptbl to find all PEBs.
>>>
>>> Fix this by setting the pointers to free'd ubi_wl_entry to NULL.
>>
>> There are 2 more places:
>> tree_destroy() and protection_queue_destroy() where ubi_wl_entry is released. Both functions used on power down so all should be good as is, just wanted to make sure you didn't add
>> ubi->lookuptbl[e2->pnum] = NULL there on purpose.
>
> I fear you're looking at the old patch series.
> Please have a look at:
> "[PATCH 3/6] UBI: Fix stale pointers in ubi->lookuptbl" sent on 30.11.2014.
>
> Thanks,
> //richard
>
oh, you're right. Started from the oldest. now I see you released v2.
Sorry. So I should be looking at "Fastmap update v2 (pile 1-7)", right?
Thanks,
Tanya Brokhman
--
Qualcomm Israel, on behalf of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
Am 04.12.2014 um 13:37 schrieb Tanya Brokhman:
> On 12/4/2014 1:04 PM, Richard Weinberger wrote:
>> Tanya,
>>
>> Am 04.12.2014 um 12:00 schrieb Tanya Brokhman:
>>> Hi Richard
>>>
>>> On 10/29/2014 2:45 PM, Richard Weinberger wrote:
>>>> In some error paths the WL sub-system gives up on a PEB
>>>> and frees it's ubi_wl_entry struct but does not set
>>>> the entry in ubi->lookuptbl to NULL.
>>>> Fastmap can stumble over such a stale pointer as it uses
>>>> ubi->lookuptbl to find all PEBs.
>>>>
>>>> Fix this by setting the pointers to free'd ubi_wl_entry to NULL.
>>>
>>> There are 2 more places:
>>> tree_destroy() and protection_queue_destroy() where ubi_wl_entry is released. Both functions used on power down so all should be good as is, just wanted to make sure you didn't add
>>> ubi->lookuptbl[e2->pnum] = NULL there on purpose.
>>
>> I fear you're looking at the old patch series.
>> Please have a look at:
>> "[PATCH 3/6] UBI: Fix stale pointers in ubi->lookuptbl" sent on 30.11.2014.
>>
>> Thanks,
>> //richard
>>
>
> oh, you're right. Started from the oldest. now I see you released v2. Sorry. So I should be looking at "Fastmap update v2 (pile 1-7)", right?
Correct. :)
Thanks,
//richard