[PATCH v2 09/20] libnd: support for legacy (non-aliasing) nvdimms

2015-04-28 Thread Dan Williams
The libnd region driver is an intermediary driver that translates
non-volatile "region"s into "namespace" sub-devices that are surfaced by
persistent memory block-device drivers (PMEM and BLK).

ACPI 6 introduces the concept that a given nvdimm may offer multiple
access modes to its media through either direct PMEM load/store access,
or windowed BLK mode.  Existing nvdimms mostly implement a PMEM
interface, some offer a BLK-like mode, but never both.  If an nvdimm is
single interfaced, then there is no need for dimm metadata labels.  For
these devices we can take the region boundaries directly to create a
child namespace device (nd_namespace_io).

Signed-off-by: Dan Williams 
---
 drivers/block/nd/Makefile |2 +
 drivers/block/nd/acpi.c   |1 
 drivers/block/nd/bus.c|   26 +
 drivers/block/nd/core.c   |   13 +++-
 drivers/block/nd/dimm.c   |2 -
 drivers/block/nd/libnd.h  |6 +-
 drivers/block/nd/namespace_devs.c |  111 +
 drivers/block/nd/nd-private.h |9 ++-
 drivers/block/nd/nd.h |8 +++
 drivers/block/nd/region.c |   88 +
 drivers/block/nd/region_devs.c|   61 
 include/linux/nd.h|   10 +++
 include/uapi/linux/ndctl.h|   10 +++
 13 files changed, 338 insertions(+), 9 deletions(-)
 create mode 100644 drivers/block/nd/namespace_devs.c
 create mode 100644 drivers/block/nd/region.c

diff --git a/drivers/block/nd/Makefile b/drivers/block/nd/Makefile
index 6010469c4d4c..0fb0891e1817 100644
--- a/drivers/block/nd/Makefile
+++ b/drivers/block/nd/Makefile
@@ -23,3 +23,5 @@ libnd-y += bus.o
 libnd-y += dimm_devs.o
 libnd-y += dimm.o
 libnd-y += region_devs.o
+libnd-y += region.o
+libnd-y += namespace_devs.o
diff --git a/drivers/block/nd/acpi.c b/drivers/block/nd/acpi.c
index 41d0bb732b3e..c3dda74f73d7 100644
--- a/drivers/block/nd/acpi.c
+++ b/drivers/block/nd/acpi.c
@@ -774,6 +774,7 @@ static struct attribute_group 
nd_acpi_region_attribute_group = {
 static const struct attribute_group *nd_acpi_region_attribute_groups[] = {
_region_attribute_group,
_mapping_attribute_group,
+   _device_attribute_group,
_acpi_region_attribute_group,
NULL,
 };
diff --git a/drivers/block/nd/bus.c b/drivers/block/nd/bus.c
index 7bd79f30e5e7..46568d182559 100644
--- a/drivers/block/nd/bus.c
+++ b/drivers/block/nd/bus.c
@@ -13,6 +13,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -33,6 +34,12 @@ static int to_nd_device_type(struct device *dev)
 {
if (is_nd_dimm(dev))
return ND_DEVICE_DIMM;
+   else if (is_nd_pmem(dev))
+   return ND_DEVICE_REGION_PMEM;
+   else if (is_nd_blk(dev))
+   return ND_DEVICE_REGION_BLK;
+   else if (is_nd_pmem(dev->parent) || is_nd_blk(dev->parent))
+   return nd_region_to_namespace_type(to_nd_region(dev->parent));
 
return 0;
 }
@@ -50,27 +57,46 @@ static int nd_bus_match(struct device *dev, struct 
device_driver *drv)
return test_bit(to_nd_device_type(dev), _drv->type);
 }
 
