|
@@ -174,6 +174,40 @@ static int add_corrupted(struct ubi_attach_info *ai, int pnum, int ec)
|
|
|
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 = kmem_cache_alloc(ai->aeb_slab_cache, GFP_KERNEL);
|
|
|
+ if (!aeb)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ aeb->pnum = pnum;
|
|
|
+ aeb->vol_id = be32_to_cpu(vidh->vol_id);
|
|
|
+ aeb->sqnum = be64_to_cpu(vidh->sqnum);
|
|
|
+ aeb->ec = ec;
|
|
|
+ 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
|
|
@@ -803,13 +837,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
|
|
@@ -817,9 +864,9 @@ 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);
|
|
|
+ long long ec;
|
|
|
int err, bitflips = 0, vol_id = -1, ec_err = 0;
|
|
|
|
|
|
dbg_bld("scan PEB %d", pnum);
|
|
@@ -935,6 +982,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
|
|
@@ -991,21 +1052,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)
|
|
@@ -1037,7 +1092,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;
|
|
|
|
|
@@ -1186,6 +1246,10 @@ static void destroy_ai(struct ubi_attach_info *ai)
|
|
|
list_del(&aeb->u.list);
|
|
|
kmem_cache_free(ai->aeb_slab_cache, aeb);
|
|
|
}
|
|
|
+ list_for_each_entry_safe(aeb, aeb_tmp, &ai->fastmap, u.list) {
|
|
|
+ list_del(&aeb->u.list);
|
|
|
+ kmem_cache_free(ai->aeb_slab_cache, aeb);
|
|
|
+ }
|
|
|
|
|
|
/* Destroy the volume RB-tree */
|
|
|
rb = ai->volumes.rb_node;
|
|
@@ -1245,7 +1309,7 @@ static int scan_all(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
|
|
cond_resched();
|
|
|
|
|
|
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;
|
|
|
}
|
|
@@ -1311,6 +1375,7 @@ static struct ubi_attach_info *alloc_ai(void)
|
|
|
INIT_LIST_HEAD(&ai->free);
|
|
|
INIT_LIST_HEAD(&ai->erase);
|
|
|
INIT_LIST_HEAD(&ai->alien);
|
|
|
+ INIT_LIST_HEAD(&ai->fastmap);
|
|
|
ai->volumes = RB_ROOT;
|
|
|
ai->aeb_slab_cache = kmem_cache_create("ubi_aeb_slab_cache",
|
|
|
sizeof(struct ubi_ainf_peb),
|
|
@@ -1326,7 +1391,7 @@ static struct ubi_attach_info *alloc_ai(void)
|
|
|
#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
|
|
|
*
|
|
@@ -1337,52 +1402,58 @@ static struct ubi_attach_info *alloc_ai(void)
|
|
|
*/
|
|
|
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;
|
|
|
|
|
|
+ scan_ai = alloc_ai();
|
|
|
+ if (!scan_ai)
|
|
|
+ goto out;
|
|
|
+
|
|
|
ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
|
|
|
if (!ech)
|
|
|
- goto out;
|
|
|
+ goto out_ai;
|
|
|
|
|
|
vidh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
|
|
|
if (!vidh)
|
|
|
goto out_ech;
|
|
|
|
|
|
for (pnum = 0; pnum < UBI_FM_MAX_START; pnum++) {
|
|
|
- int vol_id = -1;
|
|
|
- unsigned long long sqnum = -1;
|
|
|
cond_resched();
|
|
|
|
|
|
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);
|
|
|
|
|
|
- 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);
|
|
|
|
|
|
- destroy_ai(*ai);
|
|
|
- *ai = alloc_ai();
|
|
|
- if (!*ai)
|
|
|
- return -ENOMEM;
|
|
|
+ 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 ubi_scan_fastmap(ubi, *ai, fm_anchor);
|
|
|
+ return err;
|
|
|
|
|
|
out_vidh:
|
|
|
ubi_free_vid_hdr(ubi, vidh);
|
|
|
out_ech:
|
|
|
kfree(ech);
|
|
|
+out_ai:
|
|
|
+ destroy_ai(scan_ai);
|
|
|
out:
|
|
|
return err;
|
|
|
}
|