Merge tag 'upstream-4.9-rc1' of git://git.infradead.org/linux-ubifs
[cascardo/linux.git] / drivers / mtd / ubi / eba.c
index ebf5172..95c4048 100644 (file)
 /* 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;
 }