mtd: part: Create the master device node when partitioned
authorDan Ehrenberg <dehrenberg@chromium.org>
Thu, 2 Apr 2015 22:15:10 +0000 (15:15 -0700)
committerBrian Norris <computersforpeace@gmail.com>
Mon, 6 Apr 2015 00:44:01 +0000 (17:44 -0700)
For many use cases, it helps to have a device node for the entire
MTD device as well as device nodes for the individual partitions.
For example, this allows querying the entire device's properties.
A common idiom is to create an additional partition which spans
over the whole device.

This patch makes a config option, CONFIG_MTD_PARTITIONED_MASTER,
which makes the master partition present even when the device is
partitioned. This isn't turned on by default since it presents
a backwards-incompatible device numbering.

The patch also makes the parent of a partition device be the master,
if the config flag is set, now that the master is a full device.

Signed-off-by: Dan Ehrenberg <dehrenberg@chromium.org>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
drivers/mtd/Kconfig
drivers/mtd/mtdcore.c
drivers/mtd/mtdpart.c

index 71fea895ce38d34c20beee385182277241525431..a03ad2951c7b423975c566dfce20902b7fd074f0 100644 (file)
@@ -309,6 +309,19 @@ config MTD_SWAP
          The driver provides wear leveling by storing erase counter into the
          OOB.
 
+config MTD_PARTITIONED_MASTER
+       bool "Retain master device when partitioned"
+       default n
+       depends on MTD
+       help
+         For historical reasons, by default, either a master is present or
+         several partitions are present, but not both. The concern was that
+         data listed in multiple partitions was dangerous; however, SCSI does
+         this and it is frequently useful for applications. This config option
+         leaves the master in even if the device is partitioned. It also makes
+         the parent of the partition device be the master device, rather than
+         what lies behind the master.
+
 source "drivers/mtd/chips/Kconfig"
 
 source "drivers/mtd/maps/Kconfig"
index 11883bd26d9d35e8234cc7dc74f51b7ed57d7566..d172195fbd15fcbd947a2bc0780d0f9044cba2fd 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/gfp.h>
 #include <linux/slab.h>
 #include <linux/reboot.h>
+#include <linux/kconfig.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
@@ -501,6 +502,29 @@ out_error:
        return ret;
 }
 
+static int mtd_add_device_partitions(struct mtd_info *mtd,
+                                    struct mtd_partition *real_parts,
+                                    int nbparts)
+{
+       int ret;
+
+       if (nbparts == 0 || IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
+               ret = add_mtd_device(mtd);
+               if (ret == 1)
+                       return -ENODEV;
+       }
+
+       if (nbparts > 0) {
+               ret = add_mtd_partitions(mtd, real_parts, nbparts);
+               if (ret && IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
+                       del_mtd_device(mtd);
+               return ret;
+       }
+
+       return 0;
+}
+
+
 /**
  * mtd_device_parse_register - parse partitions and register an MTD device.
  *
@@ -523,7 +547,8 @@ out_error:
  *   found this functions tries to fallback to information specified in
  *   @parts/@nr_parts.
  * * If any partitioning info was found, this function registers the found
- *   partitions.
+ *   partitions. If the MTD_PARTITIONED_MASTER option is set, then the device
+ *   as a whole is registered first.
  * * If no partitions were found this function just registers the MTD device
  *   @mtd and exits.
  *
@@ -534,27 +559,21 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
                              const struct mtd_partition *parts,
                              int nr_parts)
 {
-       int err;
-       struct mtd_partition *real_parts;
+       int ret;
+       struct mtd_partition *real_parts = NULL;
 
-       err = parse_mtd_partitions(mtd, types, &real_parts, parser_data);
-       if (err <= 0 && nr_parts && parts) {
+       ret = parse_mtd_partitions(mtd, types, &real_parts, parser_data);
+       if (ret <= 0 && nr_parts && parts) {
                real_parts = kmemdup(parts, sizeof(*parts) * nr_parts,
                                     GFP_KERNEL);
                if (!real_parts)
-                       err = -ENOMEM;
+                       ret = -ENOMEM;
                else
-                       err = nr_parts;
+                       ret = nr_parts;
        }
 
-       if (err > 0) {
-               err = add_mtd_partitions(mtd, real_parts, err);
-               kfree(real_parts);
-       } else if (err == 0) {
-               err = add_mtd_device(mtd);
-               if (err == 1)
-                       err = -ENODEV;
-       }
+       if (ret >= 0)
+               ret = mtd_add_device_partitions(mtd, real_parts, ret);
 
        /*
         * FIXME: some drivers unfortunately call this function more than once.
@@ -569,7 +588,8 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
                register_reboot_notifier(&mtd->reboot_notifier);
        }
 
-       return err;
+       kfree(real_parts);
+       return ret;
 }
 EXPORT_SYMBOL_GPL(mtd_device_parse_register);
 
index e779de315ade505f1a508edefc541f7a4223af97..a19ec5a4e4095888ad8aaba63496d84328f18040 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/err.h>
+#include <linux/kconfig.h>
 
 #include "mtdcore.h"
 
@@ -379,10 +380,17 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
        slave->mtd.name = name;
        slave->mtd.owner = master->owner;
 
-       /* NOTE:  we don't arrange MTDs as a tree; it'd be error-prone
-        * to have the same data be in two different partitions.
+       /* NOTE: Historically, we didn't arrange MTDs as a tree out of
+        * concern for showing the same data in multiple partitions.
+        * However, it is very useful to have the master node present,
+        * so the MTD_PARTITIONED_MASTER option allows that. The master
+        * will have device nodes etc only if this is set, so make the
+        * parent conditional on that option. Note, this is a way to
+        * distinguish between the master and the partition in sysfs.
         */
-       slave->mtd.dev.parent = master->dev.parent;
+       slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) ?
+                               &master->dev :
+                               master->dev.parent;
 
        slave->mtd._read = part_read;
        slave->mtd._write = part_write;
@@ -631,8 +639,8 @@ EXPORT_SYMBOL_GPL(mtd_del_partition);
  * and registers slave MTD objects which are bound to the master according to
  * the partition definitions.
  *
- * We don't register the master, or expect the caller to have done so,
- * for reasons of data integrity.
+ * For historical reasons, this function's caller only registers the master
+ * if the MTD_PARTITIONED_MASTER config option is set.
  */
 
 int add_mtd_partitions(struct mtd_info *master,