mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-25 18:30:06 +00:00
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
1 changed files with 108 additions and 84 deletions
|
@ -553,6 +553,102 @@ int ubi_eba_read_leb_sg(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
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.
|
||||
* @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
|
||||
* 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 new physical eraseblock number in case of success, and a negative
|
||||
* error code in case of failure.
|
||||
* Returns 0 in case of success, and a negative 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,
|
||||
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_vid_hdr *vid_hdr;
|
||||
uint32_t crc;
|
||||
|
||||
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
|
||||
if (!vid_hdr)
|
||||
return -ENOMEM;
|
||||
|
||||
retry:
|
||||
new_pnum = ubi_wl_get_peb(ubi);
|
||||
if (new_pnum < 0) {
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
up_read(&ubi->fm_eba_sem);
|
||||
return new_pnum;
|
||||
for (tries = 0; tries <= UBI_IO_RETRIES; tries++) {
|
||||
bool retry;
|
||||
|
||||
err = try_recover_peb(vol, pnum, lnum, buf, offset, len,
|
||||
vid_hdr, &retry);
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
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 a new issue