All of lore.kernel.org
 help / color / mirror / Atom feed
* [ndctl PATCH v2 0/4] ndctl: add support for HPE type N SMART health data
@ 2016-09-13 17:41 Brian Boylston
  2016-09-13 17:41 ` [ndctl PATCH v2 1/4] libndctl: introduce ndctl_smart_ops Brian Boylston
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Brian Boylston @ 2016-09-13 17:41 UTC (permalink / raw)
  To: linux-nvdimm

This set of patches adds support for the HPE SMART DSM functions and enables
ndctl to report DIMM health data for HPE type N NVDIMMs.  The relevant
firmware interfaces are described in [1].

The first patch virtualizes the ndctl_cmd_smart*() family of libndctl
interfaces into a set of ndctl_smart_ops, allowing runtime implementation
differentiation depending on the firmware support provided by a DIMM.

The second and third patches add miscellaneous pieces needed for the
final patch:

The fourth patch adds a set of ndctl_smart_ops for the HPE1 DSM family,
based on the firmware interfaces defined in [1].  These ndctl_smart_ops
translate the HPE1 DSM output to match the interface of the existing
Intel DSM-inspired smart_ops.  This delivers health reporting parity for
HPE type N NVDIMMs, however:

When evaluating this ndctl_smart_ops approach, please consider our goal of
adding JSON exports for some of the additional health data defined in [1].
I expect this would entail adding additional accessor functions to
ndctl_smart_ops, but it's not clear whether or how to extend the existing
get_flags()/check flags/get_data() model used by util_dimm_health_to_json().

If you'd like to test these changes, note the following:

. Some of the DSM functions for HPE type N NVDIMMs, including the ones used
  by this patch, require the acpi_ipmi kernel module to be loaded, and you
  may need to manually modprobe it.

. Without [2], you'll need to include '--idle' in your ndctl invocation as
  ndctl will consider type Ns to be disabled and will otherwise omit them.

. Without [3], "alarm_temperature" and "alarm_spares" will be inaccurate.

[1] https://github.com/HewlettPackard/hpe-nvm/raw/master/Documentation/NFIT_DSM_DDR4_NVDIMM-N_v84s.pdf
[2] https://lists.01.org/pipermail/linux-nvdimm/2016-August/006619.html
[3] https://lists.01.org/pipermail/linux-nvdimm/2016-September/006810.html

Changes in v2:
  New approach: taught libndctl how to translate between the HPE1 DSM
  family and the existing ndctl_cmd_smart*() libndctl interfaces
  (as suggested by Dan).

Brian Boylston (4):
  libndctl: introduce ndctl_smart_ops
  libndctl: record dsm family in add_dimm()
  libndctl: enable ND_CMD_CALL
  libndctl: add support for the HPE1 family of DSM SMART functions

 ndctl/Makefile.am            |   1 +
 ndctl/lib/libndctl-hpe1.c    | 303 ++++++++++++++++++++++++++++++++++++++
 ndctl/lib/libndctl-private.h |  22 +++
 ndctl/lib/libndctl-smart.c   | 111 +++++++++++---
 ndctl/lib/libndctl.c         |  20 ++-
 ndctl/lib/ndctl-hpe1.h       | 335 +++++++++++++++++++++++++++++++++++++++++++
 ndctl/libndctl.h.in          |   1 +
 7 files changed, 769 insertions(+), 24 deletions(-)
 create mode 100644 ndctl/lib/libndctl-hpe1.c
 create mode 100644 ndctl/lib/ndctl-hpe1.h

-- 
2.8.3

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [ndctl PATCH v2 1/4] libndctl: introduce ndctl_smart_ops
  2016-09-13 17:41 [ndctl PATCH v2 0/4] ndctl: add support for HPE type N SMART health data Brian Boylston
@ 2016-09-13 17:41 ` Brian Boylston
  2016-09-15  2:22   ` Dan Williams
  2016-09-13 17:41 ` [ndctl PATCH v2 2/4] libndctl: record dsm family in add_dimm() Brian Boylston
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 9+ messages in thread
From: Brian Boylston @ 2016-09-13 17:41 UTC (permalink / raw)
  To: linux-nvdimm

Add a layer of indirection for the ndctl_cmd_smart*() family of
interfaces.  This will allow the underlying implementation to be
switched based on the DSM family supported by the DIMM.

Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Brian Boylston <brian.boylston@hpe.com>
---
 ndctl/lib/libndctl-private.h |  21 ++++++++
 ndctl/lib/libndctl-smart.c   | 111 ++++++++++++++++++++++++++++++++++---------
 ndctl/lib/libndctl.c         |   8 ++++
 ndctl/libndctl.h.in          |   1 +
 4 files changed, 118 insertions(+), 23 deletions(-)

diff --git a/ndctl/lib/libndctl-private.h b/ndctl/lib/libndctl-private.h
index 65ef86d..8d2ebfc 100644
--- a/ndctl/lib/libndctl-private.h
+++ b/ndctl/lib/libndctl-private.h
@@ -201,6 +201,27 @@ struct ndctl_cmd {
 	};
 };
 
+struct ndctl_smart_ops {
+	struct ndctl_cmd *(*new_smart)(struct ndctl_dimm *);
+	unsigned int (*smart_get_flags)(struct ndctl_cmd *);
+	unsigned int (*smart_get_health)(struct ndctl_cmd *);
+	unsigned int (*smart_get_temperature)(struct ndctl_cmd *);
+	unsigned int (*smart_get_spares)(struct ndctl_cmd *);
+	unsigned int (*smart_get_alarm_flags)(struct ndctl_cmd *);
+	unsigned int (*smart_get_life_used)(struct ndctl_cmd *);
+	unsigned int (*smart_get_shutdown_state)(struct ndctl_cmd *);
+	unsigned int (*smart_get_vendor_size)(struct ndctl_cmd *);
+	unsigned char *(*smart_get_vendor_data)(struct ndctl_cmd *);
+	struct ndctl_cmd *(*new_smart_threshold)(struct ndctl_dimm *);
+	unsigned int (*smart_threshold_get_alarm_control)(struct ndctl_cmd *);
+	unsigned int (*smart_threshold_get_temperature)(struct ndctl_cmd *);
+	unsigned int (*smart_threshold_get_spares)(struct ndctl_cmd *);
+};
+
+#if HAS_SMART == 1
+struct ndctl_smart_ops intel_smart_ops;
+#endif
+
 /* internal library helpers for conditionally defined command numbers */
 #ifdef HAVE_NDCTL_ARS
 static const int nd_cmd_ars_status = ND_CMD_ARS_STATUS;
diff --git a/ndctl/lib/libndctl-smart.c b/ndctl/lib/libndctl-smart.c
index cba1e9d..a172541 100644
--- a/ndctl/lib/libndctl-smart.c
+++ b/ndctl/lib/libndctl-smart.c
@@ -16,7 +16,55 @@
 #include <ndctl/libndctl.h>
 #include "libndctl-private.h"
 
-NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_smart(struct ndctl_dimm *dimm)
+/*
+ * The smart_dimm_op() and smart_cmd_op() macros are used here to
+ * define the wrappers around the ndctl_smart_ops:
+ */
+
+#define smart_dimm_op(name, op) \
+NDCTL_EXPORT struct ndctl_cmd *name( \
+		struct ndctl_dimm *dimm) \
+{ \
+	struct ndctl_smart_ops *ops = ndctl_dimm_get_smart_ops(dimm); \
+	if (ops && ops->op) \
+		return ops->op(dimm); \
+	else \
+		return NULL; \
+}
+
+smart_dimm_op(ndctl_dimm_cmd_new_smart, new_smart)
+smart_dimm_op(ndctl_dimm_cmd_new_smart_threshold, new_smart_threshold)
+
+#define smart_cmd_op(name, op, rettype, defretvalue) \
+NDCTL_EXPORT rettype name(struct ndctl_cmd *cmd) \
+{ \
+	if (cmd->dimm) { \
+		struct ndctl_smart_ops *ops = ndctl_dimm_get_smart_ops(cmd->dimm); \
+		if (ops && ops->op) \
+			return ops->op(cmd); \
+	} \
+	return defretvalue; \
+}
+
+smart_cmd_op(ndctl_cmd_smart_get_flags, smart_get_flags, unsigned int, 0)
+smart_cmd_op(ndctl_cmd_smart_get_health, smart_get_health, unsigned int, 0)
+smart_cmd_op(ndctl_cmd_smart_get_temperature, smart_get_temperature, unsigned int, 0)
+smart_cmd_op(ndctl_cmd_smart_get_spares, smart_get_spares, unsigned int, 0)
+smart_cmd_op(ndctl_cmd_smart_get_alarm_flags, smart_get_alarm_flags, unsigned int, 0)
+smart_cmd_op(ndctl_cmd_smart_get_life_used, smart_get_life_used, unsigned int, 0)
+smart_cmd_op(ndctl_cmd_smart_get_shutdown_state, smart_get_shutdown_state, unsigned int, 0)
+smart_cmd_op(ndctl_cmd_smart_get_vendor_size, smart_get_vendor_size, unsigned int, 0)
+smart_cmd_op(ndctl_cmd_smart_get_vendor_data, smart_get_vendor_data, unsigned char *, NULL)
+smart_cmd_op(ndctl_cmd_smart_threshold_get_alarm_control, smart_threshold_get_alarm_control, unsigned int, 0)
+smart_cmd_op(ndctl_cmd_smart_threshold_get_temperature, smart_threshold_get_temperature, unsigned int, 0)
+smart_cmd_op(ndctl_cmd_smart_threshold_get_spares, smart_threshold_get_spares, unsigned int, 0)
+
+/*
+ * The following intel_dimm_*() and intel_smart_*() functions implement
+ * the ndctl_smart_ops for the Intel DSM family (NVDIMM_FAMILY_INTEL):
+ */
+
+static struct ndctl_cmd *intel_dimm_cmd_new_smart(struct ndctl_dimm *dimm)
 {
 	struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm);
 	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
@@ -45,66 +93,66 @@ NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_smart(struct ndctl_dimm *dimm)
 	return cmd;
 }
 
-static int smart_valid(struct ndctl_cmd *cmd)
+static int intel_smart_valid(struct ndctl_cmd *cmd)
 {
 	if (cmd->type != ND_CMD_SMART || cmd->status != 0)
 		return cmd->status < 0 ? cmd->status : -EINVAL;
 	return 0;
 }
 
-#define smart_get_field(cmd, field) \
-NDCTL_EXPORT unsigned int ndctl_cmd_smart_get_##field(struct ndctl_cmd *cmd) \
+#define intel_smart_get_field(cmd, field) \
+static unsigned int intel_cmd_smart_get_##field(struct ndctl_cmd *cmd) \
 { \
 	struct nd_smart_payload *smart_data; \
-	if (smart_valid(cmd) < 0) \
+	if (intel_smart_valid(cmd) < 0) \
 		return UINT_MAX; \
 	smart_data = (struct nd_smart_payload *) cmd->smart->data; \
 	return smart_data->field; \
 }
 
-smart_get_field(cmd, flags)
-smart_get_field(cmd, health)
-smart_get_field(cmd, temperature)
-smart_get_field(cmd, spares)
-smart_get_field(cmd, alarm_flags)
-smart_get_field(cmd, life_used)
-smart_get_field(cmd, shutdown_state)
-smart_get_field(cmd, vendor_size)
+intel_smart_get_field(cmd, flags)
+intel_smart_get_field(cmd, health)
+intel_smart_get_field(cmd, temperature)
+intel_smart_get_field(cmd, spares)
+intel_smart_get_field(cmd, alarm_flags)
+intel_smart_get_field(cmd, life_used)
+intel_smart_get_field(cmd, shutdown_state)
+intel_smart_get_field(cmd, vendor_size)
 
-NDCTL_EXPORT unsigned char *ndctl_cmd_smart_get_vendor_data(struct ndctl_cmd *cmd)
+static unsigned char *intel_cmd_smart_get_vendor_data(struct ndctl_cmd *cmd)
 {
 	struct nd_smart_payload *smart_data;
 
-	if (smart_valid(cmd) < 0)
+	if (intel_smart_valid(cmd) < 0)
 		return NULL;
 	smart_data = (struct nd_smart_payload *) cmd->smart->data;
 	return (unsigned char *) smart_data->vendor_data;
 }
 
-static int smart_threshold_valid(struct ndctl_cmd *cmd)
+static int intel_smart_threshold_valid(struct ndctl_cmd *cmd)
 {
 	if (cmd->type != ND_CMD_SMART_THRESHOLD || cmd->status != 0)
 		return cmd->status < 0 ? cmd->status : -EINVAL;
 	return 0;
 }
 
-#define smart_threshold_get_field(cmd, field) \
-NDCTL_EXPORT unsigned int ndctl_cmd_smart_threshold_get_##field( \
+#define intel_smart_threshold_get_field(cmd, field) \
+static unsigned int intel_cmd_smart_threshold_get_##field( \
 			struct ndctl_cmd *cmd) \
 { \
 	struct nd_smart_threshold_payload *smart_t_data; \
-	if (smart_threshold_valid(cmd) < 0) \
+	if (intel_smart_threshold_valid(cmd) < 0) \
 		return UINT_MAX; \
 	smart_t_data = (struct nd_smart_threshold_payload *) \
 			cmd->smart_t->data; \
 	return smart_t_data->field; \
 }
 
-smart_threshold_get_field(cmd, alarm_control)
-smart_threshold_get_field(cmd, temperature)
-smart_threshold_get_field(cmd, spares)
+intel_smart_threshold_get_field(cmd, alarm_control)
+intel_smart_threshold_get_field(cmd, temperature)
+intel_smart_threshold_get_field(cmd, spares)
 
-NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_smart_threshold(
+static struct ndctl_cmd *intel_dimm_cmd_new_smart_threshold(
 		struct ndctl_dimm *dimm)
 {
 	struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm);
@@ -133,3 +181,20 @@ NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_smart_threshold(
 
 	return cmd;
 }
+
+struct ndctl_smart_ops intel_smart_ops = {
+	.new_smart = intel_dimm_cmd_new_smart,
+	.smart_get_flags = intel_cmd_smart_get_flags,
+	.smart_get_health = intel_cmd_smart_get_health,
+	.smart_get_temperature = intel_cmd_smart_get_temperature,
+	.smart_get_spares = intel_cmd_smart_get_spares,
+	.smart_get_alarm_flags = intel_cmd_smart_get_alarm_flags,
+	.smart_get_life_used = intel_cmd_smart_get_life_used,
+	.smart_get_shutdown_state = intel_cmd_smart_get_shutdown_state,
+	.smart_get_vendor_size = intel_cmd_smart_get_vendor_size,
+	.smart_get_vendor_data = intel_cmd_smart_get_vendor_data,
+	.new_smart_threshold = intel_dimm_cmd_new_smart_threshold,
+	.smart_threshold_get_alarm_control = intel_cmd_smart_threshold_get_alarm_control,
+	.smart_threshold_get_temperature = intel_cmd_smart_threshold_get_temperature,
+	.smart_threshold_get_spares = intel_cmd_smart_threshold_get_spares,
+};
diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c
index 93f79f7..9a9dc74 100644
--- a/ndctl/lib/libndctl.c
+++ b/ndctl/lib/libndctl.c
@@ -125,6 +125,7 @@ struct ndctl_bus {
 struct ndctl_dimm {
 	struct kmod_module *module;
 	struct ndctl_bus *bus;
+	struct ndctl_smart_ops *smart_ops;
 	unsigned int handle, major, minor, serial;
 	unsigned short phys_id;
 	unsigned short vendor_id;
@@ -1149,6 +1150,8 @@ static int add_dimm(void *parent, int id, const char *dimm_base)
 		goto err_read;
 	dimm->dsm_mask = parse_commands(buf, 1);
 
+	dimm->smart_ops = &intel_smart_ops;
+
 	dimm->dimm_buf = calloc(1, strlen(dimm_base) + 50);
 	if (!dimm->dimm_buf)
 		goto err_read;
@@ -1459,6 +1462,11 @@ NDCTL_EXPORT struct ndctl_bus *ndctl_dimm_get_bus(struct ndctl_dimm *dimm)
 	return dimm->bus;
 }
 
+NDCTL_EXPORT struct ndctl_smart_ops *ndctl_dimm_get_smart_ops(struct ndctl_dimm *dimm)
+{
+	return dimm->smart_ops;
+}
+
 NDCTL_EXPORT struct ndctl_ctx *ndctl_dimm_get_ctx(struct ndctl_dimm *dimm)
 {
 	return dimm->bus->ctx;
diff --git a/ndctl/libndctl.h.in b/ndctl/libndctl.h.in
index 451466a..679225c 100644
--- a/ndctl/libndctl.h.in
+++ b/ndctl/libndctl.h.in
@@ -150,6 +150,7 @@ unsigned int ndctl_dimm_handle_get_channel(struct ndctl_dimm *dimm);
 unsigned int ndctl_dimm_handle_get_dimm(struct ndctl_dimm *dimm);
 const char *ndctl_dimm_get_devname(struct ndctl_dimm *dimm);
 struct ndctl_bus *ndctl_dimm_get_bus(struct ndctl_dimm *dimm);
+struct ndctl_smart_ops *ndctl_dimm_get_smart_ops(struct ndctl_dimm *dimm);
 struct ndctl_ctx *ndctl_dimm_get_ctx(struct ndctl_dimm *dimm);
 struct ndctl_dimm *ndctl_dimm_get_by_handle(struct ndctl_bus *bus,
 		unsigned int handle);
-- 
2.8.3

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [ndctl PATCH v2 2/4] libndctl: record dsm family in add_dimm()
  2016-09-13 17:41 [ndctl PATCH v2 0/4] ndctl: add support for HPE type N SMART health data Brian Boylston
  2016-09-13 17:41 ` [ndctl PATCH v2 1/4] libndctl: introduce ndctl_smart_ops Brian Boylston
@ 2016-09-13 17:41 ` Brian Boylston
  2016-09-15  0:44   ` Dan Williams
  2016-09-13 17:41 ` [ndctl PATCH v2 3/4] libndctl: enable ND_CMD_CALL Brian Boylston
  2016-09-13 17:41 ` [ndctl PATCH v2 4/4] libndctl: add support for the HPE1 family of DSM SMART functions Brian Boylston
  3 siblings, 1 reply; 9+ messages in thread
From: Brian Boylston @ 2016-09-13 17:41 UTC (permalink / raw)
  To: linux-nvdimm

The recorded DSM family can be used to provide family-specific
functionality.

Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Brian Boylston <brian.boylston@hpe.com>
---
 ndctl/lib/libndctl.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c
index 9a9dc74..c824d83 100644
--- a/ndctl/lib/libndctl.c
+++ b/ndctl/lib/libndctl.c
@@ -136,6 +136,7 @@ struct ndctl_dimm {
 	unsigned short subsystem_revision_id;
 	unsigned short manufacturing_date;
 	unsigned char manufacturing_location;
+	unsigned long dsm_family;
 	unsigned long dsm_mask;
 	char *unique_id;
 	char *dimm_path;
@@ -1145,6 +1146,11 @@ static int add_dimm(void *parent, int id, const char *dimm_base)
 	if (sscanf(buf, "%d:%d", &dimm->major, &dimm->minor) != 2)
 		goto err_read;
 
+	sprintf(path, "%s/nfit/family", dimm_base);
+	if (sysfs_read_attr(ctx, path, buf) < 0)
+		goto err_read;
+	dimm->dsm_family = strtoul(buf, NULL, 0);
+
 	sprintf(path, "%s/commands", dimm_base);
 	if (sysfs_read_attr(ctx, path, buf) < 0)
 		goto err_read;
-- 
2.8.3

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [ndctl PATCH v2 3/4] libndctl: enable ND_CMD_CALL
  2016-09-13 17:41 [ndctl PATCH v2 0/4] ndctl: add support for HPE type N SMART health data Brian Boylston
  2016-09-13 17:41 ` [ndctl PATCH v2 1/4] libndctl: introduce ndctl_smart_ops Brian Boylston
  2016-09-13 17:41 ` [ndctl PATCH v2 2/4] libndctl: record dsm family in add_dimm() Brian Boylston
@ 2016-09-13 17:41 ` Brian Boylston
  2016-09-13 17:41 ` [ndctl PATCH v2 4/4] libndctl: add support for the HPE1 family of DSM SMART functions Brian Boylston
  3 siblings, 0 replies; 9+ messages in thread
From: Brian Boylston @ 2016-09-13 17:41 UTC (permalink / raw)
  To: linux-nvdimm

. Enable parsing /sys/bus/nd/devices/nmemX/commands for ND_CMD_CALL
. Enable translation of ND_CMD_CALL to ND_IOCTL_CALL

Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Brian Boylston <brian.boylston@hpe.com>
---
 ndctl/lib/libndctl.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c
index c824d83..1e07520 100644
--- a/ndctl/lib/libndctl.c
+++ b/ndctl/lib/libndctl.c
@@ -719,7 +719,7 @@ static int to_dsm_index(const char *name, int dimm)
 	int i, end_cmd;
 
 	if (dimm) {
-		end_cmd = ND_CMD_VENDOR;
+		end_cmd = ND_CMD_CALL;
 		cmd_name_fn = nvdimm_cmd_name;
 	} else {
 		end_cmd = nd_cmd_clear_error;
@@ -2184,6 +2184,7 @@ static int to_ioctl_cmd(int cmd, int dimm)
 	case ND_CMD_GET_CONFIG_DATA:        return ND_IOCTL_GET_CONFIG_DATA;
 	case ND_CMD_SET_CONFIG_DATA:        return ND_IOCTL_SET_CONFIG_DATA;
 	case ND_CMD_VENDOR:                 return ND_IOCTL_VENDOR;
+	case ND_CMD_CALL:                   return ND_IOCTL_CALL;
 	case ND_CMD_VENDOR_EFFECT_LOG_SIZE:
 	case ND_CMD_VENDOR_EFFECT_LOG:
 	default:
-- 
2.8.3

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [ndctl PATCH v2 4/4] libndctl: add support for the HPE1 family of DSM SMART functions
  2016-09-13 17:41 [ndctl PATCH v2 0/4] ndctl: add support for HPE type N SMART health data Brian Boylston
                   ` (2 preceding siblings ...)
  2016-09-13 17:41 ` [ndctl PATCH v2 3/4] libndctl: enable ND_CMD_CALL Brian Boylston
@ 2016-09-13 17:41 ` Brian Boylston
  3 siblings, 0 replies; 9+ messages in thread
From: Brian Boylston @ 2016-09-13 17:41 UTC (permalink / raw)
  To: linux-nvdimm

This patch introduces a set of ndctl_smart_ops for the HPE1 DSM family.
The implementation calls the HPE1 DSM functions defined in [1] and
translates the results to match the existing Intel DSM-inspired
smart_ops.  This delivers health reporting parity for HPE type N
NVDIMMs, but we are planning a future patch to add reporting for some of
the additional health data defined in [1].

Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Brian Boylston <brian.boylston@hpe.com>
---
 ndctl/Makefile.am            |   1 +
 ndctl/lib/libndctl-hpe1.c    | 303 ++++++++++++++++++++++++++++++++++++++
 ndctl/lib/libndctl-private.h |   1 +
 ndctl/lib/libndctl.c         |   5 +-
 ndctl/lib/ndctl-hpe1.h       | 335 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 644 insertions(+), 1 deletion(-)
 create mode 100644 ndctl/lib/libndctl-hpe1.c
 create mode 100644 ndctl/lib/ndctl-hpe1.h

diff --git a/ndctl/Makefile.am b/ndctl/Makefile.am
index 04f3a63..fdec355 100644
--- a/ndctl/Makefile.am
+++ b/ndctl/Makefile.am
@@ -31,6 +31,7 @@ endif
 
 if ENABLE_SMART
 lib_libndctl_la_SOURCES += lib/libndctl-smart.c
+lib_libndctl_la_SOURCES += lib/libndctl-hpe1.c
 endif
 
 bin_PROGRAMS = ndctl
diff --git a/ndctl/lib/libndctl-hpe1.c b/ndctl/lib/libndctl-hpe1.c
new file mode 100644
index 0000000..b61acf4
--- /dev/null
+++ b/ndctl/lib/libndctl-hpe1.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2016 Hewlett Packard Enterprise Development LP
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+#include <stdlib.h>
+#include <limits.h>
+#include <util/log.h>
+#include <ndctl/libndctl.h>
+#include "libndctl-private.h"
+
+#include "ndctl-hpe1.h"
+
+#define CMD_HPE1(_c) ((struct ndn_pkg_hpe1 *)((_c)->cmd_buf))
+#define CMD_HPE1_SMART(_c) \
+	((struct ndn_hpe1_smart_data *)(CMD_HPE1(_c)->u.smart.data))
+#define CMD_HPE1_SMART_THRESH(_c) \
+	((struct ndn_hpe1_smart_threshold_data *)(CMD_HPE1(_c)->u.thresh.data))
+
+static struct ndctl_cmd *hpe1_dimm_cmd_new_smart(struct ndctl_dimm *dimm)
+{
+	struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm);
+	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
+	struct ndctl_cmd *cmd;
+	size_t size;
+	struct ndn_pkg_hpe1 *hpe1;
+
+	if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_CALL)) {
+		dbg(ctx, "unsupported cmd\n");
+		return NULL;
+	}
+
+	size = sizeof(*cmd) + sizeof(struct ndn_pkg_hpe1);
+	cmd = calloc(1, size);
+	if (!cmd)
+		return NULL;
+
+	cmd->dimm = dimm;
+	ndctl_cmd_ref(cmd);
+	cmd->type = ND_CMD_CALL;
+	cmd->size = size;
+	cmd->status = 1;
+
+	hpe1 = CMD_HPE1(cmd);
+	hpe1->gen.nd_family = NVDIMM_FAMILY_HPE1;
+	hpe1->gen.nd_command = NDN_HPE1_CMD_SMART;
+	hpe1->gen.nd_fw_size = 0;
+	hpe1->gen.nd_size_in = offsetof(struct ndn_hpe1_smart, status);
+	hpe1->gen.nd_size_out = sizeof(hpe1->u.smart);
+	hpe1->u.smart.status = 3;
+
+	hpe1->u.smart.in_valid_flags = 0;
+	hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_HEALTH_VALID;
+	hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_TEMP_VALID;
+	hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_SPARES_VALID;
+	hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_ALARM_VALID;
+	hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_USED_VALID;
+	hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_SHUTDOWN_VALID;
+	hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_VENDOR_VALID;
+
+	cmd->firmware_status = &hpe1->u.smart.status;
+
+	return cmd;
+}
+
+static int hpe1_smart_valid(struct ndctl_cmd *cmd)
+{
+	if (cmd->type != ND_CMD_CALL ||
+	    cmd->size != sizeof(*cmd) + sizeof(struct ndn_pkg_hpe1) ||
+	    CMD_HPE1(cmd)->gen.nd_family != NVDIMM_FAMILY_HPE1 ||
+	    CMD_HPE1(cmd)->gen.nd_command != NDN_HPE1_CMD_SMART ||
+	    cmd->status != 0)
+		return cmd->status < 0 ? cmd->status : -EINVAL;
+	return 0;
+}
+
+static unsigned int hpe1_cmd_smart_get_flags(struct ndctl_cmd *cmd)
+{
+	unsigned int hpe1flags;
+	unsigned int flags;
+
+	if (hpe1_smart_valid(cmd) < 0)
+		return UINT_MAX;
+
+	hpe1flags = CMD_HPE1_SMART(cmd)->out_valid_flags;
+	flags = 0;
+	if (hpe1flags & NDN_HPE1_SMART_HEALTH_VALID)
+		flags |= ND_SMART_HEALTH_VALID;
+	if (hpe1flags & NDN_HPE1_SMART_TEMP_VALID)
+		flags |= ND_SMART_TEMP_VALID ;
+	if (hpe1flags & NDN_HPE1_SMART_SPARES_VALID)
+		flags |= ND_SMART_SPARES_VALID;
+	if (hpe1flags & NDN_HPE1_SMART_ALARM_VALID)
+		flags |= ND_SMART_ALARM_VALID;
+	if (hpe1flags & NDN_HPE1_SMART_USED_VALID)
+		flags |= ND_SMART_USED_VALID;
+	if (hpe1flags & NDN_HPE1_SMART_SHUTDOWN_VALID)
+		flags |= ND_SMART_SHUTDOWN_VALID;
+	if (hpe1flags & NDN_HPE1_SMART_VENDOR_VALID)
+		flags |= ND_SMART_VENDOR_VALID;
+
+	return flags;
+}
+
+static unsigned int hpe1_cmd_smart_get_health(struct ndctl_cmd *cmd)
+{
+	unsigned char hpe1health;
+	unsigned int health;
+
+	if (hpe1_smart_valid(cmd) < 0)
+		return UINT_MAX;
+
+	hpe1health = CMD_HPE1_SMART(cmd)->stat_summary;
+	health = 0;
+	if (hpe1health & NDN_HPE1_SMART_NONCRIT_HEALTH)
+		health |= ND_SMART_NON_CRITICAL_HEALTH;;
+	if (hpe1health & NDN_HPE1_SMART_CRITICAL_HEALTH)
+		health |= ND_SMART_CRITICAL_HEALTH;
+	if (hpe1health & NDN_HPE1_SMART_FATAL_HEALTH)
+		health |= ND_SMART_FATAL_HEALTH;
+
+	return health;
+}
+
+static unsigned int hpe1_cmd_smart_get_temperature(struct ndctl_cmd *cmd)
+{
+	if (hpe1_smart_valid(cmd) < 0)
+		return UINT_MAX;
+
+	return CMD_HPE1_SMART(cmd)->curr_temp;
+}
+
+static unsigned int hpe1_cmd_smart_get_spares(struct ndctl_cmd *cmd)
+{
+	if (hpe1_smart_valid(cmd) < 0)
+		return UINT_MAX;
+
+	return CMD_HPE1_SMART(cmd)->spare_blocks;
+}
+
+static unsigned int hpe1_cmd_smart_get_alarm_flags(struct ndctl_cmd *cmd)
+{
+	unsigned int hpe1flags;
+	unsigned int flags;
+
+	if (hpe1_smart_valid(cmd) < 0)
+		return UINT_MAX;
+
+	hpe1flags = CMD_HPE1_SMART(cmd)->alarm_trips;
+	flags = 0;
+	if (hpe1flags & NDN_HPE1_SMART_TEMP_TRIP)
+		flags |= ND_SMART_TEMP_TRIP;
+	if (hpe1flags & NDN_HPE1_SMART_SPARE_TRIP)
+		flags |= ND_SMART_SPARE_TRIP;
+
+	return flags;
+}
+
+static unsigned int hpe1_cmd_smart_get_life_used(struct ndctl_cmd *cmd)
+{
+	if (hpe1_smart_valid(cmd) < 0)
+		return UINT_MAX;
+
+	return CMD_HPE1_SMART(cmd)->device_life;
+}
+
+static unsigned int hpe1_cmd_smart_get_shutdown_state(struct ndctl_cmd *cmd)
+{
+	unsigned int shutdown;
+
+	if (hpe1_smart_valid(cmd) < 0)
+		return UINT_MAX;
+
+	shutdown = CMD_HPE1_SMART(cmd)->last_shutdown_stat;
+	if (shutdown == NDN_HPE1_SMART_LASTSAVEGOOD)
+		return 0;
+	else
+		return 1;
+}
+
+static unsigned int hpe1_cmd_smart_get_vendor_size(struct ndctl_cmd *cmd)
+{
+	if (hpe1_smart_valid(cmd) < 0)
+		return UINT_MAX;
+
+	return CMD_HPE1_SMART(cmd)->vndr_spec_data_size;
+}
+
+static unsigned char *hpe1_cmd_smart_get_vendor_data(struct ndctl_cmd *cmd)
+{
+	if (hpe1_smart_valid(cmd) < 0)
+		return NULL;
+
+	return CMD_HPE1_SMART(cmd)->vnd_spec_data;
+}
+
+
+static struct ndctl_cmd *hpe1_dimm_cmd_new_smart_threshold(struct ndctl_dimm *dimm)
+{
+	struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm);
+	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
+	struct ndctl_cmd *cmd;
+	size_t size;
+	struct ndn_pkg_hpe1 *hpe1;
+
+	if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_CALL)) {
+		dbg(ctx, "unsupported cmd\n");
+		return NULL;
+	}
+
+	size = sizeof(*cmd) + sizeof(struct ndn_pkg_hpe1);
+	cmd = calloc(1, size);
+	if (!cmd)
+		return NULL;
+
+	cmd->dimm = dimm;
+	ndctl_cmd_ref(cmd);
+	cmd->type = ND_CMD_CALL;
+	cmd->size = size;
+	cmd->status = 1;
+
+	hpe1 = CMD_HPE1(cmd);
+	hpe1->gen.nd_family = NVDIMM_FAMILY_HPE1;
+	hpe1->gen.nd_command = NDN_HPE1_CMD_SMART_THRESHOLD;
+	hpe1->gen.nd_fw_size = 0;
+	hpe1->gen.nd_size_in = offsetof(struct ndn_hpe1_smart_threshold, status);
+	hpe1->gen.nd_size_out = sizeof(hpe1->u.smart);
+	hpe1->u.thresh.status = 3;
+
+	cmd->firmware_status = &hpe1->u.thresh.status;
+
+	return cmd;
+}
+
+static int hpe1_smart_threshold_valid(struct ndctl_cmd *cmd)
+{
+	if (cmd->type != ND_CMD_CALL ||
+	    cmd->size != sizeof(*cmd) + sizeof(struct ndn_pkg_hpe1) ||
+	    CMD_HPE1(cmd)->gen.nd_family != NVDIMM_FAMILY_HPE1 ||
+	    CMD_HPE1(cmd)->gen.nd_command != NDN_HPE1_CMD_SMART_THRESHOLD ||
+	    cmd->status != 0)
+		return cmd->status < 0 ? cmd->status : -EINVAL;
+	return 0;
+}
+
+static unsigned int hpe1_cmd_smart_threshold_get_alarm_control(struct ndctl_cmd *cmd)
+{
+	unsigned int hpe1flags;
+	unsigned int flags;
+
+	if (hpe1_smart_threshold_valid(cmd) < 0)
+		return UINT_MAX;
+
+	hpe1flags = CMD_HPE1_SMART_THRESH(cmd)->threshold_alarm_ctl;
+	flags = 0;
+	if (hpe1flags & NDN_HPE1_SMART_TEMP_TRIP)
+		flags |= ND_SMART_TEMP_TRIP;
+	if (hpe1flags & NDN_HPE1_SMART_SPARE_TRIP)
+		flags |= ND_SMART_SPARE_TRIP;
+
+	return flags;
+}
+
+static unsigned int hpe1_cmd_smart_threshold_get_temperature(struct ndctl_cmd *cmd)
+{
+	if (hpe1_smart_threshold_valid(cmd) < 0)
+		return UINT_MAX;
+
+	return CMD_HPE1_SMART_THRESH(cmd)->temp_threshold;
+}
+
+static unsigned int hpe1_cmd_smart_threshold_get_spares(struct ndctl_cmd *cmd)
+{
+	if (hpe1_smart_threshold_valid(cmd) < 0)
+		return UINT_MAX;
+
+	return CMD_HPE1_SMART_THRESH(cmd)->spare_block_threshold;
+}
+
+struct ndctl_smart_ops hpe1_smart_ops = {
+	.new_smart = hpe1_dimm_cmd_new_smart,
+	.smart_get_flags = hpe1_cmd_smart_get_flags,
+	.smart_get_health = hpe1_cmd_smart_get_health,
+	.smart_get_temperature = hpe1_cmd_smart_get_temperature,
+	.smart_get_spares = hpe1_cmd_smart_get_spares,
+	.smart_get_alarm_flags = hpe1_cmd_smart_get_alarm_flags,
+	.smart_get_life_used = hpe1_cmd_smart_get_life_used,
+	.smart_get_shutdown_state = hpe1_cmd_smart_get_shutdown_state,
+	.smart_get_vendor_size = hpe1_cmd_smart_get_vendor_size,
+	.smart_get_vendor_data = hpe1_cmd_smart_get_vendor_data,
+	.new_smart_threshold = hpe1_dimm_cmd_new_smart_threshold,
+	.smart_threshold_get_alarm_control = hpe1_cmd_smart_threshold_get_alarm_control,
+	.smart_threshold_get_temperature = hpe1_cmd_smart_threshold_get_temperature,
+	.smart_threshold_get_spares = hpe1_cmd_smart_threshold_get_spares,
+};
diff --git a/ndctl/lib/libndctl-private.h b/ndctl/lib/libndctl-private.h
index 8d2ebfc..52a3c71 100644
--- a/ndctl/lib/libndctl-private.h
+++ b/ndctl/lib/libndctl-private.h
@@ -220,6 +220,7 @@ struct ndctl_smart_ops {
 
 #if HAS_SMART == 1
 struct ndctl_smart_ops intel_smart_ops;
+struct ndctl_smart_ops hpe1_smart_ops;
 #endif
 
 /* internal library helpers for conditionally defined command numbers */
diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c
index 1e07520..c4c8cae 100644
--- a/ndctl/lib/libndctl.c
+++ b/ndctl/lib/libndctl.c
@@ -1156,7 +1156,10 @@ static int add_dimm(void *parent, int id, const char *dimm_base)
 		goto err_read;
 	dimm->dsm_mask = parse_commands(buf, 1);
 
-	dimm->smart_ops = &intel_smart_ops;
+	if (dimm->dsm_family == NVDIMM_FAMILY_HPE1)
+		dimm->smart_ops = &hpe1_smart_ops;
+	else
+		dimm->smart_ops = &intel_smart_ops;
 
 	dimm->dimm_buf = calloc(1, strlen(dimm_base) + 50);
 	if (!dimm->dimm_buf)
diff --git a/ndctl/lib/ndctl-hpe1.h b/ndctl/lib/ndctl-hpe1.h
new file mode 100644
index 0000000..0d41aab
--- /dev/null
+++ b/ndctl/lib/ndctl-hpe1.h
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2016 Hewlett Packard Enterprise Development LP
+ * Copyright (c) 2014-2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+#ifndef __NDCTL_HPE1_H__
+#define __NDCTL_HPE1_H__
+
+enum {
+	NDN_HPE1_CMD_QUERY = 0,
+
+	/* non-root commands */
+	NDN_HPE1_CMD_SMART = 1,
+	NDN_HPE1_CMD_SMART_THRESHOLD = 2,
+	NDN_HPE1_CMD_GET_CONFIG_SIZE = 4,
+	NDN_HPE1_CMD_GET_CONFIG_DATA = 5,
+	NDN_HPE1_CMD_SET_CONFIG_DATA = 6,
+	NDN_HPE1_CMD_GET_IDENT = 10,
+	NDN_HPE1_CMD_GET_ES_IDENT = 11,
+	NDN_HPE1_CMD_GET_LAST_BACKUP = 12,
+	NDN_HPE1_CMD_SET_LIFE_THRESHOLD = 13,
+	NDN_HPE1_CMD_ERRINJ_QUERY = 18,
+	NDN_HPE1_CMD_ERRINJ_INJECT = 19,
+	NDN_HPE1_CMD_ERRINJ_STATUS = 20,
+};
+
+/* NDN_HPE1_CMD_SMART */
+struct ndn_hpe1_smart {
+	__u32 in_valid_flags;
+	__u32 status;
+	__u8 data[124];
+} __attribute__((packed));
+
+/* ndn_hpe1_smart.in_valid_flags / ndn_hpe1_smart_data.out_valid_flags */
+#define NDN_HPE1_SMART_HEALTH_VALID	(1 << 0)
+#define NDN_HPE1_SMART_TEMP_VALID	(1 << 1)
+#define NDN_HPE1_SMART_SPARES_VALID	(1 << 2)
+#define NDN_HPE1_SMART_ALARM_VALID	(1 << 3)
+#define NDN_HPE1_SMART_USED_VALID	(1 << 4)
+#define NDN_HPE1_SMART_SHUTDOWN_VALID	(1 << 5)
+#define NDN_HPE1_SMART_STATS_VALID	(1 << 6)
+#define NDN_HPE1_SMART_DETAIL_VALID	(1 << 7)
+#define NDN_HPE1_SMART_ENERGY_VALID	(1 << 8)
+#define NDN_HPE1_SMART_VENDOR_VALID	(1 << 9)
+#define NDN_HPE1_SMART_NOTIFIED		(1 << 31)
+
+/* ndn_hpe1_smart_data.stat_summary */
+#define NDN_HPE1_SMART_NONCRIT_HEALTH	(1 << 0)
+#define NDN_HPE1_SMART_CRITICAL_HEALTH	(1 << 1)
+#define NDN_HPE1_SMART_FATAL_HEALTH	(1 << 2)
+
+/* ndn_hpe1_smart_data.alarm_trips */
+#define NDN_HPE1_SMART_TEMP_TRIP	(1 << 0)
+#define NDN_HPE1_SMART_SPARE_TRIP	(1 << 1)
+#define NDN_HPE1_SMART_LIFEWARN_TRIP	(1 << 2)
+#define NDN_HPE1_SMART_LIFEERR_TRIP	(1 << 3)
+#define NDN_HPE1_SMART_ESLIFEWARN_TRIP	(1 << 4)
+#define NDN_HPE1_SMART_ESLIFEERR_TRIP	(1 << 5)
+#define NDN_HPE1_SMART_ESTEMPWARN_TRIP	(1 << 6)
+#define NDN_HPE1_SMART_ESTEMPERR_TRIP	(1 << 7)
+
+/* ndn_hpe1_smart_data.last_shutdown_stat */
+#define NDN_HPE1_SMART_LASTSAVEGOOD	(1 << 1)
+
+/* ndn_hpe1_smart_data.mod_hlth_stat */
+#define NDN_HPE1_SMART_ES_FAILURE	(1 << 0)
+#define NDN_HPE1_SMART_CTLR_FAILURE	(1 << 1)
+#define NDN_HPE1_SMART_UE_TRIP		(1 << 2)
+#define NDN_HPE1_SMART_CE_TRIP		(1 << 3)
+#define NDN_HPE1_SMART_SAVE_FAILED	(1 << 4)
+#define NDN_HPE1_SMART_RESTORE_FAILED	(1 << 5)
+#define NDN_HPE1_SMART_ARM_FAILED	(1 << 6)
+#define NDN_HPE1_SMART_ERASE_FAILED	(1 << 7)
+#define NDN_HPE1_SMART_CONFIG_ERROR	(1 << 8)
+#define NDN_HPE1_SMART_FW_ERROR		(1 << 9)
+#define NDN_HPE1_SMART_VENDOR_ERROR	(1 << 10)
+
+struct ndn_hpe1_smart_data {
+	__u32	out_valid_flags;
+	__u8	stat_summary;
+	__u16	curr_temp;
+	__u8	spare_blocks;
+	__u16	alarm_trips;
+	__u8	device_life;
+	__u8	last_shutdown_stat;
+	__u16	last_save_op_dur;
+	__u16	last_restore_op_dur;
+	__u16	last_erase_op_dur;
+	__u16	res1;
+	__u32	save_ops;
+	__u32	restore_ops;
+	__u32	erase_ops;
+	__u32	life_save_ops;
+	__u32	life_restore_ops;
+	__u32	life_erase_ops;
+	__u32	life_mod_pwr_cycles;
+	__u32	mod_hlth_stat;
+	__u32	energy_src_check;
+	__u8	energy_src_life_percent;
+	__u16	energy_src_curr_temp;
+	__u8	res2;
+	__u16	energy_src_total_runtime;
+	__u16	vndr_spec_data_size;
+	__u8	vnd_spec_data[60];
+} __attribute__((packed));
+
+/* NDN_HPE1_CMD_SMART_THRESHOLD */
+struct ndn_hpe1_smart_threshold {
+	__u32 status;
+	__u8 data[32];
+} __attribute__((packed));
+
+struct ndn_hpe1_smart_threshold_data {
+	__u16	threshold_alarm_ctl;
+	__u16	temp_threshold;
+	__u8	spare_block_threshold;
+	__u8	res1[3];
+	__u8	dev_lifewarn_threshold;
+	__u8	dev_lifeerr_threshold;
+	__u8	res2[6];
+	__u8	es_lifewarn_threshold;
+	__u8	es_lifeerr_threshold;
+	__u8	es_tempwarn_threshold;
+	__u8	es_temperr_threshold;
+	__u8	res3[4];
+	__u64	res4;
+} __attribute__((packed));
+
+/* NDN_HPE1_CMD_GET_CONFIG_SIZE */
+struct ndn_hpe1_get_config_size {
+	__u32 status;
+	__u32 config_size;
+	__u32 max_xfer;
+} __attribute__((packed));
+
+/* NDN_HPE1_CMD_GET_CONFIG_DATA */
+struct ndn_hpe1_get_config_data_hdr {
+	__u32 in_offset;
+	__u32 in_length;
+	__u32 status;
+	__u8 out_buf[0];
+} __attribute__((packed));
+
+/* NDN_HPE1_CMD_SET_CONFIG_DATA */
+struct ndn_hpe1_set_config_hdr {
+	__u32 in_offset;
+	__u32 in_length;
+	__u8 in_buf[0];
+} __attribute__((packed));
+
+
+/* ndn_hpe1_get_id.sup_backup_trigger */
+#define NDN_HPE1_BKUP_SUPPORT_CKE	(1 << 0)
+#define NDN_HPE1_BKUP_SUPPORT_EXTERNAL	(1 << 1)
+#define NDN_HPE1_BKUP_SUPPORT_12V	(1 << 2)
+#define NDN_HPE1_BKUP_SUPPORT_I2C	(1 << 3)
+#define NDN_HPE1_BKUP_SUPPORT_SAVEN	(1 << 4)
+
+/* NDN_HPE1_CMD_GET_IDENT */
+struct ndn_hpe1_get_id {
+	__u32	status;
+	__u8	spec_rev;
+	__u8	num_stnd_pages;
+	__u32	hw_rev;
+	__u8	sup_backup_trigger;
+	__u16	max_op_retries;
+	__u8	__res1[3];
+	__u32	backup_op_timeout;
+	__u32	restore_op_timeout;
+	__u32	erase_op_timeout;
+	__u32	arm_op_timeout;
+	__u32	fw_op_timeout;
+	__u32	region_block_size;
+	__u16	min_op_temp;
+	__u16	max_op_temp;
+	__u8	curr_fw_slot;
+	__u8	res2[1];
+	__u16	num_fw_slots;
+	__u8	fw_slot_revision[0];
+} __attribute__((packed));
+
+/* ndn_hpe1_get_energy_src_id.attr */
+#define NDN_HPE1_ES_ATTR_BUILTIN	(1 << 0)
+#define NDN_HPE1_ES_ATTR_TETHERED	(1 << 1)
+#define NDN_HPE1_ES_ATTR_SHARED		(1 << 2)
+
+/* ndn_hpe1_get_energy_src_id.tech */
+#define NDN_HPE1_ES_TECH_UNDEFINED	(1 << 0)
+#define NDN_HPE1_ES_TECH_SUPERCAP	(1 << 1)
+#define NDN_HPE1_ES_TECH_BATTERY	(1 << 2)
+#define NDN_HPE1_ES_TECH_HYBRIDCAP	(1 << 3)
+
+/* NDN_HPE1_CMD_GET_ES_IDENT */
+struct ndn_hpe1_get_energy_src_id {
+	__u32	status;
+	__u8	energy_src_policy;
+	__u8	attr;
+	__u8	tech;
+	__u8	reserved;
+	__u16	hw_rev;
+	__u16	fw_rev;
+	__u32	charge_timeout;
+	__u16	min_op_temp;
+	__u16	max_op_temp;
+} __attribute__((packed));
+
+/* ndn_hpe1_last_backup_info.last_backup_initiation */
+#define NDN_HPE1_LASTBKUP_SAVEN		(1 << 0)
+#define NDN_HPE1_LASTBKUP_EXTERNAL	(1 << 1)
+#define NDN_HPE1_LASTBKUP_CKE		(1 << 2)
+#define NDN_HPE1_LASTBKUP_FW		(1 << 3)
+#define NDN_HPE1_LASTBKUP_RESETN	(1 << 4)
+
+/* ndn_hpe1_last_backup_info.ctlr_backup_stat */
+#define NDN_HPE1_LASTBKUP_GTG		(1 << 0)
+#define NDN_HPE1_LASTBKUP_SDRAM_FAULT	(1 << 1)
+#define NDN_HPE1_LASTBKUP_GEN_FAULT	(1 << 2)
+
+/* NDN_HPE1_CMD_GET_LAST_BACKUP */
+struct ndn_hpe1_get_last_backup {
+	__u32	status;
+	__u32	last_backup_info;
+} __attribute__((packed));
+
+struct ndn_hpe1_last_backup_info {
+	__u8	backup_image;
+	__u8	backup_cmplt_stat;
+	__u8	last_backup_initiation;
+	__u8	ctlr_backup_stat;
+} __attribute__((packed));
+
+
+/* NDN_HPE1_CMD_SET_LIFE_THRESHOLD */
+struct ndn_hpe1_set_lifetime_threshold {
+	__u8	in_nvm_lifetime_warn_threshold;
+	__u32	status;
+} __attribute__((packed));
+
+
+/* ndn_hpe1_inj_err.in_err_typ
+ * ndn_hpe1_get_inj_err_status.err_inj_type
+ * log2(ndn_hpe1_query_err_inj_cap.err_inj_cap)
+ */
+enum {
+	NDN_HPE1_EINJ_DEV_NONCRIT = 1,
+	NDN_HPE1_EINJ_DEV_CRIT = 2,
+	NDN_HPE1_EINJ_DEV_FATAL = 3,
+	NDN_HPE1_EINJ_UE_BACKUP = 4,
+	NDN_HPE1_EINJ_UE_RESTORE = 5,
+	NDN_HPE1_EINJ_UE_ERASE = 6,
+	NDN_HPE1_EINJ_UE_ARM = 7,
+	NDN_HPE1_EINJ_BADBLOCK = 8,
+	NDN_HPE1_EINJ_ES_FAULT = 9,
+	NDN_HPE1_EINJ_ES_LOWCHARGE = 10,
+	NDN_HPE1_EINJ_ES_TEMPWARN = 11,
+	NDN_HPE1_EINJ_ES_TEMPERR = 12,
+	NDN_HPE1_EINJ_ES_LIFEWARN = 13,
+	NDN_HPE1_EINJ_ES_LIFEERR = 14,
+	NDN_HPE1_EINJ_DEV_LIFEWARN = 15,
+	NDN_HPE1_EINJ_DEV_LIFEERR = 16,
+	NDN_HPE1_EINJ_FWUPDATE_ERR = 17,
+	NDN_HPE1_EINJ_CTRL_ERR = 18,
+};
+
+/* ndn_hpe1_inj_err.in_options / ndn_hpe1_get_inj_err_status.err_inj_opt */
+enum {
+	NDN_HPE1_EINJ_OPT_SINGLE = 0,
+	NDN_HPE1_EINJ_OPT_PERSISTENT = 1,
+	NDN_HPE1_EINJ_OPT_CLEAR = 2,
+};
+
+/* ndn_hpe1_get_inj_err_status.err_inj_stat_info */
+enum {
+	NDN_HPE1_EINJ_STAT_NONE = 0,
+	NDN_HPE1_EINJ_STAT_INJECTED = 1,
+	NDN_HPE1_EINJ_STAT_PENDING = 2,
+};
+
+/* NDN_HPE1_CMD_ERRINJ_QUERY */
+struct ndn_hpe1_query_err_inj_cap {
+	__u32	status;
+	__u8	err_inj_cap[32];
+} __attribute__((packed));
+
+
+/* NDN_HPE1_CMD_ERRINJ_INJECT */
+struct ndn_hpe1_inj_err {
+	__u8	in_err_typ;
+	__u8	in_options;
+	__u32	status;
+} __attribute__((packed));
+
+/* NDN_HPE1_CMD_ERRINJ_STATUS */
+struct ndn_hpe1_get_inj_err_status {
+	__u32	status;
+	__u8	err_inj_stat_info;
+	__u8	err_inj_type;
+	__u8	err_inj_opt;
+} __attribute__((packed));
+
+union ndn_hpe1_cmd {
+	__u64					query;
+	struct ndn_hpe1_smart			smart;
+	struct ndn_hpe1_smart_threshold		thresh;
+	struct ndn_hpe1_get_config_size		get_size;
+	struct ndn_hpe1_get_config_data_hdr	get_data;
+	struct ndn_hpe1_get_id			get_id;
+	struct ndn_hpe1_get_energy_src_id	get_energy_src_id;
+	struct ndn_hpe1_get_last_backup		get_last_backup;
+	struct ndn_hpe1_last_backup_info	last_backup_info;
+	struct ndn_hpe1_set_lifetime_threshold	set_life_thresh;
+	struct ndn_hpe1_query_err_inj_cap	err_cap;
+	struct ndn_hpe1_inj_err			inj_err;
+	struct ndn_hpe1_get_inj_err_status	inj_err_stat;
+
+	unsigned char				buf[128];
+};
+
+struct ndn_pkg_hpe1 {
+	struct nd_cmd_pkg gen;
+	union ndn_hpe1_cmd u;
+} __attribute__((packed));
+
+#define NDN_IOCTL_HPE1_PASSTHRU		_IOWR(ND_IOCTL, ND_CMD_CALL, \
+					struct ndn_pkg_hpe1)
+
+#endif /* __NDCTL_HPE1_H__ */
-- 
2.8.3

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [ndctl PATCH v2 2/4] libndctl: record dsm family in add_dimm()
  2016-09-13 17:41 ` [ndctl PATCH v2 2/4] libndctl: record dsm family in add_dimm() Brian Boylston
@ 2016-09-15  0:44   ` Dan Williams
  2016-09-15 18:17     ` Boylston, Brian
  0 siblings, 1 reply; 9+ messages in thread
From: Dan Williams @ 2016-09-15  0:44 UTC (permalink / raw)
  To: Brian Boylston; +Cc: linux-nvdimm

On Tue, Sep 13, 2016 at 10:41 AM, Brian Boylston <brian.boylston@hpe.com> wrote:
> The recorded DSM family can be used to provide family-specific
> functionality.
>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Signed-off-by: Brian Boylston <brian.boylston@hpe.com>
> ---
>  ndctl/lib/libndctl.c | 6 ++++++
>  1 file changed, 6 insertions(+)
>
> diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c
> index 9a9dc74..c824d83 100644
> --- a/ndctl/lib/libndctl.c
> +++ b/ndctl/lib/libndctl.c
> @@ -136,6 +136,7 @@ struct ndctl_dimm {
>         unsigned short subsystem_revision_id;
>         unsigned short manufacturing_date;
>         unsigned char manufacturing_location;
> +       unsigned long dsm_family;
>         unsigned long dsm_mask;
>         char *unique_id;
>         char *dimm_path;
> @@ -1145,6 +1146,11 @@ static int add_dimm(void *parent, int id, const char *dimm_base)
>         if (sscanf(buf, "%d:%d", &dimm->major, &dimm->minor) != 2)
>                 goto err_read;
>
> +       sprintf(path, "%s/nfit/family", dimm_base);
> +       if (sysfs_read_attr(ctx, path, buf) < 0)
> +               goto err_read;
> +       dimm->dsm_family = strtoul(buf, NULL, 0);


Hmm I don't think it should it be fatal to add_dimm() for a failure to
have a family.  It's reasonable for a DIMM to exist in the NFIT, but
have no DSMs or a DSM implementation that is new to the kernel.  Can
we make this continue on and teach consumers of ->dsm_family that it
might be an error value?
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [ndctl PATCH v2 1/4] libndctl: introduce ndctl_smart_ops
  2016-09-13 17:41 ` [ndctl PATCH v2 1/4] libndctl: introduce ndctl_smart_ops Brian Boylston
@ 2016-09-15  2:22   ` Dan Williams
  0 siblings, 0 replies; 9+ messages in thread
From: Dan Williams @ 2016-09-15  2:22 UTC (permalink / raw)
  To: Brian Boylston; +Cc: linux-nvdimm

On Tue, Sep 13, 2016 at 10:41 AM, Brian Boylston <brian.boylston@hpe.com> wrote:
> Add a layer of indirection for the ndctl_cmd_smart*() family of
> interfaces.  This will allow the underlying implementation to be
> switched based on the DSM family supported by the DIMM.
>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Signed-off-by: Brian Boylston <brian.boylston@hpe.com>
> ---
>  ndctl/lib/libndctl-private.h |  21 ++++++++
>  ndctl/lib/libndctl-smart.c   | 111 ++++++++++++++++++++++++++++++++++---------
>  ndctl/lib/libndctl.c         |   8 ++++
>  ndctl/libndctl.h.in          |   1 +
>  4 files changed, 118 insertions(+), 23 deletions(-)

Looks good to me and passes my regression tests.

Just one minor comment below:

>
> diff --git a/ndctl/lib/libndctl-private.h b/ndctl/lib/libndctl-private.h
> index 65ef86d..8d2ebfc 100644
> --- a/ndctl/lib/libndctl-private.h
> +++ b/ndctl/lib/libndctl-private.h
> @@ -201,6 +201,27 @@ struct ndctl_cmd {
>         };
>  };
>
> +struct ndctl_smart_ops {
> +       struct ndctl_cmd *(*new_smart)(struct ndctl_dimm *);
> +       unsigned int (*smart_get_flags)(struct ndctl_cmd *);
> +       unsigned int (*smart_get_health)(struct ndctl_cmd *);
> +       unsigned int (*smart_get_temperature)(struct ndctl_cmd *);
> +       unsigned int (*smart_get_spares)(struct ndctl_cmd *);
> +       unsigned int (*smart_get_alarm_flags)(struct ndctl_cmd *);
> +       unsigned int (*smart_get_life_used)(struct ndctl_cmd *);
> +       unsigned int (*smart_get_shutdown_state)(struct ndctl_cmd *);
> +       unsigned int (*smart_get_vendor_size)(struct ndctl_cmd *);
> +       unsigned char *(*smart_get_vendor_data)(struct ndctl_cmd *);
> +       struct ndctl_cmd *(*new_smart_threshold)(struct ndctl_dimm *);
> +       unsigned int (*smart_threshold_get_alarm_control)(struct ndctl_cmd *);
> +       unsigned int (*smart_threshold_get_temperature)(struct ndctl_cmd *);
> +       unsigned int (*smart_threshold_get_spares)(struct ndctl_cmd *);
> +};
> +
> +#if HAS_SMART == 1
> +struct ndctl_smart_ops intel_smart_ops;
> +#endif
> +
>  /* internal library helpers for conditionally defined command numbers */
>  #ifdef HAVE_NDCTL_ARS
>  static const int nd_cmd_ars_status = ND_CMD_ARS_STATUS;
> diff --git a/ndctl/lib/libndctl-smart.c b/ndctl/lib/libndctl-smart.c
> index cba1e9d..a172541 100644
> --- a/ndctl/lib/libndctl-smart.c
> +++ b/ndctl/lib/libndctl-smart.c
> @@ -16,7 +16,55 @@
>  #include <ndctl/libndctl.h>
>  #include "libndctl-private.h"
>
> -NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_smart(struct ndctl_dimm *dimm)
> +/*
> + * The smart_dimm_op() and smart_cmd_op() macros are used here to
> + * define the wrappers around the ndctl_smart_ops:
> + */
> +
> +#define smart_dimm_op(name, op) \
> +NDCTL_EXPORT struct ndctl_cmd *name( \
> +               struct ndctl_dimm *dimm) \
> +{ \
> +       struct ndctl_smart_ops *ops = ndctl_dimm_get_smart_ops(dimm); \
> +       if (ops && ops->op) \
> +               return ops->op(dimm); \
> +       else \
> +               return NULL; \
> +}
> +
> +smart_dimm_op(ndctl_dimm_cmd_new_smart, new_smart)
> +smart_dimm_op(ndctl_dimm_cmd_new_smart_threshold, new_smart_threshold)

There's only two of these routines and I don't suppose there will need
to have many more variants in the future.  Let's just open code them
and not use a macro.  The rest looks good.
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: [ndctl PATCH v2 2/4] libndctl: record dsm family in add_dimm()
  2016-09-15  0:44   ` Dan Williams
