X-Git-Url: http://git.cascardo.info/?a=blobdiff_plain;f=drivers%2Fmtd%2Fubi%2Feba.c;h=95c4048a371e87b6f5517b01109b19db85cedc56;hb=4c609922a3ae0248597785d1f9adc8f142a80aef;hp=ebf517271d2925452d911eb8df9a9d973b05926c;hpb=d5bd1eba19230347acfc4960581a15bfb459a615;p=cascardo%2Flinux.git diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index ebf517271d29..95c4048a371e 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -49,6 +49,30 @@ /* Number of physical eraseblocks reserved for atomic LEB change operation */ #define EBA_RESERVED_PEBS 1 +/** + * struct ubi_eba_entry - structure encoding a single LEB -> PEB association + * @pnum: the physical eraseblock number attached to the LEB + * + * This structure is encoding a LEB -> PEB association. Note that the LEB + * number is not stored here, because it is the index used to access the + * entries table. + */ +struct ubi_eba_entry { + int pnum; +}; + +/** + * struct ubi_eba_table - LEB -> PEB association information + * @entries: the LEB to PEB mapping (one entry per LEB). + * + * This structure is private to the EBA logic and should be kept here. + * It is encoding the LEB to PEB association table, and is subject to + * changes. + */ +struct ubi_eba_table { + struct ubi_eba_entry *entries; +}; + /** * next_sqnum - get next sequence number. * @ubi: UBI device description object @@ -83,6 +107,110 @@ static int ubi_get_compat(const struct ubi_device *ubi, int vol_id) return 0; } +/** + * ubi_eba_get_ldesc - get information about a LEB + * @vol: volume description object + * @lnum: logical eraseblock number + * @ldesc: the LEB descriptor to fill + * + * Used to query information about a specific LEB. + * It is currently only returning the physical position of the LEB, but will be + * extended to provide more information. + */ +void ubi_eba_get_ldesc(struct ubi_volume *vol, int lnum, + struct ubi_eba_leb_desc *ldesc) +{ + ldesc->lnum = lnum; + ldesc->pnum = vol->eba_tbl->entries[lnum].pnum; +} + +/** + * ubi_eba_create_table - allocate a new EBA table and initialize it with all + * LEBs unmapped + * @vol: volume containing the EBA table to copy + * @nentries: number of entries in the table + * + * Allocate a new EBA table and initialize it with all LEBs unmapped. + * Returns a valid pointer if it succeed, an ERR_PTR() otherwise. + */ +struct ubi_eba_table *ubi_eba_create_table(struct ubi_volume *vol, + int nentries) +{ + struct ubi_eba_table *tbl; + int err = -ENOMEM; + int i; + + tbl = kzalloc(sizeof(*tbl), GFP_KERNEL); + if (!tbl) + return ERR_PTR(-ENOMEM); + + tbl->entries = kmalloc_array(nentries, sizeof(*tbl->entries), + GFP_KERNEL); + if (!tbl->entries) + goto err; + + for (i = 0; i < nentries; i++) + tbl->entries[i].pnum = UBI_LEB_UNMAPPED; + + return tbl; + +err: + kfree(tbl->entries); + kfree(tbl); + + return ERR_PTR(err); +} + +/** + * ubi_eba_destroy_table - destroy an EBA table + * @tbl: the table to destroy + * + * Destroy an EBA table. + */ +void ubi_eba_destroy_table(struct ubi_eba_table *tbl) +{ + if (!tbl) + return; + + kfree(tbl->entries); + kfree(tbl); +} + +/** + * ubi_eba_copy_table - copy the EBA table attached to vol into another table + * @vol: volume containing the EBA table to copy + * @dst: destination + * @nentries: number of entries to copy + * + * Copy the EBA table stored in vol into the one pointed by dst. + */ +void ubi_eba_copy_table(struct ubi_volume *vol, struct ubi_eba_table *dst, + int nentries) +{ + struct ubi_eba_table *src; + int i; + + ubi_assert(dst && vol && vol->eba_tbl); + + src = vol->eba_tbl; + + for (i = 0; i < nentries; i++) + dst->entries[i].pnum = src->entries[i].pnum; +} + +/** + * ubi_eba_replace_table - assign a new EBA table to a volume + * @vol: volume containing the EBA table to copy + * @tbl: new EBA table + * + * Assign a new EBA table to the volume and release the old one. + */ +void ubi_eba_replace_table(struct ubi_volume *vol, struct ubi_eba_table *tbl) +{ + ubi_eba_destroy_table(vol->eba_tbl); + vol->eba_tbl = tbl; +} + /** * ltree_lookup - look up the lock tree. * @ubi: UBI device description object @@ -311,6 +439,18 @@ static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum) spin_unlock(&ubi->ltree_lock); } +/** + * ubi_eba_is_mapped - check if a LEB is mapped. + * @vol: volume description object + * @lnum: logical eraseblock number + * + * This function returns true if the LEB is mapped, false otherwise. + */ +bool ubi_eba_is_mapped(struct ubi_volume *vol, int lnum) +{ + return vol->eba_tbl->entries[lnum].pnum >= 0; +} + /** * ubi_eba_unmap_leb - un-map logical eraseblock. * @ubi: UBI device description object @@ -333,7 +473,7 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, if (err) return err; - pnum = vol->eba_tbl[lnum]; + pnum = vol->eba_tbl->entries[lnum].pnum; if (pnum < 0) /* This logical eraseblock is already unmapped */ goto out_unlock; @@ -341,7 +481,7 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum); down_read(&ubi->fm_eba_sem); - vol->eba_tbl[lnum] = UBI_LEB_UNMAPPED; + vol->eba_tbl->entries[lnum].pnum = UBI_LEB_UNMAPPED; up_read(&ubi->fm_eba_sem); err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 0); @@ -373,6 +513,7 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, void *buf, int offset, int len, int check) { int err, pnum, scrub = 0, vol_id = vol->vol_id; + struct ubi_vid_io_buf *vidb; struct ubi_vid_hdr *vid_hdr; uint32_t uninitialized_var(crc); @@ -380,7 +521,7 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, if (err) return err; - pnum = vol->eba_tbl[lnum]; + pnum = vol->eba_tbl->entries[lnum].pnum; if (pnum < 0) { /* * The logical eraseblock is not mapped, fill the whole buffer @@ -403,13 +544,15 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, retry: if (check) { - vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); - if (!vid_hdr) { + vidb = ubi_alloc_vid_buf(ubi, GFP_NOFS); + if (!vidb) { err = -ENOMEM; goto out_unlock; } - err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1); + vid_hdr = ubi_get_vid_hdr(vidb); + + err = ubi_io_read_vid_hdr(ubi, pnum, vidb, 1); if (err && err != UBI_IO_BITFLIPS) { if (err > 0) { /* @@ -455,7 +598,7 @@ retry: ubi_assert(len == be32_to_cpu(vid_hdr->data_size)); crc = be32_to_cpu(vid_hdr->data_crc); - ubi_free_vid_hdr(ubi, vid_hdr); + ubi_free_vid_buf(vidb); } err = ubi_io_read_data(ubi, buf, pnum, offset, len); @@ -492,7 +635,7 @@ retry: return err; out_free: - ubi_free_vid_hdr(ubi, vid_hdr); + ubi_free_vid_buf(vidb); out_unlock: leb_read_unlock(ubi, vol_id, lnum); return err; @@ -554,49 +697,47 @@ int ubi_eba_read_leb_sg(struct ubi_device *ubi, struct ubi_volume *vol, } /** - * recover_peb - recover from write failure. - * @ubi: UBI device description object + * try_recover_peb - try to recover from write failure. + * @vol: volume description object * @pnum: the physical eraseblock to recover - * @vol_id: volume ID * @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 + * @vidb: VID buffer + * @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 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. + * 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 recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum, - const void *buf, int offset, int len) +static int try_recover_peb(struct ubi_volume *vol, int pnum, int lnum, + const void *buf, int offset, int len, + struct ubi_vid_io_buf *vidb, bool *retry) { - int err, idx = vol_id2idx(ubi, vol_id), new_pnum, data_size, tries = 0; - struct ubi_volume *vol = ubi->volumes[idx]; + struct ubi_device *ubi = vol->ubi; struct ubi_vid_hdr *vid_hdr; + int new_pnum, err, vol_id = vol->vol_id, data_size; uint32_t crc; - vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); - if (!vid_hdr) - return -ENOMEM; + *retry = false; -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; + 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); + err = ubi_io_read_vid_hdr(ubi, pnum, vidb, 1); if (err && err != UBI_IO_BITFLIPS) { if (err > 0) err = -EIO; - up_read(&ubi->fm_eba_sem); goto out_put; } @@ -608,12 +749,12 @@ retry: /* 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); + if (err && err != UBI_IO_BITFLIPS) goto out_unlock; - } } + *retry = true; + memcpy(ubi->peb_buf + offset, buf, len); data_size = offset + len; @@ -622,50 +763,140 @@ retry: 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_vid_hdr(ubi, new_pnum, vidb); + if (err) + goto out_unlock; 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; - } +out_unlock: mutex_unlock(&ubi->buf_mutex); - ubi_free_vid_hdr(ubi, vid_hdr); - vol->eba_tbl[lnum] = new_pnum; + if (!err) + vol->eba_tbl->entries[lnum].pnum = new_pnum; + +out_put: 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; + 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); + } -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; +/** + * recover_peb - recover from write failure. + * @ubi: UBI device description object + * @pnum: the physical eraseblock to recover + * @vol_id: volume ID + * @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 + * + * 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. + * 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), tries; + struct ubi_volume *vol = ubi->volumes[idx]; + struct ubi_vid_io_buf *vidb; + + vidb = ubi_alloc_vid_buf(ubi, GFP_NOFS); + if (!vidb) + return -ENOMEM; + + for (tries = 0; tries <= UBI_IO_RETRIES; tries++) { + bool retry; + + err = try_recover_peb(vol, pnum, lnum, buf, offset, len, vidb, + &retry); + if (!err || !retry) + break; + + ubi_msg(ubi, "try again"); } - ubi_msg(ubi, "try again"); - goto retry; + + ubi_free_vid_buf(vidb); + + return err; +} + +/** + * try_write_vid_and_data - try to write VID header and data to a new PEB. + * @vol: volume description object + * @lnum: logical eraseblock number + * @vidb: the VID buffer to write + * @buf: buffer containing the data + * @offset: where to start writing data + * @len: how many bytes should be written + * + * This function tries to write VID header and data belonging to logical + * eraseblock @lnum of volume @vol to a new physical eraseblock. Returns zero + * in case of success and a negative error code in case of failure. + * In case of error, it is possible that something was still written to the + * flash media, but may be some garbage. + */ +static int try_write_vid_and_data(struct ubi_volume *vol, int lnum, + struct ubi_vid_io_buf *vidb, const void *buf, + int offset, int len) +{ + struct ubi_device *ubi = vol->ubi; + int pnum, opnum, err, vol_id = vol->vol_id; + + pnum = ubi_wl_get_peb(ubi); + if (pnum < 0) { + err = pnum; + goto out_put; + } + + opnum = vol->eba_tbl->entries[lnum].pnum; + + dbg_eba("write VID hdr and %d bytes at offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + err = ubi_io_write_vid_hdr(ubi, pnum, vidb); + if (err) { + ubi_warn(ubi, "failed to write VID header to LEB %d:%d, PEB %d", + vol_id, lnum, pnum); + goto out_put; + } + + if (len) { + err = ubi_io_write_data(ubi, buf, pnum, offset, len); + if (err) { + ubi_warn(ubi, + "failed to write %d bytes at offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + goto out_put; + } + } + + vol->eba_tbl->entries[lnum].pnum = pnum; + +out_put: + up_read(&ubi->fm_eba_sem); + + if (err && pnum >= 0) + err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); + else if (!err && opnum >= 0) + err = ubi_wl_put_peb(ubi, vol_id, lnum, opnum, 0); + + return err; } /** @@ -681,11 +912,13 @@ write_error: * @vol. Returns zero in case of success and a negative error code in case * of failure. In case of error, it is possible that something was still * written to the flash media, but may be some garbage. + * This function retries %UBI_IO_RETRIES times before giving up. */ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, const void *buf, int offset, int len) { - int err, pnum, tries = 0, vol_id = vol->vol_id; + int err, pnum, tries, vol_id = vol->vol_id; + struct ubi_vid_io_buf *vidb; struct ubi_vid_hdr *vid_hdr; if (ubi->ro_mode) @@ -695,7 +928,7 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, if (err) return err; - pnum = vol->eba_tbl[lnum]; + pnum = vol->eba_tbl->entries[lnum].pnum; if (pnum >= 0) { dbg_eba("write %d bytes at offset %d of LEB %d:%d, PEB %d", len, offset, vol_id, lnum, pnum); @@ -706,23 +939,23 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, if (err == -EIO && ubi->bad_allowed) err = recover_peb(ubi, pnum, vol_id, lnum, buf, offset, len); - if (err) - ubi_ro_mode(ubi); } - leb_write_unlock(ubi, vol_id, lnum); - return err; + + goto out; } /* * The logical eraseblock is not mapped. We have to get a free physical * eraseblock and write the volume identifier header there first. */ - vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); - if (!vid_hdr) { + vidb = ubi_alloc_vid_buf(ubi, GFP_NOFS); + if (!vidb) { leb_write_unlock(ubi, vol_id, lnum); return -ENOMEM; } + vid_hdr = ubi_get_vid_hdr(vidb); + vid_hdr->vol_type = UBI_VID_DYNAMIC; vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); vid_hdr->vol_id = cpu_to_be32(vol_id); @@ -730,67 +963,30 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, vid_hdr->compat = ubi_get_compat(ubi, vol_id); vid_hdr->data_pad = cpu_to_be32(vol->data_pad); -retry: - pnum = ubi_wl_get_peb(ubi); - if (pnum < 0) { - ubi_free_vid_hdr(ubi, vid_hdr); - leb_write_unlock(ubi, vol_id, lnum); - up_read(&ubi->fm_eba_sem); - return pnum; - } - - dbg_eba("write VID hdr and %d bytes at offset %d of LEB %d:%d, PEB %d", - len, offset, vol_id, lnum, pnum); - - err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); - if (err) { - ubi_warn(ubi, "failed to write VID header to LEB %d:%d, PEB %d", - vol_id, lnum, pnum); - up_read(&ubi->fm_eba_sem); - goto write_error; - } + for (tries = 0; tries <= UBI_IO_RETRIES; tries++) { + err = try_write_vid_and_data(vol, lnum, vidb, buf, offset, len); + if (err != -EIO || !ubi->bad_allowed) + break; - if (len) { - err = ubi_io_write_data(ubi, buf, pnum, offset, len); - if (err) { - ubi_warn(ubi, "failed to write %d bytes at offset %d of LEB %d:%d, PEB %d", - len, offset, vol_id, lnum, pnum); - up_read(&ubi->fm_eba_sem); - goto write_error; - } + /* + * Fortunately, this is the first write operation to this + * physical eraseblock, so just put it and request a new one. + * We assume that if this physical eraseblock went bad, the + * erase code will handle that. + */ + vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); + ubi_msg(ubi, "try another PEB"); } - vol->eba_tbl[lnum] = pnum; - up_read(&ubi->fm_eba_sem); - - leb_write_unlock(ubi, vol_id, lnum); - ubi_free_vid_hdr(ubi, vid_hdr); - return 0; + ubi_free_vid_buf(vidb); -write_error: - if (err != -EIO || !ubi->bad_allowed) { +out: + if (err) ubi_ro_mode(ubi); - leb_write_unlock(ubi, vol_id, lnum); - ubi_free_vid_hdr(ubi, vid_hdr); - return err; - } - /* - * Fortunately, this is the first write operation to this physical - * eraseblock, so just put it and request a new one. We assume that if - * this physical eraseblock went bad, the erase code will handle that. - */ - err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); - if (err || ++tries > UBI_IO_RETRIES) { - ubi_ro_mode(ubi); - leb_write_unlock(ubi, vol_id, lnum); - ubi_free_vid_hdr(ubi, vid_hdr); - return err; - } + leb_write_unlock(ubi, vol_id, lnum); - vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); - ubi_msg(ubi, "try another PEB"); - goto retry; + return err; } /** @@ -818,7 +1014,8 @@ write_error: int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, const void *buf, int len, int used_ebs) { - int err, pnum, tries = 0, data_size = len, vol_id = vol->vol_id; + int err, tries, data_size = len, vol_id = vol->vol_id; + struct ubi_vid_io_buf *vidb; struct ubi_vid_hdr *vid_hdr; uint32_t crc; @@ -831,15 +1028,15 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, else ubi_assert(!(len & (ubi->min_io_size - 1))); - vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); - if (!vid_hdr) + vidb = ubi_alloc_vid_buf(ubi, GFP_NOFS); + if (!vidb) return -ENOMEM; + vid_hdr = ubi_get_vid_hdr(vidb); + err = leb_write_lock(ubi, vol_id, lnum); - if (err) { - ubi_free_vid_hdr(ubi, vid_hdr); - return err; - } + if (err) + goto out; vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); vid_hdr->vol_id = cpu_to_be32(vol_id); @@ -853,66 +1050,26 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, vid_hdr->used_ebs = cpu_to_be32(used_ebs); vid_hdr->data_crc = cpu_to_be32(crc); -retry: - pnum = ubi_wl_get_peb(ubi); - if (pnum < 0) { - ubi_free_vid_hdr(ubi, vid_hdr); - leb_write_unlock(ubi, vol_id, lnum); - up_read(&ubi->fm_eba_sem); - return pnum; - } + ubi_assert(vol->eba_tbl->entries[lnum].pnum < 0); - dbg_eba("write VID hdr and %d bytes at LEB %d:%d, PEB %d, used_ebs %d", - len, vol_id, lnum, pnum, used_ebs); + for (tries = 0; tries <= UBI_IO_RETRIES; tries++) { + err = try_write_vid_and_data(vol, lnum, vidb, buf, 0, len); + if (err != -EIO || !ubi->bad_allowed) + break; - err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); - if (err) { - ubi_warn(ubi, "failed to write VID header to LEB %d:%d, PEB %d", - vol_id, lnum, pnum); - up_read(&ubi->fm_eba_sem); - goto write_error; + vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); + ubi_msg(ubi, "try another PEB"); } - err = ubi_io_write_data(ubi, buf, pnum, 0, len); - if (err) { - ubi_warn(ubi, "failed to write %d bytes of data to PEB %d", - len, pnum); - up_read(&ubi->fm_eba_sem); - goto write_error; - } - - ubi_assert(vol->eba_tbl[lnum] < 0); - vol->eba_tbl[lnum] = pnum; - up_read(&ubi->fm_eba_sem); + if (err) + ubi_ro_mode(ubi); leb_write_unlock(ubi, vol_id, lnum); - ubi_free_vid_hdr(ubi, vid_hdr); - return 0; - -write_error: - if (err != -EIO || !ubi->bad_allowed) { - /* - * This flash device does not admit of bad eraseblocks or - * something nasty and unexpected happened. Switch to read-only - * mode just in case. - */ - ubi_ro_mode(ubi); - leb_write_unlock(ubi, vol_id, lnum); - ubi_free_vid_hdr(ubi, vid_hdr); - return err; - } - err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); - if (err || ++tries > UBI_IO_RETRIES) { - ubi_ro_mode(ubi); - leb_write_unlock(ubi, vol_id, lnum); - ubi_free_vid_hdr(ubi, vid_hdr); - return err; - } +out: + ubi_free_vid_buf(vidb); - vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); - ubi_msg(ubi, "try another PEB"); - goto retry; + return err; } /* @@ -935,7 +1092,8 @@ write_error: int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, const void *buf, int len) { - int err, pnum, old_pnum, tries = 0, vol_id = vol->vol_id; + int err, tries, vol_id = vol->vol_id; + struct ubi_vid_io_buf *vidb; struct ubi_vid_hdr *vid_hdr; uint32_t crc; @@ -953,10 +1111,12 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0); } - vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); - if (!vid_hdr) + vidb = ubi_alloc_vid_buf(ubi, GFP_NOFS); + if (!vidb) return -ENOMEM; + vid_hdr = ubi_get_vid_hdr(vidb); + mutex_lock(&ubi->alc_mutex); err = leb_write_lock(ubi, vol_id, lnum); if (err) @@ -974,70 +1134,31 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, vid_hdr->copy_flag = 1; vid_hdr->data_crc = cpu_to_be32(crc); -retry: - pnum = ubi_wl_get_peb(ubi); - if (pnum < 0) { - err = pnum; - up_read(&ubi->fm_eba_sem); - goto out_leb_unlock; - } - - dbg_eba("change LEB %d:%d, PEB %d, write VID hdr to PEB %d", - vol_id, lnum, vol->eba_tbl[lnum], pnum); + dbg_eba("change LEB %d:%d", vol_id, lnum); - err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); - if (err) { - ubi_warn(ubi, "failed to write VID header to LEB %d:%d, PEB %d", - vol_id, lnum, pnum); - up_read(&ubi->fm_eba_sem); - goto write_error; - } + for (tries = 0; tries <= UBI_IO_RETRIES; tries++) { + err = try_write_vid_and_data(vol, lnum, vidb, buf, 0, len); + if (err != -EIO || !ubi->bad_allowed) + break; - err = ubi_io_write_data(ubi, buf, pnum, 0, len); - if (err) { - ubi_warn(ubi, "failed to write %d bytes of data to PEB %d", - len, pnum); - up_read(&ubi->fm_eba_sem); - goto write_error; + vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); + ubi_msg(ubi, "try another PEB"); } - old_pnum = vol->eba_tbl[lnum]; - vol->eba_tbl[lnum] = pnum; - up_read(&ubi->fm_eba_sem); - - if (old_pnum >= 0) { - err = ubi_wl_put_peb(ubi, vol_id, lnum, old_pnum, 0); - if (err) - goto out_leb_unlock; - } + /* + * This flash device does not admit of bad eraseblocks or + * something nasty and unexpected happened. Switch to read-only + * mode just in case. + */ + if (err) + ubi_ro_mode(ubi); -out_leb_unlock: leb_write_unlock(ubi, vol_id, lnum); + out_mutex: mutex_unlock(&ubi->alc_mutex); - ubi_free_vid_hdr(ubi, vid_hdr); + ubi_free_vid_buf(vidb); return err; - -write_error: - if (err != -EIO || !ubi->bad_allowed) { - /* - * This flash device does not admit of bad eraseblocks or - * something nasty and unexpected happened. Switch to read-only - * mode just in case. - */ - ubi_ro_mode(ubi); - goto out_leb_unlock; - } - - err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); - if (err || ++tries > UBI_IO_RETRIES) { - ubi_ro_mode(ubi); - goto out_leb_unlock; - } - - vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); - ubi_msg(ubi, "try another PEB"); - goto retry; } /** @@ -1082,12 +1203,15 @@ static int is_error_sane(int err) * o a negative error code in case of failure. */ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, - struct ubi_vid_hdr *vid_hdr) + struct ubi_vid_io_buf *vidb) { int err, vol_id, lnum, data_size, aldata_size, idx; + struct ubi_vid_hdr *vid_hdr = ubi_get_vid_hdr(vidb); 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); @@ -1142,9 +1266,9 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, * probably waiting on @ubi->move_mutex. No need to continue the work, * cancel it. */ - if (vol->eba_tbl[lnum] != from) { + if (vol->eba_tbl->entries[lnum].pnum != from) { dbg_wl("LEB %d:%d is no longer mapped to PEB %d, mapped to PEB %d, cancel", - vol_id, lnum, from, vol->eba_tbl[lnum]); + vol_id, lnum, from, vol->eba_tbl->entries[lnum].pnum); err = MOVE_CANCEL_RACE; goto out_unlock_leb; } @@ -1196,7 +1320,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, } vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); - err = ubi_io_write_vid_hdr(ubi, to, vid_hdr); + err = ubi_io_write_vid_hdr(ubi, to, vidb); if (err) { if (err == -EIO) err = MOVE_TARGET_WR_ERR; @@ -1206,7 +1330,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, cond_resched(); /* Read the VID header back and check if it was written correctly */ - err = ubi_io_read_vid_hdr(ubi, to, vid_hdr, 1); + err = ubi_io_read_vid_hdr(ubi, to, vidb, 1); if (err) { if (err != UBI_IO_BITFLIPS) { ubi_warn(ubi, "error %d while reading VID header back from PEB %d", @@ -1229,10 +1353,8 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, cond_resched(); } - ubi_assert(vol->eba_tbl[lnum] == from); - down_read(&ubi->fm_eba_sem); - vol->eba_tbl[lnum] = to; - up_read(&ubi->fm_eba_sem); + ubi_assert(vol->eba_tbl->entries[lnum].pnum == from); + vol->eba_tbl->entries[lnum].pnum = to; out_unlock_buf: mutex_unlock(&ubi->buf_mutex); @@ -1388,7 +1510,7 @@ out_free: */ int ubi_eba_init(struct ubi_device *ubi, struct ubi_attach_info *ai) { - int i, j, err, num_volumes; + int i, err, num_volumes; struct ubi_ainf_volume *av; struct ubi_volume *vol; struct ubi_ainf_peb *aeb; @@ -1404,35 +1526,39 @@ int ubi_eba_init(struct ubi_device *ubi, struct ubi_attach_info *ai) num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT; for (i = 0; i < num_volumes; i++) { + struct ubi_eba_table *tbl; + vol = ubi->volumes[i]; if (!vol) continue; cond_resched(); - vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int), - GFP_KERNEL); - if (!vol->eba_tbl) { - err = -ENOMEM; + tbl = ubi_eba_create_table(vol, vol->reserved_pebs); + if (IS_ERR(tbl)) { + err = PTR_ERR(tbl); goto out_free; } - for (j = 0; j < vol->reserved_pebs; j++) - vol->eba_tbl[j] = UBI_LEB_UNMAPPED; + ubi_eba_replace_table(vol, tbl); av = ubi_find_av(ai, idx2vol_id(ubi, i)); if (!av) continue; ubi_rb_for_each_entry(rb, aeb, &av->root, u.rb) { - if (aeb->lnum >= vol->reserved_pebs) + if (aeb->lnum >= vol->reserved_pebs) { /* * This may happen in case of an unclean reboot * during re-size. */ ubi_move_aeb_to_list(av, aeb, &ai->erase); - else - vol->eba_tbl[aeb->lnum] = aeb->pnum; + } else { + struct ubi_eba_entry *entry; + + entry = &vol->eba_tbl->entries[aeb->lnum]; + entry->pnum = aeb->pnum; + } } } @@ -1469,8 +1595,7 @@ out_free: for (i = 0; i < num_volumes; i++) { if (!ubi->volumes[i]) continue; - kfree(ubi->volumes[i]->eba_tbl); - ubi->volumes[i]->eba_tbl = NULL; + ubi_eba_replace_table(ubi->volumes[i], NULL); } return err; }