static int ubi_attach_fastmap()

in ubi/fastmap.c [546:799]


static int ubi_attach_fastmap(struct ubi_device *ubi,
			      struct ubi_attach_info *ai,
			      struct ubi_fastmap_layout *fm)
{
	struct list_head used, free;
	struct ubi_ainf_volume *av;
	struct ubi_ainf_peb *aeb, *tmp_aeb, *_tmp_aeb;
	struct ubi_fm_sb *fmsb;
	struct ubi_fm_hdr *fmhdr;
	struct ubi_fm_scan_pool *fmpl, *fmpl_wl;
	struct ubi_fm_ec *fmec;
	struct ubi_fm_volhdr *fmvhdr;
	struct ubi_fm_eba *fm_eba;
	int ret, i, j, pool_size, wl_pool_size;
	size_t fm_pos = 0, fm_size = ubi->fm_size;
	unsigned long long max_sqnum = 0;
	void *fm_raw = ubi->fm_buf;

	INIT_LIST_HEAD(&used);
	INIT_LIST_HEAD(&free);
	ai->min_ec = UBI_MAX_ERASECOUNTER;

	fmsb = (struct ubi_fm_sb *)(fm_raw);
	ai->max_sqnum = fmsb->sqnum;
	fm_pos += sizeof(struct ubi_fm_sb);
	if (fm_pos >= fm_size)
		goto fail_bad;

	fmhdr = (struct ubi_fm_hdr *)(fm_raw + fm_pos);
	fm_pos += sizeof(*fmhdr);
	if (fm_pos >= fm_size)
		goto fail_bad;

	if (be32_to_cpu(fmhdr->magic) != UBI_FM_HDR_MAGIC) {
		ubi_err(ubi, "bad fastmap header magic: 0x%x, expected: 0x%x",
			be32_to_cpu(fmhdr->magic), UBI_FM_HDR_MAGIC);
		goto fail_bad;
	}

	fmpl = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
	fm_pos += sizeof(*fmpl);
	if (fm_pos >= fm_size)
		goto fail_bad;
	if (be32_to_cpu(fmpl->magic) != UBI_FM_POOL_MAGIC) {
		ubi_err(ubi, "bad fastmap pool magic: 0x%x, expected: 0x%x",
			be32_to_cpu(fmpl->magic), UBI_FM_POOL_MAGIC);
		goto fail_bad;
	}

	fmpl_wl = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
	fm_pos += sizeof(*fmpl_wl);
	if (fm_pos >= fm_size)
		goto fail_bad;
	if (be32_to_cpu(fmpl_wl->magic) != UBI_FM_POOL_MAGIC) {
		ubi_err(ubi, "bad fastmap WL pool magic: 0x%x, expected: 0x%x",
			be32_to_cpu(fmpl_wl->magic), UBI_FM_POOL_MAGIC);
		goto fail_bad;
	}

	pool_size = be16_to_cpu(fmpl->size);
	wl_pool_size = be16_to_cpu(fmpl_wl->size);
	fm->max_pool_size = be16_to_cpu(fmpl->max_size);
	fm->max_wl_pool_size = be16_to_cpu(fmpl_wl->max_size);

	if (pool_size > UBI_FM_MAX_POOL_SIZE || pool_size < 0) {
		ubi_err(ubi, "bad pool size: %i", pool_size);
		goto fail_bad;
	}

	if (wl_pool_size > UBI_FM_MAX_POOL_SIZE || wl_pool_size < 0) {
		ubi_err(ubi, "bad WL pool size: %i", wl_pool_size);
		goto fail_bad;
	}


	if (fm->max_pool_size > UBI_FM_MAX_POOL_SIZE ||
	    fm->max_pool_size < 0) {
		ubi_err(ubi, "bad maximal pool size: %i", fm->max_pool_size);
		goto fail_bad;
	}

	if (fm->max_wl_pool_size > UBI_FM_MAX_POOL_SIZE ||
	    fm->max_wl_pool_size < 0) {
		ubi_err(ubi, "bad maximal WL pool size: %i",
			fm->max_wl_pool_size);
		goto fail_bad;
	}

	/* read EC values from free list */
	for (i = 0; i < be32_to_cpu(fmhdr->free_peb_count); i++) {
		fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
		fm_pos += sizeof(*fmec);
		if (fm_pos >= fm_size)
			goto fail_bad;

		add_aeb(ai, &ai->free, be32_to_cpu(fmec->pnum),
			be32_to_cpu(fmec->ec), 0);
	}

	/* read EC values from used list */
	for (i = 0; i < be32_to_cpu(fmhdr->used_peb_count); i++) {
		fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
		fm_pos += sizeof(*fmec);
		if (fm_pos >= fm_size)
			goto fail_bad;

		add_aeb(ai, &used, be32_to_cpu(fmec->pnum),
			be32_to_cpu(fmec->ec), 0);
	}

	/* read EC values from scrub list */
	for (i = 0; i < be32_to_cpu(fmhdr->scrub_peb_count); i++) {
		fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
		fm_pos += sizeof(*fmec);
		if (fm_pos >= fm_size)
			goto fail_bad;

		add_aeb(ai, &used, be32_to_cpu(fmec->pnum),
			be32_to_cpu(fmec->ec), 1);
	}

