mtd: create an mtd_ooblayout_ops struct to ease ECC layout definition
authorBoris Brezillon <boris.brezillon@free-electrons.com>
Wed, 3 Feb 2016 18:01:31 +0000 (19:01 +0100)
committerBoris Brezillon <boris.brezillon@free-electrons.com>
Tue, 19 Apr 2016 20:05:55 +0000 (22:05 +0200)
ECC layout definitions are currently exposed using the nand_ecclayout
struct which embeds oobfree and eccpos arrays with predefined size.
This approach was acceptable when NAND chips were providing relatively
small OOB regions, but MLC and TLC now provide OOB regions of several
hundreds of bytes, which implies a non negligible overhead for everybody
even those who only need to support legacy NANDs.

Create an mtd_ooblayout_ops interface providing the same functionality
(expose the ECC and oobfree layout) without the need for this huge
structure.

The mtd->ecclayout is now deprecated and should be replaced by the
equivalent mtd_ooblayout_ops. In the meantime we provide a wrapper around
the ->ecclayout field to ease migration to this new model.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
drivers/mtd/mtdchar.c
drivers/mtd/mtdconcat.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdpart.c
include/linux/mtd/mtd.h

index cd64ab76dd7bb03576ae794b1c0bc2553ada3efc..3fad2c7425b087db5ae32bd112bfb450b6281a07 100644 (file)
@@ -888,7 +888,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
        {
                struct nand_oobinfo oi;
 
-               if (!mtd->ecclayout)
+               if (!mtd->ooblayout)
                        return -EOPNOTSUPP;
 
                ret = get_oobinfo(mtd, &oi);
@@ -982,7 +982,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
        {
                struct nand_ecclayout_user *usrlay;
 
-               if (!mtd->ecclayout)
+               if (!mtd->ooblayout)
                        return -EOPNOTSUPP;
 
                usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL);
index 481565e5fbaaa87b25924e7ee57e2c9ce1563ca8..d573606b91c2a57a4ff07e040b79fd86a7762c5b 100644 (file)
@@ -777,7 +777,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],       /* subdevices to c
 
        }
 
-       mtd_set_ecclayout(&concat->mtd, subdev[0]->ecclayout);
+       mtd_set_ooblayout(&concat->mtd, subdev[0]->ooblayout);
 
        concat->num_subdev = num_devs;
        concat->mtd.name = name;
index 0290c41e44fc432d5b664b5d89942d30322b13b0..134ed2f7b919959a9676114c65b8aa1f72951b4f 100644 (file)
@@ -1035,49 +1035,15 @@ EXPORT_SYMBOL_GPL(mtd_write_oob);
 int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
                      struct mtd_oob_region *oobecc)
 {
-       int eccbyte = 0, cursection = 0, length = 0, eccpos = 0;
-
        memset(oobecc, 0, sizeof(*oobecc));
 
        if (!mtd || section < 0)
                return -EINVAL;
 
-       if (!mtd->ecclayout)
+       if (!mtd->ooblayout || !mtd->ooblayout->ecc)
                return -ENOTSUPP;
 
-       /*
-        * This logic allows us to reuse the ->ecclayout information and
-        * expose them as ECC regions (as done for the OOB free regions).
-        *
-        * TODO: this should be dropped as soon as we get rid of the
-        * ->ecclayout field.
-        */
-       for (eccbyte = 0; eccbyte < mtd->ecclayout->eccbytes; eccbyte++) {
-               eccpos = mtd->ecclayout->eccpos[eccbyte];
-
-               if (eccbyte < mtd->ecclayout->eccbytes - 1) {
-                       int neccpos = mtd->ecclayout->eccpos[eccbyte + 1];
-
-                       if (eccpos + 1 == neccpos) {
-                               length++;
-                               continue;
-                       }
-               }
-
-               if (section == cursection)
-                       break;
-
-               length = 0;
-               cursection++;
-       }
-
-       if (cursection != section || eccbyte >= mtd->ecclayout->eccbytes)
-               return -ERANGE;
-
-       oobecc->length = length + 1;
-       oobecc->offset = eccpos - length;
-
-       return 0;
+       return mtd->ooblayout->ecc(mtd, section, oobecc);
 }
 EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc);
 
@@ -1106,16 +1072,10 @@ int mtd_ooblayout_free(struct mtd_info *mtd, int section,
        if (!mtd || section < 0)
                return -EINVAL;
 
-       if (!mtd->ecclayout)
+       if (!mtd->ooblayout || !mtd->ooblayout->free)
                return -ENOTSUPP;
 
-       if (section >= MTD_MAX_OOBFREE_ENTRIES_LARGE)
-               return -ERANGE;
-
-       oobfree->offset = mtd->ecclayout->oobfree[section].offset;
-       oobfree->length = mtd->ecclayout->oobfree[section].length;
-
-       return 0;
+       return mtd->ooblayout->free(mtd, section, oobfree);
 }
 EXPORT_SYMBOL_GPL(mtd_ooblayout_free);
 
@@ -1416,6 +1376,123 @@ int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd)
 }
 EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes);
 
+/**
+ * mtd_ecclayout_ecc - Default ooblayout_ecc iterator implementation
+ * @mtd: MTD device structure
+ * @section: ECC section. Depending on the layout you may have all the ECC
+ *          bytes stored in a single contiguous section, or one section
+ *          per ECC chunk (and sometime several sections for a single ECC
+ *          ECC chunk)
+ * @oobecc: OOB region struct filled with the appropriate ECC position
+ *         information
+ *
+ * This function is just a wrapper around the mtd->ecclayout field and is
+ * here to ease the transition to the mtd_ooblayout_ops approach.
+ * All it does is convert the layout->eccpos information into proper oob
+ * region definitions.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int mtd_ecclayout_ecc(struct mtd_info *mtd, int section,
+                            struct mtd_oob_region *oobecc)
+{
+       int eccbyte = 0, cursection = 0, length = 0, eccpos = 0;
+
+       if (!mtd->ecclayout)
+               return -ENOTSUPP;
+
+       /*
+        * This logic allows us to reuse the ->ecclayout information and
+        * expose them as ECC regions (as done for the OOB free regions).
+        *
+        * TODO: this should be dropped as soon as we get rid of the
+        * ->ecclayout field.
+        */
+       for (eccbyte = 0; eccbyte < mtd->ecclayout->eccbytes; eccbyte++) {
+               eccpos = mtd->ecclayout->eccpos[eccbyte];
+
+               if (eccbyte < mtd->ecclayout->eccbytes - 1) {
+                       int neccpos = mtd->ecclayout->eccpos[eccbyte + 1];
+
+                       if (eccpos + 1 == neccpos) {
+                               length++;
+                               continue;
+                       }
+               }
+
+               if (section == cursection)
+                       break;
+
+               length = 0;
+               cursection++;
+       }
+
+       if (cursection != section || eccbyte >= mtd->ecclayout->eccbytes)
+               return -ERANGE;
+
+       oobecc->length = length + 1;
+       oobecc->offset = eccpos - length;
+
+       return 0;
+}
+
+/**
+ * mtd_ecclayout_ecc - Default ooblayout_free iterator implementation
+ * @mtd: MTD device structure
+ * @section: Free section. Depending on the layout you may have all the free
+ *          bytes stored in a single contiguous section, or one section
+ *          per ECC chunk (and sometime several sections for a single ECC
+ *          ECC chunk)
+ * @oobfree: OOB region struct filled with the appropriate free position
+ *          information
+ *
+ * This function is just a wrapper around the mtd->ecclayout field and is
+ * here to ease the transition to the mtd_ooblayout_ops approach.
+ * All it does is convert the layout->oobfree information into proper oob
+ * region definitions.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int mtd_ecclayout_free(struct mtd_info *mtd, int section,
+                             struct mtd_oob_region *oobfree)
+{
+       struct nand_ecclayout *layout = mtd->ecclayout;
+
+       if (!layout)
+               return -ENOTSUPP;
+
+       if (section >= MTD_MAX_OOBFREE_ENTRIES_LARGE ||
+           !layout->oobfree[section].length)
+               return -ERANGE;
+
+       oobfree->offset = layout->oobfree[section].offset;
+       oobfree->length = layout->oobfree[section].length;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops mtd_ecclayout_wrapper_ops = {
+       .ecc = mtd_ecclayout_ecc,
+       .free = mtd_ecclayout_free,
+};
+
+/**
+ * mtd_set_ecclayout - Attach an ecclayout to an MTD device
+ * @mtd: MTD device structure
+ * @ecclayout: The ecclayout to attach to the device
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+void mtd_set_ecclayout(struct mtd_info *mtd, struct nand_ecclayout *ecclayout)
+{
+       if (!mtd || !ecclayout)
+               return;
+
+       mtd->ecclayout = ecclayout;
+       mtd_set_ooblayout(mtd, &mtd_ecclayout_wrapper_ops);
+}
+EXPORT_SYMBOL_GPL(mtd_set_ecclayout);
+
 /*
  * Method to access the protection register area, present in some flash
  * devices. The user data is one time programmable but the factory data is read
index f53d9d72b23ae683aeabf562b28e2e63dd00e1a2..1f13e32556f869634146c102ef1f2b0aea8fe4f1 100644 (file)
@@ -317,6 +317,27 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
        return res;
 }
 
+static int part_ooblayout_ecc(struct mtd_info *mtd, int section,
+                             struct mtd_oob_region *oobregion)
+{
+       struct mtd_part *part = mtd_to_part(mtd);
+
+       return mtd_ooblayout_ecc(part->master, section, oobregion);
+}
+
+static int part_ooblayout_free(struct mtd_info *mtd, int section,
+                              struct mtd_oob_region *oobregion)
+{
+       struct mtd_part *part = mtd_to_part(mtd);
+
+       return mtd_ooblayout_free(part->master, section, oobregion);
+}
+
+static const struct mtd_ooblayout_ops part_ooblayout_ops = {
+       .ecc = part_ooblayout_ecc,
+       .free = part_ooblayout_free,
+};
+
 static inline void free_partition(struct mtd_part *p)
 {
        kfree(p->mtd.name);
@@ -533,7 +554,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
                        part->name);
        }
 
-       mtd_set_ecclayout(&slave->mtd, master->ecclayout);
+       mtd_set_ooblayout(&slave->mtd, &part_ooblayout_ops);
        slave->mtd.ecc_step_size = master->ecc_step_size;
        slave->mtd.ecc_strength = master->ecc_strength;
        slave->mtd.bitflip_threshold = master->bitflip_threshold;
index e62da8462493edc77dddb016659e5010d978866f..177bf314ad70b469428169a447e39698028e6821 100644 (file)
@@ -101,6 +101,9 @@ struct mtd_oob_ops {
  * similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained
  * for export to user-space via the ECCGETLAYOUT ioctl.
  * nand_ecclayout should be expandable in the future simply by the above macros.
+ *
+ * This structure is now deprecated, you should use struct nand_ecclayout_ops
+ * to describe your OOB layout.
  */
 struct nand_ecclayout {
        __u32 eccbytes;
@@ -123,6 +126,22 @@ struct mtd_oob_region {
        u32 length;
 };
 
+/*
+ * struct mtd_ooblayout_ops - NAND OOB layout operations
+ * @ecc: function returning an ECC region in the OOB area.
+ *      Should return -ERANGE if %section exceeds the total number of
+ *      ECC sections.
+ * @free: function returning a free region in the OOB area.
+ *       Should return -ERANGE if %section exceeds the total number of
+ *       free sections.
+ */
+struct mtd_ooblayout_ops {
+       int (*ecc)(struct mtd_info *mtd, int section,
+                  struct mtd_oob_region *oobecc);
+       int (*free)(struct mtd_info *mtd, int section,
+                   struct mtd_oob_region *oobfree);
+};
+
 struct module; /* only needed for owner field in mtd_info */
 
 struct mtd_info {
@@ -181,9 +200,12 @@ struct mtd_info {
        const char *name;
        int index;
 
-       /* ECC layout structure pointer - read only! */
+       /* [Deprecated] ECC layout structure pointer - read only! */
        struct nand_ecclayout *ecclayout;
 
+       /* OOB layout description */
+       const struct mtd_ooblayout_ops *ooblayout;
+
        /* the ecc step size. */
        unsigned int ecc_step_size;
 
@@ -286,10 +308,12 @@ int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf,
 int mtd_ooblayout_count_freebytes(struct mtd_info *mtd);
 int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd);
 
-static inline void mtd_set_ecclayout(struct mtd_info *mtd,
-                                    struct nand_ecclayout *ecclayout)
+void mtd_set_ecclayout(struct mtd_info *mtd, struct nand_ecclayout *ecclayout);
+
+static inline void mtd_set_ooblayout(struct mtd_info *mtd,
+                                    const struct mtd_ooblayout_ops *ooblayout)
 {
-       mtd->ecclayout = ecclayout;
+       mtd->ooblayout = ooblayout;
 }
 
 static inline void mtd_set_of_node(struct mtd_info *mtd,