ubi: Rework Fastmap attach base code
authorRichard Weinberger <richard@nod.at>
Tue, 14 Jun 2016 08:12:15 +0000 (10:12 +0200)
committerRichard Weinberger <richard@nod.at>
Fri, 29 Jul 2016 21:32:42 +0000 (23:32 +0200)
Introduce a new list to the UBI attach information
object to be able to deal better with old and corrupted
Fastmap eraseblocks.
Also move more Fastmap specific code into fastmap.c.

Signed-off-by: Richard Weinberger <richard@nod.at>
drivers/mtd/ubi/attach.c
drivers/mtd/ubi/fastmap.c
drivers/mtd/ubi/ubi.h
drivers/mtd/ubi/wl.c

index 6e5fb1decb4ecd408138713d6fc3509fc8cedc55..bd6fc528b10a22bfbaf209922f685212e8ad02a7 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(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
@@ -822,18 +856,15 @@ static bool vol_ignored(int vol_id)
  * @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
  *
  * 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
  * "attaching info" structure. Returns zero if the physical eraseblock was
  * 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)
+static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum)
 {
-       long long uninitialized_var(ec);
+       long long ec;
        int err, bitflips = 0, vol_id = -1, ec_err = 0;
 
        dbg_bld("scan PEB %d", pnum);
@@ -1005,10 +1036,6 @@ 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_ignored(vol_id)) {
                int lnum = be32_to_cpu(vidh->lnum);
 
@@ -1049,7 +1076,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;
 
@@ -1198,6 +1230,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;
@@ -1257,7 +1293,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);
                if (err < 0)
                        goto out_vidh;
        }
@@ -1323,6 +1359,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),
@@ -1349,52 +1386,54 @@ 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);
                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;
-
-       destroy_ai(*ai);
-       *ai = alloc_ai();
-       if (!*ai)
-               return -ENOMEM;
+       err = ubi_scan_fastmap(ubi, *ai, scan_ai);
+       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;
 }
index ab337e67e1f39a3c72d739117ba541ebec8a0632..12bdb09023279a8984166cc8442a5499adecbd81 100644 (file)
@@ -849,28 +849,58 @@ fail:
        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;
+}
+
 /**
  * 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_hdr *vh;
        struct ubi_ec_hdr *ech;
        struct ubi_fastmap_layout *fm;
-       int i, used_blocks, pnum, ret = 0;
+       struct ubi_ainf_peb *tmp_aeb, *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;
+
+       /* Move all (possible) fastmap blocks into our new attach structure. */
+       list_for_each_entry_safe(aeb, tmp_aeb, &scan_ai->fastmap, u.list)
+               list_move_tail(&aeb->u.list, &ai->fastmap);
+
        down_write(&ubi->fm_protect);
        memset(ubi->fm_buf, 0, ubi->fm_size);
 
index 91075b603d824331129ea6b56d12ee85e3c910ab..c8b90a866d2743c3b5c83437a3aba1efa2745b09 100644 (file)
@@ -703,6 +703,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)
@@ -731,6 +733,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;
@@ -911,7 +914,7 @@ 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);
 #else
 static inline int ubi_update_fastmap(struct ubi_device *ubi) { return 0; }
 #endif
@@ -1120,4 +1123,27 @@ static inline bool ubi_is_fm_vol(int vol_id)
        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__ */
index 959c7b12e0b1be4863960e43fa9dfbe67f2eb58a..f4533266d7b26cbac8ca44861d88277a5d0cbabc 100644 (file)
@@ -1598,19 +1598,44 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
                }
        }
 
-       dbg_wl("found %i PEBs", found_pebs);
+       list_for_each_entry(aeb, &ai->fastmap, u.list) {
+               cond_resched();
+
+               e = ubi_find_fm_block(ubi, aeb->pnum);
 
-       if (ubi->fm) {
-               ubi_assert(ubi->good_peb_count ==
-                          found_pebs + ubi->fm->used_blocks);
+               if (e) {
+                       ubi_assert(!ubi->lookuptbl[e->pnum]);
+                       ubi->lookuptbl[e->pnum] = e;
+               } else {
+                       /*
+                        * 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;
+
+                       e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL);
+                       if (!e)
+                               goto out_free;
 
-               for (i = 0; i < ubi->fm->used_blocks; i++) {
-                       e = ubi->fm->e[i];
+                       e->pnum = aeb->pnum;
+                       e->ec = aeb->ec;
+                       ubi_assert(!ubi->lookuptbl[e->pnum]);
                        ubi->lookuptbl[e->pnum] = e;
+                       if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0)) {
+                               wl_entry_destroy(ubi, e);
+                               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);