[PATCH v2 06/20] libnd: ndctl.h, the nd ioctl abi

2015-04-28 Thread Dan Williams
Most configuration of the nd-subsystem is done via nd-sysfs attributes.
However, some nd buses, particularly the ACPI.NFIT bus, define a small
set of messages that can be passed to the platform.  For convenience we
derivce the initial nd-ioctl-command formats directly from the NFIT DSM
formats.

ND_CMD_SMART: media health and diagnostics
ND_CMD_GET_CONFIG_SIZE: size of the label space
ND_CMD_GET_CONFIG_DATA: read label space
ND_CMD_SET_CONFIG_DATA: write label space
ND_CMD_VENDOR: vendor-specific command passthrough
ND_CMD_ARS_CAP: report address-range-scrubbing capabilities
ND_CMD_START_ARS: initiate scrubbing
ND_CMD_QUERY_ARS: report on scrubbing state
ND_CMD_SMART_THRESHOLD: configure alarm thresholds for smart events

If a platform later defines different commands than this set it is
straightforward to extend support to those formats.

Most of the commands target a specific dimm.  However, the
address-range-scrubbing commands target the bus.  The 'commands'
attribute in sysfs of an nd-bus, or an nd-dimm enumerate the supported
commands for that object.

Cc: 
Cc: Robert Moore 
Cc: Rafael J. Wysocki 
Reported-by: Nicholas Moulin 
Signed-off-by: Dan Williams 
---
 drivers/block/nd/Kconfig  |   12 ++
 drivers/block/nd/acpi.c   |  237 ++
 drivers/block/nd/acpi_nfit.h  |3 
 drivers/block/nd/bus.c|  324 -
 drivers/block/nd/core.c   |   16 ++
 drivers/block/nd/dimm_devs.c  |   38 -
 drivers/block/nd/libnd.h  |   25 +++
 drivers/block/nd/nd-private.h |3 
 drivers/block/nd/test/nfit.c  |   78 ++
 include/uapi/linux/Kbuild |1 
 include/uapi/linux/ndctl.h|  178 +++
 11 files changed, 903 insertions(+), 12 deletions(-)
 create mode 100644 include/uapi/linux/ndctl.h

diff --git a/drivers/block/nd/Kconfig b/drivers/block/nd/Kconfig
index 09f0135147ca..d2d84451e82c 100644
--- a/drivers/block/nd/Kconfig
+++ b/drivers/block/nd/Kconfig
@@ -37,6 +37,18 @@ config ND_ACPI
  addition to storage devices this also enables libnd craft
  ACPI._DSM messages for platform/dimm configuration.
 
+config ND_ACPI_DEBUG
+   bool "ACPI: Extra nd_acpi debugging"
+   depends on ND_ACPI
+   depends on DYNAMIC_DEBUG
+   default n
+   help
+ Enabling this option causes the nd_acpi driver to dump the
+ input and output buffers of _DSM operations on the ACPI0012
+ device and its children.  This can be very verbose, so leave
+ it disabled unless you are debugging a hardware / firmware
+ issue.
+
 config NFIT_TEST
tristate "NFIT TEST: Manufactured NFIT for interface testing"
depends on DMA_CMA
diff --git a/drivers/block/nd/acpi.c b/drivers/block/nd/acpi.c
index af6684341c9b..c46e166695f7 100644
--- a/drivers/block/nd/acpi.c
+++ b/drivers/block/nd/acpi.c
@@ -12,6 +12,7 @@
  */
 #include 
 #include 
+#include 
 #include 
 #include 
 #include "acpi_nfit.h"
@@ -25,11 +26,160 @@ enum {
NFIT_ACPI_NOTIFY_TABLE = 0x80,
 };
 
+static u8 nd_acpi_uuids[2][16]; /* initialized at nd_acpi_init */
+
+static u8 *nd_acpi_bus_uuid(void)
+{
+   return nd_acpi_uuids[0];
+}
+
+static u8 *nd_acpi_dimm_uuid(void)
+{
+   return nd_acpi_uuids[1];
+}
+
+static struct acpi_nfit_desc *to_acpi_nfit_desc(struct nd_bus_descriptor 
*nd_desc)
+{
+   return container_of(nd_desc, struct acpi_nfit_desc, nd_desc);
+}
+
+static struct acpi_device *to_acpi_dev(struct acpi_nfit_desc *acpi_desc)
+{
+   struct nd_bus_descriptor *nd_desc = _desc->nd_desc;
+
+   /*
+* If provider == 'ACPI.NFIT' we can assume 'dev' is a struct
+* acpi_device.
+*/
+   if (!nd_desc->provider_name
+   || strcmp(nd_desc->provider_name, "ACPI.NFIT") != 0)
+   return NULL;
+
+   return to_acpi_device(acpi_desc->dev);
+}
+
 static int nd_acpi_ctl(struct nd_bus_descriptor *nd_desc,
struct nd_dimm *nd_dimm, unsigned int cmd, void *buf,
unsigned int buf_len)
 {
-   return -ENOTTY;
+   struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc);
+   const struct nd_cmd_desc const *desc = NULL;
+   union acpi_object in_obj, in_buf, *out_obj;
+   struct device *dev = acpi_desc->dev;
+   const char *cmd_name, *dimm_name;
+   unsigned long dsm_mask;
+   acpi_handle handle;
+   u32 offset;
+   int rc, i;
+   u8 *uuid;
+
+   if (nd_dimm) {
+   struct nfit_mem *nfit_mem = nd_dimm_provider_data(nd_dimm);
+   struct acpi_device *adev = nfit_mem->adev;
+
+   if (!adev)
+   return -ENOTTY;
+   dimm_name = dev_name(>dev);
+   cmd_name = nd_dimm_cmd_name(cmd);
+   dsm_mask = nfit_mem->dsm_mask;
+   desc = nd_cmd_dimm_desc(cmd);
+   uuid = nd_acpi_dimm_uuid();
+   

[PATCH v2 06/20] libnd: ndctl.h, the nd ioctl abi

2015-04-28 Thread Dan Williams
Most configuration of the nd-subsystem is done via nd-sysfs attributes.
However, some nd buses, particularly the ACPI.NFIT bus, define a small
set of messages that can be passed to the platform.  For convenience we
derivce the initial nd-ioctl-command formats directly from the NFIT DSM
formats.

ND_CMD_SMART: media health and diagnostics
ND_CMD_GET_CONFIG_SIZE: size of the label space
ND_CMD_GET_CONFIG_DATA: read label space
ND_CMD_SET_CONFIG_DATA: write label space
ND_CMD_VENDOR: vendor-specific command passthrough
ND_CMD_ARS_CAP: report address-range-scrubbing capabilities
ND_CMD_START_ARS: initiate scrubbing
ND_CMD_QUERY_ARS: report on scrubbing state
ND_CMD_SMART_THRESHOLD: configure alarm thresholds for smart events

If a platform later defines different commands than this set it is
straightforward to extend support to those formats.

Most of the commands target a specific dimm.  However, the
address-range-scrubbing commands target the bus.  The 'commands'
attribute in sysfs of an nd-bus, or an nd-dimm enumerate the supported
commands for that object.

Cc: linux-a...@vger.kernel.org
Cc: Robert Moore robert.mo...@intel.com
Cc: Rafael J. Wysocki rafael.j.wyso...@intel.com
Reported-by: Nicholas Moulin nicholas.w.mou...@linux.intel.com
Signed-off-by: Dan Williams dan.j.willi...@intel.com
---
 drivers/block/nd/Kconfig  |   12 ++
 drivers/block/nd/acpi.c   |  237 ++
 drivers/block/nd/acpi_nfit.h  |3 
 drivers/block/nd/bus.c|  324 -
 drivers/block/nd/core.c   |   16 ++
 drivers/block/nd/dimm_devs.c  |   38 -
 drivers/block/nd/libnd.h  |   25 +++
 drivers/block/nd/nd-private.h |3 
 drivers/block/nd/test/nfit.c  |   78 ++
 include/uapi/linux/Kbuild |1 
 include/uapi/linux/ndctl.h|  178 +++
 11 files changed, 903 insertions(+), 12 deletions(-)
 create mode 100644 include/uapi/linux/ndctl.h

diff --git a/drivers/block/nd/Kconfig b/drivers/block/nd/Kconfig
index 09f0135147ca..d2d84451e82c 100644
--- a/drivers/block/nd/Kconfig
+++ b/drivers/block/nd/Kconfig
@@ -37,6 +37,18 @@ config ND_ACPI
  addition to storage devices this also enables libnd craft
  ACPI._DSM messages for platform/dimm configuration.
 
+config ND_ACPI_DEBUG
+   bool ACPI: Extra nd_acpi debugging
+   depends on ND_ACPI
+   depends on DYNAMIC_DEBUG
+   default n
+   help
+ Enabling this option causes the nd_acpi driver to dump the
+ input and output buffers of _DSM operations on the ACPI0012
+ device and its children.  This can be very verbose, so leave
+ it disabled unless you are debugging a hardware / firmware
+ issue.
+
 config NFIT_TEST
tristate NFIT TEST: Manufactured NFIT for interface testing
depends on DMA_CMA
diff --git a/drivers/block/nd/acpi.c b/drivers/block/nd/acpi.c
index af6684341c9b..c46e166695f7 100644
--- a/drivers/block/nd/acpi.c
+++ b/drivers/block/nd/acpi.c
@@ -12,6 +12,7 @@
  */
 #include linux/list_sort.h
 #include linux/module.h
+#include linux/ndctl.h
 #include linux/list.h
 #include linux/acpi.h
 #include acpi_nfit.h
@@ -25,11 +26,160 @@ enum {
NFIT_ACPI_NOTIFY_TABLE = 0x80,
 };
 
+static u8 nd_acpi_uuids[2][16]; /* initialized at nd_acpi_init */
+
+static u8 *nd_acpi_bus_uuid(void)
+{
+   return nd_acpi_uuids[0];
+}
+
+static u8 *nd_acpi_dimm_uuid(void)
+{
+   return nd_acpi_uuids[1];
+}
+
+static struct acpi_nfit_desc *to_acpi_nfit_desc(struct nd_bus_descriptor 
*nd_desc)
+{
+   return container_of(nd_desc, struct acpi_nfit_desc, nd_desc);
+}
+
+static struct acpi_device *to_acpi_dev(struct acpi_nfit_desc *acpi_desc)
+{
+   struct nd_bus_descriptor *nd_desc = acpi_desc-nd_desc;
+
+   /*
+* If provider == 'ACPI.NFIT' we can assume 'dev' is a struct
+* acpi_device.
+*/
+   if (!nd_desc-provider_name
+   || strcmp(nd_desc-provider_name, ACPI.NFIT) != 0)
+   return NULL;
+
+   return to_acpi_device(acpi_desc-dev);
+}
+
 static int nd_acpi_ctl(struct nd_bus_descriptor *nd_desc,
struct nd_dimm *nd_dimm, unsigned int cmd, void *buf,
unsigned int buf_len)
 {
-   return -ENOTTY;
+   struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc);
+   const struct nd_cmd_desc const *desc = NULL;
+   union acpi_object in_obj, in_buf, *out_obj;
+   struct device *dev = acpi_desc-dev;
+   const char *cmd_name, *dimm_name;
+   unsigned long dsm_mask;
+   acpi_handle handle;
+   u32 offset;
+   int rc, i;
+   u8 *uuid;
+
+   if (nd_dimm) {
+   struct nfit_mem *nfit_mem = nd_dimm_provider_data(nd_dimm);
+   struct acpi_device *adev = nfit_mem-adev;
+
+   if (!adev)
+   return -ENOTTY;
+   dimm_name = dev_name(adev-dev);
+