[PATCH 13/21] nd: add interleave-set state-tracking infrastructure

2015-04-17 Thread Dan Williams
On platforms that have firmware support for reading/writing per-dimm
label space, a portion of the dimm may be accessible via an interleave
set PMEM mapping in addition to the dimm's BLK (block-data-window
aperture(s)) interface.  A label, stored in a "configuration data
region" on the dimm, disambiguates which dimm addresses are accessed
through which exclusive interface.

Add infrastructure that allows the kernel to block modifications to a
label in the set while any member dimm is active.  Note that this is
meant only for enforcing "no modifications of active labels" via the
coarse ioctl command.  Adding/deleting namespaces from an active
interleave set will only be possible via sysfs.

Another aspect of tracking interleave sets is tracking their integrity
when DIMMs in a set are physically re-ordered.  For this purpose we
generate an "interleave-set cookie" that can be recorded in a label and
validated against the current configuration.

Signed-off-by: Dan Williams 
---
 drivers/block/nd/bus.c |   41 +
 drivers/block/nd/core.c|   51 
 drivers/block/nd/dimm_devs.c   |   18 
 drivers/block/nd/nd-private.h  |   17 
 drivers/block/nd/nd.h  |4 +
 drivers/block/nd/region_devs.c |  176 
 6 files changed, 305 insertions(+), 2 deletions(-)

diff --git a/drivers/block/nd/bus.c b/drivers/block/nd/bus.c
index c98fe05a4c9b..944d7d7845fe 100644
--- a/drivers/block/nd/bus.c
+++ b/drivers/block/nd/bus.c
@@ -79,7 +79,10 @@ static int nd_bus_probe(struct device *dev)
if (!try_module_get(provider))
return -ENXIO;
 
+   nd_region_probe_start(nd_bus, dev);
rc = nd_drv->probe(dev);
+   nd_region_probe_end(nd_bus, dev, rc);
+
dev_dbg(_bus->dev, "%s.probe(%s) = %d\n", dev->driver->name,
dev_name(dev), rc);
if (rc != 0)
@@ -95,6 +98,8 @@ static int nd_bus_remove(struct device *dev)
int rc;
 
rc = nd_drv->remove(dev);
+   nd_region_notify_remove(nd_bus, dev, rc);
+
dev_dbg(_bus->dev, "%s.remove(%s) = %d\n", dev->driver->name,
dev_name(dev), rc);
module_put(provider);
@@ -269,6 +274,33 @@ void nd_bus_destroy_ndctl(struct nd_bus *nd_bus)
device_destroy(nd_class, MKDEV(nd_bus_major, nd_bus->id));
 }
 
