Commit 727dc612 authored by Dan Ehrenberg's avatar Dan Ehrenberg Committed by Brian Norris

mtd: part: Create the master device node when partitioned

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: default avatarDan Ehrenberg <dehrenberg@chromium.org>
Signed-off-by: default avatarBrian Norris <computersforpeace@gmail.com>
parent 9cd5196e
...@@ -309,6 +309,19 @@ config MTD_SWAP ...@@ -309,6 +309,19 @@ config MTD_SWAP
The driver provides wear leveling by storing erase counter into the The driver provides wear leveling by storing erase counter into the
OOB. 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/chips/Kconfig"
source "drivers/mtd/maps/Kconfig" source "drivers/mtd/maps/Kconfig"
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/kconfig.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
...@@ -501,6 +502,29 @@ int del_mtd_device(struct mtd_info *mtd) ...@@ -501,6 +502,29 @@ int del_mtd_device(struct mtd_info *mtd)
return ret; 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. * mtd_device_parse_register - parse partitions and register an MTD device.
* *
...@@ -523,7 +547,8 @@ int del_mtd_device(struct mtd_info *mtd) ...@@ -523,7 +547,8 @@ int del_mtd_device(struct mtd_info *mtd)
* found this functions tries to fallback to information specified in * found this functions tries to fallback to information specified in
* @parts/@nr_parts. * @parts/@nr_parts.
* * If any partitioning info was found, this function registers the found * * 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 * * If no partitions were found this function just registers the MTD device
* @mtd and exits. * @mtd and exits.
* *
...@@ -534,27 +559,21 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, ...@@ -534,27 +559,21 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
const struct mtd_partition *parts, const struct mtd_partition *parts,
int nr_parts) int nr_parts)
{ {
int err; int ret;
struct mtd_partition *real_parts; struct mtd_partition *real_parts = NULL;
err = parse_mtd_partitions(mtd, types, &real_parts, parser_data); ret = parse_mtd_partitions(mtd, types, &real_parts, parser_data);
if (err <= 0 && nr_parts && parts) { if (ret <= 0 && nr_parts && parts) {
real_parts = kmemdup(parts, sizeof(*parts) * nr_parts, real_parts = kmemdup(parts, sizeof(*parts) * nr_parts,
GFP_KERNEL); GFP_KERNEL);
if (!real_parts) if (!real_parts)
err = -ENOMEM; ret = -ENOMEM;
else else
err = nr_parts; ret = nr_parts;
} }
if (err > 0) { if (ret >= 0)
err = add_mtd_partitions(mtd, real_parts, err); ret = mtd_add_device_partitions(mtd, real_parts, ret);
kfree(real_parts);
} else if (err == 0) {
err = add_mtd_device(mtd);
if (err == 1)
err = -ENODEV;
}
/* /*
* FIXME: some drivers unfortunately call this function more than once. * 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, ...@@ -569,7 +588,8 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
register_reboot_notifier(&mtd->reboot_notifier); register_reboot_notifier(&mtd->reboot_notifier);
} }
return err; kfree(real_parts);
return ret;
} }
EXPORT_SYMBOL_GPL(mtd_device_parse_register); EXPORT_SYMBOL_GPL(mtd_device_parse_register);
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/kconfig.h>
#include "mtdcore.h" #include "mtdcore.h"
...@@ -379,10 +380,17 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, ...@@ -379,10 +380,17 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
slave->mtd.name = name; slave->mtd.name = name;
slave->mtd.owner = master->owner; slave->mtd.owner = master->owner;
/* NOTE: we don't arrange MTDs as a tree; it'd be error-prone /* NOTE: Historically, we didn't arrange MTDs as a tree out of
* to have the same data be in two different partitions. * 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._read = part_read;
slave->mtd._write = part_write; slave->mtd._write = part_write;
...@@ -631,8 +639,8 @@ EXPORT_SYMBOL_GPL(mtd_del_partition); ...@@ -631,8 +639,8 @@ EXPORT_SYMBOL_GPL(mtd_del_partition);
* and registers slave MTD objects which are bound to the master according to * and registers slave MTD objects which are bound to the master according to
* the partition definitions. * the partition definitions.
* *
* We don't register the master, or expect the caller to have done so, * For historical reasons, this function's caller only registers the master
* for reasons of data integrity. * if the MTD_PARTITIONED_MASTER config option is set.
*/ */
int add_mtd_partitions(struct mtd_info *master, int add_mtd_partitions(struct mtd_info *master,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment