@@ -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::
@@ -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
@@ -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;
@@ -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)
{
@@ -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;
@@ -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,
@@ -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;
+}
@@ -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);
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(+)