@@ -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)
new file mode 100644
@@ -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]
@@ -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
@@ -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
new file mode 100644
@@ -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", ¶m.bus, "bus-id", "filter by bus"),
+ OPT_STRING('d', "dimm", ¶m.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;
+}
@@ -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
@@ -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;
+}
@@ -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__ */