+static struct module *to_bus_provider(struct device *dev)
+{
+   /* pin bus providers while regions are enabled */
+   if (is_nd_pmem(dev) || is_nd_blk(dev)) {
+   struct nd_bus *nd_bus = walk_to_nd_bus(dev);
+
+   return nd_bus->module;
+   }
+   return NULL;
+}
+
 static int nd_bus_probe(struct device *dev)
 {
struct nd_device_driver *nd_drv = to_nd_device_driver(dev->driver);
+   struct module *provider = to_bus_provider(dev);
struct nd_bus *nd_bus = walk_to_nd_bus(dev);
int rc;
 
+   if (!try_module_get(provider))
+   return -ENXIO;
+
rc = nd_drv->probe(dev);
dev_dbg(_bus->dev, "%s.probe(%s) = %d\n", dev->driver->name,
dev_name(dev), rc);
+   if (rc != 0)
+   module_put(provider);
return rc;
 }
 
 static int nd_bus_remove(struct device *dev)
 {
struct nd_device_driver *nd_drv = to_nd_device_driver(dev->driver);
+   struct module *provider = to_bus_provider(dev);
struct nd_bus *nd_bus = walk_to_nd_bus(dev);
int rc;
 
rc = nd_drv->remove(dev);
dev_dbg(_bus->dev, "%s.remove(%s) = %d\n", dev->driver->name,
dev_name(dev), rc);
+   module_put(provider);
return rc;
 }
 
diff --git a/drivers/block/nd/core.c b/drivers/block/nd/core.c
index d8d1c9cb3f16..646e424ae36c 100644
--- a/drivers/block/nd/core.c
+++ b/drivers/block/nd/core.c
@@ -133,8 +133,8 @@ struct attribute_group nd_bus_attribute_group = {
 };
 EXPORT_SYMBOL_GPL(nd_bus_attribute_group);
 
