>From 822dfc1c4799743b0edad0848f7ef6ffc30294f1 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 22 Apr 2008 18:41:05 +0300 Subject: [PATCH] UBI: optimize scanning a bit When scanning the MTD device, we do 2 read requests - one for the EC header and one for the VID header. These are small 64-byte requests. In fact, MTD anyway reads whole NAND page and we might as well make only one read request. Some flashes have a kind of "burst" read which makes it faster to read 2 consequtive NAND pages at once than reading them separately. This optimization is targeted on such flashes. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/scan.c | 104 ++++++++++++++++++++++++++--------------------- drivers/mtd/ubi/scan.h | 11 +++++- 2 files changed, 67 insertions(+), 48 deletions(-) diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 96d410e..44dc214 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -51,10 +51,6 @@ static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si); #define paranoid_check_si(ubi, si) 0 #endif -/* Temporary variables used during scanning */ -static struct ubi_ec_hdr *ech; -static struct ubi_vid_hdr *vidh; - /** * add_to_list - add physical eraseblock to a list. * @si: scanning information @@ -754,7 +750,7 @@ struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi, static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum) { long long uninitialized_var(ec); - int err, bitflips = 0, vol_id, ec_corr = 0; + int err, bitflips = 0, vol_id, ec_corr = 0, read_err = 0; dbg_bld("scan PEB %d", pnum); @@ -771,16 +767,29 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum return 0; } - err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); + err = ubi_io_read(ubi, si->read_buf, pnum, 0, si->read_len); + if (err) { + if (err != UBI_IO_BITFLIPS && err != -EBADMSG) + return err; + + read_err = err; + } + + err = ubi_io_check_ec_hdr(ubi, pnum, si->ech, 0); if (err < 0) return err; else if (err == UBI_IO_BITFLIPS) bitflips = 1; - else if (err == UBI_IO_PEB_EMPTY) - return add_to_list(si, pnum, UBI_SCAN_UNKNOWN_EC, &si->erase); - else if (err == UBI_IO_BAD_EC_HDR) { + else if (err == UBI_IO_PEB_EMPTY && !read_err) { + if (!read_err) + return add_to_list(si, pnum, UBI_SCAN_UNKNOWN_EC, + &si->erase); + err = UBI_IO_BAD_EC_HDR; + } + + if (err == UBI_IO_BAD_EC_HDR) { /* - * We have to also look at the VID header, possibly it is not + * We also have to look at the VID header, possibly it is not * corrupted. Set %bitflips flag in order to make this PEB be * moved and EC be re-created. */ @@ -793,13 +802,13 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum if (!ec_corr) { /* Make sure UBI version is OK */ - if (ech->version != UBI_VERSION) { + if (si->ech->version != UBI_VERSION) { ubi_err("this UBI version is %d, image version is %d", - UBI_VERSION, (int)ech->version); + UBI_VERSION, (int)si->ech->version); return -EINVAL; } - ec = be64_to_cpu(ech->ec); + ec = be64_to_cpu(si->ech->ec); if (ec > UBI_MAX_ERASECOUNTER) { /* * Erase counter overflow. The EC headers have 64 bits @@ -810,14 +819,14 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum */ ubi_err("erase counter overflow, max is %d", UBI_MAX_ERASECOUNTER); - ubi_dbg_dump_ec_hdr(ech); + ubi_dbg_dump_ec_hdr(si->ech); return -EINVAL; } } /* OK, we've done with the EC header, let's look at the VID header */ - err = ubi_io_read_vid_hdr(ubi, pnum, vidh, 0); + err = ubi_io_check_vid_hdr(ubi, pnum, si->vidh, 0); if (err < 0) return err; else if (err == UBI_IO_BITFLIPS) @@ -837,12 +846,12 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum goto adjust_mean_ec; } - vol_id = be32_to_cpu(vidh->vol_id); + vol_id = be32_to_cpu(si->vidh->vol_id); if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOLUME_ID) { - int lnum = be32_to_cpu(vidh->lnum); + int lnum = be32_to_cpu(si->vidh->lnum); /* Unsupported internal volume */ - switch (vidh->compat) { + switch (si->vidh->compat) { case UBI_COMPAT_DELETE: ubi_msg("\"delete\" compatible internal volume %d:%d" " found, remove it", vol_id, lnum); @@ -875,7 +884,7 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum } /* Both UBI headers seem to be fine */ - err = ubi_scan_add_used(ubi, si, pnum, ec, vidh, bitflips); + err = ubi_scan_add_used(ubi, si, pnum, ec, si->vidh, bitflips); if (err) return err; @@ -918,14 +927,21 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi) si->volumes = RB_ROOT; si->is_empty = 1; - err = -ENOMEM; - ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); - if (!ech) + /* + * During scanning we read EC and VID headers at one go in to the + * @si->read_buf, and then check EC and VID header. This must be faster + * than doing 2 small read operations. + */ + si->read_len = ubi->vid_hdr_offset + UBI_VID_HDR_SIZE; + si->read_len = ALIGN(si->read_len, ubi->min_io_size); + si->read_buf = kmalloc(si->read_len, GFP_KERNEL); + if (!si->read_buf) { + err = -ENOMEM; goto out_si; + } - vidh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); - if (!vidh) - goto out_ech; + si->ech = si->read_buf; + si->vidh = si->read_buf + ubi->vid_hdr_offset; for (pnum = 0; pnum < ubi->peb_count; pnum++) { cond_resched(); @@ -933,7 +949,7 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi) dbg_msg("process PEB %d", pnum); err = process_eb(ubi, si, pnum); if (err < 0) - goto out_vidh; + goto out_si; } dbg_msg("scanning is finished"); @@ -974,18 +990,11 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi) if (err) { if (err > 0) err = -EINVAL; - goto out_vidh; + goto out_si; } - ubi_free_vid_hdr(ubi, vidh); - kfree(ech); - return si; -out_vidh: - ubi_free_vid_hdr(ubi, vidh); -out_ech: - kfree(ech); out_si: ubi_scan_destroy_si(si); return ERR_PTR(err); @@ -1073,6 +1082,7 @@ void ubi_scan_destroy_si(struct ubi_scan_info *si) } } + kfree(si->read_buf); kfree(si); } @@ -1221,7 +1231,7 @@ static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si) last_seb = seb; - err = ubi_io_read_vid_hdr(ubi, seb->pnum, vidh, 1); + err = ubi_io_read_vid_hdr(ubi, seb->pnum, si->vidh, 1); if (err && err != UBI_IO_BITFLIPS) { ubi_err("VID header is not OK (%d)", err); if (err > 0) @@ -1229,44 +1239,44 @@ static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si) return err; } - vol_type = vidh->vol_type == UBI_VID_DYNAMIC ? + vol_type = si->vidh->vol_type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; if (sv->vol_type != vol_type) { ubi_err("bad vol_type"); goto bad_vid_hdr; } - if (seb->sqnum != be64_to_cpu(vidh->sqnum)) { + if (seb->sqnum != be64_to_cpu(si->vidh->sqnum)) { ubi_err("bad sqnum %llu", seb->sqnum); goto bad_vid_hdr; } - if (sv->vol_id != be32_to_cpu(vidh->vol_id)) { + if (sv->vol_id != be32_to_cpu(si->vidh->vol_id)) { ubi_err("bad vol_id %d", sv->vol_id); goto bad_vid_hdr; } - if (sv->compat != vidh->compat) { - ubi_err("bad compat %d", vidh->compat); + if (sv->compat != si->vidh->compat) { + ubi_err("bad compat %d", si->vidh->compat); goto bad_vid_hdr; } - if (seb->lnum != be32_to_cpu(vidh->lnum)) { + if (seb->lnum != be32_to_cpu(si->vidh->lnum)) { ubi_err("bad lnum %d", seb->lnum); goto bad_vid_hdr; } - if (sv->used_ebs != be32_to_cpu(vidh->used_ebs)) { + if (sv->used_ebs != be32_to_cpu(si->vidh->used_ebs)) { ubi_err("bad used_ebs %d", sv->used_ebs); goto bad_vid_hdr; } - if (sv->data_pad != be32_to_cpu(vidh->data_pad)) { + if (sv->data_pad != be32_to_cpu(si->vidh->data_pad)) { ubi_err("bad data_pad %d", sv->data_pad); goto bad_vid_hdr; } - if (seb->leb_ver != be32_to_cpu(vidh->leb_ver)) { + if (seb->leb_ver != be32_to_cpu(si->vidh->leb_ver)) { ubi_err("bad leb_ver %u", seb->leb_ver); goto bad_vid_hdr; } @@ -1275,12 +1285,12 @@ static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si) if (!last_seb) continue; - if (sv->highest_lnum != be32_to_cpu(vidh->lnum)) { + if (sv->highest_lnum != be32_to_cpu(si->vidh->lnum)) { ubi_err("bad highest_lnum %d", sv->highest_lnum); goto bad_vid_hdr; } - if (sv->last_data_size != be32_to_cpu(vidh->data_size)) { + if (sv->last_data_size != be32_to_cpu(si->vidh->data_size)) { ubi_err("bad last_data_size %d", sv->last_data_size); goto bad_vid_hdr; } @@ -1346,7 +1356,7 @@ bad_sv: bad_vid_hdr: ubi_err("bad scanning information about volume %d", sv->vol_id); ubi_dbg_dump_sv(sv); - ubi_dbg_dump_vid_hdr(vidh); + ubi_dbg_dump_vid_hdr(si->vidh); out: ubi_dbg_dump_stack(); diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h index 966b9b6..57cf793 100644 --- a/drivers/mtd/ubi/scan.h +++ b/drivers/mtd/ubi/scan.h @@ -93,7 +93,7 @@ struct ubi_scan_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., * @bad_peb_count: count of bad physical eraseblocks - * those belonging to "preserve"-compatible internal volumes) + * those belonging to "preserve"-compatible internal volumes) * @vols_found: number of volumes found during scanning * @highest_vol_id: highest volume ID * @alien_peb_count: count of physical eraseblocks in the @alien list @@ -104,6 +104,11 @@ struct ubi_scan_volume { * @mean_ec: mean erase counter value * @ec_sum: a temporary variable used when calculating @mean_ec * @ec_count: a temporary variable used when calculating @mean_ec + * @read_buf: temporary buffer where the EC and VID headers are read during + * scanning + * @ech: points to the beginning of the EC header in @buf + * @vidh: points to the beginning of the VID header in @buf + * @read_len: how much is read from each eraseblock during scanning * * This data structure contains the result of scanning and may be used by other * UBI units to build final UBI data structures, further error-recovery and so @@ -126,6 +131,10 @@ struct ubi_scan_info { int mean_ec; uint64_t ec_sum; int ec_count; + void *read_buf; + struct ubi_ec_hdr *ech; + struct ubi_vid_hdr *vidh; + int read_len; }; struct ubi_device; -- 1.5.4.1