Merge branch 'linux-next' of git://git.infradead.org/ubi-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 21 Sep 2009 15:13:55 +0000 (08:13 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 21 Sep 2009 15:13:55 +0000 (08:13 -0700)
* 'linux-next' of git://git.infradead.org/ubi-2.6:
  UBI: improve NOR flash erasure quirk
  UBI: introduce flash dump helper
  UBI: eliminate possible undefined behaviour
  UBI: print a warning if too many PEBs are corrupted
  UBI: amend NOR flash pre-erase quirk
  UBI: print a message if ECH is corrupted and VIDH is ok

drivers/mtd/ubi/debug.c
drivers/mtd/ubi/debug.h
drivers/mtd/ubi/io.c
drivers/mtd/ubi/scan.c
drivers/mtd/ubi/scan.h
drivers/mtd/ubi/ubi.h

index 54b0186..4876977 100644 (file)
@@ -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 */
index a4da7a0..f30bcb3 100644 (file)
@@ -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
index 4cb6992..8aa51e7 100644 (file)
@@ -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;
 }
 
 /**
index b847745..e7161ad 100644 (file)
@@ -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;
@@ -935,6 +938,19 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi)
        if (si->is_empty)
                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.
index 1017cf1..bab3169 100644 (file)
@@ -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;
 };
 
index 6a5fe96..c290f51 100644 (file)
@@ -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.