diff mbox

[07/11] UBI: Fastmap: Fix scan regression

Message ID 1340982869-77042-8-git-send-email-richard@nod.at
State New, archived
Headers show

Commit Message

Richard Weinberger June 29, 2012, 3:14 p.m. UTC
... resuse scan_peb(), don't rescan the first 64 PEBs.

Signed-off-by: Richard Weinberger <richard@nod.at>
---
 drivers/mtd/ubi/attach.c  |  134 ++++++++++++++++++++++++++++++---------------
 drivers/mtd/ubi/fastmap.c |   64 ++--------------------
 drivers/mtd/ubi/ubi.h     |    2 +-
 3 files changed, 95 insertions(+), 105 deletions(-)
diff mbox

Patch

diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c
index 9878cc2..bc1e4bf 100644
--- a/drivers/mtd/ubi/attach.c
+++ b/drivers/mtd/ubi/attach.c
@@ -817,10 +817,11 @@  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 pnum, int *vid, unsigned long long *sqnum,
+		    int find_fastmap)
 {
 	long long uninitialized_var(ec);
-	int err, bitflips = 0, vol_id, ec_err = 0;
+	int err, bitflips = 0, vol_id = -1, ec_err = 0;
 
 	dbg_bld("scan PEB %d", pnum);
 
@@ -991,8 +992,13 @@  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 (!find_fastmap &&
+	   (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOLUME_ID)) {
 		int lnum = be32_to_cpu(vidh->lnum);
 
 		/* Unsupported internal volume */
@@ -1221,7 +1227,7 @@  static void destroy_ai(struct ubi_device *ubi, struct ubi_attach_info *ai)
  * information about it in form of a "struct ubi_attach_info" object. In case
  * of failure, an error code is returned.
  */
-static int scan_all(struct ubi_device *ubi, struct ubi_attach_info *ai)
+static int scan_all(struct ubi_device *ubi, struct ubi_attach_info *ai, int start)
 {
 	int err, pnum;
 	struct rb_node *rb1, *rb2;
@@ -1229,11 +1235,6 @@  static int scan_all(struct ubi_device *ubi, struct ubi_attach_info *ai)
 	struct ubi_ainf_peb *aeb;
 
 	err = -ENOMEM;
-	ai->aeb_slab_cache = kmem_cache_create("ubi_aeb_slab_cache",
-					       sizeof(struct ubi_ainf_peb),
-					       0, 0, NULL);
-	if (!ai->aeb_slab_cache)
-		goto out_ai;
 
 	ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
 	if (!ech)
@@ -1243,11 +1244,11 @@  static int scan_all(struct ubi_device *ubi, struct ubi_attach_info *ai)
 	if (!vidh)
 		goto out_ech;
 
-	for (pnum = 0; pnum < ubi->peb_count; pnum++) {
+	for (pnum = start; pnum < ubi->peb_count; pnum++) {
 		cond_resched();
 
 		dbg_gen("process PEB %d", pnum);
-		err = scan_peb(ubi, ai, pnum);
+		err = scan_peb(ubi, ai, pnum, NULL, NULL, 0);
 		if (err < 0)
 			goto out_vidh;
 	}
@@ -1303,17 +1304,77 @@  out_ai:
 	return err;
 }
 
+/**
+ * scan_fastmap - try to find a fastmap and attach from it.
+ * @ubi: UBI device description object
+ * @ai: attach info object
+ */
+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;
+
+	err = -ENOMEM;
+
+	ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
+	if (!ech)
+		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, 1);
+		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;
+
+	return ubi_scan_fastmap(ubi, ai, fm_anchor);
+
+out_vidh:
+	ubi_free_vid_hdr(ubi, vidh);
+out_ech:
+	kfree(ech);
+out_ai:
+	destroy_ai(ubi, ai);
+	return err;
+}
 static struct ubi_attach_info *alloc_ai(void)
 {
-	static struct ubi_attach_info *ai;
+	struct ubi_attach_info *ai;
 
 	ai = kzalloc(sizeof(struct ubi_attach_info), GFP_KERNEL);
-	if (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;
+	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;
+	ai->aeb_slab_cache = kmem_cache_create("ubi_aeb_slab_cache",
+					       sizeof(struct ubi_ainf_peb),
+					       0, 0, NULL);
+	if (!ai->aeb_slab_cache) {
+		kfree(ai);
+		ai = NULL;
 	}
 
 	return ai;
@@ -1337,35 +1398,18 @@  int ubi_attach(struct ubi_device *ubi, int force_scan)
 		return -ENOMEM;
 
 	if (force_scan)
-		err = scan_all(ubi, ai);
+		err = scan_all(ubi, ai, 0);
 	else {
-		/* TODO: this is a regression. If I have an old image, and I do
-		 * not want to use fastmap, I will be forced to waste time for
-		 * useless scan of 64 first eraseblocks. Not good.
-		 *
-		 * Can you teach ubi_scan_fastmap() to use 'scan_peb()'
-		 * function for scanning and build normal ai information? If it
-		 * finds fastmap - it can destroy the collected ai. If it does
-		 * not find, it returns ai. Then you just confinue scanning.
-		 *
-		 * I buess what we'll need is:
-		 * 1. scan_all() -> scan_range(..., int pnum1, int pnum2);
-		 * 2. ubi_scan_fastmap() returns the pnum of the last scanned
-		 *    eraseblock if fastmap was not found;
-		 *    Also 'ubi_scan_fastmap()' uses scan_peb() for scanning.
-		 * 3. You call 'scan_range(..., pnum, c->peb_cnt - 1)' and
-		 *    it continues.
-		 *
-		 * And no regressions.
-		 */
-		err = ubi_scan_fastmap(ubi, ai);
+		err = scan_fast(ubi, ai);
 		if (err > 0) {
-			destroy_ai(ubi, ai);
-			ai = alloc_ai();
-			if (!ai)
-				return -ENOMEM;
+			if (err != UBI_NO_FASTMAP) {
+				destroy_ai(ubi, ai);
+				ai = alloc_ai();
+				if (!ai)
+					return -ENOMEM;
+			}
 
-			err = scan_all(ubi, ai);
+			err = scan_all(ubi, ai, UBI_FM_MAX_START);
 		}
 	}
 
@@ -1398,7 +1442,7 @@  int ubi_attach(struct ubi_device *ubi, int force_scan)
 		if (!scan_ai)
 			goto out_wl;
 
-		err = scan_all(ubi, scan_ai);
+		err = scan_all(ubi, scan_ai, 0);
 		if (err) {
 			kfree(scan_ai);
 			goto out_wl;
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index 9b4fd34..6276039 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -819,82 +819,28 @@  fail:
 }
 
 /**
- * ubi_find_fastmap - searches the first UBI_FM_MAX_START PEBs for the
- * fastmap super block.
- * @ubi: UBI device object
- * @fm_start: Pointer where the fastmap suber block PEB number will be stored.
- *
- * Returns:
- *  - 0 on success: (fm_start contains suber block PEB number)
- *  - < 0 on failure (fm_start is -1)
- */
-static int ubi_find_fastmap(struct ubi_device *ubi, int *fm_start)
-{
-	int i, ret = -ENOENT;
-	struct ubi_vid_hdr *vhdr;
-	unsigned long long max_sqnum = 0, sqnum;
-
-	vhdr = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
-	if (!vhdr)
-		return -ENOMEM;
-
-	*fm_start = -1;
-	for (i = 0; i < UBI_FM_MAX_START; i++) {
-		if (ubi_io_is_bad(ubi, i))
-			continue;
-
-		ret = ubi_io_read_vid_hdr(ubi, i, vhdr, 0);
-		if (ret < 0)
-			goto out;
-		else if (ret > 0 && ret != UBI_IO_BITFLIPS)
-			continue;
-
-		if (be32_to_cpu(vhdr->vol_id) == UBI_FM_SB_VOLUME_ID) {
-			sqnum = be64_to_cpu(vhdr->sqnum);
-			dbg_bld("found a fastmap super block at PEB %i " \
-				"sqnum: %llu", i, sqnum);
-
-			if (sqnum > max_sqnum) {
-				max_sqnum = sqnum;
-				*fm_start = i;
-			}
-		}
-	}
-
-	if (*fm_start > -1)
-		ret = 0;
-out:
-	ubi_free_vid_hdr(ubi, vhdr);
-	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
  *
  * 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 ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
+		     int fm_anchor)
 {
 	struct ubi_fm_sb *fmsb;
 	struct ubi_vid_hdr *vh;
 	struct ubi_ec_hdr *ech;
 	struct ubi_fastmap_layout *fm;
-	int i, used_blocks, pnum, sb_pnum = 0, ret = 0;
+	int i, used_blocks, pnum, ret = 0;
 	void *fm_raw = NULL;
 	size_t fm_size;
 	__be32 crc, tmp_crc;
 	unsigned long long sqnum = 0;
 
-	ret = ubi_find_fastmap(ubi, &sb_pnum);
-	if (ret)
-		return ret;
-	if (sb_pnum == -1)
-		return UBI_NO_FASTMAP;
-
 	fmsb = kmalloc(sizeof(*fmsb), GFP_KERNEL);
 	if (!fmsb) {
 		ret = -ENOMEM;
@@ -908,7 +854,7 @@  int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai)
 		goto free_raw;
 	}
 
-	ret = ubi_io_read(ubi, fmsb, sb_pnum, ubi->leb_start, sizeof(*fmsb));
+	ret = ubi_io_read(ubi, fmsb, fm_anchor, ubi->leb_start, sizeof(*fmsb));
 	if (ret && ret != UBI_IO_BITFLIPS) {
 		kfree(fmsb);
 		kfree(fm);
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 8e2592d..fef9e92 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -816,7 +816,7 @@  int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb,
 
 /* fastmap.c */
 int ubi_update_fastmap(struct ubi_device *ubi);
-int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai);
+int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, int fm_anchor);
 
 /*
  * ubi_rb_for_each_entry - walk an RB-tree.