mtd: bitflip_threshold added to mtd_info and sysfs
authorMike Dunn <mikedunn@newsguy.com>
Wed, 25 Apr 2012 19:06:08 +0000 (12:06 -0700)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Mon, 14 May 2012 04:11:39 +0000 (23:11 -0500)
An element 'bitflip_threshold' is added to struct mtd_info, and also exposed as
a read/write variable in sysfs.  This will be used to determine whether or not
mtd_read() returns -EUCLEAN or 0 (absent a hard error).  If the driver leaves it
as zero, mtd will set it to a default value of ecc_strength.

This v2 adds the line that propagates bitflip_threshold from the master to the
partitions - thanks Ivan¹.

¹ http://lists.infradead.org/pipermail/linux-mtd/2012-April/040900.html

Signed-off-by: Mike Dunn <mikedunn@newsguy.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/mtdcore.c
drivers/mtd/mtdpart.c
include/linux/mtd/mtd.h

index 43d18180b46ed54e32765af0fbb85a1e0819432e..78835080856a9dc17df3b40857bb2dfa08d54277 100644 (file)
@@ -135,3 +135,39 @@ Description:
                have multiple ecc steps within each writesize region.
 
                In the case of devices lacking any ECC capability, it is 0.
+
+What:          /sys/class/mtd/mtdX/bitflip_threshold
+Date:          April 2012
+KernelVersion: 3.4
+Contact:       linux-mtd@lists.infradead.org
+Description:
+               This allows the user to examine and adjust the criteria by which
+               mtd returns -EUCLEAN from mtd_read().  If the maximum number of
+               bit errors that were corrected on any single region comprising
+               an ecc step (as reported by the driver) equals or exceeds this
+               value, -EUCLEAN is returned.  Otherwise, absent an error, 0 is
+               returned.  Higher layers (e.g., UBI) use this return code as an
+               indication that an erase block may be degrading and should be
+               scrutinized as a candidate for being marked as bad.
+
+               The initial value may be specified by the flash device driver.
+               If not, then the default value is ecc_strength.
+
+               The introduction of this feature brings a subtle change to the
+               meaning of the -EUCLEAN return code.  Previously, it was
+               interpreted to mean simply "one or more bit errors were
+               corrected".  Its new interpretation can be phrased as "a
+               dangerously high number of bit errors were corrected on one or
+               more regions comprising an ecc step".  The precise definition of
+               "dangerously high" can be adjusted by the user with
+               bitflip_threshold.  Users are discouraged from doing this,
+               however, unless they know what they are doing and have intimate
+               knowledge of the properties of their device.  Broadly speaking,
+               bitflip_threshold should be low enough to detect genuine erase
+               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.
+
+               This is generally applicable only to NAND flash devices with ECC
+               capability.  It is ignored on devices lacking ECC capability.
index 090e849d3dcdbb7792a767116971812e3d21624e..6a7cba1e24e61fbff8965744923005e18c835319 100644 (file)
@@ -259,6 +259,34 @@ static ssize_t mtd_ecc_strength_show(struct device *dev,
 }
 static DEVICE_ATTR(ecc_strength, S_IRUGO, mtd_ecc_strength_show, NULL);
 
+static ssize_t mtd_bitflip_threshold_show(struct device *dev,
+                                         struct device_attribute *attr,
+                                         char *buf)
+{
+       struct mtd_info *mtd = dev_get_drvdata(dev);
+
+       return snprintf(buf, PAGE_SIZE, "%u\n", mtd->bitflip_threshold);
+}
+
+static ssize_t mtd_bitflip_threshold_store(struct device *dev,
+                                          struct device_attribute *attr,
+                                          const char *buf, size_t count)
+{
+       struct mtd_info *mtd = dev_get_drvdata(dev);
+       unsigned int bitflip_threshold;
+       int retval;
+
+       retval = kstrtouint(buf, 0, &bitflip_threshold);
+       if (retval)
+               return retval;
+
+       mtd->bitflip_threshold = bitflip_threshold;
+       return count;
+}
+static DEVICE_ATTR(bitflip_threshold, S_IRUGO | S_IWUSR,
+                  mtd_bitflip_threshold_show,
+                  mtd_bitflip_threshold_store);
+
 static struct attribute *mtd_attrs[] = {
        &dev_attr_type.attr,
        &dev_attr_flags.attr,
@@ -270,6 +298,7 @@ static struct attribute *mtd_attrs[] = {
        &dev_attr_numeraseregions.attr,
        &dev_attr_name.attr,
        &dev_attr_ecc_strength.attr,
+       &dev_attr_bitflip_threshold.attr,
        NULL,
 };
 
@@ -332,6 +361,10 @@ int add_mtd_device(struct mtd_info *mtd)
        mtd->index = i;
        mtd->usecount = 0;
 
+       /* default value if not set by driver */
+       if (mtd->bitflip_threshold == 0)
+               mtd->bitflip_threshold = mtd->ecc_strength;
+
        if (is_power_of_2(mtd->erasesize))
                mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
        else
index 9651c06de0a9298f4db58265524ecaebe2c2b9b4..ec75d44e0253c3885df7aa90764ead18e027dca9 100644 (file)
@@ -517,6 +517,8 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
 
        slave->mtd.ecclayout = master->ecclayout;
        slave->mtd.ecc_strength = master->ecc_strength;
+       slave->mtd.bitflip_threshold = master->bitflip_threshold;
+
        if (master->_block_isbad) {
                uint64_t offs = 0;
 
index cd0119d19cd9bce1d6ae43962ed0a0b1e1f3fd41..63dadc0dfb629a74f6e578a16255ffc4cdb4d56a 100644 (file)
@@ -157,6 +157,15 @@ struct mtd_info {
        unsigned int erasesize_mask;
        unsigned int writesize_mask;
 
+       /*
+        * read ops return -EUCLEAN if max number of bitflips corrected on any
+        * one region comprising an ecc step equals or exceeds this value.
+        * Settable by driver, else defaults to ecc_strength.  User can override
+        * in sysfs.  N.B. The meaning of the -EUCLEAN return code has changed;
+        * see Documentation/ABI/testing/sysfs-class-mtd for more detail.
+        */
+       unsigned int bitflip_threshold;
+
        // Kernel-only stuff starts here.
        const char *name;
        int index;