summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2018-09-11 17:23:33 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2018-09-11 17:23:33 +0200
commit87e86dd252bdbbb8b429f28445b92fccfb988819 (patch)
tree7b9dc61882448594aa21569f1fdb57a927bb97a7
parent149d33a6cd94debad2e9692b5bd8d6a059cd1354 (diff)
parent6ce04c65f3262f1d0b709291aa6ddf23e712e608 (diff)
downloadbarebox-87e86dd252bdbbb8b429f28445b92fccfb988819.tar.gz
barebox-87e86dd252bdbbb8b429f28445b92fccfb988819.tar.xz
Merge branch 'for-next/ubi'
-rw-r--r--drivers/mtd/ubi/Kconfig2
-rw-r--r--drivers/mtd/ubi/attach.c473
-rw-r--r--drivers/mtd/ubi/build.c17
-rw-r--r--drivers/mtd/ubi/eba.c785
-rw-r--r--drivers/mtd/ubi/fastmap-wl.c6
-rw-r--r--drivers/mtd/ubi/fastmap.c300
-rw-r--r--drivers/mtd/ubi/io.c41
-rw-r--r--drivers/mtd/ubi/kapi.c18
-rw-r--r--drivers/mtd/ubi/ubi-media.h22
-rw-r--r--drivers/mtd/ubi/ubi.h194
-rw-r--r--drivers/mtd/ubi/upd.c8
-rw-r--r--drivers/mtd/ubi/vmt.c65
-rw-r--r--drivers/mtd/ubi/vtbl.c32
-rw-r--r--drivers/mtd/ubi/wl.c161
-rw-r--r--drivers/mtd/ubi/wl.h2
15 files changed, 1419 insertions, 707 deletions
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index 4c497931f1..9a344082b7 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -2,7 +2,7 @@ menuconfig MTD_UBI
tristate "Enable UBI - Unsorted block images"
select CRC32
help
- UBI is a software layer above MTD layer which admits of LVM-like
+ UBI is a software layer above MTD layer which admits use of LVM-like
logical volumes on top of MTD devices, hides some complexities of
flash chips like wear and bad blocks and provides some other useful
capabilities. Please, consult the MTD web site for more details
diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c
index ff4b4e7acb..eee0e55ba9 100644
--- a/drivers/mtd/ubi/attach.c
+++ b/drivers/mtd/ubi/attach.c
@@ -85,9 +85,132 @@
static int self_check_ai(struct ubi_device *ubi, struct ubi_attach_info *ai);
-/* Temporary variables used during scanning */
-static struct ubi_ec_hdr *ech;
-static struct ubi_vid_hdr *vidh;
+#define AV_FIND BIT(0)
+#define AV_ADD BIT(1)
+#define AV_FIND_OR_ADD (AV_FIND | AV_ADD)
+
+/**
+ * find_or_add_av - internal function to find a volume, add a volume or do
+ * both (find and add if missing).
+ * @ai: attaching information
+ * @vol_id: the requested volume ID
+ * @flags: a combination of the %AV_FIND and %AV_ADD flags describing the
+ * expected operation. If only %AV_ADD is set, -EEXIST is returned
+ * if the volume already exists. If only %AV_FIND is set, NULL is
+ * returned if the volume does not exist. And if both flags are
+ * set, the helper first tries to find an existing volume, and if
+ * it does not exist it creates a new one.
+ * @created: in value used to inform the caller whether it"s a newly created
+ * volume or not.
+ *
+ * This function returns a pointer to a volume description or an ERR_PTR if
+ * the operation failed. It can also return NULL if only %AV_FIND is set and
+ * the volume does not exist.
+ */
+static struct ubi_ainf_volume *find_or_add_av(struct ubi_attach_info *ai,
+ int vol_id, unsigned int flags,
+ bool *created)
+{
+ struct ubi_ainf_volume *av;
+ struct rb_node **p = &ai->volumes.rb_node, *parent = NULL;
+
+ /* Walk the volume RB-tree to look if this volume is already present */
+ while (*p) {
+ parent = *p;
+ av = rb_entry(parent, struct ubi_ainf_volume, rb);
+
+ if (vol_id == av->vol_id) {
+ *created = false;
+
+ if (!(flags & AV_FIND))
+ return ERR_PTR(-EEXIST);
+
+ return av;
+ }
+
+ if (vol_id > av->vol_id)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ if (!(flags & AV_ADD))
+ return NULL;
+
+ /* The volume is absent - add it */
+ av = kzalloc(sizeof(*av), GFP_KERNEL);
+ if (!av)
+ return ERR_PTR(-ENOMEM);
+
+ av->vol_id = vol_id;
+
+ if (vol_id > ai->highest_vol_id)
+ ai->highest_vol_id = vol_id;
+
+ rb_link_node(&av->rb, parent, p);
+ rb_insert_color(&av->rb, &ai->volumes);
+ ai->vols_found += 1;
+ *created = true;
+ dbg_bld("added volume %d", vol_id);
+ return av;
+}
+
+/**
+ * ubi_find_or_add_av - search for a volume in the attaching information and
+ * add one if it does not exist.
+ * @ai: attaching information
+ * @vol_id: the requested volume ID
+ * @created: whether the volume has been created or not
+ *
+ * This function returns a pointer to the new volume description or an
+ * ERR_PTR if the operation failed.
+ */
+static struct ubi_ainf_volume *ubi_find_or_add_av(struct ubi_attach_info *ai,
+ int vol_id, bool *created)
+{
+ return find_or_add_av(ai, vol_id, AV_FIND_OR_ADD, created);
+}
+
+/**
+ * ubi_alloc_aeb - allocate an aeb element
+ * @ai: attaching information
+ * @pnum: physical eraseblock number
+ * @ec: erase counter of the physical eraseblock
+ *
+ * Allocate an aeb object and initialize the pnum and ec information.
+ * vol_id and lnum are set to UBI_UNKNOWN, and the other fields are
+ * initialized to zero.
+ * Note that the element is not added in any list or RB tree.
+ */
+struct ubi_ainf_peb *ubi_alloc_aeb(struct ubi_attach_info *ai, int pnum,
+ int ec)
+{
+ struct ubi_ainf_peb *aeb;
+
+ aeb = kzalloc(sizeof(*aeb), GFP_KERNEL);
+ if (!aeb)
+ return NULL;
+
+ aeb->pnum = pnum;
+ aeb->ec = ec;
+ aeb->vol_id = UBI_UNKNOWN;
+ aeb->lnum = UBI_UNKNOWN;
+
+ return aeb;
+}
+
+/**
+ * ubi_free_aeb - free an aeb element
+ * @ai: attaching information
+ * @aeb: the element to free
+ *
+ * Free an aeb object. The caller must have removed the element from any list
+ * or RB tree.
+ */
+void ubi_free_aeb(struct ubi_attach_info *ai, struct ubi_ainf_peb *aeb)
+{
+ kfree(aeb);
+}
/**
* add_to_list - add physical eraseblock to a list.
@@ -125,14 +248,12 @@ static int add_to_list(struct ubi_attach_info *ai, int pnum, int vol_id,
} else
BUG();
- aeb = kzalloc(sizeof(*aeb), GFP_KERNEL);
+ aeb = ubi_alloc_aeb(ai, pnum, ec);
if (!aeb)
return -ENOMEM;
- aeb->pnum = pnum;
aeb->vol_id = vol_id;
aeb->lnum = lnum;
- aeb->ec = ec;
if (to_head)
list_add(&aeb->u.list, list);
else
@@ -157,18 +278,48 @@ static int add_corrupted(struct ubi_attach_info *ai, int pnum, int ec)
dbg_bld("add to corrupted: PEB %d, EC %d", pnum, ec);
- aeb = kzalloc(sizeof(aeb), GFP_KERNEL);
+ aeb = ubi_alloc_aeb(ai, pnum, ec);
if (!aeb)
return -ENOMEM;
ai->corr_peb_count += 1;
- aeb->pnum = pnum;
- aeb->ec = ec;
list_add(&aeb->u.list, &ai->corr);
return 0;
}
/**
+ * add_fastmap - add a Fastmap related physical eraseblock.
+ * @ai: attaching information
+ * @pnum: physical eraseblock number the VID header came from
+ * @vid_hdr: the volume identifier header
+ * @ec: erase counter of the physical eraseblock
+ *
+ * This function allocates a 'struct ubi_ainf_peb' object for a Fastamp
+ * physical eraseblock @pnum and adds it to the 'fastmap' list.
+ * Such blocks can be Fastmap super and data blocks from both the most
+ * recent Fastmap we're attaching from or from old Fastmaps which will
+ * be erased.
+ */
+static int add_fastmap(struct ubi_attach_info *ai, int pnum,
+ struct ubi_vid_hdr *vid_hdr, int ec)
+{
+ struct ubi_ainf_peb *aeb;
+
+ aeb = ubi_alloc_aeb(ai, pnum, ec);
+ if (!aeb)
+ return -ENOMEM;
+
+ aeb->vol_id = be32_to_cpu(vid_hdr->vol_id);
+ aeb->sqnum = be64_to_cpu(vid_hdr->sqnum);
+ list_add(&aeb->u.list, &ai->fastmap);
+
+ dbg_bld("add to fastmap list: PEB %d, vol_id %d, sqnum: %llu", pnum,
+ aeb->vol_id, aeb->sqnum);
+
+ return 0;
+}
+
+/**
* validate_vid_hdr - check volume identifier header.
* @ubi: UBI device description object
* @vid_hdr: the volume identifier header to check
@@ -254,44 +405,20 @@ static struct ubi_ainf_volume *add_volume(struct ubi_attach_info *ai,
const struct ubi_vid_hdr *vid_hdr)
{
struct ubi_ainf_volume *av;
- struct rb_node **p = &ai->volumes.rb_node, *parent = NULL;
+ bool created;
ubi_assert(vol_id == be32_to_cpu(vid_hdr->vol_id));
- /* Walk the volume RB-tree to look if this volume is already present */
- while (*p) {
- parent = *p;
- av = rb_entry(parent, struct ubi_ainf_volume, rb);
-
- if (vol_id == av->vol_id)
- return av;
-
- if (vol_id > av->vol_id)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
- }
-
- /* The volume is absent - add it */
- av = kmalloc(sizeof(struct ubi_ainf_volume), GFP_KERNEL);
- if (!av)
- return ERR_PTR(-ENOMEM);
+ av = ubi_find_or_add_av(ai, vol_id, &created);
+ if (IS_ERR(av) || !created)
+ return av;
- av->highest_lnum = av->leb_count = 0;
- av->vol_id = vol_id;
- av->root = RB_ROOT;
av->used_ebs = be32_to_cpu(vid_hdr->used_ebs);
av->data_pad = be32_to_cpu(vid_hdr->data_pad);
av->compat = vid_hdr->compat;
av->vol_type = vid_hdr->vol_type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME
: UBI_STATIC_VOLUME;
- if (vol_id > ai->highest_vol_id)
- ai->highest_vol_id = vol_id;
- rb_link_node(&av->rb, parent, p);
- rb_insert_color(&av->rb, &ai->volumes);
- ai->vols_found += 1;
- dbg_bld("added volume %d", vol_id);
return av;
}
@@ -320,7 +447,7 @@ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb,
{
int len, err, second_is_newer, bitflips = 0, corrupted = 0;
uint32_t data_crc, crc;
- struct ubi_vid_hdr *vh = NULL;
+ struct ubi_vid_io_buf *vidb = NULL;
unsigned long long sqnum2 = be64_to_cpu(vid_hdr->sqnum);
if (sqnum2 == aeb->sqnum) {
@@ -363,12 +490,12 @@ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb,
return bitflips << 1;
}
- vh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
- if (!vh)
+ vidb = ubi_alloc_vid_buf(ubi, GFP_KERNEL);
+ if (!vidb)
return -ENOMEM;
pnum = aeb->pnum;
- err = ubi_io_read_vid_hdr(ubi, pnum, vh, 0);
+ err = ubi_io_read_vid_hdr(ubi, pnum, vidb, 0);
if (err) {
if (err == UBI_IO_BITFLIPS)
bitflips = 1;
@@ -382,7 +509,7 @@ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb,
}
}
- vid_hdr = vh;
+ vid_hdr = ubi_get_vid_hdr(vidb);
}
/* Read the data of the copy and check the CRC */
@@ -406,7 +533,7 @@ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb,
bitflips |= !!err;
}
- ubi_free_vid_hdr(ubi, vh);
+ ubi_free_vid_buf(vidb);
if (second_is_newer)
dbg_bld("second PEB %d is newer, copy_flag is set", pnum);
@@ -417,7 +544,7 @@ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb,
out_unlock:
out_free_vidh:
- ubi_free_vid_hdr(ubi, vh);
+ ubi_free_vid_buf(vidb);
return err;
}
@@ -562,12 +689,10 @@ int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum,
if (err)
return err;
- aeb = kzalloc(sizeof(*aeb), GFP_KERNEL);
+ aeb = ubi_alloc_aeb(ai, pnum, ec);
if (!aeb)
return -ENOMEM;
- aeb->ec = ec;
- aeb->pnum = pnum;
aeb->vol_id = vol_id;
aeb->lnum = lnum;
aeb->scrub = bitflips;
@@ -586,6 +711,21 @@ int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum,
}
/**
+ * ubi_add_av - add volume to the attaching information.
+ * @ai: attaching information
+ * @vol_id: the requested volume ID
+ *
+ * This function returns a pointer to the new volume description or an
+ * ERR_PTR if the operation failed.
+ */
+struct ubi_ainf_volume *ubi_add_av(struct ubi_attach_info *ai, int vol_id)
+{
+ bool created;
+
+ return find_or_add_av(ai, vol_id, AV_ADD, &created);
+}
+
+/**
* ubi_find_av - find volume in the attaching information.
* @ai: attaching information
* @vol_id: the requested volume ID
@@ -596,24 +736,15 @@ int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum,
struct ubi_ainf_volume *ubi_find_av(const struct ubi_attach_info *ai,
int vol_id)
{
- struct ubi_ainf_volume *av;
- struct rb_node *p = ai->volumes.rb_node;
+ bool created;
- while (p) {
- av = rb_entry(p, struct ubi_ainf_volume, rb);
-
- if (vol_id == av->vol_id)
- return av;
-
- if (vol_id > av->vol_id)
- p = p->rb_left;
- else
- p = p->rb_right;
- }
-
- return NULL;
+ return find_or_add_av((struct ubi_attach_info *)ai, vol_id, AV_FIND,
+ &created);
}
+static void destroy_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av,
+ struct list_head *list);
+
/**
* ubi_remove_av - delete attaching information about a volume.
* @ai: attaching information
@@ -621,19 +752,10 @@ struct ubi_ainf_volume *ubi_find_av(const struct ubi_attach_info *ai,
*/
void ubi_remove_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av)
{
- struct rb_node *rb;
- struct ubi_ainf_peb *aeb;
-
dbg_bld("remove attaching information about volume %d", av->vol_id);
- while ((rb = rb_first(&av->root))) {
- aeb = rb_entry(rb, struct ubi_ainf_peb, u.rb);
- rb_erase(&aeb->u.rb, &av->root);
- list_add_tail(&aeb->u.list, &ai->erase);
- }
-
rb_erase(&av->rb, &ai->volumes);
- kfree(av);
+ destroy_av(ai, av, &ai->erase);
ai->vols_found -= 1;
}
@@ -792,13 +914,26 @@ out_unlock:
return err;
}
+static bool vol_ignored(int vol_id)
+{
+ switch (vol_id) {
+ case UBI_LAYOUT_VOLUME_ID:
+ return true;
+ }
+
+#ifdef CONFIG_MTD_UBI_FASTMAP
+ return ubi_is_fm_vol(vol_id);
+#else
+ return false;
+#endif
+}
+
/**
* scan_peb - scan and process UBI headers of a PEB.
* @ubi: UBI device description object
* @ai: attaching information
* @pnum: the physical eraseblock number
- * @vid: The volume ID of the found volume will be stored in this pointer
- * @sqnum: The sqnum of the found volume will be stored in this pointer
+ * @fast: true if we're scanning for a Fastmap
*
* This function reads UBI headers of PEB @pnum, checks them, and adds
* information about this PEB to the corresponding list or RB-tree in the
@@ -806,9 +941,12 @@ out_unlock:
* successfully handled and a negative error code in case of failure.
*/
static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai,
- int pnum, int *vid, unsigned long long *sqnum)
+ int pnum, bool fast)
{
- long long uninitialized_var(ec);
+ struct ubi_ec_hdr *ech = ai->ech;
+ struct ubi_vid_io_buf *vidb = ai->vidb;
+ struct ubi_vid_hdr *vidh = ubi_get_vid_hdr(vidb);
+ long long ec;
int err, bitflips = 0, vol_id = -1, ec_err = 0;
dbg_bld("scan PEB %d", pnum);
@@ -905,7 +1043,7 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai,
/* OK, we've done with the EC header, let's look at the VID header */
- err = ubi_io_read_vid_hdr(ubi, pnum, vidh, 0);
+ err = ubi_io_read_vid_hdr(ubi, pnum, vidb, 0);
if (err < 0)
return err;
switch (err) {
@@ -924,6 +1062,20 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai,
*/
ai->maybe_bad_peb_count += 1;
case UBI_IO_BAD_HDR:
+ /*
+ * If we're facing a bad VID header we have to drop *all*
+ * Fastmap data structures we find. The most recent Fastmap
+ * could be bad and therefore there is a chance that we attach
+ * from an old one. On a fine MTD stack a PEB must not render
+ * bad all of a sudden, but the reality is different.
+ * So, let's be paranoid and help finding the root cause by
+ * falling back to scanning mode instead of attaching with a
+ * bad EBA table and cause data corruption which is hard to
+ * analyze.
+ */
+ if (fast)
+ ai->force_full_scan = 1;
+
if (ec_err)
/*
* Both headers are corrupted. There is a possibility
@@ -980,21 +1132,15 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai,
}
vol_id = be32_to_cpu(vidh->vol_id);
- if (vid)
- *vid = vol_id;
- if (sqnum)
- *sqnum = be64_to_cpu(vidh->sqnum);
- if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOLUME_ID) {
+ if (vol_id > UBI_MAX_VOLUMES && !vol_ignored(vol_id)) {
int lnum = be32_to_cpu(vidh->lnum);
/* Unsupported internal volume */
switch (vidh->compat) {
case UBI_COMPAT_DELETE:
- if (vol_id != UBI_FM_SB_VOLUME_ID
- && vol_id != UBI_FM_DATA_VOLUME_ID) {
- ubi_msg(ubi, "\"delete\" compatible internal volume %d:%d found, will remove it",
- vol_id, lnum);
- }
+ ubi_msg(ubi, "\"delete\" compatible internal volume %d:%d found, will remove it",
+ vol_id, lnum);
+
err = add_to_list(ai, pnum, vol_id, lnum,
ec, 1, &ai->erase);
if (err)
@@ -1026,7 +1172,12 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai,
if (ec_err)
ubi_warn(ubi, "valid VID header but corrupted EC header at PEB %d",
pnum);
- err = ubi_add_to_av(ubi, ai, pnum, ec, vidh, bitflips);
+
+ if (ubi_is_fm_vol(vol_id))
+ err = add_fastmap(ai, pnum, vidh, ec);
+ else
+ err = ubi_add_to_av(ubi, ai, pnum, ec, vidh, bitflips);
+
if (err)
return err;
@@ -1120,10 +1271,12 @@ static int late_analysis(struct ubi_device *ubi, struct ubi_attach_info *ai)
* destroy_av - free volume attaching information.
* @av: volume attaching information
* @ai: attaching information
+ * @list: put the aeb elements in there if !NULL, otherwise free them
*
* This function destroys the volume attaching information.
*/
-static void destroy_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av)
+static void destroy_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av,
+ struct list_head *list)
{
struct ubi_ainf_peb *aeb;
struct rb_node *this = av->root.rb_node;
@@ -1143,7 +1296,10 @@ static void destroy_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av)
this->rb_right = NULL;
}
- kfree(aeb);
+ if (list)
+ list_add_tail(&aeb->u.list, list);
+ else
+ ubi_free_aeb(ai, aeb);
}
}
kfree(av);
@@ -1161,19 +1317,23 @@ static void destroy_ai(struct ubi_attach_info *ai)
list_for_each_entry_safe(aeb, aeb_tmp, &ai->alien, u.list) {
list_del(&aeb->u.list);
- kfree(aeb);
+ ubi_free_aeb(ai, aeb);
}
list_for_each_entry_safe(aeb, aeb_tmp, &ai->erase, u.list) {
list_del(&aeb->u.list);
- kfree(aeb);
+ ubi_free_aeb(ai, aeb);
}
list_for_each_entry_safe(aeb, aeb_tmp, &ai->corr, u.list) {
list_del(&aeb->u.list);
- kfree(aeb);
+ ubi_free_aeb(ai, aeb);
}
list_for_each_entry_safe(aeb, aeb_tmp, &ai->free, u.list) {
list_del(&aeb->u.list);
- kfree(aeb);
+ ubi_free_aeb(ai, aeb);
+ }
+ list_for_each_entry_safe(aeb, aeb_tmp, &ai->fastmap, u.list) {
+ list_del(&aeb->u.list);
+ ubi_free_aeb(ai, aeb);
}
/* Destroy the volume RB-tree */
@@ -1194,7 +1354,7 @@ static void destroy_ai(struct ubi_attach_info *ai)
rb->rb_right = NULL;
}
- destroy_av(ai, av);
+ destroy_av(ai, av, NULL);
}
}
@@ -1221,17 +1381,17 @@ static int scan_all(struct ubi_device *ubi, struct ubi_attach_info *ai,
err = -ENOMEM;
- ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
- if (!ech)
+ ai->ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
+ if (!ai->ech)
return err;
- vidh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
- if (!vidh)
+ ai->vidb = ubi_alloc_vid_buf(ubi, GFP_KERNEL);
+ if (!ai->vidb)
goto out_ech;
for (pnum = start; pnum < ubi->peb_count; pnum++) {
dbg_gen("process PEB %d", pnum);
- err = scan_peb(ubi, ai, pnum, NULL, NULL);
+ err = scan_peb(ubi, ai, pnum, false);
if (err < 0)
goto out_vidh;
}
@@ -1273,22 +1433,40 @@ static int scan_all(struct ubi_device *ubi, struct ubi_attach_info *ai,
if (err)
goto out_vidh;
- ubi_free_vid_hdr(ubi, vidh);
- kfree(ech);
+ ubi_free_vid_buf(ai->vidb);
+ kfree(ai->ech);
return 0;
out_vidh:
- ubi_free_vid_hdr(ubi, vidh);
+ ubi_free_vid_buf(ai->vidb);
out_ech:
- kfree(ech);
+ kfree(ai->ech);
return err;
}
+static struct ubi_attach_info *alloc_ai(void)
+{
+ struct ubi_attach_info *ai;
+
+ ai = kzalloc(sizeof(struct ubi_attach_info), GFP_KERNEL);
+ if (!ai)
+ return ai;
+
+ INIT_LIST_HEAD(&ai->corr);
+ INIT_LIST_HEAD(&ai->free);
+ INIT_LIST_HEAD(&ai->erase);
+ INIT_LIST_HEAD(&ai->alien);
+ INIT_LIST_HEAD(&ai->fastmap);
+ ai->volumes = RB_ROOT;
+
+ return ai;
+}
+
#ifdef CONFIG_MTD_UBI_FASTMAP
/**
- * scan_fastmap - try to find a fastmap and attach from it.
+ * scan_fast - try to find a fastmap and attach from it.
* @ubi: UBI device description object
* @ai: attach info object
*
@@ -1297,71 +1475,64 @@ out_ech:
* UBI_NO_FASTMAP denotes that no fastmap was found.
* UBI_BAD_FASTMAP denotes that the found fastmap was invalid.
*/
-static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info *ai)
+static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info **ai)
{
- int err, pnum, fm_anchor = -1;
- unsigned long long max_sqnum = 0;
+ int err, pnum;
+ struct ubi_attach_info *scan_ai;
err = -ENOMEM;
- ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
- if (!ech)
+ scan_ai = alloc_ai();
+ if (!scan_ai)
goto out;
- vidh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
- if (!vidh)
+ scan_ai->ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
+ if (!scan_ai->ech)
+ goto out_ai;
+
+ scan_ai->vidb = ubi_alloc_vid_buf(ubi, GFP_KERNEL);
+ if (!scan_ai->vidb)
goto out_ech;
for (pnum = 0; pnum < UBI_FM_MAX_START; pnum++) {
- int vol_id = -1;
- unsigned long long sqnum = -1;
-
dbg_gen("process PEB %d", pnum);
- err = scan_peb(ubi, ai, pnum, &vol_id, &sqnum);
+ err = scan_peb(ubi, scan_ai, pnum, true);
if (err < 0)
goto out_vidh;
-
- if (vol_id == UBI_FM_SB_VOLUME_ID && sqnum > max_sqnum) {
- max_sqnum = sqnum;
- fm_anchor = pnum;
- }
}
- ubi_free_vid_hdr(ubi, vidh);
- kfree(ech);
+ ubi_free_vid_buf(scan_ai->vidb);
+ kfree(scan_ai->ech);
- if (fm_anchor < 0)
- return UBI_NO_FASTMAP;
+ if (scan_ai->force_full_scan)
+ err = UBI_NO_FASTMAP;
+ else
+ err = ubi_scan_fastmap(ubi, *ai, scan_ai);
- return ubi_scan_fastmap(ubi, ai, fm_anchor);
+ if (err) {
+ /*
+ * Didn't attach via fastmap, do a full scan but reuse what
+ * we've aready scanned.
+ */
+ destroy_ai(*ai);
+ *ai = scan_ai;
+ } else
+ destroy_ai(scan_ai);
+
+ return err;
out_vidh:
- ubi_free_vid_hdr(ubi, vidh);
+ ubi_free_vid_buf(scan_ai->vidb);
out_ech:
- kfree(ech);
+ kfree(scan_ai->ech);
+out_ai:
+ destroy_ai(scan_ai);
out:
return err;
}
#endif
-static struct ubi_attach_info *alloc_ai(const char *slab_name)
-{
- struct ubi_attach_info *ai;
-
- ai = kzalloc(sizeof(struct ubi_attach_info), GFP_KERNEL);
- if (!ai)
- return ai;
-
- INIT_LIST_HEAD(&ai->corr);
- INIT_LIST_HEAD(&ai->free);
- INIT_LIST_HEAD(&ai->erase);
- INIT_LIST_HEAD(&ai->alien);
- ai->volumes = RB_ROOT;
-
- return ai;
-}
-
/**
* ubi_attach - attach an MTD device.
* @ubi: UBI device descriptor
@@ -1375,7 +1546,7 @@ int ubi_attach(struct ubi_device *ubi, int force_scan)
int err;
struct ubi_attach_info *ai;
- ai = alloc_ai("ubi_aeb_slab_cache");
+ ai = alloc_ai();
if (!ai)
return -ENOMEM;
@@ -1389,11 +1560,11 @@ int ubi_attach(struct ubi_device *ubi, int force_scan)
if (force_scan)
err = scan_all(ubi, ai, 0);
else {
- err = scan_fast(ubi, ai);
+ err = scan_fast(ubi, &ai);
if (err > 0 || mtd_is_eccerr(err)) {
if (err != UBI_NO_FASTMAP) {
destroy_ai(ai);
- ai = alloc_ai("ubi_aeb_slab_cache2");
+ ai = alloc_ai();
if (!ai)
return -ENOMEM;
@@ -1432,7 +1603,7 @@ int ubi_attach(struct ubi_device *ubi, int force_scan)
if (ubi->fm && ubi_dbg_chk_gen(ubi)) {
struct ubi_attach_info *scan_ai;
- scan_ai = alloc_ai("ubi_ckh_aeb_slab_cache");
+ scan_ai = alloc_ai();
if (!scan_ai) {
err = -ENOMEM;
goto out_wl;
@@ -1475,6 +1646,8 @@ out_ai:
*/
static int self_check_ai(struct ubi_device *ubi, struct ubi_attach_info *ai)
{
+ struct ubi_vid_io_buf *vidb = ai->vidb;
+ struct ubi_vid_hdr *vidh = ubi_get_vid_hdr(vidb);
int pnum, err, vols_found = 0;
struct rb_node *rb1, *rb2;
struct ubi_ainf_volume *av;
@@ -1604,7 +1777,7 @@ static int self_check_ai(struct ubi_device *ubi, struct ubi_attach_info *ai)
last_aeb = aeb;
- err = ubi_io_read_vid_hdr(ubi, aeb->pnum, vidh, 1);
+ err = ubi_io_read_vid_hdr(ubi, aeb->pnum, vidb, 1);
if (err && err != UBI_IO_BITFLIPS) {
ubi_err(ubi, "VID header is not OK (%d)",
err);
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 40fa890c9e..536077ebf3 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -218,7 +218,8 @@ void ubi_free_internal_volumes(struct ubi_device *ubi)
for (i = ubi->vtbl_slots;
i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
- kfree(ubi->volumes[i]->eba_tbl);
+ ubi_eba_replace_table(ubi->volumes[i], NULL);
+ ubi_fastmap_destroy_checkmap(ubi->volumes[i]);
kfree(ubi->volumes[i]);
}
}
@@ -522,7 +523,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
for (i = 0; i < UBI_MAX_DEVICES; i++) {
ubi = ubi_devices[i];
if (ubi && mtd == ubi->mtd) {
- ubi_debug("mtd%d is already attached to ubi%d",
+ pr_err("ubi: mtd%d is already attached to ubi%d\n",
mtd->index, i);
return -EEXIST;
}
@@ -537,7 +538,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
* no sense to attach emulated MTD devices, so we prohibit this.
*/
if (mtd->type == MTD_UBIVOLUME) {
- ubi_err(ubi, "refuse attaching mtd%d - it is already emulated on top of UBI",
+ pr_err("ubi: refuse attaching mtd%d - it is already emulated on top of UBI\n",
mtd->index);
return -EINVAL;
}
@@ -548,7 +549,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
if (!ubi_devices[ubi_num])
break;
if (ubi_num == UBI_MAX_DEVICES) {
- ubi_err(ubi, "only %d UBI devices may be created",
+ pr_err("ubi: only %d UBI devices may be created\n",
UBI_MAX_DEVICES);
return -ENFILE;
}
@@ -558,7 +559,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
/* Make sure ubi_num is not busy */
if (ubi_devices[ubi_num]) {
- ubi_err(ubi, "already exists");
+ pr_err("ubi: ubi%i already exists\n", ubi_num);
return -EEXIST;
}
}
@@ -633,6 +634,9 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
goto out_detach;
}
+ /* Make device "available" before it becomes accessible via sysfs */
+ ubi_devices[ubi_num] = ubi;
+
err = uif_init(ubi, &ref);
if (err)
goto out_detach;
@@ -668,11 +672,10 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
dev_add_param_uint32_ro(&ubi->dev, "available_pebs", &ubi->avail_pebs, "%u");
dev_add_param_uint32_ro(&ubi->dev, "reserved_pebs", &ubi->rsvd_pebs, "%u");
- ubi_devices[ubi_num] = ubi;
-
return ubi_num;
out_detach:
+ ubi_devices[ubi_num] = NULL;
ubi_wl_close(ubi);
ubi_free_internal_volumes(ubi);
vfree(ubi->vtbl);
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
index 31dbcd29c3..3aae3029e5 100644
--- a/drivers/mtd/ubi/eba.c
+++ b/drivers/mtd/ubi/eba.c
@@ -44,6 +44,30 @@
#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
*
@@ -76,6 +100,110 @@ static int ubi_get_compat(const struct ubi_device *ubi, int vol_id)
}
/**
+ * 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 = kzalloc(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
* @vol_id: volume ID
@@ -240,7 +368,7 @@ static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum)
}
/**
- * leb_write_lock - lock logical eraseblock for writing.
+ * leb_write_trylock - try to lock logical eraseblock for writing.
* @ubi: UBI device description object
* @vol_id: volume ID
* @lnum: logical eraseblock number
@@ -280,6 +408,18 @@ static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum)
}
/**
+ * 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
* @vol: volume description object
@@ -301,14 +441,14 @@ 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;
dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum);
- vol->eba_tbl[lnum] = UBI_LEB_UNMAPPED;
+ vol->eba_tbl->entries[lnum].pnum = UBI_LEB_UNMAPPED;
err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 0);
out_unlock:
@@ -316,6 +456,101 @@ out_unlock:
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;
+ struct ubi_vid_hdr *vid_hdr;
+
+ if (!ubi->fast_attach)
+ return 0;
+
+ if (!vol->checkmap || test_bit(lnum, vol->checkmap))
+ 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;
+
+ vol->eba_tbl->entries[lnum].pnum = UBI_LEB_UNMAPPED;
+ 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;
+ } else {
+ int found_vol_id, found_lnum;
+
+ ubi_assert(err == 0 || err == UBI_IO_BITFLIPS);
+
+ vid_hdr = ubi_get_vid_hdr(vidb);
+ found_vol_id = be32_to_cpu(vid_hdr->vol_id);
+ found_lnum = be32_to_cpu(vid_hdr->lnum);
+
+ if (found_lnum != lnum || found_vol_id != vol->vol_id) {
+ ubi_err(ubi, "EBA mismatch! PEB %i is LEB %i:%i instead of LEB %i:%i",
+ *pnum, found_vol_id, found_lnum, vol->vol_id, lnum);
+ ubi_ro_mode(ubi);
+ err = -EINVAL;
+ goto out_free;
+ }
+ }
+
+ set_bit(lnum, vol->checkmap);
+ 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: UBI device description object
@@ -339,6 +574,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);
@@ -346,8 +582,14 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
if (err)
return err;
- pnum = vol->eba_tbl[lnum];
- if (pnum < 0) {
+ pnum = vol->eba_tbl->entries[lnum].pnum;
+ 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
* with 0xFF bytes. The exception is static volumes for which
@@ -369,13 +611,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) {
/*
@@ -392,8 +636,25 @@ retry:
pnum, vol_id, lnum);
err = -EBADMSG;
} else {
- err = -EINVAL;
- ubi_ro_mode(ubi);
+ /*
+ * Ending up here in the non-Fastmap case
+ * is a clear bug as the VID header had to
+ * be present at scan time to have it referenced.
+ * With fastmap the story is more complicated.
+ * Fastmap has the mapping info without the need
+ * of a full scan. So the LEB could have been
+ * unmapped, Fastmap cannot know this and keeps
+ * the LEB referenced.
+ * This is valid and works as the layer above UBI
+ * has to do bookkeeping about used/referenced
+ * LEBs in any case.
+ */
+ if (ubi->fast_attach) {
+ err = -EBADMSG;
+ } else {
+ err = -EINVAL;
+ ubi_ro_mode(ubi);
+ }
}
}
goto out_free;
@@ -404,7 +665,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);
@@ -441,62 +702,60 @@ 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;
}
/**
- * 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);
- 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;
goto out_put;
}
- vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
- err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr);
- if (err)
- goto write_error;
+ vid_hdr = ubi_get_vid_hdr(vidb);
+ ubi_assert(vid_hdr->vol_type == UBI_VID_DYNAMIC);
- data_size = offset + len;
memset(ubi->peb_buf + offset, 0xFF, len);
/* Read everything before the area where the write failure happened */
@@ -506,39 +765,145 @@ retry:
goto out_unlock;
}
+ *retry = true;
+
memcpy(ubi->peb_buf + offset, buf, len);
- err = ubi_io_write_data(ubi, ubi->peb_buf, new_pnum, 0, data_size);
+ 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, vidb);
if (err)
- goto write_error;
-
- ubi_free_vid_hdr(ubi, vid_hdr);
-
- vol->eba_tbl[lnum] = new_pnum;
- ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1);
+ goto out_unlock;
- ubi_msg(ubi, "data was successfully recovered");
- return 0;
+ err = ubi_io_write_data(ubi, ubi->peb_buf, new_pnum, 0, data_size);
out_unlock:
+ if (!err)
+ vol->eba_tbl->entries[lnum].pnum = new_pnum;
+
out_put:
- ubi_wl_put_peb(ubi, vol_id, lnum, new_pnum, 1);
- ubi_free_vid_hdr(ubi, vid_hdr);
+
+ 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;
+}
-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:
+ 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;
}
/**
@@ -554,11 +919,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)
@@ -568,7 +935,13 @@ 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) {
+ err = check_mapping(ubi, vol, lnum, &pnum);
+ if (err < 0)
+ goto out;
+ }
+
if (pnum >= 0) {
dbg_eba("write %d bytes at offset %d of LEB %d:%d, PEB %d",
len, offset, vol_id, lnum, pnum);
@@ -579,23 +952,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);
@@ -603,63 +976,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);
- 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);
- 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);
- 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;
-
- 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;
}
/**
@@ -687,7 +1027,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;
@@ -700,15 +1041,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);
@@ -722,62 +1063,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);
- return pnum;
- }
-
- dbg_eba("write VID hdr and %d bytes at LEB %d:%d, PEB %d, used_ebs %d",
- len, vol_id, lnum, pnum, used_ebs);
+ ubi_assert(vol->eba_tbl->entries[lnum].pnum < 0);
- 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);
- 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);
- goto write_error;
+ vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
+ ubi_msg(ubi, "try another PEB");
}
- ubi_assert(vol->eba_tbl[lnum] < 0);
- vol->eba_tbl[lnum] = pnum;
+ 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;
}
/*
@@ -800,7 +1105,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;
@@ -818,10 +1124,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);
+
err = leb_write_lock(ubi, vol_id, lnum);
if (err)
goto out_mutex;
@@ -838,65 +1146,30 @@ 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;
- goto out_leb_unlock;
- }
+ dbg_eba("change LEB %d:%d", vol_id, lnum);
- dbg_eba("change LEB %d:%d, PEB %d, write VID hdr to PEB %d",
- vol_id, lnum, vol->eba_tbl[lnum], pnum);
+ 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);
- goto write_error;
- }
-
- 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);
- 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;
-
- 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:
- 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;
}
/**
@@ -941,9 +1214,10 @@ 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;
@@ -999,9 +1273,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;
}
@@ -1050,7 +1324,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;
@@ -1058,7 +1332,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
}
/* 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",
@@ -1077,34 +1351,10 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
err = MOVE_TARGET_WR_ERR;
goto out_unlock_buf;
}
-
- /*
- * We've written the data and are going to read it back to make
- * sure it was written correctly.
- */
- memset(ubi->peb_buf, 0xFF, aldata_size);
- err = ubi_io_read_data(ubi, ubi->peb_buf, to, 0, aldata_size);
- if (err) {
- if (err != UBI_IO_BITFLIPS) {
- ubi_warn(ubi, "error %d while reading data back from PEB %d",
- err, to);
- if (is_error_sane(err))
- err = MOVE_TARGET_RD_ERR;
- } else
- err = MOVE_TARGET_BITFLIPS;
- goto out_unlock_buf;
- }
-
- if (crc != crc32(UBI_CRC32_INIT, ubi->peb_buf, data_size)) {
- ubi_warn(ubi, "read data back from PEB %d and it is different",
- to);
- err = -EINVAL;
- goto out_unlock_buf;
- }
}
- ubi_assert(vol->eba_tbl[lnum] == from);
- vol->eba_tbl[lnum] = to;
+ ubi_assert(vol->eba_tbl->entries[lnum].pnum == from);
+ vol->eba_tbl->entries[lnum].pnum = to;
out_unlock_buf:
out_unlock_leb:
@@ -1259,7 +1509,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;
@@ -1273,33 +1523,37 @@ 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;
- 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;
+ }
}
}
@@ -1336,8 +1590,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;
}
diff --git a/drivers/mtd/ubi/fastmap-wl.c b/drivers/mtd/ubi/fastmap-wl.c
index 1885b155dd..08593ea23a 100644
--- a/drivers/mtd/ubi/fastmap-wl.c
+++ b/drivers/mtd/ubi/fastmap-wl.c
@@ -52,7 +52,7 @@ static void return_unused_pool_pebs(struct ubi_device *ubi,
}
}
-static int anchor_pebs_avalible(struct rb_root *root)
+static int anchor_pebs_available(struct rb_root *root)
{
struct rb_node *p;
struct ubi_wl_entry *e;
@@ -230,7 +230,7 @@ int ubi_ensure_anchor_pebs(struct ubi_device *ubi)
wrk->anchor = 1;
wrk->func = &wear_leveling_worker;
- schedule_ubi_work(ubi, wrk);
+ __schedule_ubi_work(ubi, wrk);
return 0;
}
@@ -268,7 +268,7 @@ int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *fm_e,
}
vol_id = lnum ? UBI_FM_DATA_VOLUME_ID : UBI_FM_SB_VOLUME_ID;
- return schedule_erase(ubi, e, vol_id, lnum, torture);
+ return schedule_erase(ubi, e, vol_id, lnum, torture, true);
}
/**
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index 9975722698..84c2912bf5 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -44,21 +44,23 @@ size_t ubi_calc_fm_size(struct ubi_device *ubi)
* Returns a new struct ubi_vid_hdr on success.
* NULL indicates out of memory.
*/
-static struct ubi_vid_hdr *new_fm_vhdr(struct ubi_device *ubi, int vol_id)
+static struct ubi_vid_io_buf *new_fm_vbuf(struct ubi_device *ubi, int vol_id)
{
- struct ubi_vid_hdr *new;
+ struct ubi_vid_io_buf *new;
+ struct ubi_vid_hdr *vh;
- new = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
+ new = ubi_alloc_vid_buf(ubi, GFP_KERNEL);
if (!new)
goto out;
- new->vol_type = UBI_VID_DYNAMIC;
- new->vol_id = cpu_to_be32(vol_id);
+ vh = ubi_get_vid_hdr(new);
+ vh->vol_type = UBI_VID_DYNAMIC;
+ vh->vol_id = cpu_to_be32(vol_id);
/* UBI implementations without fastmap support have to delete the
* fastmap.
*/
- new->compat = UBI_COMPAT_DELETE;
+ vh->compat = UBI_COMPAT_DELETE;
out:
return new;
@@ -79,12 +81,10 @@ static int add_aeb(struct ubi_attach_info *ai, struct list_head *list,
{
struct ubi_ainf_peb *aeb;
- aeb = kzalloc(sizeof(*aeb), GFP_KERNEL);
+ aeb = ubi_alloc_aeb(ai, pnum, ec);
if (!aeb)
return -ENOMEM;
- aeb->pnum = pnum;
- aeb->ec = ec;
aeb->lnum = -1;
aeb->scrub = scrub;
aeb->copy_flag = aeb->sqnum = 0;
@@ -120,40 +120,19 @@ static struct ubi_ainf_volume *add_vol(struct ubi_attach_info *ai, int vol_id,
int last_eb_bytes)
{
struct ubi_ainf_volume *av;
- struct rb_node **p = &ai->volumes.rb_node, *parent = NULL;
-
- while (*p) {
- parent = *p;
- av = rb_entry(parent, struct ubi_ainf_volume, rb);
-
- if (vol_id > av->vol_id)
- p = &(*p)->rb_left;
- else if (vol_id < av->vol_id)
- p = &(*p)->rb_right;
- else
- return ERR_PTR(-EINVAL);
- }
- av = kmalloc(sizeof(struct ubi_ainf_volume), GFP_KERNEL);
- if (!av)
- goto out;
+ av = ubi_add_av(ai, vol_id);
+ if (IS_ERR(av))
+ return av;
- av->highest_lnum = av->leb_count = av->used_ebs = 0;
- av->vol_id = vol_id;
av->data_pad = data_pad;
av->last_data_size = last_eb_bytes;
av->compat = 0;
av->vol_type = vol_type;
- av->root = RB_ROOT;
if (av->vol_type == UBI_STATIC_VOLUME)
av->used_ebs = used_ebs;
dbg_bld("found volume (ID %i)", vol_id);
-
- rb_link_node(&av->rb, parent, p);
- rb_insert_color(&av->rb, &ai->volumes);
-
-out:
return av;
}
@@ -169,9 +148,8 @@ static void assign_aeb_to_av(struct ubi_attach_info *ai,
struct ubi_ainf_volume *av)
{
struct ubi_ainf_peb *tmp_aeb;
- struct rb_node **p = &ai->volumes.rb_node, *parent = NULL;
+ struct rb_node **p = &av->root.rb_node, *parent = NULL;
- p = &av->root.rb_node;
while (*p) {
parent = *p;
@@ -231,7 +209,8 @@ static int update_vol(struct ubi_device *ubi, struct ubi_attach_info *ai,
*/
if (aeb->pnum == new_aeb->pnum) {
ubi_assert(aeb->lnum == new_aeb->lnum);
- kfree(new_aeb);
+
+ ubi_free_aeb(ai, new_aeb);
return 0;
}
@@ -242,12 +221,10 @@ static int update_vol(struct ubi_device *ubi, struct ubi_attach_info *ai,
/* new_aeb is newer */
if (cmp_res & 1) {
- victim = kzalloc(sizeof(*victim), GFP_KERNEL);
+ victim = ubi_alloc_aeb(ai, aeb->pnum, aeb->ec);
if (!victim)
return -ENOMEM;
- victim->ec = aeb->ec;
- victim->pnum = aeb->pnum;
list_add_tail(&victim->u.list, &ai->erase);
if (av->highest_lnum == be32_to_cpu(new_vh->lnum))
@@ -261,7 +238,8 @@ static int update_vol(struct ubi_device *ubi, struct ubi_attach_info *ai,
aeb->pnum = new_aeb->pnum;
aeb->copy_flag = new_vh->copy_flag;
aeb->scrub = new_aeb->scrub;
- kfree(new_aeb);
+ aeb->sqnum = new_aeb->sqnum;
+ ubi_free_aeb(ai, new_aeb);
/* new_aeb is older */
} else {
@@ -303,41 +281,24 @@ static int process_pool_aeb(struct ubi_device *ubi, struct ubi_attach_info *ai,
struct ubi_vid_hdr *new_vh,
struct ubi_ainf_peb *new_aeb)
{
- struct ubi_ainf_volume *av, *tmp_av = NULL;
- struct rb_node **p = &ai->volumes.rb_node, *parent = NULL;
- int found = 0;
+ int vol_id = be32_to_cpu(new_vh->vol_id);
+ struct ubi_ainf_volume *av;
- if (be32_to_cpu(new_vh->vol_id) == UBI_FM_SB_VOLUME_ID ||
- be32_to_cpu(new_vh->vol_id) == UBI_FM_DATA_VOLUME_ID) {
- kfree(new_aeb);
+ if (vol_id == UBI_FM_SB_VOLUME_ID || vol_id == UBI_FM_DATA_VOLUME_ID) {
+ ubi_free_aeb(ai, new_aeb);
return 0;
}
/* Find the volume this SEB belongs to */
- while (*p) {
- parent = *p;
- tmp_av = rb_entry(parent, struct ubi_ainf_volume, rb);
-
- if (be32_to_cpu(new_vh->vol_id) > tmp_av->vol_id)
- p = &(*p)->rb_left;
- else if (be32_to_cpu(new_vh->vol_id) < tmp_av->vol_id)
- p = &(*p)->rb_right;
- else {
- found = 1;
- break;
- }
- }
-
- if (found)
- av = tmp_av;
- else {
+ av = ubi_find_av(ai, vol_id);
+ if (!av) {
ubi_err(ubi, "orphaned volume in fastmap pool!");
- kfree(new_aeb);
+ ubi_free_aeb(ai, new_aeb);
return UBI_BAD_FASTMAP;
}
- ubi_assert(be32_to_cpu(new_vh->vol_id) == av->vol_id);
+ ubi_assert(vol_id == av->vol_id);
return update_vol(ubi, ai, av, new_vh, new_aeb);
}
@@ -356,16 +317,13 @@ static void unmap_peb(struct ubi_attach_info *ai, int pnum)
struct rb_node *node, *node2;
struct ubi_ainf_peb *aeb;
- for (node = rb_first(&ai->volumes); node; node = rb_next(node)) {
- av = rb_entry(node, struct ubi_ainf_volume, rb);
-
- for (node2 = rb_first(&av->root); node2;
- node2 = rb_next(node2)) {
- aeb = rb_entry(node2, struct ubi_ainf_peb, u.rb);
+ ubi_rb_for_each_entry(node, av, &ai->volumes, rb) {
+ ubi_rb_for_each_entry(node2, aeb, &av->root, u.rb) {
if (aeb->pnum == pnum) {
rb_erase(&aeb->u.rb, &av->root);
av->leb_count--;
- kfree(aeb);
+
+ ubi_free_aeb(ai, aeb);
return;
}
}
@@ -388,6 +346,7 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai,
__be32 *pebs, int pool_size, unsigned long long *max_sqnum,
struct list_head *lfree)
{
+ struct ubi_vid_io_buf *vb;
struct ubi_vid_hdr *vh;
struct ubi_ec_hdr *ech;
struct ubi_ainf_peb *new_aeb;
@@ -397,12 +356,14 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai,
if (!ech)
return -ENOMEM;
- vh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
- if (!vh) {
+ vb = ubi_alloc_vid_buf(ubi, GFP_KERNEL);
+ if (!vb) {
kfree(ech);
return -ENOMEM;
}
+ vh = ubi_get_vid_hdr(vb);
+
dbg_bld("scanning fastmap pool: size = %i", pool_size);
/*
@@ -443,15 +404,16 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai,
goto out;
}
- err = ubi_io_read_vid_hdr(ubi, pnum, vh, 0);
+ err = ubi_io_read_vid_hdr(ubi, pnum, vb, 0);
if (err == UBI_IO_FF || err == UBI_IO_FF_BITFLIPS) {
unsigned long long ec = be64_to_cpu(ech->ec);
unmap_peb(ai, pnum);
dbg_bld("Adding PEB to free: %i", pnum);
+
if (err == UBI_IO_FF_BITFLIPS)
- add_aeb(ai, lfree, pnum, ec, 1);
- else
- add_aeb(ai, lfree, pnum, ec, 0);
+ scrub = 1;
+
+ add_aeb(ai, lfree, pnum, ec, scrub);
continue;
} else if (err == 0 || err == UBI_IO_BITFLIPS) {
dbg_bld("Found non empty PEB:%i in pool", pnum);
@@ -459,14 +421,12 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai,
if (err == UBI_IO_BITFLIPS)
scrub = 1;
- new_aeb = kzalloc(sizeof(*new_aeb), GFP_KERNEL);
+ new_aeb = ubi_alloc_aeb(ai, pnum, be64_to_cpu(ech->ec));
if (!new_aeb) {
ret = -ENOMEM;
goto out;
}
- new_aeb->ec = be64_to_cpu(ech->ec);
- new_aeb->pnum = pnum;
new_aeb->lnum = be32_to_cpu(vh->lnum);
new_aeb->sqnum = be64_to_cpu(vh->sqnum);
new_aeb->copy_flag = vh->copy_flag;
@@ -490,7 +450,7 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai,
}
out:
- ubi_free_vid_hdr(ubi, vh);
+ ubi_free_vid_buf(vb);
kfree(ech);
return ret;
}
@@ -512,7 +472,7 @@ static int count_fastmap_pebs(struct ubi_attach_info *ai)
list_for_each_entry(aeb, &ai->free, u.list)
n++;
- ubi_rb_for_each_entry(rb1, av, &ai->volumes, rb)
+ ubi_rb_for_each_entry(rb1, av, &ai->volumes, rb)
ubi_rb_for_each_entry(rb2, aeb, &av->root, u.rb)
n++;
@@ -548,11 +508,6 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
INIT_LIST_HEAD(&used);
INIT_LIST_HEAD(&lfree);
- INIT_LIST_HEAD(&ai->corr);
- INIT_LIST_HEAD(&ai->free);
- INIT_LIST_HEAD(&ai->erase);
- INIT_LIST_HEAD(&ai->alien);
- ai->volumes = RB_ROOT;
ai->min_ec = UBI_MAX_ERASECOUNTER;
fmsb = (struct ubi_fm_sb *)(fm_raw);
@@ -687,11 +642,11 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
fmvhdr->vol_type,
be32_to_cpu(fmvhdr->last_eb_bytes));
- if (!av)
- goto fail_bad;
- if (PTR_ERR(av) == -EINVAL) {
- ubi_err(ubi, "volume (ID %i) already exists",
- fmvhdr->vol_id);
+ if (IS_ERR(av)) {
+ if (PTR_ERR(av) == -EEXIST)
+ ubi_err(ubi, "volume (ID %i) already exists",
+ fmvhdr->vol_id);
+
goto fail_bad;
}
@@ -778,38 +733,94 @@ fail_bad:
fail:
list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &used, u.list) {
list_del(&tmp_aeb->u.list);
- free(tmp_aeb);
+ ubi_free_aeb(ai, tmp_aeb);
}
list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &lfree, u.list) {
list_del(&tmp_aeb->u.list);
- free(tmp_aeb);
+ ubi_free_aeb(ai, tmp_aeb);
+ }
+
+ return ret;
+}
+
+/**
+ * find_fm_anchor - find the most recent Fastmap superblock (anchor)
+ * @ai: UBI attach info to be filled
+ */
+static int find_fm_anchor(struct ubi_attach_info *ai)
+{
+ int ret = -1;
+ struct ubi_ainf_peb *aeb;
+ unsigned long long max_sqnum = 0;
+
+ list_for_each_entry(aeb, &ai->fastmap, u.list) {
+ if (aeb->vol_id == UBI_FM_SB_VOLUME_ID && aeb->sqnum > max_sqnum) {
+ max_sqnum = aeb->sqnum;
+ ret = aeb->pnum;
+ }
}
return ret;
}
+static struct ubi_ainf_peb *clone_aeb(struct ubi_attach_info *ai,
+ struct ubi_ainf_peb *old)
+{
+ struct ubi_ainf_peb *new;
+
+ new = ubi_alloc_aeb(ai, old->pnum, old->ec);
+ if (!new)
+ return NULL;
+
+ new->vol_id = old->vol_id;
+ new->sqnum = old->sqnum;
+ new->lnum = old->lnum;
+ new->scrub = old->scrub;
+ new->copy_flag = old->copy_flag;
+
+ return new;
+}
+
/**
* ubi_scan_fastmap - scan the fastmap.
* @ubi: UBI device object
* @ai: UBI attach info to be filled
- * @fm_anchor: The fastmap starts at this PEB
+ * @scan_ai: UBI attach info from the first 64 PEBs,
+ * used to find the most recent Fastmap data structure
*
* Returns 0 on success, UBI_NO_FASTMAP if no fastmap was found,
* UBI_BAD_FASTMAP if one was found but is not usable.
* < 0 indicates an internal error.
*/
int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
- int fm_anchor)
+ struct ubi_attach_info *scan_ai)
{
struct ubi_fm_sb *fmsb, *fmsb2;
+ struct ubi_vid_io_buf *vb;
struct ubi_vid_hdr *vh;
struct ubi_ec_hdr *ech;
struct ubi_fastmap_layout *fm;
- int i, used_blocks, pnum, ret = 0;
+ struct ubi_ainf_peb *aeb;
+ int i, used_blocks, pnum, fm_anchor, ret = 0;
size_t fm_size;
__be32 crc, tmp_crc;
unsigned long long sqnum = 0;
+ fm_anchor = find_fm_anchor(scan_ai);
+ if (fm_anchor < 0)
+ return UBI_NO_FASTMAP;
+
+ /* Copy all (possible) fastmap blocks into our new attach structure. */
+ list_for_each_entry(aeb, &scan_ai->fastmap, u.list) {
+ struct ubi_ainf_peb *new;
+
+ new = clone_aeb(ai, aeb);
+ if (!new)
+ return -ENOMEM;
+
+ list_add(&new->u.list, &ai->fastmap);
+ }
+
memset(ubi->fm_buf, 0, ubi->fm_size);
fmsb = kmalloc(sizeof(*fmsb), GFP_KERNEL);
@@ -825,7 +836,7 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
goto out;
}
- ret = ubi_io_read(ubi, fmsb, fm_anchor, ubi->leb_start, sizeof(*fmsb));
+ ret = ubi_io_read_data(ubi, fmsb, fm_anchor, 0, sizeof(*fmsb));
if (ret && ret != UBI_IO_BITFLIPS)
goto free_fm_sb;
else if (ret == UBI_IO_BITFLIPS)
@@ -867,12 +878,14 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
goto free_fm_sb;
}
- vh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
- if (!vh) {
+ vb = ubi_alloc_vid_buf(ubi, GFP_KERNEL);
+ if (!vb) {
ret = -ENOMEM;
goto free_hdr;
}
+ vh = ubi_get_vid_hdr(vb);
+
for (i = 0; i < used_blocks; i++) {
int image_seq;
@@ -883,6 +896,13 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
goto free_hdr;
}
+ if (i == 0 && pnum != fm_anchor) {
+ ubi_err(ubi, "Fastmap anchor PEB mismatch: PEB: %i vs. %i",
+ pnum, fm_anchor);
+ ret = UBI_BAD_FASTMAP;
+ goto free_hdr;
+ }
+
ret = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
if (ret && ret != UBI_IO_BITFLIPS) {
ubi_err(ubi, "unable to read fastmap block# %i EC (PEB: %i)",
@@ -908,7 +928,7 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
goto free_hdr;
}
- ret = ubi_io_read_vid_hdr(ubi, pnum, vh, 0);
+ ret = ubi_io_read_vid_hdr(ubi, pnum, vb, 0);
if (ret && ret != UBI_IO_BITFLIPS) {
ubi_err(ubi, "unable to read fastmap block# %i (PEB: %i)",
i, pnum);
@@ -936,8 +956,8 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
if (sqnum < be64_to_cpu(vh->sqnum))
sqnum = be64_to_cpu(vh->sqnum);
- ret = ubi_io_read(ubi, ubi->fm_buf + (ubi->leb_size * i), pnum,
- ubi->leb_start, ubi->leb_size);
+ ret = ubi_io_read_data(ubi, ubi->fm_buf + (ubi->leb_size * i),
+ pnum, 0, ubi->leb_size);
if (ret && ret != UBI_IO_BITFLIPS) {
ubi_err(ubi, "unable to read fastmap block# %i (PEB: %i, "
"err: %i)", i, pnum, ret);
@@ -996,8 +1016,9 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
ubi_msg(ubi, "fastmap WL pool size: %d",
ubi->fm_wl_pool.max_size);
ubi->fm_disabled = 0;
+ ubi->fast_attach = 1;
- ubi_free_vid_hdr(ubi, vh);
+ ubi_free_vid_buf(vb);
kfree(ech);
out:
if (ret == UBI_BAD_FASTMAP)
@@ -1005,7 +1026,7 @@ out:
return ret;
free_hdr:
- ubi_free_vid_hdr(ubi, vh);
+ ubi_free_vid_buf(vb);
kfree(ech);
free_fm_sb:
kfree(fmsb);
@@ -1013,6 +1034,26 @@ free_fm_sb:
goto out;
}
+int ubi_fastmap_init_checkmap(struct ubi_volume *vol, int leb_count)
+{
+ struct ubi_device *ubi = vol->ubi;
+
+ if (!ubi->fast_attach)
+ return 0;
+
+ vol->checkmap = kcalloc(BITS_TO_LONGS(leb_count), sizeof(unsigned long),
+ GFP_KERNEL);
+ if (!vol->checkmap)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void ubi_fastmap_destroy_checkmap(struct ubi_volume *vol)
+{
+ kfree(vol->checkmap);
+}
+
/**
* ubi_write_fastmap - writes a fastmap.
* @ubi: UBI device object
@@ -1033,6 +1074,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
struct ubi_fm_eba *feba;
struct ubi_wl_entry *wl_e;
struct ubi_volume *vol;
+ struct ubi_vid_io_buf *avbuf, *dvbuf;
struct ubi_vid_hdr *avhdr, *dvhdr;
struct ubi_work *ubi_wrk;
struct rb_node *tmp_rb;
@@ -1042,18 +1084,21 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
fm_raw = ubi->fm_buf;
memset(ubi->fm_buf, 0, ubi->fm_size);
- avhdr = new_fm_vhdr(ubi, UBI_FM_SB_VOLUME_ID);
- if (!avhdr) {
+ avbuf = new_fm_vbuf(ubi, UBI_FM_SB_VOLUME_ID);
+ if (!avbuf) {
ret = -ENOMEM;
goto out;
}
- dvhdr = new_fm_vhdr(ubi, UBI_FM_DATA_VOLUME_ID);
- if (!dvhdr) {
+ dvbuf = new_fm_vbuf(ubi, UBI_FM_DATA_VOLUME_ID);
+ if (!dvbuf) {
ret = -ENOMEM;
goto out_kfree;
}
+ avhdr = ubi_get_vid_hdr(avbuf);
+ dvhdr = ubi_get_vid_hdr(dvbuf);
+
fmsb = (struct ubi_fm_sb *)fm_raw;
fm_pos += sizeof(*fmsb);
ubi_assert(fm_pos <= ubi->fm_size);
@@ -1184,8 +1229,12 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
fm_pos += sizeof(*feba) + (sizeof(__be32) * vol->reserved_pebs);
ubi_assert(fm_pos <= ubi->fm_size);
- for (j = 0; j < vol->reserved_pebs; j++)
- feba->pnum[j] = cpu_to_be32(vol->eba_tbl[j]);
+ for (j = 0; j < vol->reserved_pebs; j++) {
+ struct ubi_eba_leb_desc ldesc;
+
+ ubi_eba_get_ldesc(vol, j, &ldesc);
+ feba->pnum[j] = cpu_to_be32(ldesc.pnum);
+ }
feba->reserved_pebs = cpu_to_be32(j);
feba->magic = cpu_to_be32(UBI_FM_EBA_MAGIC);
@@ -1197,7 +1246,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
avhdr->lnum = 0;
dbg_bld("writing fastmap SB to PEB %i", new_fm->e[0]->pnum);
- ret = ubi_io_write_vid_hdr(ubi, new_fm->e[0]->pnum, avhdr);
+ ret = ubi_io_write_vid_hdr(ubi, new_fm->e[0]->pnum, avbuf);
if (ret) {
ubi_err(ubi, "unable to write vid_hdr to fastmap SB!");
goto out_kfree;
@@ -1217,7 +1266,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
dvhdr->lnum = cpu_to_be32(i);
dbg_bld("writing fastmap data to PEB %i sqnum %llu",
new_fm->e[i]->pnum, be64_to_cpu(dvhdr->sqnum));
- ret = ubi_io_write_vid_hdr(ubi, new_fm->e[i]->pnum, dvhdr);
+ ret = ubi_io_write_vid_hdr(ubi, new_fm->e[i]->pnum, dvbuf);
if (ret) {
ubi_err(ubi, "unable to write vid_hdr to PEB %i!",
new_fm->e[i]->pnum);
@@ -1226,8 +1275,8 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
}
for (i = 0; i < new_fm->used_blocks; i++) {
- ret = ubi_io_write(ubi, fm_raw + (i * ubi->leb_size),
- new_fm->e[i]->pnum, ubi->leb_start, ubi->leb_size);
+ ret = ubi_io_write_data(ubi, fm_raw + (i * ubi->leb_size),
+ new_fm->e[i]->pnum, 0, ubi->leb_size);
if (ret) {
ubi_err(ubi, "unable to write fastmap to PEB %i!",
new_fm->e[i]->pnum);
@@ -1241,8 +1290,8 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
dbg_bld("fastmap written!");
out_kfree:
- ubi_free_vid_hdr(ubi, avhdr);
- ubi_free_vid_hdr(ubi, dvhdr);
+ ubi_free_vid_buf(avbuf);
+ ubi_free_vid_buf(dvbuf);
out:
return ret;
}
@@ -1311,7 +1360,8 @@ static int invalidate_fastmap(struct ubi_device *ubi)
int ret;
struct ubi_fastmap_layout *fm;
struct ubi_wl_entry *e;
- struct ubi_vid_hdr *vh = NULL;
+ struct ubi_vid_io_buf *vb = NULL;
+ struct ubi_vid_hdr *vh;
if (!ubi->fm)
return 0;
@@ -1323,10 +1373,12 @@ static int invalidate_fastmap(struct ubi_device *ubi)
if (!fm)
goto out;
- vh = new_fm_vhdr(ubi, UBI_FM_SB_VOLUME_ID);
- if (!vh)
+ vb = new_fm_vbuf(ubi, UBI_FM_SB_VOLUME_ID);
+ if (!vb)
goto out_free_fm;
+ vh = ubi_get_vid_hdr(vb);
+
ret = -ENOSPC;
e = ubi_wl_get_fm_peb(ubi, 1);
if (!e)
@@ -1337,7 +1389,7 @@ static int invalidate_fastmap(struct ubi_device *ubi)
* to scanning mode.
*/
vh->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
- ret = ubi_io_write_vid_hdr(ubi, e->pnum, vh);
+ ret = ubi_io_write_vid_hdr(ubi, e->pnum, vb);
if (ret < 0) {
ubi_wl_put_fm_peb(ubi, e, 0, 0);
goto out_free_fm;
@@ -1349,7 +1401,7 @@ static int invalidate_fastmap(struct ubi_device *ubi)
ubi->fm = fm;
out:
- ubi_free_vid_hdr(ubi, vh);
+ ubi_free_vid_buf(vb);
return ret;
out_free_fm:
@@ -1519,7 +1571,7 @@ err:
ret = invalidate_fastmap(ubi);
if (ret < 0) {
- ubi_err(ubi, "Unable to invalidiate current fastmap!");
+ ubi_err(ubi, "Unable to invalidate current fastmap!");
ubi_ro_mode(ubi);
} else {
return_fm_pebs(ubi, old_fm);
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
index 6d08f92ea6..d0ea23b124 100644
--- a/drivers/mtd/ubi/io.c
+++ b/drivers/mtd/ubi/io.c
@@ -41,7 +41,7 @@
* About minimal I/O units. In general, UBI assumes flash device model where
* there is only one minimal I/O unit size. E.g., in case of NOR flash it is 1,
* in case of NAND flash it is a NAND page, etc. This is reported by MTD in the
- * @ubi->mtd->writesize field. But as an exception, UBI admits of using another
+ * @ubi->mtd->writesize field. But as an exception, UBI admits use of another
* (smaller) minimal I/O unit size for EC and VID headers to make it possible
* to do different optimizations.
*
@@ -226,6 +226,7 @@ static int nor_erase_prepare(struct ubi_device *ubi, int pnum)
loff_t addr;
uint32_t data = 0;
struct ubi_ec_hdr ec_hdr;
+ struct ubi_vid_io_buf vidb;
/*
* Note, we cannot generally define VID header buffers on stack,
@@ -252,7 +253,10 @@ static int nor_erase_prepare(struct ubi_device *ubi, int pnum)
goto error;
}
- err = ubi_io_read_vid_hdr(ubi, pnum, &vid_hdr, 0);
+ ubi_init_vid_buf(ubi, &vidb, &vid_hdr);
+ ubi_assert(&vid_hdr == ubi_get_vid_hdr(&vidb));
+
+ err = ubi_io_read_vid_hdr(ubi, pnum, &vidb, 0);
if (err != UBI_IO_BAD_HDR_EBADMSG && err != UBI_IO_BAD_HDR &&
err != UBI_IO_FF){
addr += ubi->vid_hdr_aloffset;
@@ -687,12 +691,11 @@ bad:
* ubi_io_read_vid_hdr - read and check a volume identifier header.
* @ubi: UBI device description object
* @pnum: physical eraseblock number to read from
- * @vid_hdr: &struct ubi_vid_hdr object where to store the read volume
- * identifier header
+ * @vidb: the volume identifier buffer to store data in
* @verbose: be verbose if the header is corrupted or wasn't found
*
* This function reads the volume identifier header from physical eraseblock
- * @pnum and stores it in @vid_hdr. It also checks CRC checksum of the read
+ * @pnum and stores it in @vidb. It also checks CRC checksum of the read
* volume identifier header. The error codes are the same as in
* 'ubi_io_read_ec_hdr()'.
*
@@ -700,16 +703,16 @@ bad:
* 'ubi_io_read_ec_hdr()', so refer commentaries in 'ubi_io_read_ec_hdr()'.
*/
int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
- struct ubi_vid_hdr *vid_hdr, int verbose)
+ struct ubi_vid_io_buf *vidb, int verbose)
{
int err, read_err;
uint32_t crc, magic, hdr_crc;
- void *p;
+ struct ubi_vid_hdr *vid_hdr = ubi_get_vid_hdr(vidb);
+ void *p = vidb->buffer;
dbg_io("read VID header from PEB %d", pnum);
ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
- p = (char *)vid_hdr - ubi->vid_hdr_shift;
read_err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset,
ubi->vid_hdr_shift + UBI_VID_HDR_SIZE);
if (read_err && read_err != UBI_IO_BITFLIPS && !mtd_is_eccerr(read_err))
@@ -772,23 +775,24 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
* ubi_io_write_vid_hdr - write a volume identifier header.
* @ubi: UBI device description object
* @pnum: the physical eraseblock number to write to
- * @vid_hdr: the volume identifier header to write
+ * @vidb: the volume identifier buffer to write
*
* This function writes the volume identifier header described by @vid_hdr to
* physical eraseblock @pnum. This function automatically fills the
- * @vid_hdr->magic and the @vid_hdr->version fields, as well as calculates
- * header CRC checksum and stores it at vid_hdr->hdr_crc.
+ * @vidb->hdr->magic and the @vidb->hdr->version fields, as well as calculates
+ * header CRC checksum and stores it at vidb->hdr->hdr_crc.
*
* This function returns zero in case of success and a negative error code in
* case of failure. If %-EIO is returned, the physical eraseblock probably went
* bad.
*/
int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
- struct ubi_vid_hdr *vid_hdr)
+ struct ubi_vid_io_buf *vidb)
{
+ struct ubi_vid_hdr *vid_hdr = ubi_get_vid_hdr(vidb);
int err;
uint32_t crc;
- void *p;
+ void *p = vidb->buffer;
dbg_io("write VID header to PEB %d", pnum);
ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
@@ -806,7 +810,6 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
if (err)
return err;
- p = (char *)vid_hdr - ubi->vid_hdr_shift;
err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset,
ubi->vid_hdr_alsize);
return err;
@@ -972,17 +975,19 @@ static int self_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum)
{
int err;
uint32_t crc, hdr_crc;
+ struct ubi_vid_io_buf *vidb;
struct ubi_vid_hdr *vid_hdr;
void *p;
if (!ubi_dbg_chk_io(ubi))
return 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;
- p = (char *)vid_hdr - ubi->vid_hdr_shift;
+ vid_hdr = ubi_get_vid_hdr(vidb);
+ p = vidb->buffer;
err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset,
ubi->vid_hdr_alsize);
if (err && err != UBI_IO_BITFLIPS && !mtd_is_eccerr(err))
@@ -1003,7 +1008,7 @@ static int self_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum)
err = self_check_vid_hdr(ubi, pnum, vid_hdr);
exit:
- ubi_free_vid_hdr(ubi, vid_hdr);
+ ubi_free_vid_buf(vidb);
return err;
}
diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c
index 60128a5c26..e1cab763eb 100644
--- a/drivers/mtd/ubi/kapi.c
+++ b/drivers/mtd/ubi/kapi.c
@@ -413,7 +413,7 @@ int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
return -EROFS;
- if (lnum < 0 || lnum >= vol->reserved_pebs || offset < 0 || len < 0 ||
+ if (!ubi_leb_valid(vol, lnum) || offset < 0 || len < 0 ||
offset + len > vol->usable_leb_size ||
offset & (ubi->min_io_size - 1) || len & (ubi->min_io_size - 1))
return -EINVAL;
@@ -458,7 +458,7 @@ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
return -EROFS;
- if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 ||
+ if (!ubi_leb_valid(vol, lnum) || len < 0 ||
len > vol->usable_leb_size || len & (ubi->min_io_size - 1))
return -EINVAL;
@@ -495,7 +495,7 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum)
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
return -EROFS;
- if (lnum < 0 || lnum >= vol->reserved_pebs)
+ if (!ubi_leb_valid(vol, lnum))
return -EINVAL;
if (vol->upd_marker)
@@ -555,7 +555,7 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum)
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
return -EROFS;
- if (lnum < 0 || lnum >= vol->reserved_pebs)
+ if (!ubi_leb_valid(vol, lnum))
return -EINVAL;
if (vol->upd_marker)
@@ -586,18 +586,18 @@ int ubi_leb_map(struct ubi_volume_desc *desc, int lnum)
struct ubi_volume *vol = desc->vol;
struct ubi_device *ubi = vol->ubi;
- dbg_gen("unmap LEB %d:%d", vol->vol_id, lnum);
+ dbg_gen("map LEB %d:%d", vol->vol_id, lnum);
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
return -EROFS;
- if (lnum < 0 || lnum >= vol->reserved_pebs)
+ if (!ubi_leb_valid(vol, lnum))
return -EINVAL;
if (vol->upd_marker)
return -EBADF;
- if (vol->eba_tbl[lnum] >= 0)
+ if (ubi_eba_is_mapped(vol, lnum))
return -EBADMSG;
return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0);
@@ -626,13 +626,13 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum)
dbg_gen("test LEB %d:%d", vol->vol_id, lnum);
- if (lnum < 0 || lnum >= vol->reserved_pebs)
+ if (!ubi_leb_valid(vol, lnum))
return -EINVAL;
if (vol->upd_marker)
return -EBADF;
- return vol->eba_tbl[lnum] >= 0;
+ return ubi_eba_is_mapped(vol, lnum);
}
EXPORT_SYMBOL_GPL(ubi_is_mapped);
diff --git a/drivers/mtd/ubi/ubi-media.h b/drivers/mtd/ubi/ubi-media.h
index 27c49beafe..195ff8ca82 100644
--- a/drivers/mtd/ubi/ubi-media.h
+++ b/drivers/mtd/ubi/ubi-media.h
@@ -1,24 +1,12 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/*
- * Copyright (c) International Business Machines Corp., 2006
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
- * the GNU General Public License for more details.
- *
+ * Copyright (C) International Business Machines Corp., 2006
* Authors: Artem Bityutskiy (Битюцкий Артём)
* Thomas Gleixner
* Frank Haverkamp
* Oliver Lohmann
* Andreas Arnez
- */
-
-/*
+ *
* This file defines the layout of UBI headers and all the other UBI on-flash
* data structures.
*/
@@ -225,7 +213,7 @@ struct ubi_ec_hdr {
* copy. UBI also calculates data CRC when the data is moved and stores it at
* the @data_crc field of the copy (P1). So when UBI needs to pick one physical
* eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is
- * examined. If it is cleared, the situation* is simple and the newer one is
+ * examined. If it is cleared, the situation is simple and the newer one is
* picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC
* checksum is correct, this physical eraseblock is selected (P1). Otherwise
* the older one (P) is selected.
@@ -385,7 +373,7 @@ struct ubi_vtbl_record {
#define UBI_FM_POOL_MAGIC 0x67AF4D08
#define UBI_FM_EBA_MAGIC 0xf0c040a8
-/* A fastmap supber block can be located between PEB 0 and
+/* A fastmap super block can be located between PEB 0 and
* UBI_FM_MAX_START */
#define UBI_FM_MAX_START 64
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 1f0ad386b4..b85d602055 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -140,6 +140,17 @@ enum {
};
/**
+ * struct ubi_vid_io_buf - VID buffer used to read/write VID info to/from the
+ * flash.
+ * @hdr: a pointer to the VID header stored in buffer
+ * @buffer: underlying buffer
+ */
+struct ubi_vid_io_buf {
+ struct ubi_vid_hdr *hdr;
+ void *buffer;
+};
+
+/**
* struct ubi_wl_entry - wear-leveling entry.
* @u.rb: link in the corresponding (free/used) RB-tree
* @u.list: link in the protection queue
@@ -239,6 +250,21 @@ struct ubi_fm_pool {
};
/**
+ * struct ubi_eba_leb_desc - EBA logical eraseblock descriptor
+ * @lnum: the logical eraseblock number
+ * @pnum: the physical eraseblock where the LEB can be found
+ *
+ * This structure is here to hide EBA's internal from other part of the
+ * UBI implementation.
+ *
+ * One can query the position of a LEB by calling ubi_eba_get_ldesc().
+ */
+struct ubi_eba_leb_desc {
+ int lnum;
+ int pnum;
+};
+
+/**
* struct ubi_volume - UBI volume description data structure.
* @dev: device object to make use of the the Linux device model
* @cdev: character device object to create character device
@@ -279,6 +305,9 @@ struct ubi_fm_pool {
* @changing_leb: %1 if the atomic LEB change ioctl command is in progress
* @direct_writes: %1 if direct writes are enabled for this volume
*
+ * @checkmap: bitmap to remember which PEB->LEB mappings got checked,
+ * protected by UBI LEB lock tree.
+ *
* The @corrupted field indicates that the volume's contents is corrupted.
* Since UBI protects only static volumes, this field is not relevant to
* dynamic volumes - it is user's responsibility to assure their data
@@ -314,7 +343,7 @@ struct ubi_volume {
long long upd_received;
void *upd_buf;
- int *eba_tbl;
+ struct ubi_eba_table *eba_tbl;
unsigned int checked:1;
unsigned int corrupted:1;
unsigned int upd_marker:1;
@@ -322,6 +351,10 @@ struct ubi_volume {
unsigned int changing_leb:1;
unsigned int direct_writes:1;
+#ifdef CONFIG_MTD_UBI_FASTMAP
+ unsigned long *checkmap;
+#endif
+
struct list_head list;
};
@@ -416,6 +449,7 @@ struct ubi_debug_info {
* @fm_size: fastmap size in bytes
* @fm_sem: allows ubi_update_fastmap() to block EBA table changes
* @fm_work: fastmap work queue
+ * @fast_attach: non-zero if UBI was attached by fastmap
*
* @used: RB-tree of used physical eraseblocks
* @erroneous: RB-tree of erroneous used physical eraseblocks
@@ -466,8 +500,7 @@ struct ubi_debug_info {
* @vid_hdr_aloffset: starting offset of the VID header aligned to
* @hdrs_min_io_size
* @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset
- * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or
- * not
+ * @bad_allowed: whether the MTD device admits bad physical eraseblocks or not
* @nor_flash: non-zero if working on top of NOR flash
* @max_write_size: maximum amount of bytes the underlying flash can write at a
* time (MTD write buffer size)
@@ -515,6 +548,7 @@ struct ubi_device {
struct ubi_fm_pool fm_wl_pool;
void *fm_buf;
size_t fm_size;
+ int fast_attach;
/* Wear-leveling sub-system's stuff */
struct rb_root used;
@@ -637,6 +671,8 @@ struct ubi_ainf_volume {
* @erase: list of physical eraseblocks which have to be erased
* @alien: list of physical eraseblocks which should not be used by UBI (e.g.,
* those belonging to "preserve"-compatible internal volumes)
+ * @fastmap: list of physical eraseblocks which relate to fastmap (e.g.,
+ * eraseblocks of the current and not yet erased old fastmap blocks)
* @corr_peb_count: count of PEBs in the @corr list
* @empty_peb_count: count of PEBs which are presumably empty (contain only
* 0xFF bytes)
@@ -647,6 +683,8 @@ struct ubi_ainf_volume {
* @vols_found: number of volumes found
* @highest_vol_id: highest volume ID
* @is_empty: flag indicating whether the MTD device is empty or not
+ * @force_full_scan: flag indicating whether we need to do a full scan and drop
+ all existing Fastmap data structures
* @min_ec: lowest erase counter value
* @max_ec: highest erase counter value
* @max_sqnum: highest sequence number value
@@ -654,6 +692,8 @@ struct ubi_ainf_volume {
* @ec_sum: a temporary variable used when calculating @mean_ec
* @ec_count: a temporary variable used when calculating @mean_ec
* @aeb_slab_cache: slab cache for &struct ubi_ainf_peb objects
+ * @ech: temporary EC header. Only available during scan
+ * @vidh: temporary VID buffer. Only available during scan
*
* This data structure contains the result of attaching an MTD device and may
* be used by other UBI sub-systems to build final UBI data structures, further
@@ -665,6 +705,7 @@ struct ubi_attach_info {
struct list_head free;
struct list_head erase;
struct list_head alien;
+ struct list_head fastmap;
int corr_peb_count;
int empty_peb_count;
int alien_peb_count;
@@ -673,6 +714,7 @@ struct ubi_attach_info {
int vols_found;
int highest_vol_id;
int is_empty;
+ int force_full_scan;
int min_ec;
int max_ec;
unsigned long long max_sqnum;
@@ -680,6 +722,8 @@ struct ubi_attach_info {
uint64_t ec_sum;
int ec_count;
struct kmem_cache *aeb_slab_cache;
+ struct ubi_ec_hdr *ech;
+ struct ubi_vid_io_buf *vidb;
};
/**
@@ -719,8 +763,12 @@ extern struct class *ubi_class;
extern struct blocking_notifier_head ubi_notifiers;
/* attach.c */
+struct ubi_ainf_peb *ubi_alloc_aeb(struct ubi_attach_info *ai, int pnum,
+ int ec);
+void ubi_free_aeb(struct ubi_attach_info *ai, struct ubi_ainf_peb *aeb);
int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum,
int ec, const struct ubi_vid_hdr *vid_hdr, int bitflips);
+struct ubi_ainf_volume *ubi_add_av(struct ubi_attach_info *ai, int vol_id);
struct ubi_ainf_volume *ubi_find_av(const struct ubi_attach_info *ai,
int vol_id);
void ubi_remove_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av);
@@ -762,7 +810,21 @@ int ubi_check_volume(struct ubi_device *ubi, int vol_id);
void ubi_update_reserved(struct ubi_device *ubi);
void ubi_calculate_reserved(struct ubi_device *ubi);
+static inline bool ubi_leb_valid(struct ubi_volume *vol, int lnum)
+{
+ return lnum >= 0 && lnum < vol->reserved_pebs;
+}
+
/* eba.c */
+struct ubi_eba_table *ubi_eba_create_table(struct ubi_volume *vol,
+ int nentries);
+void ubi_eba_destroy_table(struct ubi_eba_table *tbl);
+void ubi_eba_copy_table(struct ubi_volume *vol, struct ubi_eba_table *dst,
+ int nentries);
+void ubi_eba_replace_table(struct ubi_volume *vol, struct ubi_eba_table *tbl);
+void ubi_eba_get_ldesc(struct ubi_volume *vol, int lnum,
+ struct ubi_eba_leb_desc *ldesc);
+bool ubi_eba_is_mapped(struct ubi_volume *vol, int lnum);
int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
int lnum);
int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
@@ -774,7 +836,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
int lnum, const void *buf, int len);
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 ubi_eba_init(struct ubi_device *ubi, struct ubi_attach_info *ai);
unsigned long long ubi_next_sqnum(struct ubi_device *ubi);
int self_check_eba(struct ubi_device *ubi, struct ubi_attach_info *ai_fastmap,
@@ -808,9 +870,9 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum,
struct ubi_ec_hdr *ec_hdr);
int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
- struct ubi_vid_hdr *vid_hdr, int verbose);
+ struct ubi_vid_io_buf *vidb, int verbose);
int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
- struct ubi_vid_hdr *vid_hdr);
+ struct ubi_vid_io_buf *vidb);
/* build.c */
int ubi_detach_mtd_dev(int ubi_num, int anyway);
@@ -841,9 +903,13 @@ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb,
size_t ubi_calc_fm_size(struct ubi_device *ubi);
int ubi_update_fastmap(struct ubi_device *ubi);
int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
- int fm_anchor);
+ struct ubi_attach_info *scan_ai);
+int ubi_fastmap_init_checkmap(struct ubi_volume *vol, int leb_count);
+void ubi_fastmap_destroy_checkmap(struct ubi_volume *vol);
#else
static inline int ubi_update_fastmap(struct ubi_device *ubi) { return 0; }
+int static inline ubi_fastmap_init_checkmap(struct ubi_volume *vol, int leb_count) { return 0; }
+static inline void ubi_fastmap_destroy_checkmap(struct ubi_volume *vol) {}
#endif
/*
@@ -913,44 +979,68 @@ static inline void ubi_move_aeb_to_list(struct ubi_ainf_volume *av,
}
/**
- * ubi_zalloc_vid_hdr - allocate a volume identifier header object.
- * @ubi: UBI device description object
- * @gfp_flags: GFP flags to allocate with
- *
- * This function returns a pointer to the newly allocated and zero-filled
- * volume identifier header object in case of success and %NULL in case of
- * failure.
+ * ubi_init_vid_buf - Initialize a VID buffer
+ * @ubi: the UBI device
+ * @vidb: the VID buffer to initialize
+ * @buf: the underlying buffer
*/
-static inline struct ubi_vid_hdr *
-ubi_zalloc_vid_hdr(const struct ubi_device *ubi, gfp_t gfp_flags)
+static inline void ubi_init_vid_buf(const struct ubi_device *ubi,
+ struct ubi_vid_io_buf *vidb,
+ void *buf)
{
- void *vid_hdr;
+ if (buf)
+ memset(buf, 0, ubi->vid_hdr_alsize);
- vid_hdr = kzalloc(ubi->vid_hdr_alsize, gfp_flags);
- if (!vid_hdr)
+ vidb->buffer = buf;
+ vidb->hdr = buf + ubi->vid_hdr_shift;
+}
+
+/**
+ * ubi_init_vid_buf - Allocate a VID buffer
+ * @ubi: the UBI device
+ * @gfp_flags: GFP flags to use for the allocation
+ */
+static inline struct ubi_vid_io_buf *
+ubi_alloc_vid_buf(const struct ubi_device *ubi, gfp_t gfp_flags)
+{
+ struct ubi_vid_io_buf *vidb;
+ void *buf;
+
+ vidb = kzalloc(sizeof(*vidb), gfp_flags);
+ if (!vidb)
return NULL;
- /*
- * VID headers may be stored at un-aligned flash offsets, so we shift
- * the pointer.
- */
- return vid_hdr + ubi->vid_hdr_shift;
+ buf = kmalloc(ubi->vid_hdr_alsize, gfp_flags);
+ if (!buf) {
+ kfree(vidb);
+ return NULL;
+ }
+
+ ubi_init_vid_buf(ubi, vidb, buf);
+
+ return vidb;
}
/**
- * ubi_free_vid_hdr - free a volume identifier header object.
- * @ubi: UBI device description object
- * @vid_hdr: the object to free
+ * ubi_free_vid_buf - Free a VID buffer
+ * @vidb: the VID buffer to free
*/
-static inline void ubi_free_vid_hdr(const struct ubi_device *ubi,
- struct ubi_vid_hdr *vid_hdr)
+static inline void ubi_free_vid_buf(struct ubi_vid_io_buf *vidb)
{
- void *p = vid_hdr;
-
- if (!p)
+ if (!vidb)
return;
- kfree(p - ubi->vid_hdr_shift);
+ kfree(vidb->buffer);
+ kfree(vidb);
+}
+
+/**
+ * ubi_get_vid_hdr - Get the VID header attached to a VID buffer
+ * @vidb: VID buffer
+ */
+static inline struct ubi_vid_hdr *ubi_get_vid_hdr(struct ubi_vid_io_buf *vidb)
+{
+ return vidb->hdr;
}
/*
@@ -1016,4 +1106,42 @@ static inline int idx2vol_id(const struct ubi_device *ubi, int idx)
return idx;
}
+/**
+ * ubi_is_fm_vol - check whether a volume ID is a Fastmap volume.
+ * @vol_id: volume ID
+ */
+static inline bool ubi_is_fm_vol(int vol_id)
+{
+ switch (vol_id) {
+ case UBI_FM_SB_VOLUME_ID:
+ case UBI_FM_DATA_VOLUME_ID:
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * ubi_find_fm_block - check whether a PEB is part of the current Fastmap.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock to look for
+ *
+ * This function returns a wear leveling object if @pnum relates to the current
+ * fastmap, @NULL otherwise.
+ */
+static inline struct ubi_wl_entry *ubi_find_fm_block(const struct ubi_device *ubi,
+ int pnum)
+{
+ int i;
+
+ if (ubi->fm) {
+ for (i = 0; i < ubi->fm->used_blocks; i++) {
+ if (ubi->fm->e[i]->pnum == pnum)
+ return ubi->fm->e[i];
+ }
+ }
+
+ return NULL;
+}
+
#endif /* !__UBI_UBI_H__ */
diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c
index e3deb3e931..609c789ae4 100644
--- a/drivers/mtd/ubi/upd.c
+++ b/drivers/mtd/ubi/upd.c
@@ -139,11 +139,11 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol,
return err;
}
- if (bytes == 0) {
- err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL);
- if (err)
- return err;
+ err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL);
+ if (err)
+ return err;
+ if (bytes == 0) {
err = clear_update_marker(ubi, vol, 0);
if (err)
return err;
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c
index ed043646b8..6177048214 100644
--- a/drivers/mtd/ubi/vmt.c
+++ b/drivers/mtd/ubi/vmt.c
@@ -41,6 +41,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
int i, err, vol_id = req->vol_id, do_free = 1;
struct ubi_volume *vol;
struct ubi_vtbl_record vtbl_rec;
+ struct ubi_eba_table *eba_tbl = NULL;
if (ubi->ro_mode)
return -EROFS;
@@ -121,14 +122,13 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
if (err)
goto out_acc;
- vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int), GFP_KERNEL);
- if (!vol->eba_tbl) {
- err = -ENOMEM;
+ eba_tbl = ubi_eba_create_table(vol, vol->reserved_pebs);
+ if (IS_ERR(eba_tbl)) {
+ err = PTR_ERR(eba_tbl);
goto out_acc;
}
- for (i = 0; i < vol->reserved_pebs; i++)
- vol->eba_tbl[i] = UBI_LEB_UNMAPPED;
+ ubi_eba_replace_table(vol, eba_tbl);
if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
vol->used_ebs = vol->reserved_pebs;
@@ -194,7 +194,7 @@ out_sysfs:
do_free = 0;
out_mapping:
if (do_free)
- kfree(vol->eba_tbl);
+ ubi_eba_destroy_table(eba_tbl);
unregister_device(&vol->dev);
out_acc:
ubi->rsvd_pebs -= vol->reserved_pebs;
@@ -264,7 +264,7 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
if (!no_vtbl)
self_check_volumes(ubi);
- return err;
+ return 0;
out_err:
ubi_err(ubi, "cannot remove volume %d, error %d", vol_id, err);
@@ -284,10 +284,11 @@ out_unlock:
*/
int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
{
- int i, err, pebs, *new_mapping;
+ int i, err, pebs;
struct ubi_volume *vol = desc->vol;
struct ubi_device *ubi = vol->ubi;
struct ubi_vtbl_record vtbl_rec;
+ struct ubi_eba_table *new_eba_tbl = NULL;
int vol_id = vol->vol_id;
if (ubi->ro_mode)
@@ -307,12 +308,9 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
if (reserved_pebs == vol->reserved_pebs)
return 0;
- new_mapping = kmalloc(reserved_pebs * sizeof(int), GFP_KERNEL);
- if (!new_mapping)
- return -ENOMEM;
-
- for (i = 0; i < reserved_pebs; i++)
- new_mapping[i] = UBI_LEB_UNMAPPED;
+ new_eba_tbl = ubi_eba_create_table(vol, reserved_pebs);
+ if (IS_ERR(new_eba_tbl))
+ return PTR_ERR(new_eba_tbl);
if (vol->ref_count > 1) {
err = -EBUSY;
@@ -333,19 +331,10 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
}
ubi->avail_pebs -= pebs;
ubi->rsvd_pebs += pebs;
- for (i = 0; i < vol->reserved_pebs; i++)
- new_mapping[i] = vol->eba_tbl[i];
- kfree(vol->eba_tbl);
- vol->eba_tbl = new_mapping;
+ ubi_eba_copy_table(vol, new_eba_tbl, vol->reserved_pebs);
+ ubi_eba_replace_table(vol, new_eba_tbl);
}
- /* Change volume table record */
- vtbl_rec = ubi->vtbl[vol_id];
- vtbl_rec.reserved_pebs = cpu_to_be32(reserved_pebs);
- err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
- if (err)
- goto out_acc;
-
if (pebs < 0) {
for (i = 0; i < -pebs; i++) {
err = ubi_eba_unmap_leb(ubi, vol, reserved_pebs + i);
@@ -355,12 +344,28 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
ubi->rsvd_pebs += pebs;
ubi->avail_pebs -= pebs;
ubi_update_reserved(ubi);
- for (i = 0; i < reserved_pebs; i++)
- new_mapping[i] = vol->eba_tbl[i];
- kfree(vol->eba_tbl);
- vol->eba_tbl = new_mapping;
+ ubi_eba_copy_table(vol, new_eba_tbl, reserved_pebs);
+ ubi_eba_replace_table(vol, new_eba_tbl);
+ }
+
+ /*
+ * When we shrink a volume we have to flush all pending (erase) work.
+ * Otherwise it can happen that upon next attach UBI finds a LEB with
+ * lnum > highest_lnum and refuses to attach.
+ */
+ if (pebs < 0) {
+ err = ubi_wl_flush(ubi, vol_id, UBI_ALL);
+ if (err)
+ goto out_acc;
}
+ /* Change volume table record */
+ vtbl_rec = ubi->vtbl[vol_id];
+ vtbl_rec.reserved_pebs = cpu_to_be32(reserved_pebs);
+ err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
+ if (err)
+ goto out_acc;
+
vol->reserved_pebs = reserved_pebs;
if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
vol->used_ebs = reserved_pebs;
@@ -379,7 +384,7 @@ out_acc:
ubi->avail_pebs += pebs;
}
out_free:
- kfree(new_mapping);
+ kfree(new_eba_tbl);
return err;
}
diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c
index b5eb4545ef..6959564a13 100644
--- a/drivers/mtd/ubi/vtbl.c
+++ b/drivers/mtd/ubi/vtbl.c
@@ -290,15 +290,18 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_attach_info *ai,
int copy, void *vtbl)
{
int err, tries = 0;
+ struct ubi_vid_io_buf *vidb;
struct ubi_vid_hdr *vid_hdr;
struct ubi_ainf_peb *new_aeb;
dbg_gen("create volume table (copy #%d)", copy + 1);
- vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
- if (!vid_hdr)
+ vidb = ubi_alloc_vid_buf(ubi, GFP_KERNEL);
+ if (!vidb)
return -ENOMEM;
+ vid_hdr = ubi_get_vid_hdr(vidb);
+
retry:
new_aeb = ubi_early_get_peb(ubi, ai);
if (IS_ERR(new_aeb)) {
@@ -315,7 +318,7 @@ retry:
vid_hdr->sqnum = cpu_to_be64(++ai->max_sqnum);
/* The EC header is already there, write the VID header */
- err = ubi_io_write_vid_hdr(ubi, new_aeb->pnum, vid_hdr);
+ err = ubi_io_write_vid_hdr(ubi, new_aeb->pnum, vidb);
if (err)
goto write_error;
@@ -329,8 +332,8 @@ retry:
* of this LEB as it will be deleted and freed in 'ubi_add_to_av()'.
*/
err = ubi_add_to_av(ubi, ai, new_aeb->pnum, new_aeb->ec, vid_hdr, 0);
- kfree(new_aeb);
- ubi_free_vid_hdr(ubi, vid_hdr);
+ ubi_free_aeb(ai, new_aeb);
+ ubi_free_vid_buf(vidb);
return err;
write_error:
@@ -343,8 +346,9 @@ write_error:
goto retry;
}
kfree(new_aeb);
+ ubi_free_aeb(ai, new_aeb);
out_free:
- ubi_free_vid_hdr(ubi, vid_hdr);
+ ubi_free_vid_buf(vidb);
return err;
}
@@ -522,7 +526,7 @@ static int init_volumes(struct ubi_device *ubi,
const struct ubi_attach_info *ai,
const struct ubi_vtbl_record *vtbl)
{
- int i, reserved_pebs = 0;
+ int i, err, reserved_pebs = 0;
struct ubi_ainf_volume *av;
struct ubi_volume *vol;
@@ -565,6 +569,16 @@ static int init_volumes(struct ubi_device *ubi,
reserved_pebs += vol->reserved_pebs;
/*
+ * We use ubi->peb_count and not vol->reserved_pebs because
+ * we want to keep the code simple. Otherwise we'd have to
+ * resize/check the bitmap upon volume resize too.
+ * Allocating a few bytes more does not hurt.
+ */
+ err = ubi_fastmap_init_checkmap(vol, ubi->peb_count);
+ if (err)
+ return err;
+
+ /*
* In case of dynamic volume UBI knows nothing about how many
* data is stored there. So assume the whole volume is used.
*/
@@ -631,6 +645,9 @@ static int init_volumes(struct ubi_device *ubi,
reserved_pebs += vol->reserved_pebs;
ubi->vol_count += 1;
vol->ubi = ubi;
+ err = ubi_fastmap_init_checkmap(vol, UBI_LAYOUT_VOLUME_EBS);
+ if (err)
+ return err;
if (reserved_pebs > ubi->avail_pebs) {
ubi_err(ubi, "not enough PEBs, required %d, available %d",
@@ -833,6 +850,7 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_attach_info *ai)
out_free:
vfree(ubi->vtbl);
for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
+ ubi_fastmap_destroy_checkmap(ubi->volumes[i]);
kfree(ubi->volumes[i]);
ubi->volumes[i] = NULL;
}
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index a368bf9108..fa1b813702 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -554,7 +554,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
* failure.
*/
static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
- int vol_id, int lnum, int torture)
+ int vol_id, int lnum, int torture, bool nested)
{
struct ubi_work *wl_wrk;
@@ -573,7 +573,10 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
wl_wrk->lnum = lnum;
wl_wrk->torture = torture;
- schedule_ubi_work(ubi, wl_wrk);
+ if (nested)
+ __schedule_ubi_work(ubi, wl_wrk);
+ else
+ schedule_ubi_work(ubi, wl_wrk);
return 0;
}
@@ -618,11 +621,12 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
int shutdown)
{
int err, scrubbing = 0, torture = 0, protect = 0, erroneous = 0;
- int vol_id = -1, lnum = -1;
+ int erase = 0, keep = 0, vol_id = -1, lnum = -1;
#ifdef CONFIG_MTD_UBI_FASTMAP
int anchor = wrk->anchor;
#endif
struct ubi_wl_entry *e1, *e2;
+ struct ubi_vid_io_buf *vidb;
struct ubi_vid_hdr *vid_hdr;
int dst_leb_clean = 0;
@@ -630,10 +634,12 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
if (shutdown)
return 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);
+
ubi_assert(!ubi->move_from && !ubi->move_to);
ubi_assert(!ubi->move_to_put);
@@ -657,7 +663,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
#ifdef CONFIG_MTD_UBI_FASTMAP
/* Check whether we need to produce an anchor PEB */
if (!anchor)
- anchor = !anchor_pebs_avalible(&ubi->free);
+ anchor = !anchor_pebs_available(&ubi->free);
if (anchor) {
e1 = find_anchor_wl_entry(&ubi->used);
@@ -724,7 +730,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
* which is being moved was unmapped.
*/
- err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0);
+ err = ubi_io_read_vid_hdr(ubi, e1->pnum, vidb, 0);
if (err && err != UBI_IO_BITFLIPS) {
dst_leb_clean = 1;
if (err == UBI_IO_FF) {
@@ -751,6 +757,16 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
e1->pnum);
scrubbing = 1;
goto out_not_moved;
+ } else if (ubi->fast_attach && err == UBI_IO_BAD_HDR_EBADMSG) {
+ /*
+ * While a full scan would detect interrupted erasures
+ * at attach time we can face them here when attached from
+ * Fastmap.
+ */
+ dbg_wl("PEB %d has ECC errors, maybe from an interrupted erasure",
+ e1->pnum);
+ erase = 1;
+ goto out_not_moved;
}
ubi_err(ubi, "error %d while reading VID header from PEB %d",
@@ -761,7 +777,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
vol_id = be32_to_cpu(vid_hdr->vol_id);
lnum = be32_to_cpu(vid_hdr->lnum);
- err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr);
+ err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vidb);
if (err) {
if (err == MOVE_CANCEL_RACE) {
/*
@@ -786,6 +802,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
* Target PEB had bit-flips or write error - torture it.
*/
torture = 1;
+ keep = 1;
goto out_not_moved;
}
@@ -818,7 +835,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
if (scrubbing)
ubi_msg(ubi, "scrubbed PEB %d (LEB %d:%d), data moved to PEB %d",
e1->pnum, vol_id, lnum, e2->pnum);
- ubi_free_vid_hdr(ubi, vid_hdr);
+ ubi_free_vid_buf(vidb);
if (!ubi->move_to_put) {
wl_tree_add(e2, &ubi->used);
@@ -868,7 +885,7 @@ out_not_moved:
ubi->erroneous_peb_count += 1;
} else if (scrubbing)
wl_tree_add(e1, &ubi->scrub);
- else
+ else if (keep)
wl_tree_add(e1, &ubi->used);
if (dst_leb_clean) {
wl_tree_add(e2, &ubi->free);
@@ -879,7 +896,7 @@ out_not_moved:
ubi->move_from = ubi->move_to = NULL;
ubi->wl_scheduled = 0;
- ubi_free_vid_hdr(ubi, vid_hdr);
+ ubi_free_vid_buf(vidb);
if (dst_leb_clean) {
ensure_wear_leveling(ubi, 1);
} else {
@@ -888,6 +905,12 @@ out_not_moved:
goto out_ro;
}
+ if (erase) {
+ err = do_sync_erase(ubi, e1, vol_id, lnum, 1);
+ if (err)
+ goto out_ro;
+ }
+
return 0;
out_error:
@@ -900,7 +923,7 @@ out_error:
ubi->move_from = ubi->move_to = NULL;
ubi->move_to_put = ubi->wl_scheduled = 0;
- ubi_free_vid_hdr(ubi, vid_hdr);
+ ubi_free_vid_buf(vidb);
wl_entry_destroy(ubi, e1);
wl_entry_destroy(ubi, e2);
@@ -911,7 +934,7 @@ out_ro:
out_cancel:
ubi->wl_scheduled = 0;
- ubi_free_vid_hdr(ubi, vid_hdr);
+ ubi_free_vid_buf(vidb);
return 0;
}
@@ -1027,7 +1050,7 @@ static int __erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk)
int err1;
/* Re-schedule the LEB for erasure */
- err1 = schedule_erase(ubi, e, vol_id, lnum, 0);
+ err1 = schedule_erase(ubi, e, vol_id, lnum, 0, false);
if (err1) {
wl_entry_destroy(ubi, e);
err = err1;
@@ -1184,7 +1207,7 @@ retry:
}
}
- err = schedule_erase(ubi, e, vol_id, lnum, torture);
+ err = schedule_erase(ubi, e, vol_id, lnum, torture, false);
if (err)
wl_tree_add(e, &ubi->used);
@@ -1369,6 +1392,46 @@ static void shutdown_work(struct ubi_device *ubi)
}
/**
+ * erase_aeb - erase a PEB given in UBI attach info PEB
+ * @ubi: UBI device description object
+ * @aeb: UBI attach info PEB
+ * @sync: If true, erase synchronously. Otherwise schedule for erasure
+ */
+static int erase_aeb(struct ubi_device *ubi, struct ubi_ainf_peb *aeb, bool sync)
+{
+ struct ubi_wl_entry *e;
+ int err;
+
+ e = kmalloc(sizeof(*e), GFP_KERNEL);
+ if (!e)
+ return -ENOMEM;
+
+ e->pnum = aeb->pnum;
+ e->ec = aeb->ec;
+ ubi->lookuptbl[e->pnum] = e;
+
+ if (sync) {
+ err = sync_erase(ubi, e, false);
+ if (err)
+ goto out_free;
+
+ wl_tree_add(e, &ubi->free);
+ ubi->free_count++;
+ } else {
+ err = schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0, false);
+ if (err)
+ goto out_free;
+ }
+
+ return 0;
+
+out_free:
+ wl_entry_destroy(ubi, e);
+
+ return err;
+}
+
+/**
* ubi_wl_init - initialize the WL sub-system using attaching information.
* @ubi: UBI device description object
* @ai: attaching information
@@ -1399,27 +1462,21 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
INIT_LIST_HEAD(&ubi->pq[i]);
ubi->pq_head = 0;
+ ubi->free_count = 0;
list_for_each_entry_safe(aeb, tmp, &ai->erase, u.list) {
- e = kmalloc(sizeof(*e), GFP_KERNEL);
- if (!e)
- goto out_free;
-
- e->pnum = aeb->pnum;
- e->ec = aeb->ec;
- ubi->lookuptbl[e->pnum] = e;
- if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0)) {
- wl_entry_destroy(ubi, e);
+ err = erase_aeb(ubi, aeb, false);
+ if (err)
goto out_free;
- }
found_pebs++;
}
- ubi->free_count = 0;
list_for_each_entry(aeb, &ai->free, u.list) {
e = kmalloc(sizeof(*e), GFP_KERNEL);
- if (!e)
+ if (!e) {
+ err = -ENOMEM;
goto out_free;
+ }
e->pnum = aeb->pnum;
e->ec = aeb->ec;
@@ -1436,8 +1493,10 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
ubi_rb_for_each_entry(rb1, av, &ai->volumes, rb) {
ubi_rb_for_each_entry(rb2, aeb, &av->root, u.rb) {
e = kmalloc(sizeof(*e), GFP_KERNEL);
- if (!e)
+ if (!e) {
+ err = -ENOMEM;
goto out_free;
+ }
e->pnum = aeb->pnum;
e->ec = aeb->ec;
@@ -1457,19 +1516,47 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
}
}
- dbg_wl("found %i PEBs", found_pebs);
-
- if (ubi->fm) {
- ubi_assert(ubi->good_peb_count ==
- found_pebs + ubi->fm->used_blocks);
+ list_for_each_entry(aeb, &ai->fastmap, u.list) {
+ e = ubi_find_fm_block(ubi, aeb->pnum);
- for (i = 0; i < ubi->fm->used_blocks; i++) {
- e = ubi->fm->e[i];
+ if (e) {
+ ubi_assert(!ubi->lookuptbl[e->pnum]);
ubi->lookuptbl[e->pnum] = e;
+ } else {
+ bool sync = false;
+
+ /*
+ * Usually old Fastmap PEBs are scheduled for erasure
+ * and we don't have to care about them but if we face
+ * an power cut before scheduling them we need to
+ * take care of them here.
+ */
+ if (ubi->lookuptbl[aeb->pnum])
+ continue;
+
+ /*
+ * The fastmap update code might not find a free PEB for
+ * writing the fastmap anchor to and then reuses the
+ * current fastmap anchor PEB. When this PEB gets erased
+ * and a power cut happens before it is written again we
+ * must make sure that the fastmap attach code doesn't
+ * find any outdated fastmap anchors, hence we erase the
+ * outdated fastmap anchor PEBs synchronously here.
+ */
+ if (aeb->vol_id == UBI_FM_SB_VOLUME_ID)
+ sync = true;
+
+ err = erase_aeb(ubi, aeb, sync);
+ if (err)
+ goto out_free;
}
+
+ found_pebs++;
}
- else
- ubi_assert(ubi->good_peb_count == found_pebs);
+
+ dbg_wl("found %i PEBs", found_pebs);
+
+ ubi_assert(ubi->good_peb_count == found_pebs);
reserved_pebs = WL_RESERVED_PEBS;
ubi_fastmap_init(ubi, &reserved_pebs);
diff --git a/drivers/mtd/ubi/wl.h b/drivers/mtd/ubi/wl.h
index 2a1899e987..60168116db 100644
--- a/drivers/mtd/ubi/wl.h
+++ b/drivers/mtd/ubi/wl.h
@@ -1,7 +1,7 @@
#ifndef UBI_WL_H
#define UBI_WL_H
#ifdef CONFIG_MTD_UBI_FASTMAP
-static int anchor_pebs_avalible(struct rb_root *root);
+static int anchor_pebs_available(struct rb_root *root);
static struct ubi_wl_entry *find_anchor_wl_entry(struct rb_root *root);
static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi);
static void ubi_fastmap_close(struct ubi_device *ubi);