* [PATCH ndctl 0/5] cxl: firmware update support for libcxl and cxl-cli
@ 2023-04-22 3:09 Vishal Verma
2023-04-22 3:09 ` [PATCH ndctl 1/5] cxl/memdev.c: allow filtering memdevs by bus Vishal Verma
` (4 more replies)
0 siblings, 5 replies; 12+ messages in thread
From: Vishal Verma @ 2023-04-22 3:09 UTC (permalink / raw)
To: linux-cxl
Cc: nvdimm, Alison Schofield, Ira Weiny, Dave Jiang, Dan Williams,
Vishal Verma
Patch 1 is a preparatory patch that teaches memdev based commands to
filter their operand memdevs by bus. This helps restricting unit test
operations to the cxl_test bus.
Patches 2 and 3 add firmware information to the CXL memdev listing. This
is derived from the 'Get FW Info' mailbox command as well as state
information in the kernel's firmware loader mechanism in sysfs.
Patch 4 adds the libcxl APIs to perform a firmware update, and to cancel
an in-progress update, and the cxl-cli command to use these APIs to
start, wait for, and cancel firmware updates. A man page for the new
command is added as well.
Patch 5 adds a unit test to exercise all the features described above in
a cxl_test environment.
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
Vishal Verma (5):
cxl/memdev.c: allow filtering memdevs by bus
cxl/list: print firmware info in memdev listings
cxl/fw_loader: add APIs to get current state of the FW loader mechanism
cxl: add an update-firmware command
test/cxl-update-firmware: add a unit test for firmware update
Documentation/cxl/cxl-disable-memdev.txt | 2 +
Documentation/cxl/cxl-enable-memdev.txt | 2 +
Documentation/cxl/cxl-free-dpa.txt | 2 +
Documentation/cxl/cxl-read-labels.txt | 2 +
Documentation/cxl/cxl-reserve-dpa.txt | 2 +
Documentation/cxl/cxl-set-partition.txt | 2 +
Documentation/cxl/cxl-write-labels.txt | 3 +
cxl/lib/private.h | 36 ++++
cxl/lib/libcxl.c | 304 +++++++++++++++++++++++++++++++
cxl/builtin.h | 1 +
cxl/filter.h | 5 +
cxl/libcxl.h | 36 ++++
cxl/cxl.c | 1 +
cxl/filter.c | 19 ++
cxl/json.c | 97 ++++++++++
cxl/list.c | 3 +
cxl/memdev.c | 77 +++++++-
Documentation/cxl/meson.build | 1 +
cxl/lib/libcxl.sym | 10 +
test/cxl-update-firmware.sh | 195 ++++++++++++++++++++
test/meson.build | 2 +
21 files changed, 801 insertions(+), 1 deletion(-)
---
base-commit: b830c4af984e72e5849c0705669aad2ffa19db13
change-id: 20230405-vv-fw_update-905d253fcb3b
Best regards,
--
Vishal Verma <vishal.l.verma@intel.com>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH ndctl 1/5] cxl/memdev.c: allow filtering memdevs by bus
2023-04-22 3:09 [PATCH ndctl 0/5] cxl: firmware update support for libcxl and cxl-cli Vishal Verma
@ 2023-04-22 3:09 ` Vishal Verma
2023-05-19 17:57 ` Dave Jiang
2023-04-22 3:10 ` [PATCH ndctl 2/5] cxl/list: print firmware info in memdev listings Vishal Verma
` (3 subsequent siblings)
4 siblings, 1 reply; 12+ messages in thread
From: Vishal Verma @ 2023-04-22 3:09 UTC (permalink / raw)
To: linux-cxl
Cc: nvdimm, Alison Schofield, Ira Weiny, Dave Jiang, Dan Williams,
Vishal Verma
The family of memdev based commands implemented in memdev.c lacked an
option to filter the operation by bus. Add a helper to filter memdevs by
the bus they're under, and use it in memdev_action() which loops through
the requested memdevs. Update the man pages for all the affected
commands as well to include the bus filter option.
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alison Schofield <alison.schofield@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
Documentation/cxl/cxl-disable-memdev.txt | 2 ++
Documentation/cxl/cxl-enable-memdev.txt | 2 ++
Documentation/cxl/cxl-free-dpa.txt | 2 ++
Documentation/cxl/cxl-read-labels.txt | 2 ++
Documentation/cxl/cxl-reserve-dpa.txt | 2 ++
Documentation/cxl/cxl-set-partition.txt | 2 ++
Documentation/cxl/cxl-write-labels.txt | 3 +++
cxl/filter.h | 2 ++
cxl/filter.c | 19 +++++++++++++++++++
cxl/memdev.c | 4 ++++
10 files changed, 40 insertions(+)
diff --git a/Documentation/cxl/cxl-disable-memdev.txt b/Documentation/cxl/cxl-disable-memdev.txt
index edd5385..d397802 100644
--- a/Documentation/cxl/cxl-disable-memdev.txt
+++ b/Documentation/cxl/cxl-disable-memdev.txt
@@ -18,6 +18,8 @@ OPTIONS
<memory device(s)>::
include::memdev-option.txt[]
+include::bus-option.txt[]
+
-f::
--force::
DANGEROUS: Override the safety measure that blocks attempts to disable
diff --git a/Documentation/cxl/cxl-enable-memdev.txt b/Documentation/cxl/cxl-enable-memdev.txt
index 088d5e0..5b5ed66 100644
--- a/Documentation/cxl/cxl-enable-memdev.txt
+++ b/Documentation/cxl/cxl-enable-memdev.txt
@@ -23,6 +23,8 @@ OPTIONS
<memory device(s)>::
include::memdev-option.txt[]
+include::bus-option.txt[]
+
-v::
Turn on verbose debug messages in the library (if libcxl was built with
logging and debug enabled).
diff --git a/Documentation/cxl/cxl-free-dpa.txt b/Documentation/cxl/cxl-free-dpa.txt
index 73fb048..506fafd 100644
--- a/Documentation/cxl/cxl-free-dpa.txt
+++ b/Documentation/cxl/cxl-free-dpa.txt
@@ -24,6 +24,8 @@ OPTIONS
<memory device(s)>::
include::memdev-option.txt[]
+include::bus-option.txt[]
+
-d::
--decoder::
Specify the decoder to free. The CXL specification
diff --git a/Documentation/cxl/cxl-read-labels.txt b/Documentation/cxl/cxl-read-labels.txt
index 143f296..a96e7a4 100644
--- a/Documentation/cxl/cxl-read-labels.txt
+++ b/Documentation/cxl/cxl-read-labels.txt
@@ -20,6 +20,8 @@ OPTIONS
-------
include::labels-options.txt[]
+include::bus-option.txt[]
+
-o::
--output::
output file
diff --git a/Documentation/cxl/cxl-reserve-dpa.txt b/Documentation/cxl/cxl-reserve-dpa.txt
index 5e79ef2..58cc93e 100644
--- a/Documentation/cxl/cxl-reserve-dpa.txt
+++ b/Documentation/cxl/cxl-reserve-dpa.txt
@@ -24,6 +24,8 @@ OPTIONS
<memory device(s)>::
include::memdev-option.txt[]
+include::bus-option.txt[]
+
-d::
--decoder::
Specify the decoder to attempt the allocation. The CXL specification
diff --git a/Documentation/cxl/cxl-set-partition.txt b/Documentation/cxl/cxl-set-partition.txt
index f0126da..bed7f76 100644
--- a/Documentation/cxl/cxl-set-partition.txt
+++ b/Documentation/cxl/cxl-set-partition.txt
@@ -35,6 +35,8 @@ OPTIONS
<memory device(s)>::
include::memdev-option.txt[]
+include::bus-option.txt[]
+
-t::
--type=::
Type of partition, 'pmem' or 'ram' (volatile), to modify.
diff --git a/Documentation/cxl/cxl-write-labels.txt b/Documentation/cxl/cxl-write-labels.txt
index 75f42a5..8f2d139 100644
--- a/Documentation/cxl/cxl-write-labels.txt
+++ b/Documentation/cxl/cxl-write-labels.txt
@@ -21,6 +21,9 @@ not allow write access to the device's label data area.
OPTIONS
-------
include::labels-options.txt[]
+
+include::bus-option.txt[]
+
-i::
--input::
input file
diff --git a/cxl/filter.h b/cxl/filter.h
index c486514..595cde7 100644
--- a/cxl/filter.h
+++ b/cxl/filter.h
@@ -36,6 +36,8 @@ struct cxl_filter_params {
struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev,
const char *__ident,
const char *serials);
+struct cxl_memdev *util_cxl_memdev_filter_by_bus(struct cxl_memdev *memdev,
+ const char *__ident);
struct cxl_port *util_cxl_port_filter_by_memdev(struct cxl_port *port,
const char *ident,
const char *serial);
diff --git a/cxl/filter.c b/cxl/filter.c
index 90b13be..d2ab899 100644
--- a/cxl/filter.c
+++ b/cxl/filter.c
@@ -243,6 +243,25 @@ static struct cxl_port *util_cxl_port_filter_by_bus(struct cxl_port *port,
return NULL;
}
+struct cxl_memdev *util_cxl_memdev_filter_by_bus(struct cxl_memdev *memdev,
+ const char *__ident)
+{
+ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
+ struct cxl_bus *bus;
+
+ if (!__ident)
+ return memdev;
+
+ cxl_bus_foreach(ctx, bus) {
+ if (!util_cxl_bus_filter(bus, __ident))
+ continue;
+ if (bus == cxl_memdev_get_bus(memdev))
+ return memdev;
+ }
+
+ return NULL;
+}
+
static struct cxl_decoder *
util_cxl_decoder_filter_by_bus(struct cxl_decoder *decoder, const char *__ident)
{
diff --git a/cxl/memdev.c b/cxl/memdev.c
index 0b3ad02..807e859 100644
--- a/cxl/memdev.c
+++ b/cxl/memdev.c
@@ -44,6 +44,8 @@ enum cxl_setpart_type {
};
#define BASE_OPTIONS() \
+OPT_STRING('b', "bus", ¶m.bus, "bus name", \
+ "Limit operation to the specified bus"), \
OPT_BOOLEAN('v',"verbose", ¶m.verbose, "turn on debug"), \
OPT_BOOLEAN('S', "serial", ¶m.serial, "use serial numbers to id memdevs")
@@ -753,6 +755,8 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx,
if (!util_cxl_memdev_filter(memdev, memdev_filter,
serial_filter))
continue;
+ if (!util_cxl_memdev_filter_by_bus(memdev, param.bus))
+ continue;
found = true;
if (action == action_write) {
--
2.40.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH ndctl 2/5] cxl/list: print firmware info in memdev listings
2023-04-22 3:09 [PATCH ndctl 0/5] cxl: firmware update support for libcxl and cxl-cli Vishal Verma
2023-04-22 3:09 ` [PATCH ndctl 1/5] cxl/memdev.c: allow filtering memdevs by bus Vishal Verma
@ 2023-04-22 3:10 ` Vishal Verma
2023-05-19 18:21 ` Dave Jiang
2023-04-22 3:10 ` [PATCH ndctl 3/5] cxl/fw_loader: add APIs to get current state of the FW loader mechanism Vishal Verma
` (2 subsequent siblings)
4 siblings, 1 reply; 12+ messages in thread
From: Vishal Verma @ 2023-04-22 3:10 UTC (permalink / raw)
To: linux-cxl
Cc: nvdimm, Alison Schofield, Ira Weiny, Dave Jiang, Dan Williams,
Vishal Verma
Add libcxl APIs to send a 'Get Firmware Info' mailbox command, and
accessors for its data fields. Add a json representation of this data,
and add an option to cxl-list to display it under memdev listings.
Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
cxl/lib/private.h | 21 +++++++++++++
cxl/lib/libcxl.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
cxl/filter.h | 3 ++
cxl/libcxl.h | 7 +++++
cxl/json.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++
cxl/list.c | 3 ++
cxl/lib/libcxl.sym | 6 ++++
7 files changed, 214 insertions(+)
diff --git a/cxl/lib/private.h b/cxl/lib/private.h
index d648992..590d719 100644
--- a/cxl/lib/private.h
+++ b/cxl/lib/private.h
@@ -9,6 +9,7 @@
#include <ccan/endian/endian.h>
#include <ccan/short_types/short_types.h>
#include <util/size.h>
+#include <util/bitmap.h>
#define CXL_EXPORT __attribute__ ((visibility("default")))
@@ -233,6 +234,26 @@ struct cxl_cmd_get_health_info {
le32 pmem_errors;
} __attribute__((packed));
+/* CXL 3.0 8.2.9.3.1 Get Firmware Info */
+struct cxl_cmd_get_fw_info {
+ u8 num_slots;
+ u8 slot_info;
+ u8 activation_cap;
+ u8 reserved[13];
+ char slot_1_revision[0x10];
+ char slot_2_revision[0x10];
+ char slot_3_revision[0x10];
+ char slot_4_revision[0x10];
+} __attribute__((packed));
+
+#define CXL_FW_INFO_CUR_SLOT_MASK GENMASK(2, 0)
+#define CXL_FW_INFO_NEXT_SLOT_MASK GENMASK(5, 3)
+#define CXL_FW_INFO_NEXT_SLOT_SHIFT (3)
+#define CXL_FW_INFO_HAS_LIVE_ACTIVATE BIT(0)
+
+#define CXL_FW_VERSION_STR_LEN 16
+#define CXL_FW_MAX_SLOTS 4
+
/* CXL 3.0 8.2.9.8.3.2 Get Alert Configuration */
struct cxl_cmd_get_alert_config {
u8 valid_alerts;
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
index 59e5bdb..75490fd 100644
--- a/cxl/lib/libcxl.c
+++ b/cxl/lib/libcxl.c
@@ -3917,6 +3917,96 @@ CXL_EXPORT struct cxl_cmd *cxl_cmd_new_set_partition(struct cxl_memdev *memdev,
return cmd;
}
+CXL_EXPORT struct cxl_cmd *cxl_cmd_new_get_fw_info(struct cxl_memdev *memdev)
+{
+ return cxl_cmd_new_generic(memdev, CXL_MEM_COMMAND_ID_GET_FW_INFO);
+}
+
+static struct cxl_cmd_get_fw_info *cmd_to_get_fw_info(struct cxl_cmd *cmd)
+{
+ if (cxl_cmd_validate_status(cmd, CXL_MEM_COMMAND_ID_GET_FW_INFO))
+ return NULL;
+
+ return cmd->output_payload;
+}
+
+CXL_EXPORT unsigned int cxl_cmd_fw_info_get_num_slots(struct cxl_cmd *cmd)
+{
+ struct cxl_cmd_get_fw_info *c = cmd_to_get_fw_info(cmd);
+
+ if (!c)
+ return 0;
+
+ return c->num_slots;
+}
+
+CXL_EXPORT unsigned int cxl_cmd_fw_info_get_active_slot(struct cxl_cmd *cmd)
+{
+ struct cxl_cmd_get_fw_info *c = cmd_to_get_fw_info(cmd);
+
+ if (!c)
+ return 0;
+
+ return c->slot_info & CXL_FW_INFO_CUR_SLOT_MASK;
+}
+
+CXL_EXPORT unsigned int cxl_cmd_fw_info_get_staged_slot(struct cxl_cmd *cmd)
+{
+ struct cxl_cmd_get_fw_info *c = cmd_to_get_fw_info(cmd);
+
+ if (!c)
+ return 0;
+
+ return (c->slot_info & CXL_FW_INFO_NEXT_SLOT_MASK) >>
+ CXL_FW_INFO_NEXT_SLOT_SHIFT;
+}
+
+CXL_EXPORT bool cxl_cmd_fw_info_get_online_activate_capable(struct cxl_cmd *cmd)
+{
+ struct cxl_cmd_get_fw_info *c = cmd_to_get_fw_info(cmd);
+
+ if (!c)
+ return false;
+
+ return !!(c->activation_cap & CXL_FW_INFO_HAS_LIVE_ACTIVATE);
+}
+
+CXL_EXPORT int cxl_cmd_fw_info_get_fw_ver(struct cxl_cmd *cmd, int slot,
+ char *buf, unsigned int len)
+{
+ struct cxl_cmd_get_fw_info *c = cmd_to_get_fw_info(cmd);
+ char *fw_ver;
+
+ if (!c)
+ return -ENXIO;
+ if (!len)
+ return -EINVAL;
+
+ switch(slot) {
+ case 1:
+ fw_ver = &c->slot_1_revision[0];
+ break;
+ case 2:
+ fw_ver = &c->slot_2_revision[0];
+ break;
+ case 3:
+ fw_ver = &c->slot_3_revision[0];
+ break;
+ case 4:
+ fw_ver = &c->slot_4_revision[0];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (fw_ver[0] == 0)
+ return -ENOENT;
+
+ memcpy(buf, fw_ver, min(len, (unsigned int)CXL_FW_VERSION_STR_LEN));
+
+ return 0;
+}
+
CXL_EXPORT int cxl_cmd_submit(struct cxl_cmd *cmd)
{
struct cxl_memdev *memdev = cmd->memdev;
diff --git a/cxl/filter.h b/cxl/filter.h
index 595cde7..3f65990 100644
--- a/cxl/filter.h
+++ b/cxl/filter.h
@@ -27,6 +27,7 @@ struct cxl_filter_params {
bool human;
bool health;
bool partition;
+ bool fw;
bool alert_config;
bool dax;
int verbose;
@@ -81,6 +82,8 @@ static inline unsigned long cxl_filter_to_flags(struct cxl_filter_params *param)
flags |= UTIL_JSON_TARGETS;
if (param->partition)
flags |= UTIL_JSON_PARTITION;
+ if (param->fw)
+ flags |= UTIL_JSON_FIRMWARE;
if (param->alert_config)
flags |= UTIL_JSON_ALERT_CONFIG;
if (param->dax)
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
index 54d9f10..99e1b76 100644
--- a/cxl/libcxl.h
+++ b/cxl/libcxl.h
@@ -68,6 +68,13 @@ int cxl_memdev_read_label(struct cxl_memdev *memdev, void *buf, size_t length,
size_t offset);
int cxl_memdev_write_label(struct cxl_memdev *memdev, void *buf, size_t length,
size_t offset);
+struct cxl_cmd *cxl_cmd_new_get_fw_info(struct cxl_memdev *memdev);
+unsigned int cxl_cmd_fw_info_get_num_slots(struct cxl_cmd *cmd);
+unsigned int cxl_cmd_fw_info_get_active_slot(struct cxl_cmd *cmd);
+unsigned int cxl_cmd_fw_info_get_staged_slot(struct cxl_cmd *cmd);
+bool cxl_cmd_fw_info_get_online_activate_capable(struct cxl_cmd *cmd);
+int cxl_cmd_fw_info_get_fw_ver(struct cxl_cmd *cmd, int slot, char *buf,
+ unsigned int len);
#define cxl_memdev_foreach(ctx, memdev) \
for (memdev = cxl_memdev_get_first(ctx); \
diff --git a/cxl/json.c b/cxl/json.c
index e87bdd4..e6bb061 100644
--- a/cxl/json.c
+++ b/cxl/json.c
@@ -12,6 +12,84 @@
#include "json.h"
#include "../daxctl/json.h"
+#define CXL_FW_VERSION_STR_LEN 16
+#define CXL_FW_MAX_SLOTS 4
+
+static struct json_object *util_cxl_memdev_fw_to_json(
+ struct cxl_memdev *memdev, unsigned long flags)
+{
+ struct json_object *jobj;
+ struct json_object *jfw;
+ u32 field, num_slots;
+ struct cxl_cmd *cmd;
+ int rc, i;
+
+ jfw = json_object_new_object();
+ if (!jfw)
+ return NULL;
+ if (!memdev)
+ goto err_jobj;
+
+ cmd = cxl_cmd_new_get_fw_info(memdev);
+ if (!cmd)
+ goto err_jobj;
+
+ rc = cxl_cmd_submit(cmd);
+ if (rc < 0)
+ goto err_cmd;
+ rc = cxl_cmd_get_mbox_status(cmd);
+ if (rc != 0)
+ goto err_cmd;
+
+ /* fw_info fields */
+ num_slots = cxl_cmd_fw_info_get_num_slots(cmd);
+ jobj = json_object_new_int(num_slots);
+ if (jobj)
+ json_object_object_add(jfw, "num_slots", jobj);
+
+ field = cxl_cmd_fw_info_get_active_slot(cmd);
+ jobj = json_object_new_int(field);
+ if (jobj)
+ json_object_object_add(jfw, "active_slot", jobj);
+
+ field = cxl_cmd_fw_info_get_staged_slot(cmd);
+ if (field > 0 && field <= num_slots) {
+ jobj = json_object_new_int(field);
+ if (jobj)
+ json_object_object_add(jfw, "staged_slot", jobj);
+ }
+
+ rc = cxl_cmd_fw_info_get_online_activate_capable(cmd);
+ jobj = json_object_new_boolean(rc);
+ if (jobj)
+ json_object_object_add(jfw, "online_activate_capable", jobj);
+
+ for (i = 1; i <= CXL_FW_MAX_SLOTS; i++) {
+ char fw_ver[CXL_FW_VERSION_STR_LEN + 1];
+ char jkey[16];
+
+ rc = cxl_cmd_fw_info_get_fw_ver(cmd, i, fw_ver,
+ CXL_FW_VERSION_STR_LEN);
+ if (rc)
+ continue;
+ fw_ver[CXL_FW_VERSION_STR_LEN] = 0;
+ snprintf(jkey, 16, "slot_%d_version", i);
+ jobj = json_object_new_string(fw_ver);
+ if (jobj)
+ json_object_object_add(jfw, jkey, jobj);
+ }
+
+ cxl_cmd_unref(cmd);
+ return jfw;
+
+err_cmd:
+ cxl_cmd_unref(cmd);
+err_jobj:
+ json_object_put(jfw);
+ return NULL;
+
+}
+
static struct json_object *util_cxl_memdev_health_to_json(
struct cxl_memdev *memdev, unsigned long flags)
{
@@ -552,6 +630,12 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev,
json_object_object_add(jdev, "partition_info", jobj);
}
+ if (flags & UTIL_JSON_FIRMWARE) {
+ jobj = util_cxl_memdev_fw_to_json(memdev, flags);
+ if (jobj)
+ json_object_object_add(jdev, "firmware", jobj);
+ }
+
json_object_set_userdata(jdev, memdev, NULL);
return jdev;
}
diff --git a/cxl/list.c b/cxl/list.c
index c01154e..93ba51e 100644
--- a/cxl/list.c
+++ b/cxl/list.c
@@ -53,6 +53,8 @@ static const struct option options[] = {
"include memory device health information"),
OPT_BOOLEAN('I', "partition", ¶m.partition,
"include memory device partition information"),
+ OPT_BOOLEAN('F', "firmware", ¶m.fw,
+ "include memory device firmware information"),
OPT_BOOLEAN('A', "alert-config", ¶m.alert_config,
"include alert configuration information"),
OPT_INCR('v', "verbose", ¶m.verbose, "increase output detail"),
@@ -116,6 +118,7 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx)
case 3:
param.health = true;
param.partition = true;
+ param.fw = true;
param.alert_config = true;
param.dax = true;
/* fallthrough */
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
index 1c6177c..16a8671 100644
--- a/cxl/lib/libcxl.sym
+++ b/cxl/lib/libcxl.sym
@@ -248,4 +248,10 @@ global:
cxl_region_get_mode;
cxl_decoder_create_ram_region;
cxl_region_get_daxctl_region;
+ cxl_cmd_new_get_fw_info;
+ cxl_cmd_fw_info_get_num_slots;
+ cxl_cmd_fw_info_get_active_slot;
+ cxl_cmd_fw_info_get_staged_slot;
+ cxl_cmd_fw_info_get_online_activate_capable;
+ cxl_cmd_fw_info_get_fw_ver;
} LIBCXL_4;
--
2.40.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH ndctl 3/5] cxl/fw_loader: add APIs to get current state of the FW loader mechanism
2023-04-22 3:09 [PATCH ndctl 0/5] cxl: firmware update support for libcxl and cxl-cli Vishal Verma
2023-04-22 3:09 ` [PATCH ndctl 1/5] cxl/memdev.c: allow filtering memdevs by bus Vishal Verma
2023-04-22 3:10 ` [PATCH ndctl 2/5] cxl/list: print firmware info in memdev listings Vishal Verma
@ 2023-04-22 3:10 ` Vishal Verma
2023-05-19 18:49 ` Dave Jiang
2023-04-22 3:10 ` [PATCH ndctl 4/5] cxl: add an update-firmware command Vishal Verma
2023-04-22 3:10 ` [PATCH ndctl 5/5] test/cxl-update-firmware: add a unit test for firmware update Vishal Verma
4 siblings, 1 reply; 12+ messages in thread
From: Vishal Verma @ 2023-04-22 3:10 UTC (permalink / raw)
To: linux-cxl
Cc: nvdimm, Alison Schofield, Ira Weiny, Dave Jiang, Dan Williams,
Vishal Verma
Add a way to interface with the firmware loader mechanism for cxl
memdevs. Add APIs to retrieve the current status of the fw loader, and
the remaining size if a fw upload is in progress. Display these in the
'firmware' section of memdev listings.
Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
cxl/lib/private.h | 10 ++++++
cxl/lib/libcxl.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++
cxl/libcxl.h | 27 +++++++++++++++
cxl/json.c | 13 +++++++
cxl/lib/libcxl.sym | 2 ++
5 files changed, 152 insertions(+)
diff --git a/cxl/lib/private.h b/cxl/lib/private.h
index 590d719..95e0c43 100644
--- a/cxl/lib/private.h
+++ b/cxl/lib/private.h
@@ -20,6 +20,15 @@ struct cxl_pmem {
char *dev_path;
};
+struct cxl_fw_loader {
+ char *dev_path;
+ char *loading;
+ char *data;
+ char *remaining;
+ char *cancel;
+ char *status;
+};
+
struct cxl_endpoint;
struct cxl_memdev {
int id, major, minor;
@@ -39,6 +48,7 @@ struct cxl_memdev {
struct cxl_pmem *pmem;
unsigned long long serial;
struct cxl_endpoint *endpoint;
+ struct cxl_fw_loader *fwl;
};
struct cxl_dport {
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
index 75490fd..86873d7 100644
--- a/cxl/lib/libcxl.c
+++ b/cxl/lib/libcxl.c
@@ -63,12 +63,25 @@ static void free_pmem(struct cxl_pmem *pmem)
}
}
+static void free_fwl(struct cxl_fw_loader *fwl)
+{
+ if (fwl) {
+ free(fwl->loading);
+ free(fwl->data);
+ free(fwl->remaining);
+ free(fwl->cancel);
+ free(fwl->status);
+ free(fwl);
+ }
+}
+
static void free_memdev(struct cxl_memdev *memdev, struct list_head *head)
{
if (head)
list_del_from(head, &memdev->list);
kmod_module_unref(memdev->module);
free_pmem(memdev->pmem);
+ free_fwl(memdev->fwl);
free(memdev->firmware_version);
free(memdev->dev_buf);
free(memdev->dev_path);
@@ -1174,6 +1187,45 @@ static void *add_cxl_pmem(void *parent, int id, const char *br_base)
return NULL;
}
+static int add_cxl_memdev_fwl(struct cxl_memdev *memdev,
+ const char *cxlmem_base)
+{
+ const char *devname = cxl_memdev_get_devname(memdev);
+ struct cxl_fw_loader *fwl;
+
+ fwl = calloc(1, sizeof(*fwl));
+ if (!fwl)
+ return -ENOMEM;
+
+ if (asprintf(&fwl->loading, "%s/firmware/%s/loading", cxlmem_base,
+ devname) < 0)
+ goto err_read;
+ if (asprintf(&fwl->data, "%s/firmware/%s/data", cxlmem_base, devname) <
+ 0)
+ goto err_read;
+ if (asprintf(&fwl->remaining, "%s/firmware/%s/remaining_size",
+ cxlmem_base, devname) < 0)
+ goto err_read;
+ if (asprintf(&fwl->cancel, "%s/firmware/%s/cancel", cxlmem_base,
+ devname) < 0)
+ goto err_read;
+ if (asprintf(&fwl->status, "%s/firmware/%s/status", cxlmem_base,
+ devname) < 0)
+ goto err_read;
+
+ memdev->fwl = fwl;
+ return 0;
+
+ err_read:
+ free(fwl->loading);
+ free(fwl->data);
+ free(fwl->remaining);
+ free(fwl->cancel);
+ free(fwl->status);
+ free(fwl);
+ return -ENOMEM;
+}
+
static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
{
const char *devname = devpath_to_devname(cxlmem_base);
@@ -1263,6 +1315,9 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
device_parse(ctx, cxlmem_base, "pmem", memdev, add_cxl_pmem);
+ if (add_cxl_memdev_fwl(memdev, cxlmem_base))
+ goto err_read;
+
cxl_memdev_foreach(ctx, memdev_dup)
if (memdev_dup->id == memdev->id) {
free_memdev(memdev, NULL);
@@ -1373,6 +1428,51 @@ CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev
return memdev->firmware_version;
}
+static enum cxl_fwl_status cxl_fwl_get_status(struct cxl_memdev *memdev)
+{
+ const char *devname = cxl_memdev_get_devname(memdev);
+ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
+ struct cxl_fw_loader *fwl = memdev->fwl;
+ char buf[SYSFS_ATTR_SIZE];
+ int rc;
+
+ rc = sysfs_read_attr(ctx, fwl->status, buf);
+ if (rc < 0) {
+ err(ctx, "%s: failed to get fw loader status (%s)\n", devname,
+ strerror(-rc));
+ return CXL_FWL_STATUS_UNKNOWN;
+ }
+
+ return cxl_fwl_status_from_ident(buf);
+}
+
+CXL_EXPORT bool cxl_memdev_fw_update_in_progress(struct cxl_memdev *memdev)
+{
+ int status = cxl_fwl_get_status(memdev);
+
+ if (status == CXL_FWL_STATUS_IDLE)
+ return false;
+ return true;
+}
+
+CXL_EXPORT size_t cxl_memdev_fw_update_get_remaining(struct cxl_memdev *memdev)
+{
+ const char *devname = cxl_memdev_get_devname(memdev);
+ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
+ struct cxl_fw_loader *fwl = memdev->fwl;
+ char buf[SYSFS_ATTR_SIZE];
+ int rc;
+
+ rc = sysfs_read_attr(ctx, fwl->remaining, buf);
+ if (rc < 0) {
+ err(ctx, "%s: failed to get fw loader remaining size (%s)\n",
+ devname, strerror(-rc));
+ return 0;
+ }
+
+ return strtoull(buf, NULL, 0);
+}
+
static void bus_invalidate(struct cxl_bus *bus)
{
struct cxl_ctx *ctx = cxl_bus_get_ctx(bus);
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
index 99e1b76..7509abe 100644
--- a/cxl/libcxl.h
+++ b/cxl/libcxl.h
@@ -33,6 +33,31 @@ void *cxl_get_userdata(struct cxl_ctx *ctx);
void cxl_set_private_data(struct cxl_ctx *ctx, void *data);
void *cxl_get_private_data(struct cxl_ctx *ctx);
+enum cxl_fwl_status {
+ CXL_FWL_STATUS_UNKNOWN,
+ CXL_FWL_STATUS_IDLE,
+ CXL_FWL_STATUS_RECEIVING,
+ CXL_FWL_STATUS_PREPARING,
+ CXL_FWL_STATUS_TRANSFERRING,
+ CXL_FWL_STATUS_PROGRAMMING,
+};
+
+static inline enum cxl_fwl_status cxl_fwl_status_from_ident(char *status)
+{
+ if (strcmp(status, "idle") == 0)
+ return CXL_FWL_STATUS_IDLE;
+ if (strcmp(status, "receiving") == 0)
+ return CXL_FWL_STATUS_RECEIVING;
+ if (strcmp(status, "preparing") == 0)
+ return CXL_FWL_STATUS_PREPARING;
+ if (strcmp(status, "transferring") == 0)
+ return CXL_FWL_STATUS_TRANSFERRING;
+ if (strcmp(status, "programming") == 0)
+ return CXL_FWL_STATUS_PROGRAMMING;
+
+ return CXL_FWL_STATUS_UNKNOWN;
+}
+
struct cxl_memdev;
struct cxl_memdev *cxl_memdev_get_first(struct cxl_ctx *ctx);
struct cxl_memdev *cxl_memdev_get_next(struct cxl_memdev *memdev);
@@ -48,6 +73,8 @@ struct cxl_ctx *cxl_memdev_get_ctx(struct cxl_memdev *memdev);
unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev);
unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev);
const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev);
+bool cxl_memdev_fw_update_in_progress(struct cxl_memdev *memdev);
+size_t cxl_memdev_fw_update_get_remaining(struct cxl_memdev *memdev);
/* ABI spelling mistakes are forever */
static inline const char *cxl_memdev_get_firmware_version(
diff --git a/cxl/json.c b/cxl/json.c
index e6bb061..5dc0bd3 100644
--- a/cxl/json.c
+++ b/cxl/json.c
@@ -22,6 +22,7 @@ static struct json_object *util_cxl_memdev_fw_to_json(
struct json_object *jfw;
u32 field, num_slots;
struct cxl_cmd *cmd;
+ size_t remaining;
int rc, i;
jfw = json_object_new_object();
@@ -79,6 +80,18 @@ static struct json_object *util_cxl_memdev_fw_to_json(
json_object_object_add(jfw, jkey, jobj);
}
+ rc = cxl_memdev_fw_update_in_progress(memdev);
+ jobj = json_object_new_boolean(rc);
+ if (jobj)
+ json_object_object_add(jfw, "fw_update_in_progress", jobj);
+
+ if (rc == true) {
+ remaining = cxl_memdev_fw_update_get_remaining(memdev);
+ jobj = util_json_object_size(remaining, flags);
+ if (jobj)
+ json_object_object_add(jfw, "remaining_size", jobj);
+ }
+
cxl_cmd_unref(cmd);
return jfw;
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
index 16a8671..9438877 100644
--- a/cxl/lib/libcxl.sym
+++ b/cxl/lib/libcxl.sym
@@ -254,4 +254,6 @@ global:
cxl_cmd_fw_info_get_staged_slot;
cxl_cmd_fw_info_get_online_activate_capable;
cxl_cmd_fw_info_get_fw_ver;
+ cxl_memdev_fw_update_in_progress;
+ cxl_memdev_fw_update_get_remaining;
} LIBCXL_4;
--
2.40.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH ndctl 4/5] cxl: add an update-firmware command
2023-04-22 3:09 [PATCH ndctl 0/5] cxl: firmware update support for libcxl and cxl-cli Vishal Verma
` (2 preceding siblings ...)
2023-04-22 3:10 ` [PATCH ndctl 3/5] cxl/fw_loader: add APIs to get current state of the FW loader mechanism Vishal Verma
@ 2023-04-22 3:10 ` Vishal Verma
2023-04-24 23:14 ` Verma, Vishal L
2023-05-19 18:57 ` Dave Jiang
2023-04-22 3:10 ` [PATCH ndctl 5/5] test/cxl-update-firmware: add a unit test for firmware update Vishal Verma
4 siblings, 2 replies; 12+ messages in thread
From: Vishal Verma @ 2023-04-22 3:10 UTC (permalink / raw)
To: linux-cxl
Cc: nvdimm, Alison Schofield, Ira Weiny, Dave Jiang, Dan Williams,
Vishal Verma
Add a new cxl-update-firmware command to initiate a firmware update on a
given memdev. This allows using a specified file to pass in as the
firmware binary for one or more memdevs, allows for a blocking mode,
where the command only exits after the update is complete for every
specified memdev, and includes an option to cancel an in-progress
update. Add the supporting libcxl APIs for the above functions as well.
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
cxl/lib/private.h | 5 ++
cxl/lib/libcxl.c | 114 ++++++++++++++++++++++++++++++++++++++++++
cxl/builtin.h | 1 +
cxl/libcxl.h | 2 +
cxl/cxl.c | 1 +
cxl/memdev.c | 73 ++++++++++++++++++++++++++-
Documentation/cxl/meson.build | 1 +
cxl/lib/libcxl.sym | 2 +
8 files changed, 198 insertions(+), 1 deletion(-)
diff --git a/cxl/lib/private.h b/cxl/lib/private.h
index 95e0c43..6388534 100644
--- a/cxl/lib/private.h
+++ b/cxl/lib/private.h
@@ -29,6 +29,11 @@ struct cxl_fw_loader {
char *status;
};
+enum cxl_fwl_loading {
+ CXL_FWL_LOADING_END = 0,
+ CXL_FWL_LOADING_START,
+};
+
struct cxl_endpoint;
struct cxl_memdev {
int id, major, minor;
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
index 86873d7..8084857 100644
--- a/cxl/lib/libcxl.c
+++ b/cxl/lib/libcxl.c
@@ -7,6 +7,7 @@
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
+#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
@@ -1473,6 +1474,119 @@ CXL_EXPORT size_t cxl_memdev_fw_update_get_remaining(struct cxl_memdev *memdev)
return strtoull(buf, NULL, 0);
}
+static int cxl_memdev_fwl_set_loading(struct cxl_memdev *memdev,
+ enum cxl_fwl_loading loadval)
+{
+ const char *devname = cxl_memdev_get_devname(memdev);
+ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
+ struct cxl_fw_loader *fwl = memdev->fwl;
+ char buf[SYSFS_ATTR_SIZE];
+ int rc;
+
+ sprintf(buf, "%d\n", loadval);
+ rc = sysfs_write_attr(ctx, fwl->loading, buf);
+ if (rc < 0) {
+ err(ctx, "%s: failed to trigger fw loading to %d (%s)\n",
+ devname, loadval, strerror(-rc));
+ return rc;
+ }
+
+ return 0;
+}
+
+static int cxl_memdev_fwl_copy_data(struct cxl_memdev *memdev, void *fw_buf,
+ size_t size)
+{
+ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
+ struct cxl_fw_loader *fwl = memdev->fwl;
+ FILE *fwl_data;
+ size_t rw_len;
+ int rc = 0;
+
+ fwl_data = fopen(fwl->data, "w");
+ if (!fwl_data) {
+ err(ctx, "failed to open: %s: (%s)\n", fwl->data,
+ strerror(errno));
+ return -errno;
+ }
+
+ rw_len = fwrite(fw_buf, 1, size, fwl_data);
+ if (rw_len != size) {
+ rc = -ENXIO;
+ goto out_close;
+ }
+ fflush(fwl_data);
+
+out_close:
+ fclose(fwl_data);
+ return rc;
+}
+
+CXL_EXPORT int cxl_memdev_update_fw(struct cxl_memdev *memdev,
+ const char *fw_path)
+{
+ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
+ struct stat s;
+ int f_in, rc;
+ void *fw_buf;
+
+ f_in = open(fw_path, O_RDONLY);
+ if (f_in < 0) {
+ err(ctx, "failed to open: %s: (%s)\n", fw_path,
+ strerror(errno));
+ return -errno;
+ }
+
+ rc = fstat(f_in, &s);
+ if (rc < 0) {
+ err(ctx, "failed to stat: %s: (%s)\n", fw_path,
+ strerror(errno));
+ rc = -errno;
+ goto out_close;
+ }
+
+ fw_buf = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, f_in, 0);
+ if (fw_buf == MAP_FAILED) {
+ err(ctx, "failed to map: %s: (%s)\n", fw_path,
+ strerror(errno));
+ rc = -errno;
+ goto out_close;
+ }
+
+ rc = cxl_memdev_fwl_set_loading(memdev, CXL_FWL_LOADING_START);
+ if (rc)
+ goto out_unmap;
+
+ rc = cxl_memdev_fwl_copy_data(memdev, fw_buf, s.st_size);
+ if (rc)
+ goto out_unmap;
+
+ rc = cxl_memdev_fwl_set_loading(memdev, CXL_FWL_LOADING_END);
+
+out_unmap:
+ munmap(fw_buf, s.st_size);
+out_close:
+ close(f_in);
+ return rc;
+}
+
+CXL_EXPORT int cxl_memdev_cancel_fw_update(struct cxl_memdev *memdev)
+{
+ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
+ struct cxl_fw_loader *fwl = memdev->fwl;
+ int rc;
+
+ if (!cxl_memdev_fw_update_in_progress(memdev) &&
+ cxl_memdev_fw_update_get_remaining(memdev) == 0)
+ return -ENXIO;
+
+ rc = sysfs_write_attr(ctx, fwl->cancel, "1\n");
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
static void bus_invalidate(struct cxl_bus *bus)
{
struct cxl_ctx *ctx = cxl_bus_get_ctx(bus);
diff --git a/cxl/builtin.h b/cxl/builtin.h
index 9baa43b..3ec6c6c 100644
--- a/cxl/builtin.h
+++ b/cxl/builtin.h
@@ -14,6 +14,7 @@ int cmd_disable_memdev(int argc, const char **argv, struct cxl_ctx *ctx);
int cmd_enable_memdev(int argc, const char **argv, struct cxl_ctx *ctx);
int cmd_reserve_dpa(int argc, const char **argv, struct cxl_ctx *ctx);
int cmd_free_dpa(int argc, const char **argv, struct cxl_ctx *ctx);
+int cmd_update_fw(int argc, const char **argv, struct cxl_ctx *ctx);
int cmd_disable_port(int argc, const char **argv, struct cxl_ctx *ctx);
int cmd_enable_port(int argc, const char **argv, struct cxl_ctx *ctx);
int cmd_set_partition(int argc, const char **argv, struct cxl_ctx *ctx);
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
index 7509abe..2ffb39c 100644
--- a/cxl/libcxl.h
+++ b/cxl/libcxl.h
@@ -75,6 +75,8 @@ unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev);
const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev);
bool cxl_memdev_fw_update_in_progress(struct cxl_memdev *memdev);
size_t cxl_memdev_fw_update_get_remaining(struct cxl_memdev *memdev);
+int cxl_memdev_update_fw(struct cxl_memdev *memdev, const char *fw_path);
+int cxl_memdev_cancel_fw_update(struct cxl_memdev *memdev);
/* ABI spelling mistakes are forever */
static inline const char *cxl_memdev_get_firmware_version(
diff --git a/cxl/cxl.c b/cxl/cxl.c
index 3be7026..e1524b8 100644
--- a/cxl/cxl.c
+++ b/cxl/cxl.c
@@ -68,6 +68,7 @@ static struct cmd_struct commands[] = {
{ "enable-memdev", .c_fn = cmd_enable_memdev },
{ "reserve-dpa", .c_fn = cmd_reserve_dpa },
{ "free-dpa", .c_fn = cmd_free_dpa },
+ { "update-firmware", .c_fn = cmd_update_fw },
{ "disable-port", .c_fn = cmd_disable_port },
{ "enable-port", .c_fn = cmd_enable_port },
{ "set-partition", .c_fn = cmd_set_partition },
diff --git a/cxl/memdev.c b/cxl/memdev.c
index 807e859..1ad871a 100644
--- a/cxl/memdev.c
+++ b/cxl/memdev.c
@@ -23,14 +23,18 @@ struct action_context {
};
static struct parameters {
+ const char *bus;
const char *outfile;
const char *infile;
+ const char *fw_file;
unsigned len;
unsigned offset;
bool verbose;
bool serial;
bool force;
bool align;
+ bool cancel;
+ bool wait;
const char *type;
const char *size;
const char *decoder_filter;
@@ -87,6 +91,14 @@ OPT_STRING('t', "type", ¶m.type, "type", \
OPT_BOOLEAN('f', "force", ¶m.force, \
"Attempt 'expected to fail' operations")
+#define FW_OPTIONS() \
+OPT_STRING('F', "firmware-file", ¶m.fw_file, "firmware-file", \
+ "firmware image file to use for the update"), \
+OPT_BOOLEAN('c', "cancel", ¶m.cancel, \
+ "attempt to abort an in-progress firmware update"), \
+OPT_BOOLEAN('w', "wait", ¶m.wait, \
+ "wait for firmware update to complete before returning")
+
static const struct option read_options[] = {
BASE_OPTIONS(),
LABEL_OPTIONS(),
@@ -137,6 +149,12 @@ static const struct option free_dpa_options[] = {
OPT_END(),
};
+static const struct option update_fw_options[] = {
+ BASE_OPTIONS(),
+ FW_OPTIONS(),
+ OPT_END(),
+};
+
enum reserve_dpa_mode {
DPA_ALLOC,
DPA_FREE,
@@ -655,6 +673,39 @@ out_err:
return rc;
}
+static int action_update_fw(struct cxl_memdev *memdev,
+ struct action_context *actx)
+{
+ const char *devname = cxl_memdev_get_devname(memdev);
+ struct json_object *jmemdev;
+ unsigned long flags;
+ int rc;
+
+ if (param.cancel)
+ return cxl_memdev_cancel_fw_update(memdev);
+
+ if (param.fw_file) {
+ rc = cxl_memdev_update_fw(memdev, param.fw_file);
+ if (rc)
+ log_err(&ml, "%s error: %s\n", devname, strerror(-rc));
+ }
+
+ if (param.wait) {
+ while (cxl_memdev_fw_update_in_progress(memdev) ||
+ cxl_memdev_fw_update_get_remaining(memdev) > 0)
+ sleep(1);
+ }
+
+ flags = UTIL_JSON_FIRMWARE;
+ if (actx->f_out == stdout && isatty(1))
+ flags |= UTIL_JSON_HUMAN;
+ jmemdev = util_cxl_memdev_to_json(memdev, flags);
+ if (actx->jdevs && jmemdev)
+ json_object_array_add(actx->jdevs, jmemdev);
+
+ return rc;
+}
+
static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx,
int (*action)(struct cxl_memdev *memdev,
struct action_context *actx),
@@ -698,7 +749,7 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx,
}
if (action == action_setpartition || action == action_reserve_dpa ||
- action == action_free_dpa)
+ action == action_free_dpa || action == action_update_fw)
actx.jdevs = json_object_new_array();
if (err == argc) {
@@ -897,3 +948,23 @@ int cmd_free_dpa(int argc, const char **argv, struct cxl_ctx *ctx)
return count >= 0 ? 0 : EXIT_FAILURE;
}
+
+int cmd_update_fw(int argc, const char **argv, struct cxl_ctx *ctx)
+{
+ int count = memdev_action(
+ argc, argv, ctx, action_update_fw, update_fw_options,
+ "cxl update-firmware <mem0> [<mem1>..<memn>] [<options>]");
+ const char *op_string;
+
+ if (param.cancel)
+ op_string = "cancelled";
+ else if (param.wait)
+ op_string = "completed";
+ else
+ op_string = "started";
+
+ log_info(&ml, "firmware update %s on %d mem device%s\n", op_string,
+ count >= 0 ? count : 0, count > 1 ? "s" : "");
+
+ return count >= 0 ? 0 : EXIT_FAILURE;
+}
diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build
index a6d77ab..c553357 100644
--- a/Documentation/cxl/meson.build
+++ b/Documentation/cxl/meson.build
@@ -46,6 +46,7 @@ cxl_manpages = [
'cxl-enable-region.txt',
'cxl-destroy-region.txt',
'cxl-monitor.txt',
+ 'cxl-update-firmware.txt',
]
foreach man : cxl_manpages
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
index 9438877..ce0cb7f 100644
--- a/cxl/lib/libcxl.sym
+++ b/cxl/lib/libcxl.sym
@@ -256,4 +256,6 @@ global:
cxl_cmd_fw_info_get_fw_ver;
cxl_memdev_fw_update_in_progress;
cxl_memdev_fw_update_get_remaining;
+ cxl_memdev_update_fw;
+ cxl_memdev_cancel_fw_update;
} LIBCXL_4;
--
2.40.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH ndctl 5/5] test/cxl-update-firmware: add a unit test for firmware update
2023-04-22 3:09 [PATCH ndctl 0/5] cxl: firmware update support for libcxl and cxl-cli Vishal Verma
` (3 preceding siblings ...)
2023-04-22 3:10 ` [PATCH ndctl 4/5] cxl: add an update-firmware command Vishal Verma
@ 2023-04-22 3:10 ` Vishal Verma
2023-05-19 19:00 ` Dave Jiang
4 siblings, 1 reply; 12+ messages in thread
From: Vishal Verma @ 2023-04-22 3:10 UTC (permalink / raw)
To: linux-cxl
Cc: nvdimm, Alison Schofield, Ira Weiny, Dave Jiang, Dan Williams,
Vishal Verma
Add a unit test to exercise the different operating modes of the
cxl-update-firmware command. Perform an update synchronously,
asynchronously, on multiple devices, and attempt cancellation of an
in-progress update.
Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
test/cxl-update-firmware.sh | 195 ++++++++++++++++++++++++++++++++++++++++++++
test/meson.build | 2 +
2 files changed, 197 insertions(+)
diff --git a/test/cxl-update-firmware.sh b/test/cxl-update-firmware.sh
new file mode 100755
index 0000000..c6cd742
--- /dev/null
+++ b/test/cxl-update-firmware.sh
@@ -0,0 +1,195 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 Intel Corporation. All rights reserved.
+
+. $(dirname $0)/common
+
+rc=77
+
+set -ex
+
+trap 'err $LINENO' ERR
+
+check_prereq "jq"
+check_prereq "dd"
+check_prereq "sha256sum"
+
+modprobe -r cxl_test
+modprobe cxl_test
+rc=1
+
+mk_fw_file()
+{
+ size="$1"
+
+ if [[ ! $size ]]; then
+ err "$LINENO"
+ fi
+ if (( size > 64 )); then
+ err "$LINENO"
+ fi
+
+ fw_file="$(mktemp -p /tmp fw_file_XXXX)"
+ dd if=/dev/urandom of="$fw_file" bs=1M count="$size"
+ echo "$fw_file"
+}
+
+find_memdevs()
+{
+ count="$1"
+
+ if [[ ! $count ]]; then
+ count=1
+ fi
+
+ "$CXL" list -M -b "$CXL_TEST_BUS" \
+ | jq -r '.[] | select(.host | startswith("cxl_mem.")) | .memdev' \
+ | head -"$count"
+}
+
+do_update_fw()
+{
+ "$CXL" update-firmware -b "$CXL_TEST_BUS" "$@"
+}
+
+wait_complete()
+{
+ mem="$1" # single memdev, not a list
+ max_wait="$2" # in seconds
+ waited=0
+
+ while true; do
+ json="$("$CXL" list -m "$mem" -F)"
+ in_prog="$(jq -r '.[].firmware.fw_update_in_progress' <<< "$json")"
+ if [[ $in_prog == "true" ]]; then
+ sleep 1
+ waited="$((waited + 1))"
+ continue
+ else
+ break
+ fi
+ if (( waited == max_wait )); then
+ echo "completion timeout for $mem"
+ err "$LINENO"
+ fi
+ done
+}
+
+validate_json_state()
+{
+ json="$1"
+ state="$2"
+
+ while read -r in_prog_state; do
+ if [[ $in_prog_state == $state ]]; then
+ continue
+ else
+ echo "expected fw_update_in_progress:$state"
+ err "$LINENO"
+ fi
+ done < <(jq -r '.[].firmware.fw_update_in_progress' <<< "$json")
+}
+
+validate_fw_update_in_progress()
+{
+ validate_json_state "$1" "true"
+}
+
+validate_fw_update_idle()
+{
+ validate_json_state "$1" "false"
+}
+
+validate_staged_slot()
+{
+ json="$1"
+ slot="$2"
+
+ while read -r staged_slot; do
+ if [[ $staged_slot == $slot ]]; then
+ continue
+ else
+ echo "expected staged_slot:$slot"
+ err "$LINENO"
+ fi
+ done < <(jq -r '.[].firmware.staged_slot' <<< "$json")
+}
+
+check_sha()
+{
+ mem="$1"
+ file="$2"
+ csum_path="/sys/bus/platform/devices/cxl_mem.${mem#mem}/fw_buf_checksum"
+
+ mem_csum="$(cat "$csum_path")"
+ file_csum="$(sha256sum "$file" | awk '{print $1}')"
+
+ if [[ $mem_csum != $file_csum ]]; then
+ echo "checksum failure for mem$mem"
+ err "$LINENO"
+ fi
+}
+
+test_blocking_update()
+{
+ file="$(mk_fw_file 8)"
+ mem="$(find_memdevs 1)"
+ json=$(do_update_fw -F "$file" --wait "$mem")
+ validate_fw_update_idle "$json"
+ # cxl_test's starting slot is '2', so staged should be 3
+ validate_staged_slot "$json" 3
+ check_sha "$mem" "$file"
+ rm "$file"
+}
+
+test_nonblocking_update()
+{
+ file="$(mk_fw_file 16)"
+ mem="$(find_memdevs 1)"
+ json=$(do_update_fw -F "$file" "$mem")
+ validate_fw_update_in_progress "$json"
+ wait_complete "$mem" 15
+ validate_fw_update_idle "$("$CXL" list -m "$mem" -F)"
+ check_sha "$mem" "$file"
+ rm "$file"
+}
+
+test_multiple_memdev()
+{
+ num_mems=2
+
+ file="$(mk_fw_file 16)"
+ declare -a mems
+ mems=( $(find_memdevs "$num_mems") )
+ json="$(do_update_fw -F "$file" "${mems[@]}")"
+ validate_fw_update_in_progress "$json"
+ # use the in-band wait this time
+ json="$(do_update_fw --wait "${mems[@]}")"
+ validate_fw_update_idle "$json"
+ for mem in ${mems[@]}; do
+ check_sha "$mem" "$file"
+ done
+ rm "$file"
+}
+
+test_cancel()
+{
+ file="$(mk_fw_file 16)"
+ mem="$(find_memdevs 1)"
+ json=$(do_update_fw -F "$file" "$mem")
+ validate_fw_update_in_progress "$json"
+ do_update_fw --cancel "$mem"
+ # cancellation is asynchronous, and the result looks the same as idle
+ wait_complete "$mem" 15
+ validate_fw_update_idle "$("$CXL" list -m "$mem" -F)"
+ # no need to check_sha
+ rm "$file"
+}
+
+test_blocking_update
+test_nonblocking_update
+test_multiple_memdev
+test_cancel
+
+check_dmesg "$LINENO"
+modprobe -r cxl_test
diff --git a/test/meson.build b/test/meson.build
index a956885..0f4d3c4 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -155,6 +155,7 @@ cxl_sysfs = find_program('cxl-region-sysfs.sh')
cxl_labels = find_program('cxl-labels.sh')
cxl_create_region = find_program('cxl-create-region.sh')
cxl_xor_region = find_program('cxl-xor-region.sh')
+cxl_update_firmware = find_program('cxl-update-firmware.sh')
tests = [
[ 'libndctl', libndctl, 'ndctl' ],
@@ -198,6 +199,7 @@ if get_option('destructive').enabled()
tests += [
[ 'firmware-update.sh', firmware_update, 'ndctl' ],
+ [ 'cxl-update-firmware.sh', cxl_update_firmware, 'cxl' ],
[ 'pmem-ns', pmem_ns, 'ndctl' ],
[ 'sub-section.sh', sub_section, 'dax' ],
[ 'dax-dev', dax_dev, 'dax' ],
--
2.40.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH ndctl 4/5] cxl: add an update-firmware command
2023-04-22 3:10 ` [PATCH ndctl 4/5] cxl: add an update-firmware command Vishal Verma
@ 2023-04-24 23:14 ` Verma, Vishal L
2023-05-19 18:57 ` Dave Jiang
1 sibling, 0 replies; 12+ messages in thread
From: Verma, Vishal L @ 2023-04-24 23:14 UTC (permalink / raw)
To: linux-cxl@vger.kernel.org
Cc: Williams, Dan J, Schofield, Alison, Jiang, Dave,
nvdimm@lists.linux.dev, Weiny, Ira
On Fri, 2023-04-21 at 21:10 -0600, Vishal Verma wrote:
> Add a new cxl-update-firmware command to initiate a firmware update on a
> given memdev. This allows using a specified file to pass in as the
> firmware binary for one or more memdevs, allows for a blocking mode,
> where the command only exits after the update is complete for every
> specified memdev, and includes an option to cancel an in-progress
> update. Add the supporting libcxl APIs for the above functions as well.
>
> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
> ---
> cxl/lib/private.h | 5 ++
> cxl/lib/libcxl.c | 114 ++++++++++++++++++++++++++++++++++++++++++
> cxl/builtin.h | 1 +
> cxl/libcxl.h | 2 +
> cxl/cxl.c | 1 +
> cxl/memdev.c | 73 ++++++++++++++++++++++++++-
> Documentation/cxl/meson.build | 1 +
> cxl/lib/libcxl.sym | 2 +
> 8 files changed, 198 insertions(+), 1 deletion(-)
>
Looks like I forgot to 'git add Documentation/cxl/cxl-update-firmware.txt'.
I'll fix it for v2, but in the meanwhile here's what that looks like:
// SPDX-License-Identifier: GPL-2.0
cxl-update-firmware(1)
======================
NAME
----
cxl-update-firmware - update the firmware on a CXL memdev
SYNOPSIS
--------
[verse]
'cxl update-firmware <mem0> [<mem1>..<memN>] [<options>]'
DESCRIPTION
-----------
Update the firmware on one or more CXL mem devices. The mem devices
must support the set of firmware related mailbox commands.
This command doesn't directly issue the transfer / activate firmware
mailbox commands. Instead, the kernel's firmware loader facility is
used to provide the kernel with the data, and the kernel handles
performing the actual update while also managing time slicing the
transfer w.r.t. other background commands.
EXAMPLE
-------
----
# cxl update-firmware mem0 -F firmware.bin -w
[
{
"memdev":"mem0",
"pmem_size":1073741824,
"ram_size":1073741824,
"serial":0,
"numa_node":0,
"host":"cxl_mem.0",
"firmware":{
"num_slots":3,
"active_slot":2,
"online_activate_capable":false,
"slot_1_version":"cxl_test_fw_001",
"slot_2_version":"cxl_test_fw_002",
"slot_3_version":"cxl_test_new_fw",
"fw_update_in_progress":false
}
}
]
firmware update completed on 1 mem device
----
OPTIONS
-------
include::bus-option.txt[]
-F::
--firmware-file::
Firmware image file to use for the update.
-c::
--cancel::
Attempt to abort an in-progress firmware update. This may
fail depending on what stage the update process is in.
-w::
--wait::
By default, the update-firmware command only initiates the
firmware update, and returns, while the update operation
happens asynchronously in the background. This option makes
the firmware update command synchronous by waiting for it to
complete before returning.
If --wait is passed in without an accompanying firmware-file,
it will initiate a wait on any current in-progress firmware
updates, and then return.
include::verbose-option.txt[]
include::../copyright.txt[]
SEE ALSO
--------
linkcxl:cxl-list[1],
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH ndctl 1/5] cxl/memdev.c: allow filtering memdevs by bus
2023-04-22 3:09 ` [PATCH ndctl 1/5] cxl/memdev.c: allow filtering memdevs by bus Vishal Verma
@ 2023-05-19 17:57 ` Dave Jiang
0 siblings, 0 replies; 12+ messages in thread
From: Dave Jiang @ 2023-05-19 17:57 UTC (permalink / raw)
To: Vishal Verma, linux-cxl; +Cc: nvdimm, Alison Schofield, Ira Weiny, Dan Williams
On 4/21/23 8:09 PM, Vishal Verma wrote:
> The family of memdev based commands implemented in memdev.c lacked an
> option to filter the operation by bus. Add a helper to filter memdevs by
> the bus they're under, and use it in memdev_action() which loops through
> the requested memdevs. Update the man pages for all the affected
> commands as well to include the bus filter option.
>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Cc: Alison Schofield <alison.schofield@intel.com>
> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> Documentation/cxl/cxl-disable-memdev.txt | 2 ++
> Documentation/cxl/cxl-enable-memdev.txt | 2 ++
> Documentation/cxl/cxl-free-dpa.txt | 2 ++
> Documentation/cxl/cxl-read-labels.txt | 2 ++
> Documentation/cxl/cxl-reserve-dpa.txt | 2 ++
> Documentation/cxl/cxl-set-partition.txt | 2 ++
> Documentation/cxl/cxl-write-labels.txt | 3 +++
> cxl/filter.h | 2 ++
> cxl/filter.c | 19 +++++++++++++++++++
> cxl/memdev.c | 4 ++++
> 10 files changed, 40 insertions(+)
>
> diff --git a/Documentation/cxl/cxl-disable-memdev.txt b/Documentation/cxl/cxl-disable-memdev.txt
> index edd5385..d397802 100644
> --- a/Documentation/cxl/cxl-disable-memdev.txt
> +++ b/Documentation/cxl/cxl-disable-memdev.txt
> @@ -18,6 +18,8 @@ OPTIONS
> <memory device(s)>::
> include::memdev-option.txt[]
>
> +include::bus-option.txt[]
> +
> -f::
> --force::
> DANGEROUS: Override the safety measure that blocks attempts to disable
> diff --git a/Documentation/cxl/cxl-enable-memdev.txt b/Documentation/cxl/cxl-enable-memdev.txt
> index 088d5e0..5b5ed66 100644
> --- a/Documentation/cxl/cxl-enable-memdev.txt
> +++ b/Documentation/cxl/cxl-enable-memdev.txt
> @@ -23,6 +23,8 @@ OPTIONS
> <memory device(s)>::
> include::memdev-option.txt[]
>
> +include::bus-option.txt[]
> +
> -v::
> Turn on verbose debug messages in the library (if libcxl was built with
> logging and debug enabled).
> diff --git a/Documentation/cxl/cxl-free-dpa.txt b/Documentation/cxl/cxl-free-dpa.txt
> index 73fb048..506fafd 100644
> --- a/Documentation/cxl/cxl-free-dpa.txt
> +++ b/Documentation/cxl/cxl-free-dpa.txt
> @@ -24,6 +24,8 @@ OPTIONS
> <memory device(s)>::
> include::memdev-option.txt[]
>
> +include::bus-option.txt[]
> +
> -d::
> --decoder::
> Specify the decoder to free. The CXL specification
> diff --git a/Documentation/cxl/cxl-read-labels.txt b/Documentation/cxl/cxl-read-labels.txt
> index 143f296..a96e7a4 100644
> --- a/Documentation/cxl/cxl-read-labels.txt
> +++ b/Documentation/cxl/cxl-read-labels.txt
> @@ -20,6 +20,8 @@ OPTIONS
> -------
> include::labels-options.txt[]
>
> +include::bus-option.txt[]
> +
> -o::
> --output::
> output file
> diff --git a/Documentation/cxl/cxl-reserve-dpa.txt b/Documentation/cxl/cxl-reserve-dpa.txt
> index 5e79ef2..58cc93e 100644
> --- a/Documentation/cxl/cxl-reserve-dpa.txt
> +++ b/Documentation/cxl/cxl-reserve-dpa.txt
> @@ -24,6 +24,8 @@ OPTIONS
> <memory device(s)>::
> include::memdev-option.txt[]
>
> +include::bus-option.txt[]
> +
> -d::
> --decoder::
> Specify the decoder to attempt the allocation. The CXL specification
> diff --git a/Documentation/cxl/cxl-set-partition.txt b/Documentation/cxl/cxl-set-partition.txt
> index f0126da..bed7f76 100644
> --- a/Documentation/cxl/cxl-set-partition.txt
> +++ b/Documentation/cxl/cxl-set-partition.txt
> @@ -35,6 +35,8 @@ OPTIONS
> <memory device(s)>::
> include::memdev-option.txt[]
>
> +include::bus-option.txt[]
> +
> -t::
> --type=::
> Type of partition, 'pmem' or 'ram' (volatile), to modify.
> diff --git a/Documentation/cxl/cxl-write-labels.txt b/Documentation/cxl/cxl-write-labels.txt
> index 75f42a5..8f2d139 100644
> --- a/Documentation/cxl/cxl-write-labels.txt
> +++ b/Documentation/cxl/cxl-write-labels.txt
> @@ -21,6 +21,9 @@ not allow write access to the device's label data area.
> OPTIONS
> -------
> include::labels-options.txt[]
> +
> +include::bus-option.txt[]
> +
> -i::
> --input::
> input file
> diff --git a/cxl/filter.h b/cxl/filter.h
> index c486514..595cde7 100644
> --- a/cxl/filter.h
> +++ b/cxl/filter.h
> @@ -36,6 +36,8 @@ struct cxl_filter_params {
> struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev,
> const char *__ident,
> const char *serials);
> +struct cxl_memdev *util_cxl_memdev_filter_by_bus(struct cxl_memdev *memdev,
> + const char *__ident);
> struct cxl_port *util_cxl_port_filter_by_memdev(struct cxl_port *port,
> const char *ident,
> const char *serial);
> diff --git a/cxl/filter.c b/cxl/filter.c
> index 90b13be..d2ab899 100644
> --- a/cxl/filter.c
> +++ b/cxl/filter.c
> @@ -243,6 +243,25 @@ static struct cxl_port *util_cxl_port_filter_by_bus(struct cxl_port *port,
> return NULL;
> }
>
> +struct cxl_memdev *util_cxl_memdev_filter_by_bus(struct cxl_memdev *memdev,
> + const char *__ident)
> +{
> + struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
> + struct cxl_bus *bus;
> +
> + if (!__ident)
> + return memdev;
> +
> + cxl_bus_foreach(ctx, bus) {
> + if (!util_cxl_bus_filter(bus, __ident))
> + continue;
> + if (bus == cxl_memdev_get_bus(memdev))
> + return memdev;
> + }
> +
> + return NULL;
> +}
> +
> static struct cxl_decoder *
> util_cxl_decoder_filter_by_bus(struct cxl_decoder *decoder, const char *__ident)
> {
> diff --git a/cxl/memdev.c b/cxl/memdev.c
> index 0b3ad02..807e859 100644
> --- a/cxl/memdev.c
> +++ b/cxl/memdev.c
> @@ -44,6 +44,8 @@ enum cxl_setpart_type {
> };
>
> #define BASE_OPTIONS() \
> +OPT_STRING('b', "bus", ¶m.bus, "bus name", \
> + "Limit operation to the specified bus"), \
> OPT_BOOLEAN('v',"verbose", ¶m.verbose, "turn on debug"), \
> OPT_BOOLEAN('S', "serial", ¶m.serial, "use serial numbers to id memdevs")
>
> @@ -753,6 +755,8 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx,
> if (!util_cxl_memdev_filter(memdev, memdev_filter,
> serial_filter))
> continue;
> + if (!util_cxl_memdev_filter_by_bus(memdev, param.bus))
> + continue;
> found = true;
>
> if (action == action_write) {
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH ndctl 2/5] cxl/list: print firmware info in memdev listings
2023-04-22 3:10 ` [PATCH ndctl 2/5] cxl/list: print firmware info in memdev listings Vishal Verma
@ 2023-05-19 18:21 ` Dave Jiang
0 siblings, 0 replies; 12+ messages in thread
From: Dave Jiang @ 2023-05-19 18:21 UTC (permalink / raw)
To: Vishal Verma, linux-cxl; +Cc: nvdimm, Alison Schofield, Ira Weiny, Dan Williams
On 4/21/23 8:10 PM, Vishal Verma wrote:
> Add libcxl APIs to send a 'Get Firmware Info' mailbox command, and
> accessors for its data fields. Add a json representation of this data,
> and add an option to cxl-list to display it under memdev listings.
>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Just a small nit below.
> ---
> cxl/lib/private.h | 21 +++++++++++++
> cxl/lib/libcxl.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> cxl/filter.h | 3 ++
> cxl/libcxl.h | 7 +++++
> cxl/json.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++
> cxl/list.c | 3 ++
> cxl/lib/libcxl.sym | 6 ++++
> 7 files changed, 214 insertions(+)
>
> diff --git a/cxl/lib/private.h b/cxl/lib/private.h
> index d648992..590d719 100644
> --- a/cxl/lib/private.h
> +++ b/cxl/lib/private.h
> @@ -9,6 +9,7 @@
> #include <ccan/endian/endian.h>
> #include <ccan/short_types/short_types.h>
> #include <util/size.h>
> +#include <util/bitmap.h>
>
> #define CXL_EXPORT __attribute__ ((visibility("default")))
>
> @@ -233,6 +234,26 @@ struct cxl_cmd_get_health_info {
> le32 pmem_errors;
> } __attribute__((packed));
>
> +/* CXL 3.0 8.2.9.3.1 Get Firmware Info */
> +struct cxl_cmd_get_fw_info {
> + u8 num_slots;
> + u8 slot_info;
> + u8 activation_cap;
> + u8 reserved[13];
> + char slot_1_revision[0x10];
> + char slot_2_revision[0x10];
> + char slot_3_revision[0x10];
> + char slot_4_revision[0x10];
> +} __attribute__((packed));
> +
> +#define CXL_FW_INFO_CUR_SLOT_MASK GENMASK(2, 0)
> +#define CXL_FW_INFO_NEXT_SLOT_MASK GENMASK(5, 3)
> +#define CXL_FW_INFO_NEXT_SLOT_SHIFT (3)
> +#define CXL_FW_INFO_HAS_LIVE_ACTIVATE BIT(0)
> +
> +#define CXL_FW_VERSION_STR_LEN 16
> +#define CXL_FW_MAX_SLOTS 4
> +
> /* CXL 3.0 8.2.9.8.3.2 Get Alert Configuration */
> struct cxl_cmd_get_alert_config {
> u8 valid_alerts;
> diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
> index 59e5bdb..75490fd 100644
> --- a/cxl/lib/libcxl.c
> +++ b/cxl/lib/libcxl.c
> @@ -3917,6 +3917,96 @@ CXL_EXPORT struct cxl_cmd *cxl_cmd_new_set_partition(struct cxl_memdev *memdev,
> return cmd;
> }
>
> +CXL_EXPORT struct cxl_cmd *cxl_cmd_new_get_fw_info(struct cxl_memdev *memdev)
> +{
> + return cxl_cmd_new_generic(memdev, CXL_MEM_COMMAND_ID_GET_FW_INFO);
> +}
> +
> +static struct cxl_cmd_get_fw_info *cmd_to_get_fw_info(struct cxl_cmd *cmd)
> +{
> + if (cxl_cmd_validate_status(cmd, CXL_MEM_COMMAND_ID_GET_FW_INFO))
> + return NULL;
> +
> + return cmd->output_payload;
> +}
> +
> +CXL_EXPORT unsigned int cxl_cmd_fw_info_get_num_slots(struct cxl_cmd *cmd)
> +{
> + struct cxl_cmd_get_fw_info *c = cmd_to_get_fw_info(cmd);
> +
> + if (!c)
> + return 0;
> +
> + return c->num_slots;
> +}
> +
> +CXL_EXPORT unsigned int cxl_cmd_fw_info_get_active_slot(struct cxl_cmd *cmd)
> +{
> + struct cxl_cmd_get_fw_info *c = cmd_to_get_fw_info(cmd);
> +
> + if (!c)
> + return 0;
> +
> + return c->slot_info & CXL_FW_INFO_CUR_SLOT_MASK;
> +}
> +
> +CXL_EXPORT unsigned int cxl_cmd_fw_info_get_staged_slot(struct cxl_cmd *cmd)
> +{
> + struct cxl_cmd_get_fw_info *c = cmd_to_get_fw_info(cmd);
> +
> + if (!c)
> + return 0;
> +
> + return (c->slot_info & CXL_FW_INFO_NEXT_SLOT_MASK) >>
> + CXL_FW_INFO_NEXT_SLOT_SHIFT;
> +}
> +
> +CXL_EXPORT bool cxl_cmd_fw_info_get_online_activate_capable(struct cxl_cmd *cmd)
> +{
> + struct cxl_cmd_get_fw_info *c = cmd_to_get_fw_info(cmd);
> +
> + if (!c)
> + return false;
> +
> + return !!(c->activation_cap & CXL_FW_INFO_HAS_LIVE_ACTIVATE);
> +}
> +
> +CXL_EXPORT int cxl_cmd_fw_info_get_fw_ver(struct cxl_cmd *cmd, int slot,
> + char *buf, unsigned int len)
> +{
> + struct cxl_cmd_get_fw_info *c = cmd_to_get_fw_info(cmd);
> + char *fw_ver;
> +
> + if (!c)
> + return -ENXIO;
> + if (!len)
> + return -EINVAL;
> +
> + switch(slot) {
> + case 1:
> + fw_ver = &c->slot_1_revision[0];
Just a nit. You can just do
fw_ver = c->slot_1_revision;
DJ
> + break;
> + case 2:
> + fw_ver = &c->slot_2_revision[0];
> + break;
> + case 3:
> + fw_ver = &c->slot_3_revision[0];
> + break;
> + case 4:
> + fw_ver = &c->slot_4_revision[0];
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + if (fw_ver[0] == 0)
> + return -ENOENT;
> +
> + memcpy(buf, fw_ver, min(len, (unsigned int)CXL_FW_VERSION_STR_LEN));
> +
> + return 0;
> +}
> +
> CXL_EXPORT int cxl_cmd_submit(struct cxl_cmd *cmd)
> {
> struct cxl_memdev *memdev = cmd->memdev;
> diff --git a/cxl/filter.h b/cxl/filter.h
> index 595cde7..3f65990 100644
> --- a/cxl/filter.h
> +++ b/cxl/filter.h
> @@ -27,6 +27,7 @@ struct cxl_filter_params {
> bool human;
> bool health;
> bool partition;
> + bool fw;
> bool alert_config;
> bool dax;
> int verbose;
> @@ -81,6 +82,8 @@ static inline unsigned long cxl_filter_to_flags(struct cxl_filter_params *param)
> flags |= UTIL_JSON_TARGETS;
> if (param->partition)
> flags |= UTIL_JSON_PARTITION;
> + if (param->fw)
> + flags |= UTIL_JSON_FIRMWARE;
> if (param->alert_config)
> flags |= UTIL_JSON_ALERT_CONFIG;
> if (param->dax)
> diff --git a/cxl/libcxl.h b/cxl/libcxl.h
> index 54d9f10..99e1b76 100644
> --- a/cxl/libcxl.h
> +++ b/cxl/libcxl.h
> @@ -68,6 +68,13 @@ int cxl_memdev_read_label(struct cxl_memdev *memdev, void *buf, size_t length,
> size_t offset);
> int cxl_memdev_write_label(struct cxl_memdev *memdev, void *buf, size_t length,
> size_t offset);
> +struct cxl_cmd *cxl_cmd_new_get_fw_info(struct cxl_memdev *memdev);
> +unsigned int cxl_cmd_fw_info_get_num_slots(struct cxl_cmd *cmd);
> +unsigned int cxl_cmd_fw_info_get_active_slot(struct cxl_cmd *cmd);
> +unsigned int cxl_cmd_fw_info_get_staged_slot(struct cxl_cmd *cmd);
> +bool cxl_cmd_fw_info_get_online_activate_capable(struct cxl_cmd *cmd);
> +int cxl_cmd_fw_info_get_fw_ver(struct cxl_cmd *cmd, int slot, char *buf,
> + unsigned int len);
>
> #define cxl_memdev_foreach(ctx, memdev) \
> for (memdev = cxl_memdev_get_first(ctx); \
> diff --git a/cxl/json.c b/cxl/json.c
> index e87bdd4..e6bb061 100644
> --- a/cxl/json.c
> +++ b/cxl/json.c
> @@ -12,6 +12,84 @@
> #include "json.h"
> #include "../daxctl/json.h"
>
> +#define CXL_FW_VERSION_STR_LEN 16
> +#define CXL_FW_MAX_SLOTS 4
> +
> +static struct json_object *util_cxl_memdev_fw_to_json(
> + struct cxl_memdev *memdev, unsigned long flags)
> +{
> + struct json_object *jobj;
> + struct json_object *jfw;
> + u32 field, num_slots;
> + struct cxl_cmd *cmd;
> + int rc, i;
> +
> + jfw = json_object_new_object();
> + if (!jfw)
> + return NULL;
> + if (!memdev)
> + goto err_jobj;
> +
> + cmd = cxl_cmd_new_get_fw_info(memdev);
> + if (!cmd)
> + goto err_jobj;
> +
> + rc = cxl_cmd_submit(cmd);
> + if (rc < 0)
> + goto err_cmd;
> + rc = cxl_cmd_get_mbox_status(cmd);
> + if (rc != 0)
> + goto err_cmd;
> +
> + /* fw_info fields */
> + num_slots = cxl_cmd_fw_info_get_num_slots(cmd);
> + jobj = json_object_new_int(num_slots);
> + if (jobj)
> + json_object_object_add(jfw, "num_slots", jobj);
> +
> + field = cxl_cmd_fw_info_get_active_slot(cmd);
> + jobj = json_object_new_int(field);
> + if (jobj)
> + json_object_object_add(jfw, "active_slot", jobj);
> +
> + field = cxl_cmd_fw_info_get_staged_slot(cmd);
> + if (field > 0 && field <= num_slots) {
> + jobj = json_object_new_int(field);
> + if (jobj)
> + json_object_object_add(jfw, "staged_slot", jobj);
> + }
> +
> + rc = cxl_cmd_fw_info_get_online_activate_capable(cmd);
> + jobj = json_object_new_boolean(rc);
> + if (jobj)
> + json_object_object_add(jfw, "online_activate_capable", jobj);
> +
> + for (i = 1; i <= CXL_FW_MAX_SLOTS; i++) {
> + char fw_ver[CXL_FW_VERSION_STR_LEN + 1];
> + char jkey[16];
> +
> + rc = cxl_cmd_fw_info_get_fw_ver(cmd, i, fw_ver,
> + CXL_FW_VERSION_STR_LEN);
> + if (rc)
> + continue;
> + fw_ver[CXL_FW_VERSION_STR_LEN] = 0;
> + snprintf(jkey, 16, "slot_%d_version", i);
> + jobj = json_object_new_string(fw_ver);
> + if (jobj)
> + json_object_object_add(jfw, jkey, jobj);
> + }
> +
> + cxl_cmd_unref(cmd);
> + return jfw;
> +
> +err_cmd:
> + cxl_cmd_unref(cmd);
> +err_jobj:
> + json_object_put(jfw);
> + return NULL;
> +
> +}
> +
> static struct json_object *util_cxl_memdev_health_to_json(
> struct cxl_memdev *memdev, unsigned long flags)
> {
> @@ -552,6 +630,12 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev,
> json_object_object_add(jdev, "partition_info", jobj);
> }
>
> + if (flags & UTIL_JSON_FIRMWARE) {
> + jobj = util_cxl_memdev_fw_to_json(memdev, flags);
> + if (jobj)
> + json_object_object_add(jdev, "firmware", jobj);
> + }
> +
> json_object_set_userdata(jdev, memdev, NULL);
> return jdev;
> }
> diff --git a/cxl/list.c b/cxl/list.c
> index c01154e..93ba51e 100644
> --- a/cxl/list.c
> +++ b/cxl/list.c
> @@ -53,6 +53,8 @@ static const struct option options[] = {
> "include memory device health information"),
> OPT_BOOLEAN('I', "partition", ¶m.partition,
> "include memory device partition information"),
> + OPT_BOOLEAN('F', "firmware", ¶m.fw,
> + "include memory device firmware information"),
> OPT_BOOLEAN('A', "alert-config", ¶m.alert_config,
> "include alert configuration information"),
> OPT_INCR('v', "verbose", ¶m.verbose, "increase output detail"),
> @@ -116,6 +118,7 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx)
> case 3:
> param.health = true;
> param.partition = true;
> + param.fw = true;
> param.alert_config = true;
> param.dax = true;
> /* fallthrough */
> diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
> index 1c6177c..16a8671 100644
> --- a/cxl/lib/libcxl.sym
> +++ b/cxl/lib/libcxl.sym
> @@ -248,4 +248,10 @@ global:
> cxl_region_get_mode;
> cxl_decoder_create_ram_region;
> cxl_region_get_daxctl_region;
> + cxl_cmd_new_get_fw_info;
> + cxl_cmd_fw_info_get_num_slots;
> + cxl_cmd_fw_info_get_active_slot;
> + cxl_cmd_fw_info_get_staged_slot;
> + cxl_cmd_fw_info_get_online_activate_capable;
> + cxl_cmd_fw_info_get_fw_ver;
> } LIBCXL_4;
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH ndctl 3/5] cxl/fw_loader: add APIs to get current state of the FW loader mechanism
2023-04-22 3:10 ` [PATCH ndctl 3/5] cxl/fw_loader: add APIs to get current state of the FW loader mechanism Vishal Verma
@ 2023-05-19 18:49 ` Dave Jiang
0 siblings, 0 replies; 12+ messages in thread
From: Dave Jiang @ 2023-05-19 18:49 UTC (permalink / raw)
To: Vishal Verma, linux-cxl; +Cc: nvdimm, Alison Schofield, Ira Weiny, Dan Williams
On 4/21/23 8:10 PM, Vishal Verma wrote:
> Add a way to interface with the firmware loader mechanism for cxl
> memdevs. Add APIs to retrieve the current status of the fw loader, and
> the remaining size if a fw upload is in progress. Display these in the
> 'firmware' section of memdev listings.
>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
> ---
> cxl/lib/private.h | 10 ++++++
> cxl/lib/libcxl.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> cxl/libcxl.h | 27 +++++++++++++++
> cxl/json.c | 13 +++++++
> cxl/lib/libcxl.sym | 2 ++
> 5 files changed, 152 insertions(+)
>
> diff --git a/cxl/lib/private.h b/cxl/lib/private.h
> index 590d719..95e0c43 100644
> --- a/cxl/lib/private.h
> +++ b/cxl/lib/private.h
> @@ -20,6 +20,15 @@ struct cxl_pmem {
> char *dev_path;
> };
>
> +struct cxl_fw_loader {
> + char *dev_path;
> + char *loading;
> + char *data;
> + char *remaining;
> + char *cancel;
> + char *status;
> +};
> +
> struct cxl_endpoint;
> struct cxl_memdev {
> int id, major, minor;
> @@ -39,6 +48,7 @@ struct cxl_memdev {
> struct cxl_pmem *pmem;
> unsigned long long serial;
> struct cxl_endpoint *endpoint;
> + struct cxl_fw_loader *fwl;
> };
>
> struct cxl_dport {
> diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
> index 75490fd..86873d7 100644
> --- a/cxl/lib/libcxl.c
> +++ b/cxl/lib/libcxl.c
> @@ -63,12 +63,25 @@ static void free_pmem(struct cxl_pmem *pmem)
> }
> }
>
> +static void free_fwl(struct cxl_fw_loader *fwl)
> +{
> + if (fwl) {
> + free(fwl->loading);
> + free(fwl->data);
> + free(fwl->remaining);
> + free(fwl->cancel);
> + free(fwl->status);
> + free(fwl);
> + }
> +}
> +
> static void free_memdev(struct cxl_memdev *memdev, struct list_head *head)
> {
> if (head)
> list_del_from(head, &memdev->list);
> kmod_module_unref(memdev->module);
> free_pmem(memdev->pmem);
> + free_fwl(memdev->fwl);
> free(memdev->firmware_version);
> free(memdev->dev_buf);
> free(memdev->dev_path);
> @@ -1174,6 +1187,45 @@ static void *add_cxl_pmem(void *parent, int id, const char *br_base)
> return NULL;
> }
>
> +static int add_cxl_memdev_fwl(struct cxl_memdev *memdev,
> + const char *cxlmem_base)
> +{
> + const char *devname = cxl_memdev_get_devname(memdev);
> + struct cxl_fw_loader *fwl;
> +
> + fwl = calloc(1, sizeof(*fwl));
> + if (!fwl)
> + return -ENOMEM;
> +
> + if (asprintf(&fwl->loading, "%s/firmware/%s/loading", cxlmem_base,
> + devname) < 0)
> + goto err_read;
> + if (asprintf(&fwl->data, "%s/firmware/%s/data", cxlmem_base, devname) <
> + 0)
> + goto err_read;
> + if (asprintf(&fwl->remaining, "%s/firmware/%s/remaining_size",
> + cxlmem_base, devname) < 0)
> + goto err_read;
> + if (asprintf(&fwl->cancel, "%s/firmware/%s/cancel", cxlmem_base,
> + devname) < 0)
> + goto err_read;
> + if (asprintf(&fwl->status, "%s/firmware/%s/status", cxlmem_base,
> + devname) < 0)
> + goto err_read;
> +
> + memdev->fwl = fwl;
> + return 0;
> +
> + err_read:
> + free(fwl->loading);
> + free(fwl->data);
> + free(fwl->remaining);
> + free(fwl->cancel);
> + free(fwl->status);
> + free(fwl);
Just call free_fwl()?
DJ
> + return -ENOMEM;
> +}
> +
> static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
> {
> const char *devname = devpath_to_devname(cxlmem_base);
> @@ -1263,6 +1315,9 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
>
> device_parse(ctx, cxlmem_base, "pmem", memdev, add_cxl_pmem);
>
> + if (add_cxl_memdev_fwl(memdev, cxlmem_base))
> + goto err_read;
> +
> cxl_memdev_foreach(ctx, memdev_dup)
> if (memdev_dup->id == memdev->id) {
> free_memdev(memdev, NULL);
> @@ -1373,6 +1428,51 @@ CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev
> return memdev->firmware_version;
> }
>
> +static enum cxl_fwl_status cxl_fwl_get_status(struct cxl_memdev *memdev)
> +{
> + const char *devname = cxl_memdev_get_devname(memdev);
> + struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
> + struct cxl_fw_loader *fwl = memdev->fwl;
> + char buf[SYSFS_ATTR_SIZE];
> + int rc;
> +
> + rc = sysfs_read_attr(ctx, fwl->status, buf);
> + if (rc < 0) {
> + err(ctx, "%s: failed to get fw loader status (%s)\n", devname,
> + strerror(-rc));
> + return CXL_FWL_STATUS_UNKNOWN;
> + }
> +
> + return cxl_fwl_status_from_ident(buf);
> +}
> +
> +CXL_EXPORT bool cxl_memdev_fw_update_in_progress(struct cxl_memdev *memdev)
> +{
> + int status = cxl_fwl_get_status(memdev);
> +
> + if (status == CXL_FWL_STATUS_IDLE)
> + return false;
> + return true;
> +}
> +
> +CXL_EXPORT size_t cxl_memdev_fw_update_get_remaining(struct cxl_memdev *memdev)
> +{
> + const char *devname = cxl_memdev_get_devname(memdev);
> + struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
> + struct cxl_fw_loader *fwl = memdev->fwl;
> + char buf[SYSFS_ATTR_SIZE];
> + int rc;
> +
> + rc = sysfs_read_attr(ctx, fwl->remaining, buf);
> + if (rc < 0) {
> + err(ctx, "%s: failed to get fw loader remaining size (%s)\n",
> + devname, strerror(-rc));
> + return 0;
> + }
> +
> + return strtoull(buf, NULL, 0);
> +}
> +
> static void bus_invalidate(struct cxl_bus *bus)
> {
> struct cxl_ctx *ctx = cxl_bus_get_ctx(bus);
> diff --git a/cxl/libcxl.h b/cxl/libcxl.h
> index 99e1b76..7509abe 100644
> --- a/cxl/libcxl.h
> +++ b/cxl/libcxl.h
> @@ -33,6 +33,31 @@ void *cxl_get_userdata(struct cxl_ctx *ctx);
> void cxl_set_private_data(struct cxl_ctx *ctx, void *data);
> void *cxl_get_private_data(struct cxl_ctx *ctx);
>
> +enum cxl_fwl_status {
> + CXL_FWL_STATUS_UNKNOWN,
> + CXL_FWL_STATUS_IDLE,
> + CXL_FWL_STATUS_RECEIVING,
> + CXL_FWL_STATUS_PREPARING,
> + CXL_FWL_STATUS_TRANSFERRING,
> + CXL_FWL_STATUS_PROGRAMMING,
> +};
> +
> +static inline enum cxl_fwl_status cxl_fwl_status_from_ident(char *status)
> +{
> + if (strcmp(status, "idle") == 0)
> + return CXL_FWL_STATUS_IDLE;
> + if (strcmp(status, "receiving") == 0)
> + return CXL_FWL_STATUS_RECEIVING;
> + if (strcmp(status, "preparing") == 0)
> + return CXL_FWL_STATUS_PREPARING;
> + if (strcmp(status, "transferring") == 0)
> + return CXL_FWL_STATUS_TRANSFERRING;
> + if (strcmp(status, "programming") == 0)
> + return CXL_FWL_STATUS_PROGRAMMING;
> +
> + return CXL_FWL_STATUS_UNKNOWN;
> +}
> +
> struct cxl_memdev;
> struct cxl_memdev *cxl_memdev_get_first(struct cxl_ctx *ctx);
> struct cxl_memdev *cxl_memdev_get_next(struct cxl_memdev *memdev);
> @@ -48,6 +73,8 @@ struct cxl_ctx *cxl_memdev_get_ctx(struct cxl_memdev *memdev);
> unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev);
> unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev);
> const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev);
> +bool cxl_memdev_fw_update_in_progress(struct cxl_memdev *memdev);
> +size_t cxl_memdev_fw_update_get_remaining(struct cxl_memdev *memdev);
>
> /* ABI spelling mistakes are forever */
> static inline const char *cxl_memdev_get_firmware_version(
> diff --git a/cxl/json.c b/cxl/json.c
> index e6bb061..5dc0bd3 100644
> --- a/cxl/json.c
> +++ b/cxl/json.c
> @@ -22,6 +22,7 @@ static struct json_object *util_cxl_memdev_fw_to_json(
> struct json_object *jfw;
> u32 field, num_slots;
> struct cxl_cmd *cmd;
> + size_t remaining;
> int rc, i;
>
> jfw = json_object_new_object();
> @@ -79,6 +80,18 @@ static struct json_object *util_cxl_memdev_fw_to_json(
> json_object_object_add(jfw, jkey, jobj);
> }
>
> + rc = cxl_memdev_fw_update_in_progress(memdev);
> + jobj = json_object_new_boolean(rc);
> + if (jobj)
> + json_object_object_add(jfw, "fw_update_in_progress", jobj);
> +
> + if (rc == true) {
> + remaining = cxl_memdev_fw_update_get_remaining(memdev);
> + jobj = util_json_object_size(remaining, flags);
> + if (jobj)
> + json_object_object_add(jfw, "remaining_size", jobj);
> + }
> +
> cxl_cmd_unref(cmd);
> return jfw;
>
> diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
> index 16a8671..9438877 100644
> --- a/cxl/lib/libcxl.sym
> +++ b/cxl/lib/libcxl.sym
> @@ -254,4 +254,6 @@ global:
> cxl_cmd_fw_info_get_staged_slot;
> cxl_cmd_fw_info_get_online_activate_capable;
> cxl_cmd_fw_info_get_fw_ver;
> + cxl_memdev_fw_update_in_progress;
> + cxl_memdev_fw_update_get_remaining;
> } LIBCXL_4;
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH ndctl 4/5] cxl: add an update-firmware command
2023-04-22 3:10 ` [PATCH ndctl 4/5] cxl: add an update-firmware command Vishal Verma
2023-04-24 23:14 ` Verma, Vishal L
@ 2023-05-19 18:57 ` Dave Jiang
1 sibling, 0 replies; 12+ messages in thread
From: Dave Jiang @ 2023-05-19 18:57 UTC (permalink / raw)
To: Vishal Verma, linux-cxl; +Cc: nvdimm, Alison Schofield, Ira Weiny, Dan Williams
On 4/21/23 8:10 PM, Vishal Verma wrote:
> Add a new cxl-update-firmware command to initiate a firmware update on a
> given memdev. This allows using a specified file to pass in as the
> firmware binary for one or more memdevs, allows for a blocking mode,
> where the command only exits after the update is complete for every
> specified memdev, and includes an option to cancel an in-progress
> update. Add the supporting libcxl APIs for the above functions as well.
>
> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> cxl/lib/private.h | 5 ++
> cxl/lib/libcxl.c | 114 ++++++++++++++++++++++++++++++++++++++++++
> cxl/builtin.h | 1 +
> cxl/libcxl.h | 2 +
> cxl/cxl.c | 1 +
> cxl/memdev.c | 73 ++++++++++++++++++++++++++-
> Documentation/cxl/meson.build | 1 +
> cxl/lib/libcxl.sym | 2 +
> 8 files changed, 198 insertions(+), 1 deletion(-)
>
> diff --git a/cxl/lib/private.h b/cxl/lib/private.h
> index 95e0c43..6388534 100644
> --- a/cxl/lib/private.h
> +++ b/cxl/lib/private.h
> @@ -29,6 +29,11 @@ struct cxl_fw_loader {
> char *status;
> };
>
> +enum cxl_fwl_loading {
> + CXL_FWL_LOADING_END = 0,
> + CXL_FWL_LOADING_START,
> +};
> +
> struct cxl_endpoint;
> struct cxl_memdev {
> int id, major, minor;
> diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
> index 86873d7..8084857 100644
> --- a/cxl/lib/libcxl.c
> +++ b/cxl/lib/libcxl.c
> @@ -7,6 +7,7 @@
> #include <stdlib.h>
> #include <dirent.h>
> #include <unistd.h>
> +#include <sys/mman.h>
> #include <sys/stat.h>
> #include <sys/types.h>
> #include <sys/ioctl.h>
> @@ -1473,6 +1474,119 @@ CXL_EXPORT size_t cxl_memdev_fw_update_get_remaining(struct cxl_memdev *memdev)
> return strtoull(buf, NULL, 0);
> }
>
> +static int cxl_memdev_fwl_set_loading(struct cxl_memdev *memdev,
> + enum cxl_fwl_loading loadval)
> +{
> + const char *devname = cxl_memdev_get_devname(memdev);
> + struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
> + struct cxl_fw_loader *fwl = memdev->fwl;
> + char buf[SYSFS_ATTR_SIZE];
> + int rc;
> +
> + sprintf(buf, "%d\n", loadval);
> + rc = sysfs_write_attr(ctx, fwl->loading, buf);
> + if (rc < 0) {
> + err(ctx, "%s: failed to trigger fw loading to %d (%s)\n",
> + devname, loadval, strerror(-rc));
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> +static int cxl_memdev_fwl_copy_data(struct cxl_memdev *memdev, void *fw_buf,
> + size_t size)
> +{
> + struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
> + struct cxl_fw_loader *fwl = memdev->fwl;
> + FILE *fwl_data;
> + size_t rw_len;
> + int rc = 0;
> +
> + fwl_data = fopen(fwl->data, "w");
> + if (!fwl_data) {
> + err(ctx, "failed to open: %s: (%s)\n", fwl->data,
> + strerror(errno));
> + return -errno;
> + }
> +
> + rw_len = fwrite(fw_buf, 1, size, fwl_data);
> + if (rw_len != size) {
> + rc = -ENXIO;
> + goto out_close;
> + }
> + fflush(fwl_data);
> +
> +out_close:
> + fclose(fwl_data);
> + return rc;
> +}
> +
> +CXL_EXPORT int cxl_memdev_update_fw(struct cxl_memdev *memdev,
> + const char *fw_path)
> +{
> + struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
> + struct stat s;
> + int f_in, rc;
> + void *fw_buf;
> +
> + f_in = open(fw_path, O_RDONLY);
> + if (f_in < 0) {
> + err(ctx, "failed to open: %s: (%s)\n", fw_path,
> + strerror(errno));
> + return -errno;
> + }
> +
> + rc = fstat(f_in, &s);
> + if (rc < 0) {
> + err(ctx, "failed to stat: %s: (%s)\n", fw_path,
> + strerror(errno));
> + rc = -errno;
> + goto out_close;
> + }
> +
> + fw_buf = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, f_in, 0);
> + if (fw_buf == MAP_FAILED) {
> + err(ctx, "failed to map: %s: (%s)\n", fw_path,
> + strerror(errno));
> + rc = -errno;
> + goto out_close;
> + }
> +
> + rc = cxl_memdev_fwl_set_loading(memdev, CXL_FWL_LOADING_START);
> + if (rc)
> + goto out_unmap;
> +
> + rc = cxl_memdev_fwl_copy_data(memdev, fw_buf, s.st_size);
> + if (rc)
> + goto out_unmap;
> +
> + rc = cxl_memdev_fwl_set_loading(memdev, CXL_FWL_LOADING_END);
> +
> +out_unmap:
> + munmap(fw_buf, s.st_size);
> +out_close:
> + close(f_in);
> + return rc;
> +}
> +
> +CXL_EXPORT int cxl_memdev_cancel_fw_update(struct cxl_memdev *memdev)
> +{
> + struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
> + struct cxl_fw_loader *fwl = memdev->fwl;
> + int rc;
> +
> + if (!cxl_memdev_fw_update_in_progress(memdev) &&
> + cxl_memdev_fw_update_get_remaining(memdev) == 0)
> + return -ENXIO;
> +
> + rc = sysfs_write_attr(ctx, fwl->cancel, "1\n");
> + if (rc < 0)
> + return rc;
> +
> + return 0;
> +}
> +
> static void bus_invalidate(struct cxl_bus *bus)
> {
> struct cxl_ctx *ctx = cxl_bus_get_ctx(bus);
> diff --git a/cxl/builtin.h b/cxl/builtin.h
> index 9baa43b..3ec6c6c 100644
> --- a/cxl/builtin.h
> +++ b/cxl/builtin.h
> @@ -14,6 +14,7 @@ int cmd_disable_memdev(int argc, const char **argv, struct cxl_ctx *ctx);
> int cmd_enable_memdev(int argc, const char **argv, struct cxl_ctx *ctx);
> int cmd_reserve_dpa(int argc, const char **argv, struct cxl_ctx *ctx);
> int cmd_free_dpa(int argc, const char **argv, struct cxl_ctx *ctx);
> +int cmd_update_fw(int argc, const char **argv, struct cxl_ctx *ctx);
> int cmd_disable_port(int argc, const char **argv, struct cxl_ctx *ctx);
> int cmd_enable_port(int argc, const char **argv, struct cxl_ctx *ctx);
> int cmd_set_partition(int argc, const char **argv, struct cxl_ctx *ctx);
> diff --git a/cxl/libcxl.h b/cxl/libcxl.h
> index 7509abe..2ffb39c 100644
> --- a/cxl/libcxl.h
> +++ b/cxl/libcxl.h
> @@ -75,6 +75,8 @@ unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev);
> const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev);
> bool cxl_memdev_fw_update_in_progress(struct cxl_memdev *memdev);
> size_t cxl_memdev_fw_update_get_remaining(struct cxl_memdev *memdev);
> +int cxl_memdev_update_fw(struct cxl_memdev *memdev, const char *fw_path);
> +int cxl_memdev_cancel_fw_update(struct cxl_memdev *memdev);
>
> /* ABI spelling mistakes are forever */
> static inline const char *cxl_memdev_get_firmware_version(
> diff --git a/cxl/cxl.c b/cxl/cxl.c
> index 3be7026..e1524b8 100644
> --- a/cxl/cxl.c
> +++ b/cxl/cxl.c
> @@ -68,6 +68,7 @@ static struct cmd_struct commands[] = {
> { "enable-memdev", .c_fn = cmd_enable_memdev },
> { "reserve-dpa", .c_fn = cmd_reserve_dpa },
> { "free-dpa", .c_fn = cmd_free_dpa },
> + { "update-firmware", .c_fn = cmd_update_fw },
> { "disable-port", .c_fn = cmd_disable_port },
> { "enable-port", .c_fn = cmd_enable_port },
> { "set-partition", .c_fn = cmd_set_partition },
> diff --git a/cxl/memdev.c b/cxl/memdev.c
> index 807e859..1ad871a 100644
> --- a/cxl/memdev.c
> +++ b/cxl/memdev.c
> @@ -23,14 +23,18 @@ struct action_context {
> };
>
> static struct parameters {
> + const char *bus;
> const char *outfile;
> const char *infile;
> + const char *fw_file;
> unsigned len;
> unsigned offset;
> bool verbose;
> bool serial;
> bool force;
> bool align;
> + bool cancel;
> + bool wait;
> const char *type;
> const char *size;
> const char *decoder_filter;
> @@ -87,6 +91,14 @@ OPT_STRING('t', "type", ¶m.type, "type", \
> OPT_BOOLEAN('f', "force", ¶m.force, \
> "Attempt 'expected to fail' operations")
>
> +#define FW_OPTIONS() \
> +OPT_STRING('F', "firmware-file", ¶m.fw_file, "firmware-file", \
> + "firmware image file to use for the update"), \
> +OPT_BOOLEAN('c', "cancel", ¶m.cancel, \
> + "attempt to abort an in-progress firmware update"), \
> +OPT_BOOLEAN('w', "wait", ¶m.wait, \
> + "wait for firmware update to complete before returning")
> +
> static const struct option read_options[] = {
> BASE_OPTIONS(),
> LABEL_OPTIONS(),
> @@ -137,6 +149,12 @@ static const struct option free_dpa_options[] = {
> OPT_END(),
> };
>
> +static const struct option update_fw_options[] = {
> + BASE_OPTIONS(),
> + FW_OPTIONS(),
> + OPT_END(),
> +};
> +
> enum reserve_dpa_mode {
> DPA_ALLOC,
> DPA_FREE,
> @@ -655,6 +673,39 @@ out_err:
> return rc;
> }
>
> +static int action_update_fw(struct cxl_memdev *memdev,
> + struct action_context *actx)
> +{
> + const char *devname = cxl_memdev_get_devname(memdev);
> + struct json_object *jmemdev;
> + unsigned long flags;
> + int rc;
> +
> + if (param.cancel)
> + return cxl_memdev_cancel_fw_update(memdev);
> +
> + if (param.fw_file) {
> + rc = cxl_memdev_update_fw(memdev, param.fw_file);
> + if (rc)
> + log_err(&ml, "%s error: %s\n", devname, strerror(-rc));
> + }
> +
> + if (param.wait) {
> + while (cxl_memdev_fw_update_in_progress(memdev) ||
> + cxl_memdev_fw_update_get_remaining(memdev) > 0)
> + sleep(1);
> + }
> +
> + flags = UTIL_JSON_FIRMWARE;
> + if (actx->f_out == stdout && isatty(1))
> + flags |= UTIL_JSON_HUMAN;
> + jmemdev = util_cxl_memdev_to_json(memdev, flags);
> + if (actx->jdevs && jmemdev)
> + json_object_array_add(actx->jdevs, jmemdev);
> +
> + return rc;
> +}
> +
> static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx,
> int (*action)(struct cxl_memdev *memdev,
> struct action_context *actx),
> @@ -698,7 +749,7 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx,
> }
>
> if (action == action_setpartition || action == action_reserve_dpa ||
> - action == action_free_dpa)
> + action == action_free_dpa || action == action_update_fw)
> actx.jdevs = json_object_new_array();
>
> if (err == argc) {
> @@ -897,3 +948,23 @@ int cmd_free_dpa(int argc, const char **argv, struct cxl_ctx *ctx)
>
> return count >= 0 ? 0 : EXIT_FAILURE;
> }
> +
> +int cmd_update_fw(int argc, const char **argv, struct cxl_ctx *ctx)
> +{
> + int count = memdev_action(
> + argc, argv, ctx, action_update_fw, update_fw_options,
> + "cxl update-firmware <mem0> [<mem1>..<memn>] [<options>]");
> + const char *op_string;
> +
> + if (param.cancel)
> + op_string = "cancelled";
> + else if (param.wait)
> + op_string = "completed";
> + else
> + op_string = "started";
> +
> + log_info(&ml, "firmware update %s on %d mem device%s\n", op_string,
> + count >= 0 ? count : 0, count > 1 ? "s" : "");
> +
> + return count >= 0 ? 0 : EXIT_FAILURE;
> +}
> diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build
> index a6d77ab..c553357 100644
> --- a/Documentation/cxl/meson.build
> +++ b/Documentation/cxl/meson.build
> @@ -46,6 +46,7 @@ cxl_manpages = [
> 'cxl-enable-region.txt',
> 'cxl-destroy-region.txt',
> 'cxl-monitor.txt',
> + 'cxl-update-firmware.txt',
> ]
>
> foreach man : cxl_manpages
> diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
> index 9438877..ce0cb7f 100644
> --- a/cxl/lib/libcxl.sym
> +++ b/cxl/lib/libcxl.sym
> @@ -256,4 +256,6 @@ global:
> cxl_cmd_fw_info_get_fw_ver;
> cxl_memdev_fw_update_in_progress;
> cxl_memdev_fw_update_get_remaining;
> + cxl_memdev_update_fw;
> + cxl_memdev_cancel_fw_update;
> } LIBCXL_4;
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH ndctl 5/5] test/cxl-update-firmware: add a unit test for firmware update
2023-04-22 3:10 ` [PATCH ndctl 5/5] test/cxl-update-firmware: add a unit test for firmware update Vishal Verma
@ 2023-05-19 19:00 ` Dave Jiang
0 siblings, 0 replies; 12+ messages in thread
From: Dave Jiang @ 2023-05-19 19:00 UTC (permalink / raw)
To: Vishal Verma, linux-cxl; +Cc: nvdimm, Alison Schofield, Ira Weiny, Dan Williams
On 4/21/23 8:10 PM, Vishal Verma wrote:
> Add a unit test to exercise the different operating modes of the
> cxl-update-firmware command. Perform an update synchronously,
> asynchronously, on multiple devices, and attempt cancellation of an
> in-progress update.
>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> test/cxl-update-firmware.sh | 195 ++++++++++++++++++++++++++++++++++++++++++++
> test/meson.build | 2 +
> 2 files changed, 197 insertions(+)
>
> diff --git a/test/cxl-update-firmware.sh b/test/cxl-update-firmware.sh
> new file mode 100755
> index 0000000..c6cd742
> --- /dev/null
> +++ b/test/cxl-update-firmware.sh
> @@ -0,0 +1,195 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (C) 2023 Intel Corporation. All rights reserved.
> +
> +. $(dirname $0)/common
> +
> +rc=77
> +
> +set -ex
> +
> +trap 'err $LINENO' ERR
> +
> +check_prereq "jq"
> +check_prereq "dd"
> +check_prereq "sha256sum"
> +
> +modprobe -r cxl_test
> +modprobe cxl_test
> +rc=1
> +
> +mk_fw_file()
> +{
> + size="$1"
> +
> + if [[ ! $size ]]; then
> + err "$LINENO"
> + fi
> + if (( size > 64 )); then
> + err "$LINENO"
> + fi
> +
> + fw_file="$(mktemp -p /tmp fw_file_XXXX)"
> + dd if=/dev/urandom of="$fw_file" bs=1M count="$size"
> + echo "$fw_file"
> +}
> +
> +find_memdevs()
> +{
> + count="$1"
> +
> + if [[ ! $count ]]; then
> + count=1
> + fi
> +
> + "$CXL" list -M -b "$CXL_TEST_BUS" \
> + | jq -r '.[] | select(.host | startswith("cxl_mem.")) | .memdev' \
> + | head -"$count"
> +}
> +
> +do_update_fw()
> +{
> + "$CXL" update-firmware -b "$CXL_TEST_BUS" "$@"
> +}
> +
> +wait_complete()
> +{
> + mem="$1" # single memdev, not a list
> + max_wait="$2" # in seconds
> + waited=0
> +
> + while true; do
> + json="$("$CXL" list -m "$mem" -F)"
> + in_prog="$(jq -r '.[].firmware.fw_update_in_progress' <<< "$json")"
> + if [[ $in_prog == "true" ]]; then
> + sleep 1
> + waited="$((waited + 1))"
> + continue
> + else
> + break
> + fi
> + if (( waited == max_wait )); then
> + echo "completion timeout for $mem"
> + err "$LINENO"
> + fi
> + done
> +}
> +
> +validate_json_state()
> +{
> + json="$1"
> + state="$2"
> +
> + while read -r in_prog_state; do
> + if [[ $in_prog_state == $state ]]; then
> + continue
> + else
> + echo "expected fw_update_in_progress:$state"
> + err "$LINENO"
> + fi
> + done < <(jq -r '.[].firmware.fw_update_in_progress' <<< "$json")
> +}
> +
> +validate_fw_update_in_progress()
> +{
> + validate_json_state "$1" "true"
> +}
> +
> +validate_fw_update_idle()
> +{
> + validate_json_state "$1" "false"
> +}
> +
> +validate_staged_slot()
> +{
> + json="$1"
> + slot="$2"
> +
> + while read -r staged_slot; do
> + if [[ $staged_slot == $slot ]]; then
> + continue
> + else
> + echo "expected staged_slot:$slot"
> + err "$LINENO"
> + fi
> + done < <(jq -r '.[].firmware.staged_slot' <<< "$json")
> +}
> +
> +check_sha()
> +{
> + mem="$1"
> + file="$2"
> + csum_path="/sys/bus/platform/devices/cxl_mem.${mem#mem}/fw_buf_checksum"
> +
> + mem_csum="$(cat "$csum_path")"
> + file_csum="$(sha256sum "$file" | awk '{print $1}')"
> +
> + if [[ $mem_csum != $file_csum ]]; then
> + echo "checksum failure for mem$mem"
> + err "$LINENO"
> + fi
> +}
> +
> +test_blocking_update()
> +{
> + file="$(mk_fw_file 8)"
> + mem="$(find_memdevs 1)"
> + json=$(do_update_fw -F "$file" --wait "$mem")
> + validate_fw_update_idle "$json"
> + # cxl_test's starting slot is '2', so staged should be 3
> + validate_staged_slot "$json" 3
> + check_sha "$mem" "$file"
> + rm "$file"
> +}
> +
> +test_nonblocking_update()
> +{
> + file="$(mk_fw_file 16)"
> + mem="$(find_memdevs 1)"
> + json=$(do_update_fw -F "$file" "$mem")
> + validate_fw_update_in_progress "$json"
> + wait_complete "$mem" 15
> + validate_fw_update_idle "$("$CXL" list -m "$mem" -F)"
> + check_sha "$mem" "$file"
> + rm "$file"
> +}
> +
> +test_multiple_memdev()
> +{
> + num_mems=2
> +
> + file="$(mk_fw_file 16)"
> + declare -a mems
> + mems=( $(find_memdevs "$num_mems") )
> + json="$(do_update_fw -F "$file" "${mems[@]}")"
> + validate_fw_update_in_progress "$json"
> + # use the in-band wait this time
> + json="$(do_update_fw --wait "${mems[@]}")"
> + validate_fw_update_idle "$json"
> + for mem in ${mems[@]}; do
> + check_sha "$mem" "$file"
> + done
> + rm "$file"
> +}
> +
> +test_cancel()
> +{
> + file="$(mk_fw_file 16)"
> + mem="$(find_memdevs 1)"
> + json=$(do_update_fw -F "$file" "$mem")
> + validate_fw_update_in_progress "$json"
> + do_update_fw --cancel "$mem"
> + # cancellation is asynchronous, and the result looks the same as idle
> + wait_complete "$mem" 15
> + validate_fw_update_idle "$("$CXL" list -m "$mem" -F)"
> + # no need to check_sha
> + rm "$file"
> +}
> +
> +test_blocking_update
> +test_nonblocking_update
> +test_multiple_memdev
> +test_cancel
> +
> +check_dmesg "$LINENO"
> +modprobe -r cxl_test
> diff --git a/test/meson.build b/test/meson.build
> index a956885..0f4d3c4 100644
> --- a/test/meson.build
> +++ b/test/meson.build
> @@ -155,6 +155,7 @@ cxl_sysfs = find_program('cxl-region-sysfs.sh')
> cxl_labels = find_program('cxl-labels.sh')
> cxl_create_region = find_program('cxl-create-region.sh')
> cxl_xor_region = find_program('cxl-xor-region.sh')
> +cxl_update_firmware = find_program('cxl-update-firmware.sh')
>
> tests = [
> [ 'libndctl', libndctl, 'ndctl' ],
> @@ -198,6 +199,7 @@ if get_option('destructive').enabled()
>
> tests += [
> [ 'firmware-update.sh', firmware_update, 'ndctl' ],
> + [ 'cxl-update-firmware.sh', cxl_update_firmware, 'cxl' ],
> [ 'pmem-ns', pmem_ns, 'ndctl' ],
> [ 'sub-section.sh', sub_section, 'dax' ],
> [ 'dax-dev', dax_dev, 'dax' ],
>
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2023-05-19 19:00 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-04-22 3:09 [PATCH ndctl 0/5] cxl: firmware update support for libcxl and cxl-cli Vishal Verma
2023-04-22 3:09 ` [PATCH ndctl 1/5] cxl/memdev.c: allow filtering memdevs by bus Vishal Verma
2023-05-19 17:57 ` Dave Jiang
2023-04-22 3:10 ` [PATCH ndctl 2/5] cxl/list: print firmware info in memdev listings Vishal Verma
2023-05-19 18:21 ` Dave Jiang
2023-04-22 3:10 ` [PATCH ndctl 3/5] cxl/fw_loader: add APIs to get current state of the FW loader mechanism Vishal Verma
2023-05-19 18:49 ` Dave Jiang
2023-04-22 3:10 ` [PATCH ndctl 4/5] cxl: add an update-firmware command Vishal Verma
2023-04-24 23:14 ` Verma, Vishal L
2023-05-19 18:57 ` Dave Jiang
2023-04-22 3:10 ` [PATCH ndctl 5/5] test/cxl-update-firmware: add a unit test for firmware update Vishal Verma
2023-05-19 19:00 ` Dave Jiang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox