@@ -132,16 +132,35 @@ include::xable-bus-options.txt[]
-F::
--firmware::
- Include dimm firmware info in the listing. For example:
-[verse]
-{
- "dev":"nmem0",
- "firmware":{
- "current_version":0,
- "next_version":1,
- "need_powercycle":true
- }
-}
+ Include firmware info in the listing, including the state and
+ capability of runtime firmware activation:
+
+----
+# ndctl list -BDF
+[
+ {
+ "provider":"nfit_test.0",
+ "dev":"ndbus2",
+ "scrub_state":"idle",
+ "firmware":{
+ "activate_method":"suspend",
+ "activate_state":"idle"
+ },
+ "dimms":[
+ {
+ "dev":"nmem1",
+ "id":"cdab-0a-07e0-ffffffff",
+ "handle":0,
+ "phys_id":0,
+ "security":"disabled",
+ "firmware":{
+ "current_version":0,
+ "can_update":true
+ }
+ },
+...
+]
+----
-X::
--device-dax::
@@ -92,7 +92,7 @@ static int bus_action(int argc, const char **argv, const char *usage,
rc = scrub_action(bus, action);
if (rc == 0) {
success++;
- jbus = util_bus_to_json(bus);
+ jbus = util_bus_to_json(bus, 0);
if (jbus)
json_object_array_add(jbuses, jbus);
} else if (!fail)
@@ -819,6 +819,31 @@ static void parse_dimm_flags(struct ndctl_dimm *dimm, char *flags)
ndctl_dimm_get_devname(dimm), flags);
}
+static enum ndctl_fwa_state fwa_to_state(const char *fwa)
+{
+ if (strcmp(fwa, "idle") == 0)
+ return NDCTL_FWA_IDLE;
+ if (strcmp(fwa, "busy") == 0)
+ return NDCTL_FWA_BUSY;
+ if (strcmp(fwa, "armed") == 0)
+ return NDCTL_FWA_ARMED;
+ if (strcmp(fwa, "overflow") == 0)
+ return NDCTL_FWA_ARM_OVERFLOW;
+ return NDCTL_FWA_INVALID;
+}
+
+static enum ndctl_fwa_method fwa_method_to_method(const char *fwa_method)
+{
+ if (!fwa_method)
+ return NDCTL_FWA_METHOD_RESET;
+
+ if (strcmp(fwa_method, "quiesce") == 0)
+ return NDCTL_FWA_METHOD_SUSPEND;
+ if (strcmp(fwa_method, "live") == 0)
+ return NDCTL_FWA_METHOD_LIVE;
+ return NDCTL_FWA_METHOD_RESET;
+}
+
static void *add_bus(void *parent, int id, const char *ctl_base)
{
char buf[SYSFS_ATTR_SIZE];
@@ -880,6 +905,19 @@ static void *add_bus(void *parent, int id, const char *ctl_base)
if (!bus->scrub_path)
goto err_read;
+ sprintf(path, "%s/device/firmware/activate", ctl_base);
+ if (sysfs_read_attr(ctx, path, buf) < 0)
+ bus->fwa_state = NDCTL_FWA_INVALID;
+ else
+ bus->fwa_state = fwa_to_state(buf);
+
+ sprintf(path, "%s/device/firmware/capability", ctl_base);
+ if (sysfs_read_attr(ctx, path, buf) < 0)
+ bus->fwa_method = fwa_method_to_method(NULL);
+ else
+ bus->fwa_method = fwa_method_to_method(buf);
+
+
bus->bus_path = parent_dev_path("char", bus->major, bus->minor);
if (!bus->bus_path)
goto err_dev_path;
@@ -1436,6 +1474,51 @@ NDCTL_EXPORT int ndctl_bus_wait_for_scrub_completion(struct ndctl_bus *bus)
return ndctl_bus_poll_scrub_completion(bus, 0, 0);
}
+NDCTL_EXPORT enum ndctl_fwa_state ndctl_bus_get_fw_activate_state(
+ struct ndctl_bus *bus)
+{
+ struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
+ char *path = bus->bus_buf;
+ char buf[SYSFS_ATTR_SIZE];
+ int len = bus->buf_len;
+
+ if (bus->fwa_state == NDCTL_FWA_INVALID)
+ return NDCTL_FWA_INVALID;
+
+ if (snprintf(path, len, "%s/firmware/activate", bus->bus_path) >= len) {
+ err(ctx, "%s: buffer too small!\n",
+ ndctl_bus_get_devname(bus));
+ return NDCTL_FWA_INVALID;
+ }
+
+ if (sysfs_read_attr(ctx, path, buf) < 0)
+ return NDCTL_FWA_INVALID;
+
+ bus->fwa_state = fwa_to_state(buf);
+
+ return bus->fwa_state;
+}
+
+NDCTL_EXPORT enum ndctl_fwa_method ndctl_bus_get_fw_activate_method(struct ndctl_bus *bus)
+{
+ return bus->fwa_method;
+}
+
+static enum ndctl_fwa_result fwa_result_to_result(const char *result)
+{
+ if (strcmp(result, "none") == 0)
+ return NDCTL_FWA_RESULT_NONE;
+ if (strcmp(result, "success") == 0)
+ return NDCTL_FWA_RESULT_SUCCESS;
+ if (strcmp(result, "fail") == 0)
+ return NDCTL_FWA_RESULT_FAIL;
+ if (strcmp(result, "not_staged") == 0)
+ return NDCTL_FWA_RESULT_NOTSTAGED;
+ if (strcmp(result, "need_reset") == 0)
+ return NDCTL_FWA_RESULT_NEEDRESET;
+ return NDCTL_FWA_RESULT_INVALID;
+}
+
static int ndctl_bind(struct ndctl_ctx *ctx, struct kmod_module *module,
const char *devname);
static int ndctl_unbind(struct ndctl_ctx *ctx, const char *devpath);
@@ -1515,6 +1598,18 @@ static void *add_dimm(void *parent, int id, const char *dimm_base)
} else
parse_dimm_flags(dimm, buf);
+ sprintf(path, "%s/firmware/activate", dimm_base);
+ if (sysfs_read_attr(ctx, path, buf) < 0)
+ dimm->fwa_state = NDCTL_FWA_INVALID;
+ else
+ dimm->fwa_state = fwa_to_state(buf);
+
+ sprintf(path, "%s/firmware/result", dimm_base);
+ if (sysfs_read_attr(ctx, path, buf) < 0)
+ dimm->fwa_result = NDCTL_FWA_RESULT_INVALID;
+ else
+ dimm->fwa_result = fwa_result_to_result(buf);
+
if (!ndctl_bus_has_nfit(bus))
goto out;
@@ -1998,6 +2093,51 @@ NDCTL_EXPORT int ndctl_dimm_enable(struct ndctl_dimm *dimm)
return 0;
}
+NDCTL_EXPORT enum ndctl_fwa_state ndctl_dimm_get_fw_activate_state(
+ struct ndctl_dimm *dimm)
+{
+ struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm);
+ char *path = dimm->dimm_buf;
+ char buf[SYSFS_ATTR_SIZE];
+ int len = dimm->buf_len;
+
+ if (dimm->fwa_state == NDCTL_FWA_INVALID)
+ return NDCTL_FWA_INVALID;
+
+ if (snprintf(path, len, "%s/firmware/activate", dimm->dimm_path) >= len) {
+ err(ctx, "%s: buffer too small!\n", ndctl_dimm_get_devname(dimm));
+ return NDCTL_FWA_INVALID;
+ }
+
+ if (sysfs_read_attr(ctx, path, buf) < 0)
+ return NDCTL_FWA_INVALID;
+
+ dimm->fwa_state = fwa_to_state(buf);
+ return dimm->fwa_state;
+}
+
+NDCTL_EXPORT enum ndctl_fwa_result ndctl_dimm_get_fw_activate_result(
+ struct ndctl_dimm *dimm)
+{
+ struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm);
+ char *path = dimm->dimm_buf;
+ char buf[SYSFS_ATTR_SIZE];
+ int len = dimm->buf_len;
+
+ if (dimm->fwa_result == NDCTL_FWA_RESULT_INVALID)
+ return NDCTL_FWA_RESULT_INVALID;
+
+ if (snprintf(path, len, "%s/firmware/result", dimm->dimm_path) >= len) {
+ err(ctx, "%s: buffer too small!\n", ndctl_dimm_get_devname(dimm));
+ return NDCTL_FWA_RESULT_INVALID;
+ }
+
+ if (sysfs_read_attr(ctx, path, buf) < 0)
+ return NDCTL_FWA_RESULT_INVALID;
+
+ return fwa_result_to_result(buf);
+}
+
NDCTL_EXPORT struct ndctl_dimm *ndctl_dimm_get_by_handle(struct ndctl_bus *bus,
unsigned int handle)
{
@@ -431,3 +431,10 @@ LIBNDCTL_23 {
ndctl_region_get_align;
ndctl_region_set_align;
} LIBNDCTL_22;
+
+LIBNDCTL_24 {
+ ndctl_dimm_get_fw_activate_state;
+ ndctl_dimm_get_fw_activate_result;
+ ndctl_bus_get_fw_activate_state;
+ ndctl_bus_get_fw_activate_method;
+} LIBNDCTL_23;
@@ -79,6 +79,8 @@ struct ndctl_dimm {
unsigned long cmd_mask;
unsigned long nfit_dsm_mask;
long long dirty_shutdown;
+ enum ndctl_fwa_state fwa_state;
+ enum ndctl_fwa_result fwa_result;
char *unique_id;
char *dimm_path;
char *dimm_buf;
@@ -174,6 +176,8 @@ struct ndctl_bus {
char *scrub_path;
unsigned long cmd_mask;
unsigned long nfit_dsm_mask;
+ enum ndctl_fwa_state fwa_state;
+ enum ndctl_fwa_method fwa_method;
};
/**
@@ -110,6 +110,20 @@ enum ndctl_persistence_domain {
PERSISTENCE_UNKNOWN = INT_MAX,
};
+enum ndctl_fwa_state {
+ NDCTL_FWA_INVALID,
+ NDCTL_FWA_IDLE,
+ NDCTL_FWA_ARMED,
+ NDCTL_FWA_BUSY,
+ NDCTL_FWA_ARM_OVERFLOW,
+};
+
+enum ndctl_fwa_method {
+ NDCTL_FWA_METHOD_RESET,
+ NDCTL_FWA_METHOD_SUSPEND,
+ NDCTL_FWA_METHOD_LIVE,
+};
+
struct ndctl_bus;
struct ndctl_bus *ndctl_bus_get_first(struct ndctl_ctx *ctx);
struct ndctl_bus *ndctl_bus_get_next(struct ndctl_bus *bus);
@@ -139,6 +153,8 @@ unsigned int ndctl_bus_get_scrub_count(struct ndctl_bus *bus);
int ndctl_bus_get_scrub_state(struct ndctl_bus *bus);
int ndctl_bus_start_scrub(struct ndctl_bus *bus);
int ndctl_bus_has_error_injection(struct ndctl_bus *bus);
+enum ndctl_fwa_state ndctl_bus_get_fw_activate_state(struct ndctl_bus *bus);
+enum ndctl_fwa_method ndctl_bus_get_fw_activate_method(struct ndctl_bus *bus);
struct ndctl_dimm;
struct ndctl_dimm *ndctl_dimm_get_first(struct ndctl_bus *bus);
@@ -702,6 +718,18 @@ enum ND_FW_STATUS ndctl_cmd_fw_xlat_firmware_status(struct ndctl_cmd *cmd);
struct ndctl_cmd *ndctl_dimm_cmd_new_ack_shutdown_count(struct ndctl_dimm *dimm);
int ndctl_dimm_fw_update_supported(struct ndctl_dimm *dimm);
+enum ndctl_fwa_result {
+ NDCTL_FWA_RESULT_INVALID,
+ NDCTL_FWA_RESULT_NONE,
+ NDCTL_FWA_RESULT_SUCCESS,
+ NDCTL_FWA_RESULT_NOTSTAGED,
+ NDCTL_FWA_RESULT_NEEDRESET,
+ NDCTL_FWA_RESULT_FAIL,
+};
+
+enum ndctl_fwa_state ndctl_dimm_get_fw_activate_state(struct ndctl_dimm *dimm);
+enum ndctl_fwa_result ndctl_dimm_get_fw_activate_result(struct ndctl_dimm *dimm);
+
int ndctl_cmd_xlat_firmware_status(struct ndctl_cmd *cmd);
int ndctl_cmd_submit_xlat(struct ndctl_cmd *cmd);
@@ -403,11 +403,12 @@ static bool filter_bus(struct ndctl_bus *bus, struct util_filter_ctx *ctx)
}
}
- lfa->jbus = util_bus_to_json(bus);
+ lfa->jbus = util_bus_to_json(bus, lfa->flags);
if (!lfa->jbus) {
fail("\n");
return false;
}
+
json_object_array_add(lfa->jbuses, lfa->jbus);
return true;
}
@@ -122,10 +122,10 @@ void util_display_json_array(FILE *f_out, struct json_object *jarray,
json_object_put(jarray);
}
-struct json_object *util_bus_to_json(struct ndctl_bus *bus)
+struct json_object *util_bus_to_json(struct ndctl_bus *bus, unsigned long flags)
{
struct json_object *jbus = json_object_new_object();
- struct json_object *jobj;
+ struct json_object *jobj, *fw_obj = NULL;
int scrub;
if (!jbus)
@@ -150,6 +150,49 @@ struct json_object *util_bus_to_json(struct ndctl_bus *bus)
goto err;
json_object_object_add(jbus, "scrub_state", jobj);
+ if (flags & UTIL_JSON_FIRMWARE) {
+ struct ndctl_dimm *dimm;
+
+ /*
+ * Skip displaying firmware activation capability if no
+ * DIMMs support firmware update.
+ */
+ ndctl_dimm_foreach(bus, dimm)
+ if (ndctl_dimm_fw_update_supported(dimm) == 0) {
+ fw_obj = json_object_new_object();
+ break;
+ }
+ }
+
+ if (fw_obj) {
+ enum ndctl_fwa_state state;
+ enum ndctl_fwa_method method;
+
+ jobj = NULL;
+ method = ndctl_bus_get_fw_activate_method(bus);
+ if (method == NDCTL_FWA_METHOD_RESET)
+ jobj = json_object_new_string("reset");
+ if (method == NDCTL_FWA_METHOD_SUSPEND)
+ jobj = json_object_new_string("suspend");
+ if (method == NDCTL_FWA_METHOD_LIVE)
+ jobj = json_object_new_string("live");
+ if (jobj)
+ json_object_object_add(fw_obj, "activate_method", jobj);
+
+ jobj = NULL;
+ state = ndctl_bus_get_fw_activate_state(bus);
+ if (state == NDCTL_FWA_ARMED)
+ jobj = json_object_new_string("armed");
+ if (state == NDCTL_FWA_IDLE)
+ jobj = json_object_new_string("idle");
+ if (state == NDCTL_FWA_ARM_OVERFLOW)
+ jobj = json_object_new_string("overflow");
+ if (jobj)
+ json_object_object_add(fw_obj, "activate_state", jobj);
+
+ json_object_object_add(jbus, "firmware", fw_obj);
+ }
+
return jbus;
err:
json_object_put(jbus);
@@ -160,10 +203,13 @@ struct json_object *util_dimm_firmware_to_json(struct ndctl_dimm *dimm,
unsigned long flags)
{
struct json_object *jfirmware = json_object_new_object();
+ bool can_update, need_powercycle;
+ enum ndctl_fwa_result result;
+ enum ndctl_fwa_state state;
struct json_object *jobj;
struct ndctl_cmd *cmd;
- int rc;
uint64_t run, next;
+ int rc;
if (!jfirmware)
return NULL;
@@ -195,10 +241,12 @@ struct json_object *util_dimm_firmware_to_json(struct ndctl_dimm *dimm,
json_object_object_add(jfirmware, "current_version", jobj);
rc = ndctl_dimm_fw_update_supported(dimm);
- jobj = json_object_new_boolean(rc == 0);
+ can_update = rc == 0;
+ jobj = json_object_new_boolean(can_update);
if (jobj)
json_object_object_add(jfirmware, "can_update", jobj);
+
next = ndctl_cmd_fw_info_get_updated_version(cmd);
if (next == ULLONG_MAX) {
jobj = util_json_object_hex(-1, flags);
@@ -208,16 +256,61 @@ struct json_object *util_dimm_firmware_to_json(struct ndctl_dimm *dimm,
goto out;
}
- if (next != 0) {
- jobj = util_json_object_hex(next, flags);
- if (jobj)
- json_object_object_add(jfirmware,
- "next_version", jobj);
+ if (!next)
+ goto out;
+
+ jobj = util_json_object_hex(next, flags);
+ if (jobj)
+ json_object_object_add(jfirmware,
+ "next_version", jobj);
+
+ state = ndctl_dimm_get_fw_activate_state(dimm);
+ switch (state) {
+ case NDCTL_FWA_IDLE:
+ jobj = json_object_new_string("idle");
+ break;
+ case NDCTL_FWA_ARMED:
+ jobj = json_object_new_string("armed");
+ break;
+ case NDCTL_FWA_BUSY:
+ jobj = json_object_new_string("busy");
+ break;
+ default:
+ jobj = NULL;
+ break;
+ }
+ if (jobj)
+ json_object_object_add(jfirmware, "activate_state", jobj);
+
+ result = ndctl_dimm_get_fw_activate_result(dimm);
+ switch (result) {
+ case NDCTL_FWA_RESULT_NONE:
+ case NDCTL_FWA_RESULT_SUCCESS:
+ case NDCTL_FWA_RESULT_NOTSTAGED:
+ /*
+ * If a 'next' firmware version is staged then this
+ * result is stale, if the activation succeeds that is
+ * indicated by not finding a 'next' entry.
+ */
+ need_powercycle = false;
+ break;
+ case NDCTL_FWA_RESULT_NEEDRESET:
+ case NDCTL_FWA_RESULT_FAIL:
+ default:
+ /*
+ * If the last activation failed, or if the activation
+ * result is unavailable it is always the case that the
+ * only remediation is powercycle.
+ */
+ need_powercycle = true;
+ break;
+ }
+ if (need_powercycle) {
jobj = json_object_new_boolean(true);
- if (jobj)
- json_object_object_add(jfirmware,
- "need_powercycle", jobj);
+ if (!jobj)
+ goto out;
+ json_object_object_add(jfirmware, "need_powercycle", jobj);
}
ndctl_cmd_unref(cmd);
@@ -32,7 +32,8 @@ enum util_json_flags {
struct json_object;
void util_display_json_array(FILE *f_out, struct json_object *jarray,
unsigned long flags);
-struct json_object *util_bus_to_json(struct ndctl_bus *bus);
+struct json_object *util_bus_to_json(struct ndctl_bus *bus,
+ unsigned long flags);
struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm,
unsigned long flags);
struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping,
When firmware is staged check if the platform / kernel also supports live firmware activation and include that state at both the dimm and bus level. The "last activate result" is included only as a hint to whether a powercycle is required to activate the current image. If firmware activation fails the "need_powercycle" flag will appear, if firmware activation is successful then the "current_version" field will be up-to-date. Signed-off-by: Dan Williams <dan.j.williams@intel.com> --- Documentation/ndctl/ndctl-list.txt | 39 +++++++--- ndctl/bus.c | 2 - ndctl/lib/libndctl.c | 140 ++++++++++++++++++++++++++++++++++++ ndctl/lib/libndctl.sym | 7 ++ ndctl/lib/private.h | 4 + ndctl/libndctl.h | 28 +++++++ ndctl/list.c | 3 + util/json.c | 117 +++++++++++++++++++++++++++--- util/json.h | 3 + 9 files changed, 318 insertions(+), 25 deletions(-)