-struct nd_bus *nd_bus_register(struct device *parent,
-   struct nd_bus_descriptor *nd_desc)
+struct nd_bus *__nd_bus_register(struct device *parent,
+   

[PATCH v2 09/20] libnd: support for legacy (non-aliasing) nvdimms

2015-04-28 Thread Dan Williams
The libnd region driver is an intermediary driver that translates
non-volatile regions into namespace sub-devices that are surfaced by
persistent memory block-device drivers (PMEM and BLK).

ACPI 6 introduces the concept that a given nvdimm may offer multiple
access modes to its media through either direct PMEM load/store access,
or windowed BLK mode.  Existing nvdimms mostly implement a PMEM
interface, some offer a BLK-like mode, but never both.  If an nvdimm is
single interfaced, then there is no need for dimm metadata labels.  For
these devices we can take the region boundaries directly to create a
child namespace device (nd_namespace_io).

Signed-off-by: Dan Williams dan.j.willi...@intel.com
---
 drivers/block/nd/Makefile |2 +
 drivers/block/nd/acpi.c   |1 
 drivers/block/nd/bus.c|   26 +
 drivers/block/nd/core.c   |   13 +++-
 drivers/block/nd/dimm.c   |2 -
 drivers/block/nd/libnd.h  |6 +-
 drivers/block/nd/namespace_devs.c |  111 +
 drivers/block/nd/nd-private.h |9 ++-
 drivers/block/nd/nd.h |8 +++
 drivers/block/nd/region.c |   88 +
 drivers/block/nd/region_devs.c|   61 
 include/linux/nd.h|   10 +++
 include/uapi/linux/ndctl.h|   10 +++
 13 files changed, 338 insertions(+), 9 deletions(-)
 create mode 100644 drivers/block/nd/namespace_devs.c
 create mode 100644 drivers/block/nd/region.c

diff --git a/drivers/block/nd/Makefile b/drivers/block/nd/Makefile
index 6010469c4d4c..0fb0891e1817 100644
--- a/drivers/block/nd/Makefile
+++ b/drivers/block/nd/Makefile
@@ -23,3 +23,5 @@ libnd-y += bus.o
 libnd-y += dimm_devs.o
 libnd-y += dimm.o
 libnd-y += region_devs.o
+libnd-y += region.o
+libnd-y += namespace_devs.o
diff --git a/drivers/block/nd/acpi.c b/drivers/block/nd/acpi.c
index 41d0bb732b3e..c3dda74f73d7 100644
--- a/drivers/block/nd/acpi.c
+++ b/drivers/block/nd/acpi.c
@@ -774,6 +774,7 @@ static struct attribute_group 
nd_acpi_region_attribute_group = {
 static const struct attribute_group *nd_acpi_region_attribute_groups[] = {
nd_region_attribute_group,
nd_mapping_attribute_group,
+   nd_device_attribute_group,
nd_acpi_region_attribute_group,
NULL,
 };
diff --git a/drivers/block/nd/bus.c b/drivers/block/nd/bus.c
index 7bd79f30e5e7..46568d182559 100644
--- a/drivers/block/nd/bus.c
+++ b/drivers/block/nd/bus.c
@@ -13,6 +13,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME :  fmt
 #include linux/vmalloc.h
 #include linux/uaccess.h
+#include linux/module.h
 #include linux/fcntl.h
 #include linux/async.h
 #include linux/ndctl.h
@@ -33,6 +34,12 @@ static int to_nd_device_type(struct device *dev)
 {
if (is_nd_dimm(dev))
return ND_DEVICE_DIMM;
+   else if (is_nd_pmem(dev))
+   return ND_DEVICE_REGION_PMEM;
+   else if (is_nd_blk(dev))
+   return ND_DEVICE_REGION_BLK;
+   else if (is_nd_pmem(dev-parent) || is_nd_blk(dev-parent))
+   return nd_region_to_namespace_type(to_nd_region(dev-parent));
 
return 0;
 }
@@ -50,27 +57,46 @@ static int nd_bus_match(struct device *dev, struct 
device_driver *drv)
return test_bit(to_nd_device_type(dev), nd_drv-type);
 }
 
+static struct module *to_bus_provider(struct device *dev)
+{
+   /* pin bus providers while regions are enabled */
+   if (is_nd_pmem(dev) || is_nd_blk(dev)) {
+   struct nd_bus *nd_bus = walk_to_nd_bus(dev);
+
+   return nd_bus-module;
+   }
+   return NULL;
+}
+
 static int nd_bus_probe(struct device *dev)
 {
struct nd_device_driver *nd_drv = to_nd_device_driver(dev-driver);
+   struct module *provider = to_bus_provider(dev);
struct nd_bus *nd_bus = walk_to_nd_bus(dev);
int rc;
 
+   if (!try_module_get(provider))
+   return -ENXIO;
+
rc = nd_drv-probe(dev);
dev_dbg(nd_bus-dev, %s.probe(%s) = %d\n, dev-driver-name,
dev_name(dev), rc);
+   if (rc != 0)
+   module_put(provider);
return rc;
 }
 
 static int nd_bus_remove(struct device *dev)
 {
struct nd_device_driver *nd_drv = to_nd_device_driver(dev-driver);
+   struct module *provider = to_bus_provider(dev);
struct nd_bus *nd_bus = walk_to_nd_bus(dev);
int rc;
 
rc = nd_drv-remove(dev);
dev_dbg(nd_bus-dev, %s.remove(%s) = %d\n, dev-driver-name,
dev_name(dev), rc);
+   module_put(provider);
return rc;
 }
 
diff --git a/drivers/block/nd/core.c b/drivers/block/nd/core.c
index d8d1c9cb3f16..646e424ae36c 100644
--- a/drivers/block/nd/core.c
+++ b/drivers/block/nd/core.c
@@ -133,8 +133,8 @@ struct attribute_group nd_bus_attribute_group = {
 };
 EXPORT_SYMBOL_GPL(nd_bus_attribute_group);
 
-struct nd_bus *nd_bus_register(struct device *parent,
-