diff options
-rw-r--r-- | drivers/mtd/ubi/debug.c | 32 | ||||
-rw-r--r-- | drivers/mtd/ubi/debug.h | 2 | ||||
-rw-r--r-- | drivers/mtd/ubi/io.c | 49 | ||||
-rw-r--r-- | drivers/mtd/ubi/scan.c | 22 | ||||
-rw-r--r-- | drivers/mtd/ubi/scan.h | 2 | ||||
-rw-r--r-- | drivers/mtd/ubi/ubi.h | 3 |
6 files changed, 90 insertions, 20 deletions
diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c index 54b0186915f..4876977e52c 100644 --- a/drivers/mtd/ubi/debug.c +++ b/drivers/mtd/ubi/debug.c @@ -196,4 +196,36 @@ void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req) printk(KERN_DEBUG "\t1st 16 characters of name: %s\n", nm); } +/** + * ubi_dbg_dump_flash - dump a region of flash. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to dump + * @offset: the starting offset within the physical eraseblock to dump + * @len: the length of the region to dump + */ +void ubi_dbg_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len) +{ + int err; + size_t read; + void *buf; + loff_t addr = (loff_t)pnum * ubi->peb_size + offset; + + buf = vmalloc(len); + if (!buf) + return; + err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf); + if (err && err != -EUCLEAN) { + ubi_err("error %d while reading %d bytes from PEB %d:%d, " + "read %zd bytes", err, len, pnum, offset, read); + goto out; + } + + dbg_msg("dumping %d bytes of data from PEB %d, offset %d", + len, pnum, offset); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, buf, len, 1); +out: + vfree(buf); + return; +} + #endif /* CONFIG_MTD_UBI_DEBUG */ diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index a4da7a09b94..f30bcb372c0 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h @@ -55,6 +55,7 @@ void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx); void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv); void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type); void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req); +void ubi_dbg_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len); #ifdef CONFIG_MTD_UBI_DEBUG_MSG /* General debugging messages */ @@ -167,6 +168,7 @@ static inline int ubi_dbg_is_erase_failure(void) #define ubi_dbg_dump_sv(sv) ({}) #define ubi_dbg_dump_seb(seb, type) ({}) #define ubi_dbg_dump_mkvol_req(req) ({}) +#define ubi_dbg_dump_flash(ubi, pnum, offset, len) ({}) #define UBI_IO_DEBUG 0 #define DBG_DISABLE_BGT 0 diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 4cb69925d8d..8aa51e7a6a7 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -269,6 +269,7 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, ubi_err("error %d while writing %d bytes to PEB %d:%d, written " "%zd bytes", err, len, pnum, offset, written); ubi_dbg_dump_stack(); + ubi_dbg_dump_flash(ubi, pnum, offset, len); } else ubi_assert(written == len); @@ -475,30 +476,46 @@ out: */ static int nor_erase_prepare(struct ubi_device *ubi, int pnum) { - int err; + int err, err1; size_t written; loff_t addr; uint32_t data = 0; + struct ubi_vid_hdr vid_hdr; - addr = (loff_t)pnum * ubi->peb_size; + addr = (loff_t)pnum * ubi->peb_size + ubi->vid_hdr_aloffset; err = ubi->mtd->write(ubi->mtd, addr, 4, &written, (void *)&data); - if (err) { - ubi_err("error %d while writing 4 bytes to PEB %d:%d, written " - "%zd bytes", err, pnum, 0, written); - ubi_dbg_dump_stack(); - return err; + if (!err) { + addr -= ubi->vid_hdr_aloffset; + err = ubi->mtd->write(ubi->mtd, addr, 4, &written, + (void *)&data); + if (!err) + return 0; } - addr += ubi->vid_hdr_aloffset; - err = ubi->mtd->write(ubi->mtd, addr, 4, &written, (void *)&data); - if (err) { - ubi_err("error %d while writing 4 bytes to PEB %d:%d, written " - "%zd bytes", err, pnum, ubi->vid_hdr_aloffset, written); - ubi_dbg_dump_stack(); - return err; - } + /* + * We failed to write to the media. This was observed with Spansion + * S29GL512N NOR flash. Most probably the eraseblock erasure was + * interrupted at a very inappropriate moment, so it became unwritable. + * In this case we probably anyway have garbage in this PEB. + */ + err1 = ubi_io_read_vid_hdr(ubi, pnum, &vid_hdr, 0); + if (err1 == UBI_IO_BAD_VID_HDR) + /* + * The VID header is corrupted, so we can safely erase this + * PEB and not afraid that it will be treated as a valid PEB in + * case of an unclean reboot. + */ + return 0; - return 0; + /* + * The PEB contains a valid VID header, but we cannot invalidate it. + * Supposedly the flash media or the driver is screwed up, so return an + * error. + */ + ubi_err("cannot invalidate PEB %d, write returned %d read returned %d", + pnum, err, err1); + ubi_dbg_dump_flash(ubi, pnum, 0, ubi->peb_size); + return -EIO; } /** diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index b847745394b..e7161adc419 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -75,9 +75,10 @@ static int add_to_list(struct ubi_scan_info *si, int pnum, int ec, dbg_bld("add to free: PEB %d, EC %d", pnum, ec); else if (list == &si->erase) dbg_bld("add to erase: PEB %d, EC %d", pnum, ec); - else if (list == &si->corr) + else if (list == &si->corr) { dbg_bld("add to corrupted: PEB %d, EC %d", pnum, ec); - else if (list == &si->alien) + si->corr_count += 1; + } else if (list == &si->alien) dbg_bld("add to alien: PEB %d, EC %d", pnum, ec); else BUG(); @@ -864,7 +865,9 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, } } - /* Both UBI headers seem to be fine */ + if (ec_corr) + ubi_warn("valid VID header but corrupted EC header at PEB %d", + pnum); err = ubi_scan_add_used(ubi, si, pnum, ec, vidh, bitflips); if (err) return err; @@ -936,6 +939,19 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi) ubi_msg("empty MTD device detected"); /* + * Few corrupted PEBs are not a problem and may be just a result of + * unclean reboots. However, many of them may indicate some problems + * with the flash HW or driver. Print a warning in this case. + */ + if (si->corr_count >= 8 || si->corr_count >= ubi->peb_count / 4) { + ubi_warn("%d PEBs are corrupted", si->corr_count); + printk(KERN_WARNING "corrupted PEBs are:"); + list_for_each_entry(seb, &si->corr, u.list) + printk(KERN_CONT " %d", seb->pnum); + printk(KERN_CONT "\n"); + } + + /* * In case of unknown erase counter we use the mean erase counter * value. */ diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h index 1017cf12def..bab31695dac 100644 --- a/drivers/mtd/ubi/scan.h +++ b/drivers/mtd/ubi/scan.h @@ -102,6 +102,7 @@ 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 + * @corr_count: count of corrupted PEBs * @image_seq_set: indicates @ubi->image_seq is known * * This data structure contains the result of scanning and may be used by other @@ -125,6 +126,7 @@ struct ubi_scan_info { int mean_ec; uint64_t ec_sum; int ec_count; + int corr_count; int image_seq_set; }; diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 6a5fe963378..c290f51dd17 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -579,7 +579,8 @@ void ubi_do_get_volume_info(struct ubi_device *ubi, struct ubi_volume *vol, for (rb = rb_first(root), \ pos = (rb ? container_of(rb, typeof(*pos), member) : NULL); \ rb; \ - rb = rb_next(rb), pos = container_of(rb, typeof(*pos), member)) + rb = rb_next(rb), \ + pos = (rb ? container_of(rb, typeof(*pos), member) : NULL)) /** * ubi_zalloc_vid_hdr - allocate a volume identifier header object. |