summaryrefslogtreecommitdiffstats
path: root/drivers/mtd/ubi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/ubi')
-rw-r--r--drivers/mtd/ubi/debug.c32
-rw-r--r--drivers/mtd/ubi/debug.h2
-rw-r--r--drivers/mtd/ubi/io.c49
-rw-r--r--drivers/mtd/ubi/scan.c22
-rw-r--r--drivers/mtd/ubi/scan.h2
-rw-r--r--drivers/mtd/ubi/ubi.h3
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.