+static void wait_nd_bus_probe_idle(struct nd_bus *nd_bus)
+{
+   do {
+   if (nd_bus->probe_active == 0)
+   break;
+   nd_bus_unlock(_bus->dev);
+   wait_event(nd_bus->probe_wait, nd_bus->probe_active == 0);
+   nd_bus_lock(_bus->dev);
+   } while (true);
+}
+
+/* set_config requires an idle interleave set */
+static int nd_cmd_clear_to_send(struct nd_dimm *nd_dimm, unsigned int cmd)
+{
+   struct nd_bus *nd_bus;
+
+   if (!nd_dimm || cmd != NFIT_CMD_SET_CONFIG_DATA)
+   return 0;
+
+   nd_bus = walk_to_nd_bus(_dimm->dev);
+   wait_nd_bus_probe_idle(nd_bus);
+
+   if (atomic_read(_dimm->busy))
+   return -EBUSY;
+   return 0;
+}
+
 static int __nd_ioctl(struct nd_bus *nd_bus, struct nd_dimm *nd_dimm,
int read_only, unsigned int cmd, unsigned long arg)
 {
@@ -399,11 +431,18 @@ static int __nd_ioctl(struct nd_bus *nd_bus, struct 
nd_dimm *nd_dimm,
goto out;
}
 
+   nd_bus_lock(_bus->dev);
+   rc = nd_cmd_clear_to_send(nd_dimm, _IOC_NR(cmd));
+   if (rc)
+   goto out_unlock;
+
rc = nfit_desc->nfit_ctl(nfit_desc, nd_dimm, _IOC_NR(cmd), buf, 
buf_len);
if (rc < 0)
-   goto out;
+   goto out_unlock;
if (copy_to_user(p, buf, buf_len))
rc = -EFAULT;
+ out_unlock:
+   nd_bus_unlock(_bus->dev);
  out:
if (is_vmalloc_addr(buf))
vfree(buf);
diff --git a/drivers/block/nd/core.c b/drivers/block/nd/core.c
index c795e8057061..976cd5e3ebaf 100644
--- a/drivers/block/nd/core.c
+++ b/drivers/block/nd/core.c
@@ -31,6 +31,36 @@ static bool warn_checksum;
 module_param(warn_checksum, bool, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(warn_checksum, "Turn checksum errors into warnings");
 
+void nd_bus_lock(struct device *dev)
+{
+   struct nd_bus *nd_bus = walk_to_nd_bus(dev);
+
+   if (!nd_bus)
+   return;
+   mutex_lock(_bus->reconfig_mutex);
+}
+EXPORT_SYMBOL(nd_bus_lock);
+
+void nd_bus_unlock(struct device *dev)
+{
+   struct nd_bus *nd_bus = walk_to_nd_bus(dev);
+
+   if (!nd_bus)
+   return;
+   mutex_unlock(_bus->reconfig_mutex);
+}
+EXPORT_SYMBOL(nd_bus_unlock);
+
+bool is_nd_bus_locked(struct device *dev)
+{
+   struct nd_bus *nd_bus = walk_to_nd_bus(dev);
+
+   if (!nd_bus)
+   return false;
+   return mutex_is_locked(_bus->reconfig_mutex);
+}
+EXPORT_SYMBOL(is_nd_bus_locked);
+
 /**
  * nd_dimm_by_handle - lookup an nd_dimm by its corresponding nfit_handle
  * 

[PATCH 13/21] nd: add interleave-set state-tracking infrastructure

2015-04-17 Thread Dan Williams
On platforms that have firmware support for reading/writing per-dimm
label space, a portion of the dimm may be accessible via an interleave
set PMEM mapping in addition to the dimm's BLK (block-data-window
aperture(s)) interface.  A label, stored in a configuration data
region on the dimm, disambiguates which dimm addresses are accessed
through which exclusive interface.

Add infrastructure that allows the kernel to block modifications to a
label in the set while any member dimm is active.  Note that this is
meant only for enforcing no modifications of active labels via the
coarse ioctl command.  Adding/deleting namespaces from an active
interleave set will only be possible via sysfs.

Another aspect of tracking interleave sets is tracking their integrity
when DIMMs in a set are physically re-ordered.  For this purpose we
generate an interleave-set cookie that can be recorded in a label and
validated against the current configuration.

Signed-off-by: Dan Williams dan.j.willi...@intel.com
---
 drivers/block/nd/bus.c |   41 +
 drivers/block/nd/core.c|   51 
 drivers/block/nd/dimm_devs.c   |   18 
 drivers/block/nd/nd-private.h  |   17 
 drivers/block/nd/nd.h  |4 +
 drivers/block/nd/region_devs.c |  176 
 6 files changed, 305 insertions(+), 2 deletions(-)

diff --git a/drivers/block/nd/bus.c b/drivers/block/nd/bus.c
index c98fe05a4c9b..944d7d7845fe 100644
--- a/drivers/block/nd/bus.c
+++ b/drivers/block/nd/bus.c
@@ -79,7 +79,10 @@ static int nd_bus_probe(struct device *dev)
if (!try_module_get(provider))
return -ENXIO;
 
+   nd_region_probe_start(nd_bus, dev);
rc = nd_drv-probe(dev);
+   nd_region_probe_end(nd_bus, dev, rc);
+
dev_dbg(nd_bus-dev, %s.probe(%s) = %d\n, dev-driver-name,
dev_name(dev), rc);
if (rc != 0)
@@ -95,6 +98,8 @@ static int nd_bus_remove(struct device *dev)
int rc;
 
rc = nd_drv-remove(dev);
+   nd_region_notify_remove(nd_bus, dev, rc);
+
dev_dbg(nd_bus-dev, %s.remove(%s) = %d\n, dev-driver-name,
dev_name(dev), rc);
module_put(provider);
@@ -269,6 +274,33 @@ void nd_bus_destroy_ndctl(struct nd_bus *nd_bus)
device_destroy(nd_class, MKDEV(nd_bus_major, nd_bus-id));
 }
 
+static void wait_nd_bus_probe_idle(struct nd_bus *nd_bus)
+{
+   do {
+   if (nd_bus-probe_active == 0)
+   break;
+   nd_bus_unlock(nd_bus-dev);
+   wait_event(nd_bus-probe_wait, nd_bus-probe_active == 0);
+   nd_bus_lock(nd_bus-dev);
+   } while (true);
+}
+
+/* set_config requires an idle interleave set */
+static int nd_cmd_clear_to_send(struct nd_dimm *nd_dimm, unsigned int cmd)
+{
+   struct nd_bus *nd_bus;
+
+   if (!nd_dimm || cmd != NFIT_CMD_SET_CONFIG_DATA)
+   return 0;
+
+   nd_bus = walk_to_nd_bus(nd_dimm-dev);
+   wait_nd_bus_probe_idle(nd_bus);
+
+   if (atomic_read(nd_dimm-busy))
+   return -EBUSY;
+   return 0;
+}
+
 static int __nd_ioctl(struct nd_bus *nd_bus, struct nd_dimm *nd_dimm,
int read_only, unsigned int cmd, unsigned long arg)
 {
@@ -399,11 +431,18 @@ static int __nd_ioctl(struct nd_bus *nd_bus, struct 
nd_dimm *nd_dimm,
goto out;
}
 
+   nd_bus_lock(nd_bus-dev);
+   rc = nd_cmd_clear_to_send(nd_dimm, _IOC_NR(cmd));
+   if (rc)
+   goto out_unlock;
+
rc = nfit_desc-nfit_ctl(nfit_desc, nd_dimm, _IOC_NR(cmd), buf, 
buf_len);
if (rc  0)
-   goto out;
+   goto out_unlock;
if (copy_to_user(p, buf, buf_len))
rc = -EFAULT;
+ out_unlock:
+   nd_bus_unlock(nd_bus-dev);
  out:
if (is_vmalloc_addr(buf))
vfree(buf);
diff --git a/drivers/block/nd/core.c b/drivers/block/nd/core.c
index c795e8057061..976cd5e3ebaf 100644
--- a/drivers/block/nd/core.c
+++ b/drivers/block/nd/core.c
@@ -31,6 +31,36 @@ static bool warn_checksum;
 module_param(warn_checksum, bool, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(warn_checksum, Turn checksum errors into warnings);
 
+void nd_bus_lock(struct device *dev)
+{
+   struct nd_bus *nd_bus = walk_to_nd_bus(dev);
+
+   if (!nd_bus)
+   return;
+   mutex_lock(nd_bus-reconfig_mutex);
+}
+EXPORT_SYMBOL(nd_bus_lock);
+
+void nd_bus_unlock(struct device *dev)
+{
+   struct nd_bus *nd_bus = walk_to_nd_bus(dev);
+
+   if (!nd_bus)
+   return;
+   mutex_unlock(nd_bus-reconfig_mutex);
+}
+EXPORT_SYMBOL(nd_bus_unlock);
+
+bool is_nd_bus_locked(struct device *dev)
+{
+   struct nd_bus *nd_bus = walk_to_nd_bus(dev);
+
+   if (!nd_bus)
+   return false;
+   return mutex_is_locked(nd_bus-reconfig_mutex);
+}
+EXPORT_SYMBOL(is_nd_bus_locked);
+
 /**
  * nd_dimm_by_handle - lookup an nd_dimm by its corresponding