]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - drivers/mtd/ubi/attach.c
UBI: fix add_fastmap() to use the vid_hdr passed in argument
[mirror_ubuntu-zesty-kernel.git] / drivers / mtd / ubi / attach.c
index c1aaf0336cf2e389386b4a6f7c87a8609652b474..95138ae2062f58341de41fff97150f746e742f1c 100644 (file)
@@ -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(vid_hdr->vol_id);
+       aeb->sqnum = be64_to_cpu(vid_hdr->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;
 }