Exemplo n.º 1
0
/**
 * ubi_scan_add_used - add physical eraseblock to the scanning information.
 * @ubi: UBI device description object
 * @si: scanning information
 * @pnum: the physical eraseblock number
 * @ec: erase counter
 * @vid_hdr: the volume identifier header
 * @bitflips: if bit-flips were detected when this physical eraseblock was read
 *
 * This function adds information about a used physical eraseblock to the
 * 'used' tree of the corresponding volume. The function is rather complex
 * because it has to handle cases when this is not the first physical
 * eraseblock belonging to the same logical eraseblock, and the newer one has
 * to be picked, while the older one has to be dropped. This function returns
 * zero in case of success and a negative error code in case of failure.
 */
int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si,
		      int pnum, int ec, const struct ubi_vid_hdr *vid_hdr,
		      int bitflips)
{
	int err, vol_id, lnum;
	unsigned long long sqnum;
	struct ubi_scan_volume *sv;
	struct ubi_scan_leb *seb;
	struct rb_node **p, *parent = NULL;

	vol_id = be32_to_cpu(vid_hdr->vol_id);
	lnum = be32_to_cpu(vid_hdr->lnum);
	sqnum = be64_to_cpu(vid_hdr->sqnum);

	dbg_bld("PEB %d, LEB %d:%d, EC %d, sqnum %llu, bitflips %d",
		pnum, vol_id, lnum, ec, sqnum, bitflips);

	sv = add_volume(si, vol_id, pnum, vid_hdr);
	if (IS_ERR(sv))
		return PTR_ERR(sv);

	if (si->max_sqnum < sqnum)
		si->max_sqnum = sqnum;

	/*
	 * Walk the RB-tree of logical eraseblocks of volume @vol_id to look
	 * if this is the first instance of this logical eraseblock or not.
	 */
	p = &sv->root.rb_node;
	while (*p) {
		int cmp_res;

		parent = *p;
		seb = rb_entry(parent, struct ubi_scan_leb, u.rb);
		if (lnum != seb->lnum) {
			if (lnum < seb->lnum)
				p = &(*p)->rb_left;
			else
				p = &(*p)->rb_right;
			continue;
		}

		/*
		 * There is already a physical eraseblock describing the same
		 * logical eraseblock present.
		 */

		dbg_bld("this LEB already exists: PEB %d, sqnum %llu, "
			"EC %d", seb->pnum, seb->sqnum, seb->ec);

		/*
		 * Make sure that the logical eraseblocks have different
		 * sequence numbers. Otherwise the image is bad.
		 *
		 * However, if the sequence number is zero, we assume it must
		 * be an ancient UBI image from the era when UBI did not have
		 * sequence numbers. We still can attach these images, unless
		 * there is a need to distinguish between old and new
		 * eraseblocks, in which case we'll refuse the image in
		 * 'compare_lebs()'. In other words, we attach old clean
		 * images, but refuse attaching old images with duplicated
		 * logical eraseblocks because there was an unclean reboot.
		 */
		if (seb->sqnum == sqnum && sqnum != 0) {
			ubi_err("two LEBs with same sequence number %llu",
				sqnum);
			ubi_dbg_dump_seb(seb, 0);
			ubi_dbg_dump_vid_hdr(vid_hdr);
			return -EINVAL;
		}

		/*
		 * Now we have to drop the older one and preserve the newer
		 * one.
		 */
		cmp_res = compare_lebs(ubi, seb, pnum, vid_hdr);
		if (cmp_res < 0)
			return cmp_res;

		if (cmp_res & 1) {
			/*
			 * This logical eraseblock is newer than the one
			 * found earlier.
			 */
			err = validate_vid_hdr(vid_hdr, sv, pnum);
			if (err)
				return err;

			err = add_to_list(si, seb->pnum, seb->ec, cmp_res & 4,
					  &si->erase);
			if (err)
				return err;

			seb->ec = ec;
			seb->pnum = pnum;
			seb->scrub = ((cmp_res & 2) || bitflips);
			seb->copy_flag = vid_hdr->copy_flag;
			seb->sqnum = sqnum;

			if (sv->highest_lnum == lnum)
				sv->last_data_size =
					be32_to_cpu(vid_hdr->data_size);

			return 0;
		} else {
			/*
			 * This logical eraseblock is older than the one found
			 * previously.
			 */
			return add_to_list(si, pnum, ec, cmp_res & 4,
					   &si->erase);
		}
	}

	/*
	 * We've met this logical eraseblock for the first time, add it to the
	 * scanning information.
	 */

	err = validate_vid_hdr(vid_hdr, sv, pnum);
	if (err)
		return err;

	seb = kmem_cache_alloc(si->scan_leb_slab, GFP_KERNEL);
	if (!seb)
		return -ENOMEM;

	seb->ec = ec;
	seb->pnum = pnum;
	seb->lnum = lnum;
	seb->scrub = bitflips;
	seb->copy_flag = vid_hdr->copy_flag;
	seb->sqnum = sqnum;

	if (sv->highest_lnum <= lnum) {
		sv->highest_lnum = lnum;
		sv->last_data_size = be32_to_cpu(vid_hdr->data_size);
	}

	sv->leb_count += 1;
	rb_link_node(&seb->u.rb, parent, p);
	rb_insert_color(&seb->u.rb, &sv->root);
	return 0;
}
Exemplo n.º 2
0
/**
 * ubi_scan_add_used - add information about a physical eraseblock to the
 * scanning information.
 * @ubi: UBI device description object
 * @si: scanning information
 * @pnum: the physical eraseblock number
 * @ec: erase counter
 * @vid_hdr: the volume identifier header
 * @bitflips: if bit-flips were detected when this physical eraseblock was read
 *
 * This function adds information about a used physical eraseblock to the
 * 'used' tree of the corresponding volume. The function is rather complex
 * because it has to handle cases when this is not the first physical
 * eraseblock belonging to the same logical eraseblock, and the newer one has
 * to be picked, while the older one has to be dropped. This function returns
 * zero in case of success and a negative error code in case of failure.
 */
int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si,
                      int pnum, int ec, const struct ubi_vid_hdr *vid_hdr,
                      int bitflips)
{
    int err, vol_id, lnum;
    uint32_t leb_ver;
    unsigned long long sqnum;
    struct ubi_scan_volume *sv;
    struct ubi_scan_leb *seb;
    struct rb_node **p, *parent = NULL;

    vol_id = be32_to_cpu(vid_hdr->vol_id);
    lnum = be32_to_cpu(vid_hdr->lnum);
    sqnum = be64_to_cpu(vid_hdr->sqnum);
    leb_ver = be32_to_cpu(vid_hdr->leb_ver);

    dbg_bld("PEB %d, LEB %d:%d, EC %d, sqnum %llu, ver %u, bitflips %d",
            pnum, vol_id, lnum, ec, sqnum, leb_ver, bitflips);

    sv = add_volume(si, vol_id, pnum, vid_hdr);
    if (IS_ERR(sv) < 0)
        return PTR_ERR(sv);

    if (si->max_sqnum < sqnum)
        si->max_sqnum = sqnum;

    /*
     * Walk the RB-tree of logical eraseblocks of volume @vol_id to look
     * if this is the first instance of this logical eraseblock or not.
     */
    p = &sv->root.rb_node;
    while (*p) {
        int cmp_res;

        parent = *p;
        seb = rb_entry(parent, struct ubi_scan_leb, u.rb);
        if (lnum != seb->lnum) {
            if (lnum < seb->lnum)
                p = &(*p)->rb_left;
            else
                p = &(*p)->rb_right;
            continue;
        }

        /*
         * There is already a physical eraseblock describing the same
         * logical eraseblock present.
         */

        dbg_bld("this LEB already exists: PEB %d, sqnum %llu, "
                "LEB ver %u, EC %d", seb->pnum, seb->sqnum,
                seb->leb_ver, seb->ec);

        /*
         * Make sure that the logical eraseblocks have different
         * versions. Otherwise the image is bad.
         */
        if (seb->leb_ver == leb_ver && leb_ver != 0) {
            ubi_err("two LEBs with same version %u", leb_ver);
            ubi_dbg_dump_seb(seb, 0);
            ubi_dbg_dump_vid_hdr(vid_hdr);
            return -EINVAL;
        }

        /*
         * Make sure that the logical eraseblocks have different
         * sequence numbers. Otherwise the image is bad.
         *
         * FIXME: remove 'sqnum != 0' check when leb_ver is removed.
         */
        if (seb->sqnum == sqnum && sqnum != 0) {
            ubi_err("two LEBs with same sequence number %llu",
                    sqnum);
            ubi_dbg_dump_seb(seb, 0);
            ubi_dbg_dump_vid_hdr(vid_hdr);
            return -EINVAL;
        }

        /*
         * Now we have to drop the older one and preserve the newer
         * one.
         */
        cmp_res = compare_lebs(ubi, seb, pnum, vid_hdr);
        if (cmp_res < 0)
            return cmp_res;

        if (cmp_res & 1) {
            /*
             * This logical eraseblock is newer then the one
             * found earlier.
             */
            err = validate_vid_hdr(vid_hdr, sv, pnum);
            if (err)
                return err;

            if (cmp_res & 4)
                err = add_to_list(si, seb->pnum, seb->ec,
                                  &si->corr);
            else
                err = add_to_list(si, seb->pnum, seb->ec,
                                  &si->erase);
            if (err)
                return err;

            seb->ec = ec;
            seb->pnum = pnum;
            seb->scrub = ((cmp_res & 2) || bitflips);
            seb->sqnum = sqnum;
            seb->leb_ver = leb_ver;

            if (sv->highest_lnum == lnum)
                sv->last_data_size =
                    be32_to_cpu(vid_hdr->data_size);

            return 0;
        } else {
            /*
             * This logical eraseblock is older then the one found
             * previously.
             */
            if (cmp_res & 4)
                return add_to_list(si, pnum, ec, &si->corr);
            else
                return add_to_list(si, pnum, ec, &si->erase);
        }
    }

    /*
     * We've met this logical eraseblock for the first time, add it to the
     * scanning information.
     */

    err = validate_vid_hdr(vid_hdr, sv, pnum);
    if (err)
        return err;

    seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL);
    if (!seb)
        return -ENOMEM;

    seb->ec = ec;
    seb->pnum = pnum;
    seb->lnum = lnum;
    seb->sqnum = sqnum;
    seb->scrub = bitflips;
    seb->leb_ver = leb_ver;

    if (sv->highest_lnum <= lnum) {
        sv->highest_lnum = lnum;
        sv->last_data_size = be32_to_cpu(vid_hdr->data_size);
    }

    sv->leb_count += 1;
    rb_link_node(&seb->u.rb, parent, p);
    rb_insert_color(&seb->u.rb, &sv->root);
    return 0;
}