Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fdf10ed7 authored by Richard Weinberger's avatar Richard Weinberger
Browse files

ubi: Rework Fastmap attach base code



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: default avatarRichard Weinberger <richard@nod.at>
parent be801105
Loading
Loading
Loading
Loading
+69 −30
Original line number Diff line number Diff line
@@ -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);

	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;

	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 = alloc_ai();
	if (!*ai)
		return -ENOMEM;
		*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;
}
+33 −3
Original line number Diff line number Diff line
@@ -849,28 +849,58 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
	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);

+27 −1
Original line number Diff line number Diff line
@@ -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__ */
+33 −8
Original line number Diff line number Diff line
@@ -1598,18 +1598,43 @@ 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 (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;

	if (ubi->fm) {
		ubi_assert(ubi->good_peb_count ==
			   found_pebs + ubi->fm->used_blocks);
			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;
			}
		}
	else

		found_pebs++;
	}

	dbg_wl("found %i PEBs", found_pebs);

	ubi_assert(ubi->good_peb_count == found_pebs);

	reserved_pebs = WL_RESERVED_PEBS;