diff options
author | Richard Weinberger <richard@nod.at> | 2014-11-10 10:16:23 -0500 |
---|---|---|
committer | Richard Weinberger <richard@nod.at> | 2015-03-26 17:46:04 -0400 |
commit | ee59ba8b064f692a1dee99b6f3ba30b0e904b2c1 (patch) | |
tree | 365b8b071072ae08b59a5944fc14afd1674f53a5 /drivers | |
parent | 24b7a347c37f99c9d08d2d3ae9c6a56a8333429b (diff) |
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')
-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) | |||
216 | } | 216 | } |
217 | 217 | ||
218 | /** | 218 | /** |
219 | * wl_tree_destroy - destroy a wear-leveling entry. | ||
220 | * @ubi: UBI device description object | ||
221 | * @e: the wear-leveling entry to add | ||
222 | * | ||
223 | * This function destroys a wear leveling entry and removes | ||
224 | * the reference from the lookup table. | ||
225 | */ | ||
226 | static void wl_entry_destroy(struct ubi_device *ubi, struct ubi_wl_entry *e) | ||
227 | { | ||
228 | ubi->lookuptbl[e->pnum] = NULL; | ||
229 | kmem_cache_free(ubi_wl_entry_slab, e); | ||
230 | } | ||
231 | |||
232 | /** | ||
219 | * do_work - do one pending work. | 233 | * do_work - do one pending work. |
220 | * @ubi: UBI device description object | 234 | * @ubi: UBI device description object |
221 | * | 235 | * |
@@ -1258,7 +1272,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, | |||
1258 | err = do_sync_erase(ubi, e1, vol_id, lnum, 0); | 1272 | err = do_sync_erase(ubi, e1, vol_id, lnum, 0); |
1259 | if (err) { | 1273 | if (err) { |
1260 | if (e2) | 1274 | if (e2) |
1261 | kmem_cache_free(ubi_wl_entry_slab, e2); | 1275 | wl_entry_destroy(ubi, e2); |
1262 | goto out_ro; | 1276 | goto out_ro; |
1263 | } | 1277 | } |
1264 | 1278 | ||
@@ -1326,8 +1340,8 @@ out_error: | |||
1326 | spin_unlock(&ubi->wl_lock); | 1340 | spin_unlock(&ubi->wl_lock); |
1327 | 1341 | ||
1328 | ubi_free_vid_hdr(ubi, vid_hdr); | 1342 | ubi_free_vid_hdr(ubi, vid_hdr); |
1329 | kmem_cache_free(ubi_wl_entry_slab, e1); | 1343 | wl_entry_destroy(ubi, e1); |
1330 | kmem_cache_free(ubi_wl_entry_slab, e2); | 1344 | wl_entry_destroy(ubi, e2); |
1331 | 1345 | ||
1332 | out_ro: | 1346 | out_ro: |
1333 | ubi_ro_mode(ubi); | 1347 | ubi_ro_mode(ubi); |
@@ -1469,7 +1483,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, | |||
1469 | if (shutdown) { | 1483 | if (shutdown) { |
1470 | dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec); | 1484 | dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec); |
1471 | kfree(wl_wrk); | 1485 | kfree(wl_wrk); |
1472 | kmem_cache_free(ubi_wl_entry_slab, e); | 1486 | wl_entry_destroy(ubi, e); |
1473 | return 0; | 1487 | return 0; |
1474 | } | 1488 | } |
1475 | 1489 | ||
@@ -1515,7 +1529,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, | |||
1515 | return err; | 1529 | return err; |
1516 | } | 1530 | } |
1517 | 1531 | ||
1518 | kmem_cache_free(ubi_wl_entry_slab, e); | 1532 | wl_entry_destroy(ubi, e); |
1519 | if (err != -EIO) | 1533 | if (err != -EIO) |
1520 | /* | 1534 | /* |
1521 | * If this is not %-EIO, we have no idea what to do. Scheduling | 1535 | * 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) | |||
1807 | 1821 | ||
1808 | /** | 1822 | /** |
1809 | * tree_destroy - destroy an RB-tree. | 1823 | * tree_destroy - destroy an RB-tree. |
1824 | * @ubi: UBI device description object | ||
1810 | * @root: the root of the tree to destroy | 1825 | * @root: the root of the tree to destroy |
1811 | */ | 1826 | */ |
1812 | static void tree_destroy(struct rb_root *root) | 1827 | static void tree_destroy(struct ubi_device *ubi, struct rb_root *root) |
1813 | { | 1828 | { |
1814 | struct rb_node *rb; | 1829 | struct rb_node *rb; |
1815 | struct ubi_wl_entry *e; | 1830 | struct ubi_wl_entry *e; |
@@ -1831,7 +1846,7 @@ static void tree_destroy(struct rb_root *root) | |||
1831 | rb->rb_right = NULL; | 1846 | rb->rb_right = NULL; |
1832 | } | 1847 | } |
1833 | 1848 | ||
1834 | kmem_cache_free(ubi_wl_entry_slab, e); | 1849 | wl_entry_destroy(ubi, e); |
1835 | } | 1850 | } |
1836 | } | 1851 | } |
1837 | } | 1852 | } |
@@ -1962,7 +1977,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) | |||
1962 | ubi_assert(!ubi_is_fm_block(ubi, e->pnum)); | 1977 | ubi_assert(!ubi_is_fm_block(ubi, e->pnum)); |
1963 | ubi->lookuptbl[e->pnum] = e; | 1978 | ubi->lookuptbl[e->pnum] = e; |
1964 | if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0)) { | 1979 | if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0)) { |
1965 | kmem_cache_free(ubi_wl_entry_slab, e); | 1980 | wl_entry_destroy(ubi, e); |
1966 | goto out_free; | 1981 | goto out_free; |
1967 | } | 1982 | } |
1968 | 1983 | ||
@@ -2056,9 +2071,9 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) | |||
2056 | 2071 | ||
2057 | out_free: | 2072 | out_free: |
2058 | shutdown_work(ubi); | 2073 | shutdown_work(ubi); |
2059 | tree_destroy(&ubi->used); | 2074 | tree_destroy(ubi, &ubi->used); |
2060 | tree_destroy(&ubi->free); | 2075 | tree_destroy(ubi, &ubi->free); |
2061 | tree_destroy(&ubi->scrub); | 2076 | tree_destroy(ubi, &ubi->scrub); |
2062 | kfree(ubi->lookuptbl); | 2077 | kfree(ubi->lookuptbl); |
2063 | return err; | 2078 | return err; |
2064 | } | 2079 | } |
@@ -2075,7 +2090,7 @@ static void protection_queue_destroy(struct ubi_device *ubi) | |||
2075 | for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) { | 2090 | for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) { |
2076 | list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) { | 2091 | list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) { |
2077 | list_del(&e->u.list); | 2092 | list_del(&e->u.list); |
2078 | kmem_cache_free(ubi_wl_entry_slab, e); | 2093 | wl_entry_destroy(ubi, e); |
2079 | } | 2094 | } |
2080 | } | 2095 | } |
2081 | } | 2096 | } |
@@ -2107,10 +2122,10 @@ void ubi_wl_close(struct ubi_device *ubi) | |||
2107 | ubi_fastmap_close(ubi); | 2122 | ubi_fastmap_close(ubi); |
2108 | shutdown_work(ubi); | 2123 | shutdown_work(ubi); |
2109 | protection_queue_destroy(ubi); | 2124 | protection_queue_destroy(ubi); |
2110 | tree_destroy(&ubi->used); | 2125 | tree_destroy(ubi, &ubi->used); |
2111 | tree_destroy(&ubi->erroneous); | 2126 | tree_destroy(ubi, &ubi->erroneous); |
2112 | tree_destroy(&ubi->free); | 2127 | tree_destroy(ubi, &ubi->free); |
2113 | tree_destroy(&ubi->scrub); | 2128 | tree_destroy(ubi, &ubi->scrub); |
2114 | kfree(ubi->lookuptbl); | 2129 | kfree(ubi->lookuptbl); |
2115 | } | 2130 | } |
2116 | 2131 | ||