Message ID | 20230405-vv-fw_update-v1-3-722a7a5baea3@intel.com |
---|---|
State | Superseded |
Headers | show |
Series | cxl: firmware update support for libcxl and cxl-cli | expand |
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; >
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;
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(+)