[PATCH v2 14/20] libnd: pmem label sets and namespace instantiation.

2015-04-28 Thread Dan Williams
A complete label set is a PMEM-label per dimm where all the UUIDs
match and the interleave set cookie matches an active interleave set.

Present a sysfs ABI for manipulation of a PMEM-namespace's 'alt_name',
'uuid', and 'size' attributes.  A later patch will make these settings
persistent by writing back the label.

Note that PMEM allocations grow forwards from the start of an interleave
set (lowest dimm-physical-address (DPA)).  BLK-namespaces that alias
with a PMEM interleave set will grow allocations backward from the
highest DPA.

Cc: Greg KH 
Cc: Neil Brown 
Signed-off-by: Dan Williams 
---
 drivers/block/nd/bus.c|6 
 drivers/block/nd/core.c   |   64 ++
 drivers/block/nd/dimm.c   |2 
 drivers/block/nd/dimm_devs.c  |  127 +
 drivers/block/nd/label.c  |   54 ++
 drivers/block/nd/label.h  |3 
 drivers/block/nd/libnd.h  |2 
 drivers/block/nd/namespace_devs.c | 1024 +
 drivers/block/nd/nd-private.h |   14 +
 drivers/block/nd/nd.h |   33 +
 drivers/block/nd/pmem.c   |   22 +
 drivers/block/nd/region_devs.c|  145 +
 include/linux/nd.h|   24 +
 include/uapi/linux/ndctl.h|4 
 14 files changed, 1512 insertions(+), 12 deletions(-)

diff --git a/drivers/block/nd/bus.c b/drivers/block/nd/bus.c
index 8afb8d4a7e81..819259e92468 100644
--- a/drivers/block/nd/bus.c
+++ b/drivers/block/nd/bus.c
@@ -364,8 +364,10 @@ u32 nd_cmd_out_size(struct nd_dimm *nd_dimm, int cmd,
 }
 EXPORT_SYMBOL_GPL(nd_cmd_out_size);
 
