UBI: simplify recover_peb() code
recover_peb() is using a convoluted retry/exit path. Add try_recover_peb() to simplify the retry logic and make sure we have a single exit path instead of manually releasing the resource in each error path. Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com> Signed-off-by: Richard Weinberger <richard@nod.at>
This commit is contained in:
parent
7b6b749b12
commit
f036dfeb85
@ -553,6 +553,102 @@ int ubi_eba_read_leb_sg(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* try_recover_peb - try to recover from write failure.
|
||||||
|
* @vol: volume description object
|
||||||
|
* @pnum: the physical eraseblock to recover
|
||||||
|
* @lnum: logical eraseblock number
|
||||||
|
* @buf: data which was not written because of the write failure
|
||||||
|
* @offset: offset of the failed write
|
||||||
|
* @len: how many bytes should have been written
|
||||||
|
* @vid: VID header
|
||||||
|
* @retry: whether the caller should retry in case of failure
|
||||||
|
*
|
||||||
|
* This function is called in case of a write failure and moves all good data
|
||||||
|
* from the potentially bad physical eraseblock to a good physical eraseblock.
|
||||||
|
* This function also writes the data which was not written due to the failure.
|
||||||
|
* Returns 0 in case of success, and a negative error code in case of failure.
|
||||||
|
* In case of failure, the %retry parameter is set to false if this is a fatal
|
||||||
|
* error (retrying won't help), and true otherwise.
|
||||||
|
*/
|
||||||
|
static int try_recover_peb(struct ubi_volume *vol, int pnum, int lnum,
|
||||||
|
const void *buf, int offset, int len,
|
||||||
|
struct ubi_vid_hdr *vid_hdr, bool *retry)
|
||||||
|
{
|
||||||
|
struct ubi_device *ubi = vol->ubi;
|
||||||
|
int new_pnum, err, vol_id = vol->vol_id, data_size;
|
||||||
|
uint32_t crc;
|
||||||
|
|
||||||
|
*retry = false;
|
||||||
|
|
||||||
|
new_pnum = ubi_wl_get_peb(ubi);
|
||||||
|
if (new_pnum < 0) {
|
||||||
|
err = new_pnum;
|
||||||
|
goto out_put;
|
||||||
|
}
|
||||||
|
|
||||||
|
ubi_msg(ubi, "recover PEB %d, move data to PEB %d",
|
||||||
|
pnum, new_pnum);
|
||||||
|
|
||||||
|
err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1);
|
||||||
|
if (err && err != UBI_IO_BITFLIPS) {
|
||||||
|
if (err > 0)
|
||||||
|
err = -EIO;
|
||||||
|
goto out_put;
|
||||||
|
}
|
||||||
|
|
||||||
|
ubi_assert(vid_hdr->vol_type == UBI_VID_DYNAMIC);
|
||||||
|
|
||||||
|
mutex_lock(&ubi->buf_mutex);
|
||||||
|
memset(ubi->peb_buf + offset, 0xFF, len);
|
||||||
|
|
||||||
|
/* Read everything before the area where the write failure happened */
|
||||||
|
if (offset > 0) {
|
||||||
|
err = ubi_io_read_data(ubi, ubi->peb_buf, pnum, 0, offset);
|
||||||
|
if (err && err != UBI_IO_BITFLIPS)
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
*retry = true;
|
||||||
|
|
||||||
|
memcpy(ubi->peb_buf + offset, buf, len);
|
||||||
|
|
||||||
|
data_size = offset + len;
|
||||||
|
crc = crc32(UBI_CRC32_INIT, ubi->peb_buf, data_size);
|
||||||
|
vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
||||||
|
vid_hdr->copy_flag = 1;
|
||||||
|
vid_hdr->data_size = cpu_to_be32(data_size);
|
||||||
|
vid_hdr->data_crc = cpu_to_be32(crc);
|
||||||
|
err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr);
|
||||||
|
if (err)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
err = ubi_io_write_data(ubi, ubi->peb_buf, new_pnum, 0, data_size);
|
||||||
|
|
||||||
|
out_unlock:
|
||||||
|
mutex_unlock(&ubi->buf_mutex);
|
||||||
|
|
||||||
|
if (!err)
|
||||||
|
vol->eba_tbl[lnum] = new_pnum;
|
||||||
|
|
||||||
|
out_put:
|
||||||
|
up_read(&ubi->fm_eba_sem);
|
||||||
|
|
||||||
|
if (!err) {
|
||||||
|
ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1);
|
||||||
|
ubi_msg(ubi, "data was successfully recovered");
|
||||||
|
} else if (new_pnum >= 0) {
|
||||||
|
/*
|
||||||
|
* Bad luck? This physical eraseblock is bad too? Crud. Let's
|
||||||
|
* try to get another one.
|
||||||
|
*/
|
||||||
|
ubi_wl_put_peb(ubi, vol_id, lnum, new_pnum, 1);
|
||||||
|
ubi_warn(ubi, "failed to write to PEB %d", new_pnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* recover_peb - recover from write failure.
|
* recover_peb - recover from write failure.
|
||||||
* @ubi: UBI device description object
|
* @ubi: UBI device description object
|
||||||
@ -566,106 +662,34 @@ int ubi_eba_read_leb_sg(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||||||
* This function is called in case of a write failure and moves all good data
|
* This function is called in case of a write failure and moves all good data
|
||||||
* from the potentially bad physical eraseblock to a good physical eraseblock.
|
* from the potentially bad physical eraseblock to a good physical eraseblock.
|
||||||
* This function also writes the data which was not written due to the failure.
|
* This function also writes the data which was not written due to the failure.
|
||||||
* Returns new physical eraseblock number in case of success, and a negative
|
* Returns 0 in case of success, and a negative error code in case of failure.
|
||||||
* error code in case of failure.
|
* This function tries %UBI_IO_RETRIES before giving up.
|
||||||
*/
|
*/
|
||||||
static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum,
|
static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum,
|
||||||
const void *buf, int offset, int len)
|
const void *buf, int offset, int len)
|
||||||
{
|
{
|
||||||
int err, idx = vol_id2idx(ubi, vol_id), new_pnum, data_size, tries = 0;
|
int err, idx = vol_id2idx(ubi, vol_id), tries;
|
||||||
struct ubi_volume *vol = ubi->volumes[idx];
|
struct ubi_volume *vol = ubi->volumes[idx];
|
||||||
struct ubi_vid_hdr *vid_hdr;
|
struct ubi_vid_hdr *vid_hdr;
|
||||||
uint32_t crc;
|
|
||||||
|
|
||||||
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
|
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
|
||||||
if (!vid_hdr)
|
if (!vid_hdr)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
retry:
|
for (tries = 0; tries <= UBI_IO_RETRIES; tries++) {
|
||||||
new_pnum = ubi_wl_get_peb(ubi);
|
bool retry;
|
||||||
if (new_pnum < 0) {
|
|
||||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
err = try_recover_peb(vol, pnum, lnum, buf, offset, len,
|
||||||
up_read(&ubi->fm_eba_sem);
|
vid_hdr, &retry);
|
||||||
return new_pnum;
|
if (!err || !retry)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ubi_msg(ubi, "try again");
|
||||||
}
|
}
|
||||||
|
|
||||||
ubi_msg(ubi, "recover PEB %d, move data to PEB %d",
|
|
||||||
pnum, new_pnum);
|
|
||||||
|
|
||||||
err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1);
|
|
||||||
if (err && err != UBI_IO_BITFLIPS) {
|
|
||||||
if (err > 0)
|
|
||||||
err = -EIO;
|
|
||||||
up_read(&ubi->fm_eba_sem);
|
|
||||||
goto out_put;
|
|
||||||
}
|
|
||||||
|
|
||||||
ubi_assert(vid_hdr->vol_type == UBI_VID_DYNAMIC);
|
|
||||||
|
|
||||||
mutex_lock(&ubi->buf_mutex);
|
|
||||||
memset(ubi->peb_buf + offset, 0xFF, len);
|
|
||||||
|
|
||||||
/* Read everything before the area where the write failure happened */
|
|
||||||
if (offset > 0) {
|
|
||||||
err = ubi_io_read_data(ubi, ubi->peb_buf, pnum, 0, offset);
|
|
||||||
if (err && err != UBI_IO_BITFLIPS) {
|
|
||||||
up_read(&ubi->fm_eba_sem);
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(ubi->peb_buf + offset, buf, len);
|
|
||||||
|
|
||||||
data_size = offset + len;
|
|
||||||
crc = crc32(UBI_CRC32_INIT, ubi->peb_buf, data_size);
|
|
||||||
vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
|
||||||
vid_hdr->copy_flag = 1;
|
|
||||||
vid_hdr->data_size = cpu_to_be32(data_size);
|
|
||||||
vid_hdr->data_crc = cpu_to_be32(crc);
|
|
||||||
err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr);
|
|
||||||
if (err) {
|
|
||||||
mutex_unlock(&ubi->buf_mutex);
|
|
||||||
up_read(&ubi->fm_eba_sem);
|
|
||||||
goto write_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ubi_io_write_data(ubi, ubi->peb_buf, new_pnum, 0, data_size);
|
|
||||||
if (err) {
|
|
||||||
mutex_unlock(&ubi->buf_mutex);
|
|
||||||
up_read(&ubi->fm_eba_sem);
|
|
||||||
goto write_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_unlock(&ubi->buf_mutex);
|
|
||||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||||
|
|
||||||
vol->eba_tbl[lnum] = new_pnum;
|
|
||||||
up_read(&ubi->fm_eba_sem);
|
|
||||||
ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1);
|
|
||||||
|
|
||||||
ubi_msg(ubi, "data was successfully recovered");
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
out_unlock:
|
|
||||||
mutex_unlock(&ubi->buf_mutex);
|
|
||||||
out_put:
|
|
||||||
ubi_wl_put_peb(ubi, vol_id, lnum, new_pnum, 1);
|
|
||||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
write_error:
|
|
||||||
/*
|
|
||||||
* Bad luck? This physical eraseblock is bad too? Crud. Let's try to
|
|
||||||
* get another one.
|
|
||||||
*/
|
|
||||||
ubi_warn(ubi, "failed to write to PEB %d", new_pnum);
|
|
||||||
ubi_wl_put_peb(ubi, vol_id, lnum, new_pnum, 1);
|
|
||||||
if (++tries > UBI_IO_RETRIES) {
|
|
||||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
ubi_msg(ubi, "try again");
|
|
||||||
goto retry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user