Commit 5ca97ad8 authored by Richard Weinberger's avatar Richard Weinberger

UBI: Fastmap: Rework fastmap error paths

If UBI is unable to write the fastmap to the device
we have make sure that upon next attach UBI will fall
back to scanning mode.
In case we cannot ensure that they only thing we can do
is falling back to read-only mode.

The current error handling code is not powercut proof.
It could happen that a powercut while invalidating would
lead to a state where an too old fastmap could be used upon
attach.
This patch addresses the issue by writing a fake fastmap
super block to a fresh PEB instead of reerasing the existing one.
The fake fastmap super block will UBI case to do a full scan.
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
parent 61de74ce
...@@ -1300,31 +1300,87 @@ static int erase_block(struct ubi_device *ubi, int pnum) ...@@ -1300,31 +1300,87 @@ static int erase_block(struct ubi_device *ubi, int pnum)
/** /**
* invalidate_fastmap - destroys a fastmap. * invalidate_fastmap - destroys a fastmap.
* @ubi: UBI device object * @ubi: UBI device object
* @fm: the fastmap to be destroyed
* *
* This function ensures that upon next UBI attach a full scan
* is issued. We need this if UBI is about to write a new fastmap
* but is unable to do so. In this case we have two options:
* a) Make sure that the current fastmap will not be usued upon
* attach time and contine or b) fall back to RO mode to have the
* current fastmap in a valid state.
* Returns 0 on success, < 0 indicates an internal error. * Returns 0 on success, < 0 indicates an internal error.
*/ */
static int invalidate_fastmap(struct ubi_device *ubi, static int invalidate_fastmap(struct ubi_device *ubi)
struct ubi_fastmap_layout *fm)
{ {
int ret; int ret;
struct ubi_vid_hdr *vh; struct ubi_fastmap_layout *fm;
struct ubi_wl_entry *e;
struct ubi_vid_hdr *vh = NULL;
ret = erase_block(ubi, fm->e[0]->pnum); if (!ubi->fm)
if (ret < 0) return 0;
return ret;
ubi->fm = NULL;
ret = -ENOMEM;
fm = kzalloc(sizeof(*fm), GFP_KERNEL);
if (!fm)
goto out;
vh = new_fm_vhdr(ubi, UBI_FM_SB_VOLUME_ID); vh = new_fm_vhdr(ubi, UBI_FM_SB_VOLUME_ID);
if (!vh) if (!vh)
return -ENOMEM; goto out_free_fm;
/* deleting the current fastmap SB is not enough, an old SB may exist, ret = -ENOSPC;
* so create a (corrupted) SB such that fastmap will find it and fall e = ubi_wl_get_fm_peb(ubi, 1);
* back to scanning mode in any case */ if (!e)
goto out_free_fm;
/*
* Create fake fastmap such that UBI will fall back
* to scanning mode.
*/
vh->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); vh->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
ret = ubi_io_write_vid_hdr(ubi, fm->e[0]->pnum, vh); ret = ubi_io_write_vid_hdr(ubi, e->pnum, vh);
if (ret < 0) {
ubi_wl_put_fm_peb(ubi, e, 0, 0);
goto out_free_fm;
}
fm->used_blocks = 1;
fm->e[0] = e;
ubi->fm = fm;
out:
ubi_free_vid_hdr(ubi, vh);
return ret; return ret;
out_free_fm:
kfree(fm);
goto out;
}
/**
* return_fm_pebs - returns all PEBs used by a fastmap back to the
* WL sub-system.
* @ubi: UBI device object
* @fm: fastmap layout object
*/
static void return_fm_pebs(struct ubi_device *ubi,
struct ubi_fastmap_layout *fm)
{
int i;
if (!fm)
return;
for (i = 0; i < fm->used_blocks; i++) {
if (fm->e[i]) {
ubi_wl_put_fm_peb(ubi, fm->e[i], i,
fm->to_be_tortured[i]);
fm->e[i] = NULL;
}
}
} }
/** /**
...@@ -1336,7 +1392,7 @@ static int invalidate_fastmap(struct ubi_device *ubi, ...@@ -1336,7 +1392,7 @@ static int invalidate_fastmap(struct ubi_device *ubi,
*/ */
int ubi_update_fastmap(struct ubi_device *ubi) int ubi_update_fastmap(struct ubi_device *ubi)
{ {
int ret, i; int ret, i, j;
struct ubi_fastmap_layout *new_fm, *old_fm; struct ubi_fastmap_layout *new_fm, *old_fm;
struct ubi_wl_entry *tmp_e; struct ubi_wl_entry *tmp_e;
...@@ -1376,34 +1432,40 @@ int ubi_update_fastmap(struct ubi_device *ubi) ...@@ -1376,34 +1432,40 @@ int ubi_update_fastmap(struct ubi_device *ubi)
tmp_e = ubi_wl_get_fm_peb(ubi, 0); tmp_e = ubi_wl_get_fm_peb(ubi, 0);
spin_unlock(&ubi->wl_lock); spin_unlock(&ubi->wl_lock);
if (!tmp_e && !old_fm) { if (!tmp_e) {
int j; if (old_fm && old_fm->e[i]) {
ubi_err(ubi, "could not get any free erase block");
for (j = 1; j < i; j++)
ubi_wl_put_fm_peb(ubi, new_fm->e[j], j, 0);
ret = -ENOSPC;
goto err;
} else if (!tmp_e && old_fm && old_fm->e[i]) {
ret = erase_block(ubi, old_fm->e[i]->pnum); ret = erase_block(ubi, old_fm->e[i]->pnum);
if (ret < 0) { if (ret < 0) {
int j; ubi_err(ubi, "could not erase old fastmap PEB");
for (j = 1; j < i; j++) for (j = 1; j < i; j++) {
ubi_wl_put_fm_peb(ubi, new_fm->e[j], ubi_wl_put_fm_peb(ubi, new_fm->e[j],
j, 0); j, 0);
new_fm->e[j] = NULL;
ubi_err(ubi, "could not erase old fastmap PEB"); }
goto err; goto err;
} }
new_fm->e[i] = old_fm->e[i]; new_fm->e[i] = old_fm->e[i];
old_fm->e[i] = NULL;
} else {
ubi_err(ubi, "could not get any free erase block");
for (j = 1; j < i; j++) {
ubi_wl_put_fm_peb(ubi, new_fm->e[j], j, 0);
new_fm->e[j] = NULL;
}
ret = -ENOSPC;
goto err;
}
} else { } else {
new_fm->e[i] = tmp_e; new_fm->e[i] = tmp_e;
if (old_fm && old_fm->e[i]) if (old_fm && old_fm->e[i]) {
ubi_wl_put_fm_peb(ubi, old_fm->e[i], i, ubi_wl_put_fm_peb(ubi, old_fm->e[i], i,
old_fm->to_be_tortured[i]); old_fm->to_be_tortured[i]);
old_fm->e[i] = NULL;
}
} }
} }
...@@ -1412,6 +1474,7 @@ int ubi_update_fastmap(struct ubi_device *ubi) ...@@ -1412,6 +1474,7 @@ int ubi_update_fastmap(struct ubi_device *ubi)
for (i = new_fm->used_blocks; i < old_fm->used_blocks; i++) { for (i = new_fm->used_blocks; i < old_fm->used_blocks; i++) {
ubi_wl_put_fm_peb(ubi, old_fm->e[i], i, ubi_wl_put_fm_peb(ubi, old_fm->e[i], i,
old_fm->to_be_tortured[i]); old_fm->to_be_tortured[i]);
old_fm->e[i] = NULL;
} }
} }
...@@ -1424,29 +1487,33 @@ int ubi_update_fastmap(struct ubi_device *ubi) ...@@ -1424,29 +1487,33 @@ int ubi_update_fastmap(struct ubi_device *ubi)
if (!tmp_e) { if (!tmp_e) {
ret = erase_block(ubi, old_fm->e[0]->pnum); ret = erase_block(ubi, old_fm->e[0]->pnum);
if (ret < 0) { if (ret < 0) {
int i;
ubi_err(ubi, "could not erase old anchor PEB"); ubi_err(ubi, "could not erase old anchor PEB");
for (i = 1; i < new_fm->used_blocks; i++) for (i = 1; i < new_fm->used_blocks; i++) {
ubi_wl_put_fm_peb(ubi, new_fm->e[i], ubi_wl_put_fm_peb(ubi, new_fm->e[i],
i, 0); i, 0);
new_fm->e[i] = NULL;
}
goto err; goto err;
} }
new_fm->e[0] = old_fm->e[0]; new_fm->e[0] = old_fm->e[0];
new_fm->e[0]->ec = ret; new_fm->e[0]->ec = ret;
old_fm->e[0] = NULL;
} else { } else {
/* we've got a new anchor PEB, return the old one */ /* we've got a new anchor PEB, return the old one */
ubi_wl_put_fm_peb(ubi, old_fm->e[0], 0, ubi_wl_put_fm_peb(ubi, old_fm->e[0], 0,
old_fm->to_be_tortured[0]); old_fm->to_be_tortured[0]);
new_fm->e[0] = tmp_e; new_fm->e[0] = tmp_e;
old_fm->e[0] = NULL;
} }
} else { } else {
if (!tmp_e) { if (!tmp_e) {
int i;
ubi_err(ubi, "could not find any anchor PEB"); ubi_err(ubi, "could not find any anchor PEB");
for (i = 1; i < new_fm->used_blocks; i++) for (i = 1; i < new_fm->used_blocks; i++) {
ubi_wl_put_fm_peb(ubi, new_fm->e[i], i, 0); ubi_wl_put_fm_peb(ubi, new_fm->e[i], i, 0);
new_fm->e[i] = NULL;
}
ret = -ENOSPC; ret = -ENOSPC;
goto err; goto err;
...@@ -1469,19 +1536,18 @@ int ubi_update_fastmap(struct ubi_device *ubi) ...@@ -1469,19 +1536,18 @@ int ubi_update_fastmap(struct ubi_device *ubi)
return ret; return ret;
err: err:
kfree(new_fm);
ubi_warn(ubi, "Unable to write new fastmap, err=%i", ret); ubi_warn(ubi, "Unable to write new fastmap, err=%i", ret);
ret = 0; ret = invalidate_fastmap(ubi);
if (old_fm) {
ret = invalidate_fastmap(ubi, old_fm);
if (ret < 0) { if (ret < 0) {
ubi_err(ubi, "Unable to invalidiate current fastmap!"); ubi_err(ubi, "Unable to invalidiate current fastmap!");
ubi_ro_mode(ubi); ubi_ro_mode(ubi);
} } else {
else if (ret) return_fm_pebs(ubi, old_fm);
return_fm_pebs(ubi, new_fm);
ret = 0; ret = 0;
} }
kfree(new_fm);
goto out_unlock; goto out_unlock;
} }
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