/** * 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; }
/** * 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; }