	/* read EC values from erase list */
	for (i = 0; i < be32_to_cpu(fmhdr->erase_peb_count); i++) {
		fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
		fm_pos += sizeof(*fmec);
		if (fm_pos >= fm_size)
			goto fail_bad;

		add_aeb(ai, &ai->erase, be32_to_cpu(fmec->pnum),
			be32_to_cpu(fmec->ec), 1);
	}

	ai->mean_ec = div_u64(ai->ec_sum, ai->ec_count);
	ai->bad_peb_count = be32_to_cpu(fmhdr->bad_peb_count);

	/* Iterate over all volumes and read their EBA table */
	for (i = 0; i < be32_to_cpu(fmhdr->vol_count); i++) {
		fmvhdr = (struct ubi_fm_volhdr *)(fm_raw + fm_pos);
		fm_pos += sizeof(*fmvhdr);
		if (fm_pos >= fm_size)
			goto fail_bad;

		if (be32_to_cpu(fmvhdr->magic) != UBI_FM_VHDR_MAGIC) {
			ubi_err(ubi, "bad fastmap vol header magic: 0x%x, expected: 0x%x",
				be32_to_cpu(fmvhdr->magic), UBI_FM_VHDR_MAGIC);
			goto fail_bad;
		}

		av = add_vol(ai, be32_to_cpu(fmvhdr->vol_id),
			     be32_to_cpu(fmvhdr->used_ebs),
			     be32_to_cpu(fmvhdr->data_pad),
			     fmvhdr->vol_type,
			     be32_to_cpu(fmvhdr->last_eb_bytes));

		if (IS_ERR(av)) {
			if (PTR_ERR(av) == -EEXIST)
				ubi_err(ubi, "volume (ID %i) already exists",
					fmvhdr->vol_id);

			goto fail_bad;
		}

		ai->vols_found++;
		if (ai->highest_vol_id < be32_to_cpu(fmvhdr->vol_id))
			ai->highest_vol_id = be32_to_cpu(fmvhdr->vol_id);

		fm_eba = (struct ubi_fm_eba *)(fm_raw + fm_pos);
		fm_pos += sizeof(*fm_eba);
		fm_pos += (sizeof(__be32) * be32_to_cpu(fm_eba->reserved_pebs));
		if (fm_pos >= fm_size)
			goto fail_bad;

		if (be32_to_cpu(fm_eba->magic) != UBI_FM_EBA_MAGIC) {
			ubi_err(ubi, "bad fastmap EBA header magic: 0x%x, expected: 0x%x",
				be32_to_cpu(fm_eba->magic), UBI_FM_EBA_MAGIC);
			goto fail_bad;
		}

		for (j = 0; j < be32_to_cpu(fm_eba->reserved_pebs); j++) {
			int pnum = be32_to_cpu(fm_eba->pnum[j]);

			if (pnum < 0)
				continue;

			aeb = NULL;
			list_for_each_entry(tmp_aeb, &used, u.list) {
				if (tmp_aeb->pnum == pnum) {
					aeb = tmp_aeb;
					break;
				}
			}

			if (!aeb) {
				ubi_err(ubi, "PEB %i is in EBA but not in used list", pnum);
				goto fail_bad;
			}

			aeb->lnum = j;

			if (av->highest_lnum <= aeb->lnum)
				av->highest_lnum = aeb->lnum;

			assign_aeb_to_av(ai, aeb, av);

			dbg_bld("inserting PEB:%i (LEB %i) to vol %i",
				aeb->pnum, aeb->lnum, av->vol_id);
		}
	}

	ret = scan_pool(ubi, ai, fmpl->pebs, pool_size, &max_sqnum, &free);
	if (ret)
		goto fail;

	ret = scan_pool(ubi, ai, fmpl_wl->pebs, wl_pool_size, &max_sqnum, &free);
	if (ret)
		goto fail;

	if (max_sqnum > ai->max_sqnum)
		ai->max_sqnum = max_sqnum;

	list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &free, u.list)
		list_move_tail(&tmp_aeb->u.list, &ai->free);

	list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &used, u.list)
		list_move_tail(&tmp_aeb->u.list, &ai->erase);

	ubi_assert(list_empty(&free));

	/*
	 * If fastmap is leaking PEBs (must not happen), raise a
	 * fat warning and fall back to scanning mode.
	 * We do this here because in ubi_wl_init() it's too late
	 * and we cannot fall back to scanning.
	 */
	if (WARN_ON(count_fastmap_pebs(ai) != ubi->peb_count -
		    ai->bad_peb_count - fm->used_blocks))
		goto fail_bad;

	return 0;

fail_bad:
	ret = UBI_BAD_FASTMAP;
fail:
	list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &used, u.list) {
		list_del(&tmp_aeb->u.list);
		ubi_free_aeb(ai, tmp_aeb);
	}
	list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &free, u.list) {
		list_del(&tmp_aeb->u.list);
		ubi_free_aeb(ai, tmp_aeb);
	}

	return ret;
}