Commit 78193237 authored by Richard Weinberger's avatar Richard Weinberger

ubi: fastmap: Correctly handle interrupted erasures in EBA

Fastmap cannot track the LEB unmap operation, therefore it can
happen that after an interrupted erasure the mapping still looks
good from Fastmap's point of view, while reading from the PEB will
cause an ECC error and confuses the upper layer.

Instead of teaching users of UBI how to deal with that, we read back
the VID header and check for errors. If the PEB is empty or shows ECC
errors we fixup the mapping and schedule the PEB for erasure.

Fixes: dbb7d2a8 ("UBI: Add fastmap core")
Cc: <stable@vger.kernel.org>
Reported-by: default avatarmartin bayern <Martinbayern@outlook.com>
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
parent 6e7d8016
...@@ -490,6 +490,82 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, ...@@ -490,6 +490,82 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
return err; return err;
} }
#ifdef CONFIG_MTD_UBI_FASTMAP
/**
* check_mapping - check and fixup a mapping
* @ubi: UBI device description object
* @vol: volume description object
* @lnum: logical eraseblock number
* @pnum: physical eraseblock number
*
* Checks whether a given mapping is valid. Fastmap cannot track LEB unmap
* operations, if such an operation is interrupted the mapping still looks
* good, but upon first read an ECC is reported to the upper layer.
* Normaly during the full-scan at attach time this is fixed, for Fastmap
* we have to deal with it while reading.
* If the PEB behind a LEB shows this symthom we change the mapping to
* %UBI_LEB_UNMAPPED and schedule the PEB for erasure.
*
* Returns 0 on success, negative error code in case of failure.
*/
static int check_mapping(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
int *pnum)
{
int err;
struct ubi_vid_io_buf *vidb;
if (!ubi->fast_attach)
return 0;
vidb = ubi_alloc_vid_buf(ubi, GFP_NOFS);
if (!vidb)
return -ENOMEM;
err = ubi_io_read_vid_hdr(ubi, *pnum, vidb, 0);
if (err > 0 && err != UBI_IO_BITFLIPS) {
int torture = 0;
switch (err) {
case UBI_IO_FF:
case UBI_IO_FF_BITFLIPS:
case UBI_IO_BAD_HDR:
case UBI_IO_BAD_HDR_EBADMSG:
break;
default:
ubi_assert(0);
}
if (err == UBI_IO_BAD_HDR_EBADMSG || err == UBI_IO_FF_BITFLIPS)
torture = 1;
down_read(&ubi->fm_eba_sem);
vol->eba_tbl->entries[lnum].pnum = UBI_LEB_UNMAPPED;
up_read(&ubi->fm_eba_sem);
ubi_wl_put_peb(ubi, vol->vol_id, lnum, *pnum, torture);
*pnum = UBI_LEB_UNMAPPED;
} else if (err < 0) {
ubi_err(ubi, "unable to read VID header back from PEB %i: %i",
*pnum, err);
goto out_free;
}
err = 0;
out_free:
ubi_free_vid_buf(vidb);
return err;
}
#else
static int check_mapping(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
int *pnum)
{
return 0;
}
#endif
/** /**
* ubi_eba_read_leb - read data. * ubi_eba_read_leb - read data.
* @ubi: UBI device description object * @ubi: UBI device description object
...@@ -522,7 +598,13 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, ...@@ -522,7 +598,13 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
return err; return err;
pnum = vol->eba_tbl->entries[lnum].pnum; pnum = vol->eba_tbl->entries[lnum].pnum;
if (pnum < 0) { if (pnum >= 0) {
err = check_mapping(ubi, vol, lnum, &pnum);
if (err < 0)
goto out_unlock;
}
if (pnum == UBI_LEB_UNMAPPED) {
/* /*
* The logical eraseblock is not mapped, fill the whole buffer * The logical eraseblock is not mapped, fill the whole buffer
* with 0xFF bytes. The exception is static volumes for which * with 0xFF bytes. The exception is static volumes for which
...@@ -930,6 +1012,12 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, ...@@ -930,6 +1012,12 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
return err; return err;
pnum = vol->eba_tbl->entries[lnum].pnum; pnum = vol->eba_tbl->entries[lnum].pnum;
if (pnum >= 0) {
err = check_mapping(ubi, vol, lnum, &pnum);
if (err < 0)
goto out;
}
if (pnum >= 0) { if (pnum >= 0) {
dbg_eba("write %d bytes at offset %d of LEB %d:%d, PEB %d", dbg_eba("write %d bytes at offset %d of LEB %d:%d, PEB %d",
len, offset, vol_id, lnum, pnum); len, offset, vol_id, lnum, pnum);
......
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