mtd: driver _read() returns max_bitflips; mtd_read() returns -EUCLEAN
authorMike Dunn <mikedunn@newsguy.com>
Wed, 25 Apr 2012 19:06:11 +0000 (12:06 -0700)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Mon, 14 May 2012 04:14:23 +0000 (23:14 -0500)
The drivers' _read() method, absent an error, returns a non-negative integer
indicating the maximum number of bit errors that were corrected in any one
region comprising an ecc step.  MTD returns -EUCLEAN if this is >=
bitflip_threshold, 0 otherwise.  If bitflip_threshold is zero, the comparison is
not made since these devices lack ECC and always return zero in the non-error
case (thanks Brian)¹.  Note that this is a subtle change to the driver
interface.

This and the preceding patches in this set were tested with ubi on top of the
nandsim and docg4 devices, running the ubi test io_basic from mtd-utils.

¹ http://lists.infradead.org/pipermail/linux-mtd/2012-March/040468.html

Signed-off-by: Mike Dunn <mikedunn@newsguy.com>
Acked-by: Robert Jarzmik <robert.jarzmik@free.fr>
Acked-by: Brian Norris <computersforpeace@gmail.com>
Ivan Djelic <ivan.djelic@parrot.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Documentation/ABI/testing/sysfs-class-mtd
drivers/mtd/devices/docg3.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdpart.c
drivers/mtd/nand/alauda.c
drivers/mtd/nand/nand_base.c
drivers/mtd/onenand/onenand_base.c
include/linux/mtd/nand.h

index 78835080856a9dc17df3b40857bb2dfa08d54277..db1ad7e34fc3a00e14be0c1045564fe6c98bd37e 100644 (file)
@@ -167,7 +167,10 @@ Description:
                block degradation, but high enough to avoid the consequences of
                a persistent return value of -EUCLEAN on devices where sticky
                bitflips occur.  Note that if bitflip_threshold exceeds
-               ecc_strength, -EUCLEAN is never returned by the read functions.
+               ecc_strength, -EUCLEAN is never returned by mtd_read().
+               Conversely, if bitflip_threshold is zero, -EUCLEAN is always
+               returned, absent a hard error.
 
                This is generally applicable only to NAND flash devices with ECC
-               capability.  It is ignored on devices lacking ECC capability.
+               capability.  It is ignored on devices lacking ECC capability;
+               i.e., devices for which ecc_strength is zero.
index 8272c02668d6ebccf54ad75b71da07158fd617be..65d22a0439c63b455d9baaac3136ba57e57ad7e7 100644 (file)
@@ -850,6 +850,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
        u8 *buf = ops->datbuf;
        size_t len, ooblen, nbdata, nboob;
        u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1;
+       int max_bitflips = 0;
 
        if (buf)
                len = ops->len;
@@ -876,7 +877,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
        ret = 0;
        skip = from % DOC_LAYOUT_PAGE_SIZE;
        mutex_lock(&docg3->cascade->lock);
-       while (!ret && (len > 0 || ooblen > 0)) {
+       while (ret >= 0 && (len > 0 || ooblen > 0)) {
                calc_block_sector(from - skip, &block0, &block1, &page, &ofs,
                        docg3->reliable);
                nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip);
@@ -936,7 +937,8 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
                        }
                        if (ret > 0) {
                                mtd->ecc_stats.corrected += ret;
-                               ret = -EUCLEAN;
+                               max_bitflips = max(max_bitflips, ret);
+                               ret = max_bitflips;
                        }
                }
 
index 6a7cba1e24e61fbff8965744923005e18c835319..575730744fdb3ce83dc8e4a78cf65cefb3413fdc 100644 (file)
@@ -800,12 +800,24 @@ EXPORT_SYMBOL_GPL(mtd_get_unmapped_area);
 int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
             u_char *buf)
 {
+       int ret_code;
        *retlen = 0;
        if (from < 0 || from > mtd->size || len > mtd->size - from)
                return -EINVAL;
        if (!len)
                return 0;
-       return mtd->_read(mtd, from, len, retlen, buf);
+
+       /*
+        * In the absence of an error, drivers return a non-negative integer
+        * representing the maximum number of bitflips that were corrected on
+        * any one ecc region (if applicable; zero otherwise).
+        */
+       ret_code = mtd->_read(mtd, from, len, retlen, buf);
+       if (unlikely(ret_code < 0))
+               return ret_code;
+       if (mtd->ecc_strength == 0)
+               return 0;       /* device lacks ecc */
+       return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0;
 }
 EXPORT_SYMBOL_GPL(mtd_read);
 
index ec75d44e0253c3885df7aa90764ead18e027dca9..d518e4db8a0bf8665156fd2383ad40fcde0fe146 100644 (file)
@@ -67,12 +67,12 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
        stats = part->master->ecc_stats;
        res = part->master->_read(part->master, from + part->offset, len,
                                  retlen, buf);
