diff mbox series

[ndctl,RFC-PATCH,1/4] ndctl,libndctl: Implement new dimm-ops 'new_stats' and 'get_stat'

Message ID 20200518112023.147139-2-vaibhav@linux.ibm.com (mailing list archive)
State New, archived
Headers show
Series Add support for reporting PAPR NVDIMM Statistics | expand

Commit Message

Vaibhav Jain May 18, 2020, 11:20 a.m. UTC
Do necessary ndctl, libndctl changes to add support for two new dimm-ops
namely 'new_stats' and 'get_stat' that can be implemented by dimm
providers to expose dimm statistics for e.g "Controller Reset Count"
etc, to ndctl. These dimm-ops are called when newly introduced command
line argument '-S' or '--stats' is provided on with ndctl-list
command.

Following are the semantics of these new dimm-ops:

* struct ndctl_cmd *(*new_stats)(struct ndctl_dimm *):

  Return a ndctl command that can be sent to libnvdimm to fetch
  dimm-stats from kernel. On successful submission
  'dimm_ops->smart_get_flags' is called to check if ND_SMART_STATS_VALID
  flag is set which indicates dimm-stats successfully fetched from
  libnvdimm.

* int (*get_stat)(struct ndctl_cmd *, struct ndctl_dimm_stat *):

  If ND_SMART_STATS_VALID flag was returned for a command from
  'dimm-ops->smart_get_flags' then this dimm-op is called to
  incrementally retrieve dimm-stats. For each call the dimm-op
  implementer is expected to populate the provided instance of 'struct
  ndctl_dimm_stat *' with a info on name, type and value of a
  dimm-stat. In case no more dimm-stats are available the dimm-op
  should return an error.

The newly introduced 'struct ndctl_dimm_stat' holds Name, type and
value information of a single dimm-stat. The ndctl_dimm_stat.type
information is used to appropriately format the dimm-stat value in the
json output.

The patch also updates 'util/json-smart.c' introducing new function
util_dimm_stats_to_json() thats called when '--stats' command line arg
is provided, and it drives the calls to dimm-ops 'new_stats' and
'get_stat'. The function does follows this sequence:

1. Generates a new json-object named 'stats' in the json output
2. Use 'dimm_ops->new_stats' to get a ndctl_cmd instance.
3. Submit the command to libndctl.
4. In case of successful submission, retrieve flags associated with the
   command.
4. In case flag ND_SMART_STATS_VALID use dimm-op 'get_stat' to retrieve
   available dimm-stats.
5. Creates new json-object for each 'struct ndctl_dimm_stat' returned
   from dimm-op 'get_stat' based on the type of dimm-stat.
6. Adds the above create json-object as child node of the 'stats'
   json-object.
7. Return the 'stats' json object from the function.

Signed-off-by: Vaibhav Jain <vaibhav@linux.ibm.com>
---
 Documentation/ndctl/ndctl-list.txt | 24 ++++++++++
 ndctl/lib/libndctl.sym             |  5 ++
 ndctl/lib/private.h                |  6 +++
 ndctl/lib/smart.c                  | 26 +++++++++++
 ndctl/libndctl.h                   | 23 ++++++++++
 ndctl/list.c                       |  9 ++++
 ndctl/util/json-smart.c            | 73 ++++++++++++++++++++++++++++++
 util/json.h                        |  1 +
 8 files changed, 167 insertions(+)
diff mbox series

Patch

diff --git a/Documentation/ndctl/ndctl-list.txt b/Documentation/ndctl/ndctl-list.txt
index 7c7e3ac9d05c..4ba58b96fb74 100644
--- a/Documentation/ndctl/ndctl-list.txt
+++ b/Documentation/ndctl/ndctl-list.txt
@@ -129,6 +129,30 @@  include::xable-bus-options.txt[]
     "shutdown_state":"clean"
   }
 }
+-S::
+--stats::
+	Include dimm statistics in the listing. For example:
+[verse]
+{
+  "dev":"nmem0"
+  "stats":{
+    "Controller Reset Count":2,
+    "Controller Reset Elapsed Time":50800,
+    "Power-on Seconds":51400,
+    "Critical Resource Utilization":"0%",
+    "Host Load Count":4056485,
+    "Host Store Count":8956850,
+    "Host Load Duration":765053890,
+    "Host Store Duration":715390700,
+    "Media Read Count":0,
+    "Media Write Count":6178,
+    "Media Read Duration":0,
+    "Media Write Duration":9468375,
+    "Cache Read Hit Count":4056485,
+    "Cache Write Hit Count":8432554,
+    "Fast Write Count":8959962
+  }
+}
 
 -F::
 --firmware::
diff --git a/ndctl/lib/libndctl.sym b/ndctl/lib/libndctl.sym
index ac575a23d035..5b8eabc1b9d5 100644
--- a/ndctl/lib/libndctl.sym
+++ b/ndctl/lib/libndctl.sym
@@ -431,3 +431,8 @@  LIBNDCTL_23 {
 	ndctl_region_get_align;
 	ndctl_region_set_align;
 } LIBNDCTL_22;
