Commit 2e8f08de authored by Richard Weinberger's avatar Richard Weinberger

ubi: Fix races around ubi_refill_pools()

When writing a new Fastmap the first thing that happens
is refilling the pools in memory.
At this stage it is possible that new PEBs from the new pools
get already claimed and written with data.
If this happens before the new Fastmap data structure hits the
flash and we face power cut the freshly written PEB will not
scanned and unnoticed.

Solve the issue by locking the pools until Fastmap is written.

Cc: <stable@vger.kernel.org>
Fixes: dbb7d2a8 ("UBI: Add fastmap core")
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
parent 23654188
...@@ -1210,6 +1210,8 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, ...@@ -1210,6 +1210,8 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
struct ubi_volume *vol; struct ubi_volume *vol;
uint32_t crc; uint32_t crc;
ubi_assert(rwsem_is_locked(&ubi->fm_eba_sem));
vol_id = be32_to_cpu(vid_hdr->vol_id); vol_id = be32_to_cpu(vid_hdr->vol_id);
lnum = be32_to_cpu(vid_hdr->lnum); lnum = be32_to_cpu(vid_hdr->lnum);
...@@ -1352,9 +1354,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, ...@@ -1352,9 +1354,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
} }
ubi_assert(vol->eba_tbl->entries[lnum].pnum == from); ubi_assert(vol->eba_tbl->entries[lnum].pnum == from);
down_read(&ubi->fm_eba_sem);
vol->eba_tbl->entries[lnum].pnum = to; vol->eba_tbl->entries[lnum].pnum = to;
up_read(&ubi->fm_eba_sem);
out_unlock_buf: out_unlock_buf:
mutex_unlock(&ubi->buf_mutex); mutex_unlock(&ubi->buf_mutex);
......
...@@ -262,6 +262,8 @@ static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi) ...@@ -262,6 +262,8 @@ static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi)
struct ubi_fm_pool *pool = &ubi->fm_wl_pool; struct ubi_fm_pool *pool = &ubi->fm_wl_pool;
int pnum; int pnum;
ubi_assert(rwsem_is_locked(&ubi->fm_eba_sem));
if (pool->used == pool->size) { if (pool->used == pool->size) {
/* We cannot update the fastmap here because this /* We cannot update the fastmap here because this
* function is called in atomic context. * function is called in atomic context.
...@@ -303,7 +305,7 @@ int ubi_ensure_anchor_pebs(struct ubi_device *ubi) ...@@ -303,7 +305,7 @@ int ubi_ensure_anchor_pebs(struct ubi_device *ubi)
wrk->anchor = 1; wrk->anchor = 1;
wrk->func = &wear_leveling_worker; wrk->func = &wear_leveling_worker;
schedule_ubi_work(ubi, wrk); __schedule_ubi_work(ubi, wrk);
return 0; return 0;
} }
...@@ -344,7 +346,7 @@ int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *fm_e, ...@@ -344,7 +346,7 @@ int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *fm_e,
spin_unlock(&ubi->wl_lock); spin_unlock(&ubi->wl_lock);
vol_id = lnum ? UBI_FM_DATA_VOLUME_ID : UBI_FM_SB_VOLUME_ID; vol_id = lnum ? UBI_FM_DATA_VOLUME_ID : UBI_FM_SB_VOLUME_ID;
return schedule_erase(ubi, e, vol_id, lnum, torture); return schedule_erase(ubi, e, vol_id, lnum, torture, true);
} }
/** /**
......
...@@ -1492,22 +1492,30 @@ int ubi_update_fastmap(struct ubi_device *ubi) ...@@ -1492,22 +1492,30 @@ int ubi_update_fastmap(struct ubi_device *ubi)
struct ubi_wl_entry *tmp_e; struct ubi_wl_entry *tmp_e;
down_write(&ubi->fm_protect); down_write(&ubi->fm_protect);
down_write(&ubi->work_sem);
down_write(&ubi->fm_eba_sem);
ubi_refill_pools(ubi); ubi_refill_pools(ubi);
if (ubi->ro_mode || ubi->fm_disabled) { if (ubi->ro_mode || ubi->fm_disabled) {
up_write(&ubi->fm_eba_sem);
up_write(&ubi->work_sem);
up_write(&ubi->fm_protect); up_write(&ubi->fm_protect);
return 0; return 0;
} }
ret = ubi_ensure_anchor_pebs(ubi); ret = ubi_ensure_anchor_pebs(ubi);
if (ret) { if (ret) {
up_write(&ubi->fm_eba_sem);
up_write(&ubi->work_sem);
up_write(&ubi->fm_protect); up_write(&ubi->fm_protect);
return ret; return ret;
} }
new_fm = kzalloc(sizeof(*new_fm), GFP_KERNEL); new_fm = kzalloc(sizeof(*new_fm), GFP_KERNEL);
if (!new_fm) { if (!new_fm) {
up_write(&ubi->fm_eba_sem);
up_write(&ubi->work_sem);
up_write(&ubi->fm_protect); up_write(&ubi->fm_protect);
return -ENOMEM; return -ENOMEM;
} }
...@@ -1616,16 +1624,14 @@ int ubi_update_fastmap(struct ubi_device *ubi) ...@@ -1616,16 +1624,14 @@ int ubi_update_fastmap(struct ubi_device *ubi)
new_fm->e[0] = tmp_e; new_fm->e[0] = tmp_e;
} }
down_write(&ubi->work_sem);
down_write(&ubi->fm_eba_sem);
ret = ubi_write_fastmap(ubi, new_fm); ret = ubi_write_fastmap(ubi, new_fm);
up_write(&ubi->fm_eba_sem);
up_write(&ubi->work_sem);
if (ret) if (ret)
goto err; goto err;
out_unlock: out_unlock:
up_write(&ubi->fm_eba_sem);
up_write(&ubi->work_sem);
up_write(&ubi->fm_protect); up_write(&ubi->fm_protect);
kfree(old_fm); kfree(old_fm);
return ret; return ret;
......
...@@ -580,7 +580,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, ...@@ -580,7 +580,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
* failure. * failure.
*/ */
static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
int vol_id, int lnum, int torture) int vol_id, int lnum, int torture, bool nested)
{ {
struct ubi_work *wl_wrk; struct ubi_work *wl_wrk;
...@@ -599,6 +599,9 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, ...@@ -599,6 +599,9 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
wl_wrk->lnum = lnum; wl_wrk->lnum = lnum;
wl_wrk->torture = torture; wl_wrk->torture = torture;
if (nested)
__schedule_ubi_work(ubi, wl_wrk);
else
schedule_ubi_work(ubi, wl_wrk); schedule_ubi_work(ubi, wl_wrk);
return 0; return 0;
} }
...@@ -663,6 +666,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -663,6 +666,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
vid_hdr = ubi_get_vid_hdr(vidb); vid_hdr = ubi_get_vid_hdr(vidb);
down_read(&ubi->fm_eba_sem);
mutex_lock(&ubi->move_mutex); mutex_lock(&ubi->move_mutex);
spin_lock(&ubi->wl_lock); spin_lock(&ubi->wl_lock);
ubi_assert(!ubi->move_from && !ubi->move_to); ubi_assert(!ubi->move_from && !ubi->move_to);
...@@ -893,6 +897,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -893,6 +897,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
dbg_wl("done"); dbg_wl("done");
mutex_unlock(&ubi->move_mutex); mutex_unlock(&ubi->move_mutex);
up_read(&ubi->fm_eba_sem);
return 0; return 0;
/* /*
...@@ -943,6 +948,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -943,6 +948,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
} }
mutex_unlock(&ubi->move_mutex); mutex_unlock(&ubi->move_mutex);
up_read(&ubi->fm_eba_sem);
return 0; return 0;
out_error: out_error:
...@@ -964,6 +970,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -964,6 +970,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
out_ro: out_ro:
ubi_ro_mode(ubi); ubi_ro_mode(ubi);
mutex_unlock(&ubi->move_mutex); mutex_unlock(&ubi->move_mutex);
up_read(&ubi->fm_eba_sem);
ubi_assert(err != 0); ubi_assert(err != 0);
return err < 0 ? err : -EIO; return err < 0 ? err : -EIO;
...@@ -971,6 +978,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -971,6 +978,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
ubi->wl_scheduled = 0; ubi->wl_scheduled = 0;
spin_unlock(&ubi->wl_lock); spin_unlock(&ubi->wl_lock);
mutex_unlock(&ubi->move_mutex); mutex_unlock(&ubi->move_mutex);
up_read(&ubi->fm_eba_sem);
ubi_free_vid_buf(vidb); ubi_free_vid_buf(vidb);
return 0; return 0;
} }
...@@ -1093,7 +1101,7 @@ static int __erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk) ...@@ -1093,7 +1101,7 @@ static int __erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk)
int err1; int err1;
/* Re-schedule the LEB for erasure */ /* Re-schedule the LEB for erasure */
err1 = schedule_erase(ubi, e, vol_id, lnum, 0); err1 = schedule_erase(ubi, e, vol_id, lnum, 0, false);
if (err1) { if (err1) {
wl_entry_destroy(ubi, e); wl_entry_destroy(ubi, e);
err = err1; err = err1;
...@@ -1274,7 +1282,7 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum, ...@@ -1274,7 +1282,7 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum,
} }
spin_unlock(&ubi->wl_lock); spin_unlock(&ubi->wl_lock);
err = schedule_erase(ubi, e, vol_id, lnum, torture); err = schedule_erase(ubi, e, vol_id, lnum, torture, false);
if (err) { if (err) {
spin_lock(&ubi->wl_lock); spin_lock(&ubi->wl_lock);
wl_tree_add(e, &ubi->used); wl_tree_add(e, &ubi->used);
...@@ -1565,7 +1573,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) ...@@ -1565,7 +1573,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
e->pnum = aeb->pnum; e->pnum = aeb->pnum;
e->ec = aeb->ec; e->ec = aeb->ec;
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, false)) {
wl_entry_destroy(ubi, e); wl_entry_destroy(ubi, e);
goto out_free; goto out_free;
} }
...@@ -1644,7 +1652,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) ...@@ -1644,7 +1652,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
e->ec = aeb->ec; e->ec = aeb->ec;
ubi_assert(!ubi->lookuptbl[e->pnum]); ubi_assert(!ubi->lookuptbl[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, false)) {
wl_entry_destroy(ubi, e); wl_entry_destroy(ubi, e);
goto out_free; goto out_free;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment