Merge tag 'upstream-4.1-rc4' of git://git.infradead.org/linux-ubifs
[cascardo/linux.git] / drivers / mtd / mtdpart.c
index e779de3..cafdb88 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;
@@ -546,12 +554,35 @@ out_register:
        return slave;
 }
 
+static ssize_t mtd_partition_offset_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mtd_info *mtd = dev_get_drvdata(dev);
+       struct mtd_part *part = PART(mtd);
+       return snprintf(buf, PAGE_SIZE, "%lld\n", part->offset);
+}
+
+static DEVICE_ATTR(offset, S_IRUGO, mtd_partition_offset_show, NULL);
+
+static const struct attribute *mtd_partition_attrs[] = {
+       &dev_attr_offset.attr,
+       NULL
+};
+
+static int mtd_add_partition_attrs(struct mtd_part *new)
+{
+       int ret = sysfs_create_files(&new->mtd.dev.kobj, mtd_partition_attrs);
+       if (ret)
+               printk(KERN_WARNING
+                      "mtd: failed to create partition attrs, err=%d\n", ret);
+       return ret;
+}
+
 int mtd_add_partition(struct mtd_info *master, const char *name,
                      long long offset, long long length)
 {
        struct mtd_partition part;
-       struct mtd_part *p, *new;
-       uint64_t start, end;
+       struct mtd_part *new;
        int ret = 0;
 
        /* the direct offset is expected */
@@ -575,31 +606,15 @@ int mtd_add_partition(struct mtd_info *master, const char *name,
        if (IS_ERR(new))
                return PTR_ERR(new);
 
-       start = offset;
-       end = offset + length;
-
        mutex_lock(&mtd_partitions_mutex);
-       list_for_each_entry(p, &mtd_partitions, list)
-               if (p->master == master) {
-                       if ((start >= p->offset) &&
-                           (start < (p->offset + p->mtd.size)))
-                               goto err_inv;
-
-                       if ((end >= p->offset) &&
-                           (end < (p->offset + p->mtd.size)))
-                               goto err_inv;
-               }
-
        list_add(&new->list, &mtd_partitions);
        mutex_unlock(&mtd_partitions_mutex);
 
        add_mtd_device(&new->mtd);
 
+       mtd_add_partition_attrs(new);
+
        return ret;
-err_inv:
-       mutex_unlock(&mtd_partitions_mutex);
-       free_partition(new);
-       return -EINVAL;
 }
 EXPORT_SYMBOL_GPL(mtd_add_partition);
 
@@ -612,6 +627,8 @@ int mtd_del_partition(struct mtd_info *master, int partno)
        list_for_each_entry_safe(slave, next, &mtd_partitions, list)
                if ((slave->master == master) &&
                    (slave->mtd.index == partno)) {
+                       sysfs_remove_files(&slave->mtd.dev.kobj,
+                                          mtd_partition_attrs);
                        ret = del_mtd_device(&slave->mtd);
                        if (ret < 0)
                                break;
@@ -631,8 +648,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,
@@ -655,6 +672,7 @@ int add_mtd_partitions(struct mtd_info *master,
                mutex_unlock(&mtd_partitions_mutex);
 
                add_mtd_device(&slave->mtd);
+               mtd_add_partition_attrs(slave);
 
                cur_offset = slave->offset + slave->mtd.size;
        }