-       if (unlikely(res)) {
-               if (mtd_is_bitflip(res))
-                       mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected;
-               if (mtd_is_eccerr(res))
-                       mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed;
-       }
+       if (unlikely(mtd_is_eccerr(res)))
+               mtd->ecc_stats.failed +=
+                       part->master->ecc_stats.failed - stats.failed;
+       else
+               mtd->ecc_stats.corrected +=
+                       part->master->ecc_stats.corrected - stats.corrected;
        return res;
 }
 
index 4f20e1d8bef10b43546bd64d51933c0ed010093d..60a0dfdb08087a55311ee7f2e53522ecde7baa17 100644 (file)
@@ -414,7 +414,7 @@ static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len,
        }
        err = 0;
        if (corrected)
-               err = -EUCLEAN;
+               err = 1;        /* return max_bitflips per ecc step */
        if (uncorrected)
                err = -EBADMSG;
 out:
@@ -446,7 +446,7 @@ static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len,
        }
        err = 0;
        if (corrected)
-               err = -EUCLEAN;
+               err = 1;        /* return max_bitflips per ecc step */
        if (uncorrected)
                err = -EBADMSG;
        return err;
index 9f5d339a361090b311f7271417417f8b70f02567..640f1f8159e8df3defad649b335ed25f296a2233 100644 (file)
@@ -1486,6 +1486,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
                mtd->oobavail : mtd->oobsize;
 
        uint8_t *bufpoi, *oob, *buf;
+       unsigned int max_bitflips = 0;
 
        stats = mtd->ecc_stats;
 
@@ -1513,7 +1514,10 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
                                sndcmd = 0;
                        }
 
-                       /* Now read the page into the buffer */
+                       /*
+                        * Now read the page into the buffer.  Absent an error,
+                        * the read methods return max bitflips per ecc step.
+                        */
                        if (unlikely(ops->mode == MTD_OPS_RAW))
                                ret = chip->ecc.read_page_raw(mtd, chip,
                                                              bufpoi, page);
@@ -1530,15 +1534,19 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
                                break;
                        }
 
+                       max_bitflips = max_t(unsigned int, max_bitflips, ret);
+
                        /* Transfer not aligned data */
                        if (!aligned) {
                                if (!NAND_SUBPAGE_READ(chip) && !oob &&
                                    !(mtd->ecc_stats.failed - stats.failed) &&
-                                   (ops->mode != MTD_OPS_RAW))
+                                   (ops->mode != MTD_OPS_RAW)) {
                                        chip->pagebuf = realpage;
-                               else
+                                       chip->pagebuf_bitflips = ret;
+                               } else {
                                        /* Invalidate page cache */
                                        chip->pagebuf = -1;
+                               }
                                memcpy(buf, chip->buffers->databuf + col, bytes);
                        }
 
@@ -1571,6 +1579,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
                } else {
                        memcpy(buf, chip->buffers->databuf + col, bytes);
                        buf += bytes;
+                       max_bitflips = max_t(unsigned int, max_bitflips,
+                                            chip->pagebuf_bitflips);
                }
 
                readlen -= bytes;
@@ -1609,7 +1619,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
        if (mtd->ecc_stats.failed - stats.failed)
                return -EBADMSG;
 
-       return  mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+       return max_bitflips;
 }
 
 /**
index b3ce12ef359e83777280aff2b6f9fa9d58cc4dc9..7153e0d27101e3eed06ac17addcdbd4734c7ccc3 100644 (file)
@@ -1201,7 +1201,8 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
        if (mtd->ecc_stats.failed - stats.failed)
                return -EBADMSG;
 
-       return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+       /* return max bitflips per ecc step; ONENANDs correct 1 bit only */
+       return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;
 }
 
 /**
@@ -1333,7 +1334,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
        if (mtd->ecc_stats.failed - stats.failed)
                return -EBADMSG;
 
-       return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+       /* return max bitflips per ecc step; ONENANDs correct 1 bit only */
+       return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;
 }
 
 /**
index 1482340d3d9f5e0a6ba5d61a020b112320618362..2829e8be3a620771ff14c15e635d7fbcfd4fe5d6 100644 (file)
@@ -459,6 +459,8 @@ struct nand_buffers {
  * @pagemask:          [INTERN] page number mask = number of (pages / chip) - 1
  * @pagebuf:           [INTERN] holds the pagenumber which is currently in
  *                     data_buf.
+ * @pagebuf_bitflips:  [INTERN] holds the bitflip count for the page which is
+ *                     currently in data_buf.
  * @subpagesize:       [INTERN] holds the subpagesize
  * @onfi_version:      [INTERN] holds the chip ONFI version (BCD encoded),
  *                     non 0 if ONFI supported.
@@ -519,6 +521,7 @@ struct nand_chip {
        uint64_t chipsize;
        int pagemask;
        int pagebuf;
+       unsigned int pagebuf_bitflips;
        int subpagesize;
        uint8_t cellinfo;
        int badblockpos;