diff options
author | Richard Weinberger <richard@nod.at> | 2014-11-10 16:16:23 +0100 |
---|---|---|
committer | Richard Weinberger <richard@nod.at> | 2015-03-26 22:46:04 +0100 |
commit | ee59ba8b064f692a1dee99b6f3ba30b0e904b2c1 (patch) | |
tree | 365b8b071072ae08b59a5944fc14afd1674f53a5 /drivers/mtd | |
parent | 24b7a347c37f99c9d08d2d3ae9c6a56a8333429b (diff) | |
download | blackbird-op-linux-ee59ba8b064f692a1dee99b6f3ba30b0e904b2c1.tar.gz blackbird-op-linux-ee59ba8b064f692a1dee99b6f3ba30b0e904b2c1.zip |
UBI: Fix stale pointers in ubi->lookuptbl
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 introducing a new helper function which free()s
a WL entry and removes the reference from the lookup table.
Signed-off-by: Richard Weinberger <richard@nod.at>
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/ubi/wl.c | 47 |
1 files changed, 31 insertions, 16 deletions
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 609b16d45406..83848324daa2 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -216,6 +216,20 @@ static void wl_tree_add(struct ubi_wl_entry *e, struct rb_root *root) } /** + * wl_tree_destroy - destroy a wear-leveling entry. + * @ubi: UBI device description object + * @e: the wear-leveling entry to add + * + * This function destroys a wear leveling entry and removes + * the reference from the lookup table. + */ +static void wl_entry_destroy(struct ubi_device *ubi, struct ubi_wl_entry *e) +{ + ubi->lookuptbl[e->pnum] = NULL; + kmem_cache_free(ubi_wl_entry_slab, e); +} + +/** * do_work - do one pending work. * @ubi: UBI device description object * @@ -1258,7 +1272,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, err = do_sync_erase(ubi, e1, vol_id, lnum, 0); if (err) { if (e2) - kmem_cache_free(ubi_wl_entry_slab, e2); + wl_entry_destroy(ubi, e2); goto out_ro; } @@ -1326,8 +1340,8 @@ out_error: spin_unlock(&ubi->wl_lock); ubi_free_vid_hdr(ubi, vid_hdr); - kmem_cache_free(ubi_wl_entry_slab, e1); - kmem_cache_free(ubi_wl_entry_slab, e2); + wl_entry_destroy(ubi, e1); + wl_entry_destroy(ubi, e2); out_ro: ubi_ro_mode(ubi); @@ -1469,7 +1483,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); - kmem_cache_free(ubi_wl_entry_slab, e); + wl_entry_destroy(ubi, e); return 0; } @@ -1515,7 +1529,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, return err; } - kmem_cache_free(ubi_wl_entry_slab, e); + wl_entry_destroy(ubi, e); if (err != -EIO) /* * If this is not %-EIO, we have no idea what to do. Scheduling @@ -1807,9 +1821,10 @@ int ubi_wl_flush(struct ubi_device *ubi, int vol_id, int lnum) /** * tree_destroy - destroy an RB-tree. + * @ubi: UBI device description object * @root: the root of the tree to destroy */ -static void tree_destroy(struct rb_root *root) +static void tree_destroy(struct ubi_device *ubi, struct rb_root *root) { struct rb_node *rb; struct ubi_wl_entry *e; @@ -1831,7 +1846,7 @@ static void tree_destroy(struct rb_root *root) rb->rb_right = NULL; } - kmem_cache_free(ubi_wl_entry_slab, e); + wl_entry_destroy(ubi, e); } } } @@ -1962,7 +1977,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)) { - kmem_cache_free(ubi_wl_entry_slab, e); + wl_entry_destroy(ubi, e); goto out_free; } @@ -2056,9 +2071,9 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) out_free: shutdown_work(ubi); - tree_destroy(&ubi->used); - tree_destroy(&ubi->free); - tree_destroy(&ubi->scrub); + tree_destroy(ubi, &ubi->used); + tree_destroy(ubi, &ubi->free); + tree_destroy(ubi, &ubi->scrub); kfree(ubi->lookuptbl); return err; } @@ -2075,7 +2090,7 @@ static void protection_queue_destroy(struct ubi_device *ubi) for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) { list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) { list_del(&e->u.list); - kmem_cache_free(ubi_wl_entry_slab, e); + wl_entry_destroy(ubi, e); } } } @@ -2107,10 +2122,10 @@ void ubi_wl_close(struct ubi_device *ubi) ubi_fastmap_close(ubi); shutdown_work(ubi); protection_queue_destroy(ubi); - tree_destroy(&ubi->used); - tree_destroy(&ubi->erroneous); - tree_destroy(&ubi->free); - tree_destroy(&ubi->scrub); + tree_destroy(ubi, &ubi->used); + tree_destroy(ubi, &ubi->erroneous); + tree_destroy(ubi, &ubi->free); + tree_destroy(ubi, &ubi->scrub); kfree(ubi->lookuptbl); } |