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>
This commit is contained in:
Родитель
24b7a347c3
Коммит
ee59ba8b06
|
@ -215,6 +215,20 @@ static void wl_tree_add(struct ubi_wl_entry *e, struct rb_root *root)
|
||||||
rb_insert_color(&e->u.rb, root);
|
rb_insert_color(&e->u.rb, root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wl_tree_destroy - destroy a wear-leveling entry.
|
||||||
|
* @ubi: UBI device description object
|
||||||
|
* @e: the wear-leveling entry to add
|
||||||
|
*
|
||||||
|
* This function destroys a wear leveling entry and removes
|
||||||
|
* the reference from the lookup table.
|
||||||
|
*/
|
||||||
|
static void wl_entry_destroy(struct ubi_device *ubi, struct ubi_wl_entry *e)
|
||||||
|
{
|
||||||
|
ubi->lookuptbl[e->pnum] = NULL;
|
||||||
|
kmem_cache_free(ubi_wl_entry_slab, e);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* do_work - do one pending work.
|
* do_work - do one pending work.
|
||||||
* @ubi: UBI device description object
|
* @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);
|
err = do_sync_erase(ubi, e1, vol_id, lnum, 0);
|
||||||
if (err) {
|
if (err) {
|
||||||
if (e2)
|
if (e2)
|
||||||
kmem_cache_free(ubi_wl_entry_slab, e2);
|
wl_entry_destroy(ubi, e2);
|
||||||
goto out_ro;
|
goto out_ro;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1326,8 +1340,8 @@ out_error:
|
||||||
spin_unlock(&ubi->wl_lock);
|
spin_unlock(&ubi->wl_lock);
|
||||||
|
|
||||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||||
kmem_cache_free(ubi_wl_entry_slab, e1);
|
wl_entry_destroy(ubi, e1);
|
||||||
kmem_cache_free(ubi_wl_entry_slab, e2);
|
wl_entry_destroy(ubi, e2);
|
||||||
|
|
||||||
out_ro:
|
out_ro:
|
||||||
ubi_ro_mode(ubi);
|
ubi_ro_mode(ubi);
|
||||||
|
@ -1469,7 +1483,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
|
||||||
if (shutdown) {
|
if (shutdown) {
|
||||||
dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec);
|
dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec);
|
||||||
kfree(wl_wrk);
|
kfree(wl_wrk);
|
||||||
kmem_cache_free(ubi_wl_entry_slab, e);
|
wl_entry_destroy(ubi, e);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1515,7 +1529,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
kmem_cache_free(ubi_wl_entry_slab, e);
|
wl_entry_destroy(ubi, e);
|
||||||
if (err != -EIO)
|
if (err != -EIO)
|
||||||
/*
|
/*
|
||||||
* If this is not %-EIO, we have no idea what to do. Scheduling
|
* 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.
|
* tree_destroy - destroy an RB-tree.
|
||||||
|
* @ubi: UBI device description object
|
||||||
* @root: the root of the tree to destroy
|
* @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 rb_node *rb;
|
||||||
struct ubi_wl_entry *e;
|
struct ubi_wl_entry *e;
|
||||||
|
@ -1831,7 +1846,7 @@ static void tree_destroy(struct rb_root *root)
|
||||||
rb->rb_right = NULL;
|
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_assert(!ubi_is_fm_block(ubi, e->pnum));
|
||||||
ubi->lookuptbl[e->pnum] = e;
|
ubi->lookuptbl[e->pnum] = e;
|
||||||
if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0)) {
|
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;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2056,9 +2071,9 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
|
||||||
|
|
||||||
out_free:
|
out_free:
|
||||||
shutdown_work(ubi);
|
shutdown_work(ubi);
|
||||||
tree_destroy(&ubi->used);
|
tree_destroy(ubi, &ubi->used);
|
||||||
tree_destroy(&ubi->free);
|
tree_destroy(ubi, &ubi->free);
|
||||||
tree_destroy(&ubi->scrub);
|
tree_destroy(ubi, &ubi->scrub);
|
||||||
kfree(ubi->lookuptbl);
|
kfree(ubi->lookuptbl);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -2075,7 +2090,7 @@ static void protection_queue_destroy(struct ubi_device *ubi)
|
||||||
for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) {
|
for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) {
|
||||||
list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) {
|
list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) {
|
||||||
list_del(&e->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);
|
ubi_fastmap_close(ubi);
|
||||||
shutdown_work(ubi);
|
shutdown_work(ubi);
|
||||||
protection_queue_destroy(ubi);
|
protection_queue_destroy(ubi);
|
||||||
tree_destroy(&ubi->used);
|
tree_destroy(ubi, &ubi->used);
|
||||||
tree_destroy(&ubi->erroneous);
|
tree_destroy(ubi, &ubi->erroneous);
|
||||||
tree_destroy(&ubi->free);
|
tree_destroy(ubi, &ubi->free);
|
||||||
tree_destroy(&ubi->scrub);
|
tree_destroy(ubi, &ubi->scrub);
|
||||||
kfree(ubi->lookuptbl);
|
kfree(ubi->lookuptbl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче