diff mbox

[2/2] ndctl: add msft-dsm command

Message ID 20170323234751.1468-2-lijunpan2000@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Lijun Pan March 23, 2017, 11:47 p.m. UTC
From: Lijun Pan <Lijun.Pan@dell.com>

"ndctl msft-dsm" shows more generic NVDIMM info (except health)
via calling _DSM functions.

Cc: Stuart Hayes <Stuart.Hayes@dell.com>
Signed-off-by: Lijun Pan <Lijun.Pan@dell.com>
---
 Documentation/Makefile.am        |   1 +
 Documentation/ndctl-msft-dsm.txt |  83 ++++++++++++++++++
 builtin.h                        |   1 +
 ndctl/Makefile.am                |   1 +
 ndctl/builtin-msft-dsm.c         | 173 +++++++++++++++++++++++++++++++++++++
 ndctl/ndctl.c                    |   1 +
 ndctl/util/json-msft.c           | 179 +++++++++++++++++++++++++++++++++++++++
 util/json.h                      |   1 +
 8 files changed, 440 insertions(+)
 create mode 100644 Documentation/ndctl-msft-dsm.txt
 create mode 100644 ndctl/builtin-msft-dsm.c

Comments

Dan Williams March 24, 2017, 12:34 a.m. UTC | #1
On Thu, Mar 23, 2017 at 4:47 PM, Lijun Pan <lijunpan2000@gmail.com> wrote:
> From: Lijun Pan <Lijun.Pan@dell.com>
>
> "ndctl msft-dsm" shows more generic NVDIMM info (except health)
> via calling _DSM functions.
>
> Cc: Stuart Hayes <Stuart.Hayes@dell.com>
> Signed-off-by: Lijun Pan <Lijun.Pan@dell.com>
> ---
>  Documentation/Makefile.am        |   1 +
>  Documentation/ndctl-msft-dsm.txt |  83 ++++++++++++++++++
>  builtin.h                        |   1 +
>  ndctl/Makefile.am                |   1 +
>  ndctl/builtin-msft-dsm.c         | 173 +++++++++++++++++++++++++++++++++++++
>  ndctl/ndctl.c                    |   1 +
>  ndctl/util/json-msft.c           | 179 +++++++++++++++++++++++++++++++++++++++
>  util/json.h                      |   1 +
>  8 files changed, 440 insertions(+)
>  create mode 100644 Documentation/ndctl-msft-dsm.txt
>  create mode 100644 ndctl/builtin-msft-dsm.c

Hi Lijun.

This is great, thank you for extending ndctl support for the Microsoft
family of DIMMs!

However, I'll give the same feedback I gave to the initial HPE support
implementation. As much as possible I want to avoid having DIMM family
specific enabling in the ndctl utility itself. It should all live
behind an abstraction provided by libndctl. That way other
applications that might use libndctl directly can get the benefit of
the enabling. The approach we took in the HPE case is to introduce
ndctl_smart_ops and merge the smart payloads into a common json
format. While the current enabling only exposes the SMART health
fields that are similar between Intel and HPE, it need not be that way
going forward and I'm open to having json fields that are DIMM-family
exclusive.

Have a look at extending the existing "ndctl list --dimms --health"
output with Microsoft-specific fields, but otherwise reuse existing
fields where they are common across other DIMM families.
diff mbox

Patch

diff --git a/Documentation/Makefile.am b/Documentation/Makefile.am
index 6daeb56..0c04fde 100644
--- a/Documentation/Makefile.am
+++ b/Documentation/Makefile.am
@@ -13,6 +13,7 @@  man1_MANS = \
 	ndctl-create-namespace.1 \
 	ndctl-destroy-namespace.1 \
 	ndctl-list.1 \
+	ndctl-msft-dsm.1 \
 	daxctl-list.1
 
 CLEANFILES = $(man1_MANS)
diff --git a/Documentation/ndctl-msft-dsm.txt b/Documentation/ndctl-msft-dsm.txt
new file mode 100644
index 0000000..8fe03be
--- /dev/null
+++ b/Documentation/ndctl-msft-dsm.txt
@@ -0,0 +1,83 @@ 
+ndctl-msft-dsm(1)
+=================
+
+NAME
+----
+ndctl-msft-dsm - dump the NVDIMM hardware data in json via _DSM calls
+
+SYNOPSIS
+--------
+[verse]
+'ndctl msft-dsm' [<options>]
+
+Walk all the nvdimm buses in the system and list all attached devices
+along with some of their hardware data.
+
+Options can be specified to limit the output to devices of a certain
+class.  Where the classes are buses, dimms.
+
+EXAMPLE
+-------
+[verse]
+# ndctl msft-dsm --buses --dimms --idle
+
+["literal"]
+{
+  "provider":"ACPI.NFIT",
+  "dev":"ndbus0",
+  "dimms":[
+    {
+      "dev":"nmem0",
+      "id":"0000-00000000",
+      "state":"disabled",
+      "_DSM":{
+        "firmware_revision":23129,
+        "save_op_timeout":"70 seconds",
+        "restore_op_timeout":"45 seconds",
+        "erase_op_timeout":"5 seconds",
+        "arm_op_timeout":"10 seconds",
+        "firmware_op_timeout":"10 seconds",
+        "abort_op_timeout_milliseconds":255,
+        "averag_power_required_save_op_milliwatts":0,
+        "averag_idle_power_required_after_save_op_completes_milliwatts":0,
+        "min_voltage_ES_service_during_save_op_millivolts":5799,
+        "max_voltage_ES_service_during_save_op_millivolts":13799,
+        "NVM_lifetime_percentage_warning_threshold":0,
+        "NVM_lifetime_percentage_error_threshold":0,
+        "ES_lifetime_percentage_warning_threshold":0,
+        "ES_lifetime_percentage_error_threshold":0,
+        "ES_temperature_warning_threshold_celsius":0,
+        "ES_temperature_error_threshold_celsius":0,
+        "ES_lifetime_percentage":0,
+        "ES_current_temperature_celsius":0,
+        "ES_total_runtime_hours":0
+      }
+    }
+  ]
+}
+
+OPTIONS
+-------
+-b::
+--bus=::
+	A bus id. Filter listing by devices that reference the given bus.
+
+-d::
+--dimm=::
+	An 'nmemX' device name, or dimm id number. Filter listing by
+	devices that reference the given dimm.
+-B::
+--buses::
+	Include bus info in the listing
+
+-D::
+--dimms::
+	Include dimm info in the listing
+
+-i::
+--idle::
+	Include idle (not enabled) devices in the listing
+
+SEE ALSO
+--------
+linkndctl:ndctl-list[1]
diff --git a/builtin.h b/builtin.h
index 9b66196..425929d 100644
--- a/builtin.h
+++ b/builtin.h
@@ -22,6 +22,7 @@  int cmd_read_labels(int argc, const char **argv, void *ctx);
 int cmd_init_labels(int argc, const char **argv, void *ctx);
 int cmd_check_labels(int argc, const char **argv, void *ctx);
 int cmd_list(int argc, const char **argv, void *ctx);
+int cmd_msft_dsm(int argc, const char **argv, void *ctx);
 #ifdef ENABLE_TEST
 int cmd_test(int argc, const char **argv, void *ctx);
 #endif
diff --git a/ndctl/Makefile.am b/ndctl/Makefile.am
index f85f9cb..0519e52 100644
--- a/ndctl/Makefile.am
+++ b/ndctl/Makefile.am
@@ -9,6 +9,7 @@  ndctl_SOURCES = ndctl.c \
 		builtin-dimm.c \
 		 ../util/log.c \
 		builtin-list.c \
+		builtin-msft-dsm.c \
 		builtin-test.c \
 		../util/json.c \
 		util/json-msft.c
diff --git a/ndctl/builtin-msft-dsm.c b/ndctl/builtin-msft-dsm.c
new file mode 100644
index 0000000..c7a5f7d
--- /dev/null
+++ b/ndctl/builtin-msft-dsm.c
@@ -0,0 +1,173 @@ 
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <util/json.h>
+#include <util/filter.h>
+#include <json-c/json.h>
+#include <ndctl/libndctl.h>
+#include <util/parse-options.h>
+#include <ccan/array_size/array_size.h>
+
+#ifdef HAVE_NDCTL_H
+#include <linux/ndctl.h>
+#else
+#include <ndctl.h>
+#endif
+
+static struct {
+	bool buses;
+	bool dimms;
+	bool idle;
+} list;
+
+static struct {
+	const char *bus;
+	const char *type;
+	const char *dimm;
+} param;
+
+static int did_fail;
+static int jflag = JSON_C_TO_STRING_PRETTY;
+
+#define fail(fmt, ...) \
+do { \
+	did_fail = 1; \
+	fprintf(stderr, "ndctl-%s:%s:%d: " fmt, \
+			VERSION, __func__, __LINE__, ##__VA_ARGS__); \
+} while (0)
+
+static int num_list_flags(void)
+{
+	return list.buses + list.dimms;
+}
+
+int cmd_msft_dsm(int argc, const char **argv, void *ctx)
+{
+	const struct option options[] = {
+		OPT_STRING('b', "bus", &param.bus, "bus-id", "filter by bus"),
+		OPT_STRING('d', "dimm", &param.dimm, "dimm-id",
+				"filter by dimm"),
+		OPT_BOOLEAN('B', "buses", &list.buses, "include bus info"),
+		OPT_BOOLEAN('D', "dimms", &list.dimms, "include dimm info"),
+		OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"),
+		OPT_END(),
+	};
+	const char * const u[] = {
+		"ndctl msft-dsm [<options>]",
+		NULL
+	};
+	struct json_object *jdimms = NULL;
+	struct json_object *jbuses = NULL;
+	struct ndctl_bus *bus;
+	int i;
+
+        argc = parse_options(argc, argv, options, u, 0);
+	for (i = 0; i < argc; i++)
+		error("unknown parameter \"%s\"\n", argv[i]);
+
+	if (argc)
+		usage_with_options(u, options);
+
+	if (num_list_flags() == 0) {
+		list.buses = !!param.bus;
+		list.dimms = !!param.dimm;
+	}
+
+	ndctl_bus_foreach(ctx, bus) {
+		struct json_object *jbus = NULL;
+		struct ndctl_dimm *dimm;
+
+		if (!util_bus_filter(bus, param.bus)
+				|| !util_bus_filter_by_dimm(bus, param.dimm))
+			continue;
+
+		if (list.buses) {
+			if (!jbuses) {
+				jbuses = json_object_new_array();
+				if (!jbuses) {
+					fail("\n");
+					continue;
+				}
+			}
+
+			jbus = util_bus_to_json(bus);
+			if (!jbus) {
+				fail("\n");
+				continue;
+			}
+			json_object_array_add(jbuses, jbus);
+		}
+
+		ndctl_dimm_foreach(bus, dimm) {
+			struct json_object *jdimm;
+
+			/* are we emitting dimms? */
+			if (!list.dimms)
+				break;
+
+			if (!util_dimm_filter(dimm, param.dimm))
+				continue;
+
+			if (!list.idle && !ndctl_dimm_is_enabled(dimm))
+				continue;
+
+			if (!jdimms) {
+				jdimms = json_object_new_array();
+				if (!jdimms) {
+					fail("\n");
+					continue;
+				}
+
+				if (jbus)
+					json_object_object_add(jbus, "dimms", jdimms);
+			}
+
+			jdimm = util_dimm_to_json(dimm);
+			if (!jdimm) {
+				fail("\n");
+				continue;
+			}
+
+			if (ndctl_dimm_get_dsm_family(dimm) == NVDIMM_FAMILY_MSFT) {
+				struct json_object *jdsm;
+				jdsm = util_dimm_dsm_to_json_msft(dimm);
+				if (jdsm)
+					json_object_object_add(jdimm, "_DSM", jdsm);
+			}
+
+			/*
+			 * Without a bus we are collecting dimms anonymously
+			 * across the platform.
+			 */
+			json_object_array_add(jdimms, jdimm);
+		}
+
+		if (jbuses) {
+			jdimms = NULL;
+		}
+	}
+
+	if (jbuses)
+		util_display_json_array(stdout, jbuses, jflag);
+	else if ((!!jdimms) > 1) {
+		struct json_object *jplatform = json_object_new_object();
+
+		if (!jplatform) {
+			fail("\n");
+			return -ENOMEM;
+		}
+
+		if (jdimms)
+			json_object_object_add(jplatform, "dimms", jdimms);
+		printf("%s\n", json_object_to_json_string_ext(jplatform,
+					jflag));
+		json_object_put(jplatform);
+	} else if (jdimms)
+		util_display_json_array(stdout, jdimms, jflag);
+
+	if (did_fail)
+		return -ENOMEM;
+	return 0;
+}
diff --git a/ndctl/ndctl.c b/ndctl/ndctl.c
index 80a0491..aef2fc2 100644
--- a/ndctl/ndctl.c
+++ b/ndctl/ndctl.c
@@ -67,6 +67,7 @@  static struct cmd_struct commands[] = {
 	{ "check-labels", cmd_check_labels },
 	{ "list", cmd_list },
 	{ "help", cmd_help },
+	{ "msft-dsm", cmd_msft_dsm },
 	#ifdef ENABLE_TEST
 	{ "test", cmd_test },
 	#endif
diff --git a/ndctl/util/json-msft.c b/ndctl/util/json-msft.c
index 9e41fde..2dca173 100644
--- a/ndctl/util/json-msft.c
+++ b/ndctl/util/json-msft.c
@@ -79,6 +79,8 @@  struct json_object *util_dimm_health_to_json_msft(struct ndctl_dimm *dimm)
 		goto err_fd;
 	}
 
+	/* ======================== */
+
 	error = call_func(FUNC10, SIZE_IN_FUNC10, SIZE_OUT_FUNC10, fd, payload);
 	if (!error) {
 		if (!!(payload[4] & 0x0F))
@@ -115,6 +117,8 @@  struct json_object *util_dimm_health_to_json_msft(struct ndctl_dimm *dimm)
 			json_object_object_add(jhealth, "last_save_operation", jobj);
 	}
 
+	/* ======================== */
+
 	close(fd);
 	return jhealth;
 
@@ -123,3 +127,178 @@  err_fd:
 
 	return NULL;
 }
+
+struct json_object *util_dimm_dsm_to_json_msft(struct ndctl_dimm *dimm)
+{
+
+	struct json_object *jdsm = json_object_new_object();
+	struct json_object *jobj;
+	char dev_name[DEV_NAME_MAX_LENGTH + 1];
+	unsigned char payload[SIZE_OUT_MAX];
+	char str[100];
+	int fd;
+	int error;
+	int number;
+
+	memset(dev_name, 0, DEV_NAME_MAX_LENGTH + 1);
+	snprintf(dev_name, DEV_NAME_MAX_LENGTH + 1, "/dev/nmem%d", ndctl_dimm_get_id(dimm));
+	fd = open(dev_name, O_RDWR);
+	if (fd == -1) {
+		fprintf(stderr, "error opening %s errno=%d\n", dev_name, errno);
+		goto err_fd;
+	}
+
+	/* ======================== */
+
+	error = call_func(FUNC1, SIZE_IN_FUNC1, SIZE_OUT_FUNC1, fd, payload);
+	if (!error) {
+		number = payload[13] << 8 | payload[12];
+		jobj = json_object_new_int(number);
+		if (jobj)
+			json_object_object_add(jdsm, "firmware_revision", jobj);
+
+		number = ((payload[21] & 0x7f) << 8) | payload[20];
+		if (!!(payload[21] & 0x80)) {
+			memset(str, 0, 100);
+			snprintf(str, 100, "%d seconds", number);
+			jobj = json_object_new_string(str);
+		} else {
+			memset(str, 0, 100);
+			snprintf(str, 100, "%d milliseconds", number);
+			jobj = json_object_new_string(str);
+		}
+		if (jobj)
+			json_object_object_add(jdsm, "save_op_timeout", jobj);
+
+		number = ((payload[25] & 0x7f) << 8) | payload[24];
+		if (!!(payload[25] & 0x80)) {
+			memset(str, 0, 100);
+			snprintf(str, 100, "%d seconds", number);
+			jobj = json_object_new_string(str);
+		} else {
+			memset(str, 0, 100);
+			snprintf(str, 100, "%d milliseconds", number);
+			jobj = json_object_new_string(str);
+		}
+		if (jobj)
+			json_object_object_add(jdsm, "restore_op_timeout", jobj);
+
+		number = ((payload[29] & 0x7f) << 8) | payload[28];
+		if (!!(payload[29] & 0x80)) {
+			memset(str, 0, 100);
+			snprintf(str, 100, "%d seconds", number);
+			jobj = json_object_new_string(str);
+		} else {
+			memset(str, 0, 100);
+			snprintf(str, 100, "%d milliseconds", number);
+			jobj = json_object_new_string(str);
+		}
+		if (jobj)
+			json_object_object_add(jdsm, "erase_op_timeout", jobj);
+
+		number = ((payload[33] & 0x7f) << 8) | payload[32];
+		if (!!(payload[33] & 0x80)) {
+			memset(str, 0, 100);
+			snprintf(str, 100, "%d seconds", number);
+			jobj = json_object_new_string(str);
+		} else {
+			memset(str, 0, 100);
+			snprintf(str, 100, "%d milliseconds", number);
+			jobj = json_object_new_string(str);
+		}
+		if (jobj)
+			json_object_object_add(jdsm, "arm_op_timeout", jobj);
+
+		number = ((payload[37] & 0x7f) << 8) | payload[36];
+		if (!!(payload[37] & 0x80)) {
+			memset(str, 0, 100);
+			snprintf(str, 100, "%d seconds", number);
+			jobj = json_object_new_string(str);
+		} else {
+			memset(str, 0, 100);
+			snprintf(str, 100, "%d milliseconds", number);
+			jobj = json_object_new_string(str);
+		}
+		if (jobj)
+			json_object_object_add(jdsm, "firmware_op_timeout", jobj);
+
+		jobj = json_object_new_int(payload[40]);
+		if (jobj)
+			json_object_object_add(jdsm, "abort_op_timeout_milliseconds", jobj);
+	}
+
+
+	error = call_func(FUNC2, SIZE_IN_FUNC2, SIZE_OUT_FUNC2, fd, payload);
+	if (!error) {
+		jobj = json_object_new_int(payload[5] << 8 | payload[4]);
+		if (jobj)
+			json_object_object_add(jdsm, "averag_power_required_save_op_milliwatts", jobj);
+
+		jobj = json_object_new_int(payload[7] << 8 | payload[6]);
+		if (jobj)
+			json_object_object_add(jdsm, "averag_idle_power_required_after_save_op_completes_milliwatts", jobj);
+
+		jobj = json_object_new_int(payload[9] << 8 | payload[8]);
+		if (jobj)
+			json_object_object_add(jdsm, "min_voltage_ES_service_during_save_op_millivolts", jobj);
+
+		jobj = json_object_new_int(payload[11] << 8 | payload[10]);
+		if (jobj)
+			json_object_object_add(jdsm, "max_voltage_ES_service_during_save_op_millivolts", jobj);
+	}
+
+
+	error = call_func(FUNC5, SIZE_IN_FUNC5, SIZE_OUT_FUNC5, fd, payload);
+	if (!error) {
+		jobj = json_object_new_int(payload[4]);
+		if (jobj)
+			json_object_object_add(jdsm, "NVM_lifetime_percentage_warning_threshold", jobj);
+
+		jobj = json_object_new_int(payload[5]);
+		if (jobj)
+			json_object_object_add(jdsm, "NVM_lifetime_percentage_error_threshold", jobj);
+	}
+
+	error = call_func(FUNC7, SIZE_IN_FUNC7, SIZE_OUT_FUNC7, fd, payload);
+	if (!error) {
+		jobj = json_object_new_int(payload[4]);
+		if (jobj)
+			json_object_object_add(jdsm, "ES_lifetime_percentage_warning_threshold", jobj);
+
+		jobj = json_object_new_int(payload[5]);
+		if (jobj)
+			json_object_object_add(jdsm, "ES_lifetime_percentage_error_threshold", jobj);
+
+		jobj = json_object_new_int(payload[6]);
+		if (jobj)
+			json_object_object_add(jdsm, "ES_temperature_warning_threshold_celsius", jobj);
+
+		jobj = json_object_new_int(payload[7]);
+		if (jobj)
+			json_object_object_add(jdsm, "ES_temperature_error_threshold_celsius", jobj);
+	}
+
+	error = call_func(FUNC12, SIZE_IN_FUNC12, SIZE_OUT_FUNC12, fd, payload);
+	if (!error) {
+		jobj = json_object_new_int(payload[4]);
+		if (jobj)
+			json_object_object_add(jdsm, "ES_lifetime_percentage", jobj);
+
+		jobj = json_object_new_int((payload[6] << 8) | payload[5]);
+		if (jobj)
+			json_object_object_add(jdsm, "ES_current_temperature_celsius", jobj);
+
+		jobj = json_object_new_int((payload[8] << 8) | payload[7]);
+		if (jobj)
+			json_object_object_add(jdsm, "ES_total_runtime_hours", jobj);
+	}
+
+	/* ======================== */
+	close(fd);
+	return jdsm;
+
+err_fd:
+	json_object_put(jdsm);
+
+	return NULL;
+}
diff --git a/util/json.h b/util/json.h
index 3a41977..675132f 100644
--- a/util/json.h
+++ b/util/json.h
@@ -30,4 +30,5 @@  static inline struct json_object *util_dimm_health_to_json(
 }
 #endif
 struct json_object *util_dimm_health_to_json_msft(struct ndctl_dimm *dimm);
+struct json_object *util_dimm_dsm_to_json_msft(struct ndctl_dimm *dimm);
 #endif /* __NDCTL_JSON_H__ */