Skip to content

Commit

Permalink
ubi: Fix races around ubi_refill_pools()
Browse files Browse the repository at this point in the history
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: dbb7d2a ("UBI: Add fastmap core")
Signed-off-by: Richard Weinberger <richard@nod.at>
  • Loading branch information
richardweinberger committed Oct 2, 2016
1 parent 2365418 commit 2e8f08d
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 14 deletions.
4 changes: 2 additions & 2 deletions drivers/mtd/ubi/eba.c
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,8 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
struct ubi_volume *vol;
uint32_t crc;

ubi_assert(rwsem_is_locked(&ubi->fm_eba_sem));

vol_id = be32_to_cpu(vid_hdr->vol_id);
lnum = be32_to_cpu(vid_hdr->lnum);

Expand Down Expand Up @@ -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);
down_read(&ubi->fm_eba_sem);
vol->eba_tbl->entries[lnum].pnum = to;
up_read(&ubi->fm_eba_sem);

out_unlock_buf:
mutex_unlock(&ubi->buf_mutex);
Expand Down
6 changes: 4 additions & 2 deletions drivers/mtd/ubi/fastmap-wl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
int pnum;

ubi_assert(rwsem_is_locked(&ubi->fm_eba_sem));

if (pool->used == pool->size) {
/* We cannot update the fastmap here because this
* function is called in atomic context.
Expand Down Expand Up @@ -303,7 +305,7 @@ int ubi_ensure_anchor_pebs(struct ubi_device *ubi)

wrk->anchor = 1;
wrk->func = &wear_leveling_worker;
schedule_ubi_work(ubi, wrk);
__schedule_ubi_work(ubi, wrk);
return 0;
}

Expand Down Expand Up @@ -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);

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);
}

/**
Expand Down
14 changes: 10 additions & 4 deletions drivers/mtd/ubi/fastmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -1492,22 +1492,30 @@ int ubi_update_fastmap(struct ubi_device *ubi)
struct ubi_wl_entry *tmp_e;

down_write(&ubi->fm_protect);
down_write(&ubi->work_sem);
down_write(&ubi->fm_eba_sem);

ubi_refill_pools(ubi);

if (ubi->ro_mode || ubi->fm_disabled) {
up_write(&ubi->fm_eba_sem);
up_write(&ubi->work_sem);
up_write(&ubi->fm_protect);
return 0;
}

ret = ubi_ensure_anchor_pebs(ubi);
if (ret) {
up_write(&ubi->fm_eba_sem);
up_write(&ubi->work_sem);
up_write(&ubi->fm_protect);
return ret;
}

new_fm = kzalloc(sizeof(*new_fm), GFP_KERNEL);
if (!new_fm) {
up_write(&ubi->fm_eba_sem);
up_write(&ubi->work_sem);
up_write(&ubi->fm_protect);
return -ENOMEM;
}
Expand Down Expand Up @@ -1616,16 +1624,14 @@ int ubi_update_fastmap(struct ubi_device *ubi)
new_fm->e[0] = tmp_e;
}

down_write(&ubi->work_sem);
down_write(&ubi->fm_eba_sem);
ret = ubi_write_fastmap(ubi, new_fm);
up_write(&ubi->fm_eba_sem);
up_write(&ubi->work_sem);

if (ret)
goto err;

out_unlock:
up_write(&ubi->fm_eba_sem);
up_write(&ubi->work_sem);
up_write(&ubi->fm_protect);
kfree(old_fm);
return ret;
Expand Down
20 changes: 14 additions & 6 deletions drivers/mtd/ubi/wl.c
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
* failure.
*/
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;

Expand All @@ -599,7 +599,10 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
wl_wrk->lnum = lnum;
wl_wrk->torture = torture;

schedule_ubi_work(ubi, wl_wrk);
if (nested)
__schedule_ubi_work(ubi, wl_wrk);
else
schedule_ubi_work(ubi, wl_wrk);
return 0;
}

Expand Down Expand Up @@ -663,6 +666,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,

vid_hdr = ubi_get_vid_hdr(vidb);

down_read(&ubi->fm_eba_sem);
mutex_lock(&ubi->move_mutex);
spin_lock(&ubi->wl_lock);
ubi_assert(!ubi->move_from && !ubi->move_to);
Expand Down Expand Up @@ -893,6 +897,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,

dbg_wl("done");
mutex_unlock(&ubi->move_mutex);
up_read(&ubi->fm_eba_sem);
return 0;

/*
Expand Down Expand Up @@ -943,6 +948,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
}

mutex_unlock(&ubi->move_mutex);
up_read(&ubi->fm_eba_sem);
return 0;

out_error:
Expand All @@ -964,13 +970,15 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
out_ro:
ubi_ro_mode(ubi);
mutex_unlock(&ubi->move_mutex);
up_read(&ubi->fm_eba_sem);
ubi_assert(err != 0);
return err < 0 ? err : -EIO;

out_cancel:
ubi->wl_scheduled = 0;
spin_unlock(&ubi->wl_lock);
mutex_unlock(&ubi->move_mutex);
up_read(&ubi->fm_eba_sem);
ubi_free_vid_buf(vidb);
return 0;
}
Expand Down Expand Up @@ -1093,7 +1101,7 @@ static int __erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk)
int err1;

/* 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) {
wl_entry_destroy(ubi, e);
err = err1;
Expand Down Expand Up @@ -1274,7 +1282,7 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum,
}
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) {
spin_lock(&ubi->wl_lock);
wl_tree_add(e, &ubi->used);
Expand Down Expand Up @@ -1565,7 +1573,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
e->pnum = aeb->pnum;
e->ec = aeb->ec;
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);
goto out_free;
}
Expand Down Expand Up @@ -1644,7 +1652,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
e->ec = aeb->ec;
ubi_assert(!ubi->lookuptbl[e->pnum]);
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);
goto out_free;
}
Expand Down

0 comments on commit 2e8f08d

Please sign in to comment.