@ 2016-09-15 18:17     ` Boylston, Brian
  2016-09-15 19:27       ` Dan Williams
  0 siblings, 1 reply; 9+ messages in thread
From: Boylston, Brian @ 2016-09-15 18:17 UTC (permalink / raw)
  To: Dan Williams; +Cc: linux-nvdimm

Dan Williams wrote on 2016-09-14:
> On Tue, Sep 13, 2016 at 10:41 AM, Brian Boylston <brian.boylston@hpe.com> wrote:
>> The recorded DSM family can be used to provide family-specific
>> functionality.
>> 
>> Cc: Dan Williams <dan.j.williams@intel.com>
>> Signed-off-by: Brian Boylston <brian.boylston@hpe.com>
>> ---
>>  ndctl/lib/libndctl.c | 6 ++++++
>>  1 file changed, 6 insertions(+)
>>
>> diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c
>> index 9a9dc74..c824d83 100644
>> --- a/ndctl/lib/libndctl.c
>> +++ b/ndctl/lib/libndctl.c
>> @@ -136,6 +136,7 @@ struct ndctl_dimm {
>>         unsigned short subsystem_revision_id;
>>         unsigned short manufacturing_date;
>>         unsigned char manufacturing_location;
>> +       unsigned long dsm_family;
>>         unsigned long dsm_mask;
>>         char *unique_id;
>>         char *dimm_path;
>> @@ -1145,6 +1146,11 @@ static int add_dimm(void *parent, int id, const char *dimm_base)
>>         if (sscanf(buf, "%d:%d", &dimm->major, &dimm->minor) != 2)
>>                 goto err_read;
>>
>> +       sprintf(path, "%s/nfit/family", dimm_base);
>> +       if (sysfs_read_attr(ctx, path, buf) < 0)
>> +               goto err_read;
>> +       dimm->dsm_family = strtoul(buf, NULL, 0);
> 
> 
> Hmm I don't think it should it be fatal to add_dimm() for a failure to
> have a family.  It's reasonable for a DIMM to exist in the NFIT, but
> have no DSMs or a DSM implementation that is new to the kernel.  Can
> we make this continue on and teach consumers of ->dsm_family that it
> might be an error value?

Sure.  Some of the other values collected in add_dimm() use -1 as an
error or not available value; ok to use that for dsm_family?

Also, should I add a public accessor function such as
ndctl_dimm_get_dsm_family()?  (although there wouldn't be a consumer yet)

Thanks!
Brian
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [ndctl PATCH v2 2/4] libndctl: record dsm family in add_dimm()
  2016-09-15 18:17     ` Boylston, Brian
@ 2016-09-15 19:27       ` Dan Williams
  0 siblings, 0 replies; 9+ messages in thread
From: Dan Williams @ 2016-09-15 19:27 UTC (permalink / raw)
  To: Boylston, Brian; +Cc: linux-nvdimm

On Thu, Sep 15, 2016 at 11:17 AM, Boylston, Brian
<brian.boylston@hpe.com> wrote:
> Dan Williams wrote on 2016-09-14:
>> On Tue, Sep 13, 2016 at 10:41 AM, Brian Boylston <brian.boylston@hpe.com> wrote:
>>> The recorded DSM family can be used to provide family-specific
>>> functionality.
>>>
>>> Cc: Dan Williams <dan.j.williams@intel.com>
>>> Signed-off-by: Brian Boylston <brian.boylston@hpe.com>
>>> ---
>>>  ndctl/lib/libndctl.c | 6 ++++++
>>>  1 file changed, 6 insertions(+)
>>>
>>> diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c
>>> index 9a9dc74..c824d83 100644
>>> --- a/ndctl/lib/libndctl.c
>>> +++ b/ndctl/lib/libndctl.c
>>> @@ -136,6 +136,7 @@ struct ndctl_dimm {
>>>         unsigned short subsystem_revision_id;
>>>         unsigned short manufacturing_date;
>>>         unsigned char manufacturing_location;
>>> +       unsigned long dsm_family;
>>>         unsigned long dsm_mask;
>>>         char *unique_id;
>>>         char *dimm_path;
>>> @@ -1145,6 +1146,11 @@ static int add_dimm(void *parent, int id, const char *dimm_base)
>>>         if (sscanf(buf, "%d:%d", &dimm->major, &dimm->minor) != 2)
>>>                 goto err_read;
>>>
>>> +       sprintf(path, "%s/nfit/family", dimm_base);
>>> +       if (sysfs_read_attr(ctx, path, buf) < 0)
>>> +               goto err_read;
>>> +       dimm->dsm_family = strtoul(buf, NULL, 0);
>>
>>
>> Hmm I don't think it should it be fatal to add_dimm() for a failure to
>> have a family.  It's reasonable for a DIMM to exist in the NFIT, but
>> have no DSMs or a DSM implementation that is new to the kernel.  Can
>> we make this continue on and teach consumers of ->dsm_family that it
>> might be an error value?
>
> Sure.  Some of the other values collected in add_dimm() use -1 as an
> error or not available value; ok to use that for dsm_family?

Sounds good.

> Also, should I add a public accessor function such as
> ndctl_dimm_get_dsm_family()?  (although there wouldn't be a consumer yet)

I'm not opposed to it for informational purposes, but I like the
current direction where it doesn't really matter and the other ndctl
routines are taught how to multiplex for different families.
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2016-09-15 19:27 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-13 17:41 [ndctl PATCH v2 0/4] ndctl: add support for HPE type N SMART health data Brian Boylston
2016-09-13 17:41 ` [ndctl PATCH v2 1/4] libndctl: introduce ndctl_smart_ops Brian Boylston
2016-09-15  2:22   ` Dan Williams
2016-09-13 17:41 ` [ndctl PATCH v2 2/4] libndctl: record dsm family in add_dimm() Brian Boylston
2016-09-15  0:44   ` Dan Williams
2016-09-15 18:17     ` Boylston, Brian
2016-09-15 19:27       ` Dan Williams
2016-09-13 17:41 ` [ndctl PATCH v2 3/4] libndctl: enable ND_CMD_CALL Brian Boylston
2016-09-13 17:41 ` [ndctl PATCH v2 4/4] libndctl: add support for the HPE1 family of DSM SMART functions Brian Boylston

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.