-static void wait_nd_bus_probe_idle(struct nd_bus *nd_bus)
+void wait_nd_bus_probe_idle(struct device *dev)
 {
+   struct nd_bus *nd_bus = walk_to_nd_bus(dev);
+
do {
if (nd_bus->probe_active == 0)
break;
@@ -384,7 +386,7 @@ static int nd_cmd_clear_to_send(struct nd_dimm *nd_dimm, 
unsigned int cmd)
return 0;
 
nd_bus = walk_to_nd_bus(_dimm->dev);
-   wait_nd_bus_probe_idle(nd_bus);
+   wait_nd_bus_probe_idle(_bus->dev);
 
if (atomic_read(_dimm->busy))
return -EBUSY;
diff --git a/drivers/block/nd/core.c b/drivers/block/nd/core.c
index 603970d0ef3a..cf64b7a50d3a 100644
--- a/drivers/block/nd/core.c
+++ b/drivers/block/nd/core.c
@@ -13,6 +13,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -106,6 +107,69 @@ struct nd_bus *walk_to_nd_bus(struct device *nd_dev)
return NULL;
 }
 
+static bool is_uuid_sep(char sep)
+{
+   if (sep == '\n' || sep == '-' || sep == ':' || sep == '\0')
+   return true;
+   return false;
+}
+
+static int nd_uuid_parse(struct device *dev, u8 *uuid_out, const char *buf,
+   size_t len)
+{
+   const char *str = buf;
+   u8 uuid[16];
+   int i;
+
+   for (i = 0; i < 16; i++) {
+   if (!isxdigit(str[0]) || !isxdigit(str[1])) {
+   dev_dbg(dev, "%s: pos: %d buf[%zd]: %c buf[%zd]: %c\n",
+   __func__, i, str - buf, str[0],
+   str + 1 - buf, str[1]);
+   return -EINVAL;
+   }
+
+   uuid[i] = (hex_to_bin(str[0]) << 4) | hex_to_bin(str[1]);
+   str += 2;
+   if (is_uuid_sep(*str))
+   str++;
+   }
+
+   memcpy(uuid_out, uuid, sizeof(uuid));
+   return 0;
+}
+
+/**
+ * nd_uuid_store: common implementation for writing 'uuid' sysfs attributes
+ * @dev: container device for the uuid property
+ * @uuid_out: uuid buffer to replace
+ * @buf: raw sysfs buffer to parse
+ *
+ * Enforce that uuids can only be changed while the device is disabled
+ * (driver detached)
+ * LOCKING: expects device_lock() is held on entry
+ */
+int nd_uuid_store(struct device *dev, u8 **uuid_out, const char *buf,
+   size_t len)
+{
+   u8 uuid[16];
+   int rc;
+
+   if (dev->driver)
+   return -EBUSY;
+
+   rc = nd_uuid_parse(dev, uuid, buf, len);
+   if (rc)
+   return rc;
+
+   kfree(*uuid_out);
+   *uuid_out = kmemdup(uuid, sizeof(uuid), GFP_KERNEL);
+   if (!(*uuid_out))
+   return -ENOMEM;
+
+   return 0;
+}
+
 static ssize_t commands_show(struct device *dev,
struct device_attribute *attr, char *buf)
 {
diff --git a/drivers/block/nd/dimm.c b/drivers/block/nd/dimm.c
index 5477176c5de0..e2f964308672 100644
--- a/drivers/block/nd/dimm.c
+++ b/drivers/block/nd/dimm.c
@@ -86,7 +86,7 @@ static int nd_dimm_remove(struct device *dev)
nd_bus_lock(dev);
dev_set_drvdata(dev, NULL);
for_each_dpa_resource_safe(ndd, res, _r)
-   __release_region(>dpa, res->start, resource_size(res));
+   nd_dimm_free_dpa(ndd, res);
nd_bus_unlock(dev);
free_data(ndd);
 
diff --git a/drivers/block/nd/dimm_devs.c 

[PATCH v2 14/20] libnd: pmem label sets and namespace instantiation.

2015-04-28 Thread Dan Williams
A complete label set is a PMEM-label per dimm where all the UUIDs
match and the interleave set cookie matches an active interleave set.

Present a sysfs ABI for manipulation of a PMEM-namespace's 'alt_name',
'uuid', and 'size' attributes.  A later patch will make these settings
persistent by writing back the label.

Note that PMEM allocations grow forwards from the start of an interleave
set (lowest dimm-physical-address (DPA)).  BLK-namespaces that alias
with a PMEM interleave set will grow allocations backward from the
highest DPA.

Cc: Greg KH gre...@linuxfoundation.org
Cc: Neil Brown ne...@suse.de
Signed-off-by: Dan Williams dan.j.willi...@intel.com
---
 drivers/block/nd/bus.c|6 
 drivers/block/nd/core.c   |   64 ++
 drivers/block/nd/dimm.c   |2 
 drivers/block/nd/dimm_devs.c  |  127 +
 drivers/block/nd/label.c  |   54 ++
 drivers/block/nd/label.h  |3 
 drivers/block/nd/libnd.h  |2 
 drivers/block/nd/namespace_devs.c | 1024 +
 drivers/block/nd/nd-private.h |   14 +
 drivers/block/nd/nd.h |   33 +
 drivers/block/nd/pmem.c   |   22 +
 drivers/block/nd/region_devs.c|  145 +
 include/linux/nd.h|   24 +
 include/uapi/linux/ndctl.h|4 
 14 files changed, 1512 insertions(+), 12 deletions(-)

diff --git a/drivers/block/nd/bus.c b/drivers/block/nd/bus.c
index 8afb8d4a7e81..819259e92468 100644
--- a/drivers/block/nd/bus.c
+++ b/drivers/block/nd/bus.c
@@ -364,8 +364,10 @@ u32 nd_cmd_out_size(struct nd_dimm *nd_dimm, int cmd,
 }
 EXPORT_SYMBOL_GPL(nd_cmd_out_size);
 
-static void wait_nd_bus_probe_idle(struct nd_bus *nd_bus)
+void wait_nd_bus_probe_idle(struct device *dev)
 {
+   struct nd_bus *nd_bus = walk_to_nd_bus(dev);
+
do {
if (nd_bus-probe_active == 0)
break;
@@ -384,7 +386,7 @@ static int nd_cmd_clear_to_send(struct nd_dimm *nd_dimm, 
unsigned int cmd)
return 0;
 
nd_bus = walk_to_nd_bus(nd_dimm-dev);
-   wait_nd_bus_probe_idle(nd_bus);
+   wait_nd_bus_probe_idle(nd_bus-dev);
 
if (atomic_read(nd_dimm-busy))
return -EBUSY;
diff --git a/drivers/block/nd/core.c b/drivers/block/nd/core.c
index 603970d0ef3a..cf64b7a50d3a 100644
--- a/drivers/block/nd/core.c
+++ b/drivers/block/nd/core.c
@@ -13,6 +13,7 @@
 #include linux/export.h
 #include linux/module.h
 #include linux/device.h
+#include linux/ctype.h
 #include linux/ndctl.h
 #include linux/mutex.h
 #include linux/slab.h
@@ -106,6 +107,69 @@ struct nd_bus *walk_to_nd_bus(struct device *nd_dev)
return NULL;
 }
 
+static bool is_uuid_sep(char sep)
+{
+   if (sep == '\n' || sep == '-' || sep == ':' || sep == '\0')
+   return true;
+   return false;
+}
+
+static int nd_uuid_parse(struct device *dev, u8 *uuid_out, const char *buf,
+   size_t len)
+{
+   const char *str = buf;
+   u8 uuid[16];
+   int i;
+
+   for (i = 0; i  16; i++) {
+   if (!isxdigit(str[0]) || !isxdigit(str[1])) {
+   dev_dbg(dev, %s: pos: %d buf[%zd]: %c buf[%zd]: %c\n,
+   __func__, i, str - buf, str[0],
+   str + 1 - buf, str[1]);
+   return -EINVAL;
+   }
+
+   uuid[i] = (hex_to_bin(str[0])  4) | hex_to_bin(str[1]);
+   str += 2;
+   if (is_uuid_sep(*str))
+   str++;
+   }
+
+   memcpy(uuid_out, uuid, sizeof(uuid));
+   return 0;
+}
+
+/**
+ * nd_uuid_store: common implementation for writing 'uuid' sysfs attributes
+ * @dev: container device for the uuid property
+ * @uuid_out: uuid buffer to replace
+ * @buf: raw sysfs buffer to parse
+ *
+ * Enforce that uuids can only be changed while the device is disabled
+ * (driver detached)
+ * LOCKING: expects device_lock() is held on entry
+ */
+int nd_uuid_store(struct device *dev, u8 **uuid_out, const char *buf,
+   size_t len)
+{
+   u8 uuid[16];
+   int rc;
+
+   if (dev-driver)
+   return -EBUSY;
+
+   rc = nd_uuid_parse(dev, uuid, buf, len);
+   if (rc)
+   return rc;
+
+   kfree(*uuid_out);
+   *uuid_out = kmemdup(uuid, sizeof(uuid), GFP_KERNEL);
+   if (!(*uuid_out))
+   return -ENOMEM;
+
+   return 0;
+}
+
 static ssize_t commands_show(struct device *dev,
struct device_attribute *attr, char *buf)
 {
diff --git a/drivers/block/nd/dimm.c b/drivers/block/nd/dimm.c
index 5477176c5de0..e2f964308672 100644
--- a/drivers/block/nd/dimm.c
+++ b/drivers/block/nd/dimm.c
@@ -86,7 +86,7 @@ static int nd_dimm_remove(struct device *dev)
nd_bus_lock(dev);
dev_set_drvdata(dev, NULL);
for_each_dpa_resource_safe(ndd, res, _r)
-   __release_region(ndd-dpa, res-start, resource_size(res));
+