+
+LIBNDCTL_24 {
+	ndctl_dimm_cmd_new_stats;
+	ndctl_dimm_get_stat;
+} LIBNDCTL_23;
\ No newline at end of file
diff --git a/ndctl/lib/private.h b/ndctl/lib/private.h
index 679e359a1070..c4d1f42f7ac2 100644
--- a/ndctl/lib/private.h
+++ b/ndctl/lib/private.h
@@ -238,6 +238,7 @@  struct ndctl_namespace {
  * @status: negative if failed, 0 if success, > 0 if never submitted
  * @get_firmware_status: per command firmware status field retrieval
  * @iter: iterator for multi-xfer commands
+ * @private_data: Used by dimm-provider to store private data
  * @source: source cmd of an inherited iter.total_buf
  *
  * For dynamically sized commands like 'get_config', 'set_config', or
@@ -266,6 +267,7 @@  struct ndctl_cmd {
 		u32 total_xfer;
 		int dir;
 	} iter;
+	void *private_data;
 	struct ndctl_cmd *source;
 	union {
 		struct nd_cmd_ars_cap ars_cap[0];
@@ -352,6 +354,10 @@  struct ndctl_dimm_ops {
 	int (*dimm_init)(struct ndctl_dimm *);
 	/* Called just before struct ndctl_dimm is de-allocated */
 	void (*dimm_uninit)(struct ndctl_dimm *);
+	/* Return a command to fetch dimm stats */
+	struct ndctl_cmd *(*new_stats)(struct ndctl_dimm *);
+	/* Return a single dimm-stat from the command until error */
+	int (*get_stat)(struct ndctl_cmd *, struct ndctl_dimm_stat *);
 };
 
 extern struct ndctl_dimm_ops * const intel_dimm_ops;
diff --git a/ndctl/lib/smart.c b/ndctl/lib/smart.c
index 0e180cff5a3e..fbb1248c08a9 100644
--- a/ndctl/lib/smart.c
+++ b/ndctl/lib/smart.c
@@ -31,6 +31,32 @@  NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_smart(
 		return NULL;
 }
 
+NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_stats(
+		struct ndctl_dimm *dimm)
+{
+	struct ndctl_dimm_ops *ops = dimm->ops;
+
+	if (ops && ops->new_stats)
+		return ops->new_stats(dimm);
+	else
+		return NULL;
+}
+
+NDCTL_EXPORT int ndctl_dimm_get_stat(struct ndctl_cmd *cmd,
+				      struct ndctl_dimm_stat * stat)
+{
+	struct ndctl_dimm_ops *ops;
+
+	if (!cmd || !cmd->dimm)
+		return -EINVAL;
+	ops = cmd->dimm->ops;
+
+	if (ops && ops->get_stat)
+		return ops->get_stat(cmd, stat);
+	else
+		return -ENOENT;
+}
+
 NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_smart_threshold(
 		struct ndctl_dimm *dimm)
 {
diff --git a/ndctl/libndctl.h b/ndctl/libndctl.h
index daf11b8ce4ea..9fab2097a920 100644
--- a/ndctl/libndctl.h
+++ b/ndctl/libndctl.h
@@ -247,6 +247,7 @@  int ndctl_cmd_ars_stat_get_flag_overflow(struct ndctl_cmd *ars_stat);
 #define ND_SMART_ALARM_VALID	(1 << 9)
 #define ND_SMART_SHUTDOWN_VALID	(1 << 10)
 #define ND_SMART_VENDOR_VALID	(1 << 11)
+#define ND_SMART_STATS_VALID	(1 << 12)
 #define ND_SMART_SPARE_TRIP	(1 << 0)
 #define ND_SMART_MTEMP_TRIP	(1 << 1)
 #define ND_SMART_TEMP_TRIP	ND_SMART_MTEMP_TRIP
@@ -341,6 +342,28 @@  int ndctl_cmd_get_status(struct ndctl_cmd *cmd);
 unsigned int ndctl_cmd_get_firmware_status(struct ndctl_cmd *cmd);
 int ndctl_cmd_submit(struct ndctl_cmd *cmd);
 
+/* Holds a single dimm stat which can be retrived */
+struct ndctl_dimm_stat {
+        const char *name;
+        enum {
+		STAT_TYPE_BOOL,
+		STAT_TYPE_INT,
+		STAT_TYPE_INT64,
+		STAT_TYPE_DOUBLE,
+		STAT_TYPE_STR,
+		STAT_TYPE_PERCENT,
+	} type;
+	union {
+		bool bool_val;
+		int int_val;
+		long long int64_val;
+		double double_val;
+		char str_val[32];
+	} val;
+};
+
+struct ndctl_cmd *ndctl_dimm_cmd_new_stats(struct ndctl_dimm *dimm);
+int ndctl_dimm_get_stat(struct ndctl_cmd *cmd, struct ndctl_dimm_stat *stat);
 struct badblock {
 	unsigned long long offset;
 	unsigned int len;
diff --git a/ndctl/list.c b/ndctl/list.c
index 31fb1b9593a2..cda3493c2ffc 100644
--- a/ndctl/list.c
+++ b/ndctl/list.c
@@ -32,6 +32,7 @@  static struct {
 	bool namespaces;
 	bool idle;
 	bool health;
+	bool stats;
 	bool dax;
 	bool media_errors;
 	bool human;
@@ -367,6 +368,13 @@  static void filter_dimm(struct ndctl_dimm *dimm, struct util_filter_ctx *ctx)
 		}
 	}
 
+	if (list.stats) {
+		struct json_object *jstats;
+
+		jstats = util_dimm_stats_to_json(dimm);
+		json_object_object_add(jdimm, "stats", jstats);
+	}
+
 	if (list.firmware) {
 		struct json_object *jfirmware;
 
@@ -479,6 +487,7 @@  int cmd_list(int argc, const char **argv, struct ndctl_ctx *ctx)
 		OPT_BOOLEAN('D', "dimms", &list.dimms, "include dimm info"),
 		OPT_BOOLEAN('F', "firmware", &list.firmware, "include firmware info"),
 		OPT_BOOLEAN('H', "health", &list.health, "include dimm health"),
+		OPT_BOOLEAN('S', "stats", &list.stats, "include dimm stats"),
 		OPT_BOOLEAN('R', "regions", &list.regions,
 				"include region info"),
 		OPT_BOOLEAN('N', "namespaces", &list.namespaces,
diff --git a/ndctl/util/json-smart.c b/ndctl/util/json-smart.c
index a9bd17b37b4e..1312439453ee 100644
--- a/ndctl/util/json-smart.c
+++ b/ndctl/util/json-smart.c
@@ -221,3 +221,76 @@  struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm)
 		ndctl_cmd_unref(cmd);
 	return jhealth;
 }
+
+struct json_object *util_dimm_stats_to_json(struct ndctl_dimm *dimm)
+{
+	struct json_object *jstat = json_object_new_object();
+	struct json_object *jobj;
+	struct ndctl_cmd *cmd;
+	struct ndctl_dimm_stat stat = { 0 };
+	char format_buffer[32] = { 0 };
+	int rc;
+	unsigned int flags;
+
+	if (!jstat)
+		return NULL;
+
+	cmd = ndctl_dimm_cmd_new_stats(dimm);
+	if (!cmd)
+		goto err;
+
+	rc = ndctl_cmd_submit_xlat(cmd);
+	if (rc < 0) {
+		jobj = json_object_new_string("unknown");
+		if (jobj)
+			json_object_object_add(jstat, "stats", jobj);
+		goto out;
+	}
+
+	/* Check if any stats are reported */
+	flags = ndctl_cmd_smart_get_flags(cmd);
+	if (!(flags & ND_SMART_STATS_VALID))
+		goto out;
+
+	/* Iterate through the reported stats list */
+	while (ndctl_dimm_get_stat(cmd, &stat) == 0) {
+		switch(stat.type) {
+		case STAT_TYPE_BOOL:
+			jobj = json_object_new_boolean(stat.val.bool_val);
+			break;
+		case STAT_TYPE_INT:
+			jobj = json_object_new_int(stat.val.int_val);
+			break;
+		case STAT_TYPE_INT64:
+			jobj = json_object_new_int64(stat.val.int64_val);
+			break;
+		case STAT_TYPE_DOUBLE:
+			jobj = json_object_new_double(stat.val.double_val);
+			break;
+		case STAT_TYPE_STR:
+			jobj = json_object_new_string(stat.val.str_val);
+			break;
+		case STAT_TYPE_PERCENT:
+			snprintf(format_buffer, sizeof(format_buffer) - 1,
+				 "%u%%",stat.val.int_val);
+			format_buffer[sizeof(format_buffer) - 1] = '\0';
+			jobj = json_object_new_string(format_buffer);
+			break;
+		default:
+			jobj = json_object_new_string("unknown-type");
+			break;
+		};
+
+		if (jobj)
+			json_object_object_add(jstat, stat.name, jobj);
+	}
+	ndctl_cmd_unref(cmd);
+	return jstat;
+ err:
+	json_object_put(jstat);
+	jstat = NULL;
+ out:
+	if (cmd)
+		ndctl_cmd_unref(cmd);
+	return jstat;
+}
diff --git a/util/json.h b/util/json.h
index 6d39d3aa4693..e678596e1aab 100644
--- a/util/json.h
+++ b/util/json.h
@@ -56,6 +56,7 @@  struct json_object *util_json_object_size(unsigned long long size,
 struct json_object *util_json_object_hex(unsigned long long val,
 		unsigned long flags);
 struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm);
+struct json_object *util_dimm_stats_to_json(struct ndctl_dimm *dimm);
 struct json_object *util_dimm_firmware_to_json(struct ndctl_dimm *dimm,
 		unsigned long flags);
 struct json_object *util_region_capabilities_to_json(struct ndctl_region *region);