On 1/9/26 9:07 AM, Ben Cheatham wrote:
> Add injectable error information for CXL memory devices and busses.
> This information is only shown when the CXL debugfs is accessible
> (normally mounted at /sys/kernel/debug/cxl).
>
> For CXL memory devices and dports this reports whether the device
> supports poison injection. The "--media-errors"/"-L" option shows
> injected poison for memory devices.
>
> For CXL busses this shows injectable CXL protocol error types. The
> information will be the same across busses because the error types are
> system-wide. The information is presented under the bus for easier
> filtering.
>
> Signed-off-by: Ben Cheatham <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> cxl/json.c | 38 ++++++++++++++++++++++++++++++++++++++
> cxl/lib/libcxl.c | 34 +++++++++++++++++++++++++---------
> cxl/lib/libcxl.sym | 2 ++
> cxl/libcxl.h | 2 ++
> 4 files changed, 67 insertions(+), 9 deletions(-)
>
> diff --git a/cxl/json.c b/cxl/json.c
> index e9cb88a..6cdf513 100644
> --- a/cxl/json.c
> +++ b/cxl/json.c
> @@ -663,6 +663,12 @@ struct json_object *util_cxl_memdev_to_json(struct
> cxl_memdev *memdev,
> json_object_object_add(jdev, "state", jobj);
> }
>
> + if (cxl_debugfs_exists(cxl_memdev_get_ctx(memdev))) {
> + jobj =
> json_object_new_boolean(cxl_memdev_has_poison_injection(memdev));
> + if (jobj)
> + json_object_object_add(jdev, "poison_injectable", jobj);
> + }
> +
> if (flags & UTIL_JSON_PARTITION) {
> jobj = util_cxl_memdev_partition_to_json(memdev, flags);
> if (jobj)
> @@ -691,6 +697,7 @@ void util_cxl_dports_append_json(struct json_object
> *jport,
> {
> struct json_object *jobj, *jdports;
> struct cxl_dport *dport;
> + char *einj_path;
> int val;
>
> val = cxl_port_get_nr_dports(port);
> @@ -739,6 +746,13 @@ void util_cxl_dports_append_json(struct json_object
> *jport,
> if (jobj)
> json_object_object_add(jdport, "id", jobj);
>
> + einj_path = cxl_dport_get_einj_path(dport);
> + jobj = json_object_new_boolean(einj_path != NULL);
> + if (jobj)
> + json_object_object_add(jdport, "protocol_injectable",
> + jobj);
> + free(einj_path);
> +
> json_object_array_add(jdports, jdport);
> json_object_set_userdata(jdport, dport, NULL);
> }
> @@ -750,6 +764,8 @@ struct json_object *util_cxl_bus_to_json(struct cxl_bus
> *bus,
> unsigned long flags)
> {
> const char *devname = cxl_bus_get_devname(bus);
> + struct cxl_ctx *ctx = cxl_bus_get_ctx(bus);
> + struct cxl_protocol_error *perror;
> struct json_object *jbus, *jobj;
>
> jbus = json_object_new_object();
> @@ -765,6 +781,28 @@ struct json_object *util_cxl_bus_to_json(struct cxl_bus
> *bus,
> json_object_object_add(jbus, "provider", jobj);
>
> json_object_set_userdata(jbus, bus, NULL);
> +
> + if (cxl_debugfs_exists(ctx)) {
> + jobj = json_object_new_array();
> + if (!jobj)
> + return jbus;
> +
> + cxl_protocol_error_foreach(ctx, perror)
> + {
> + struct json_object *jerr_str;
> + const char *perror_str;
> +
> + perror_str = cxl_protocol_error_get_str(perror);
> +
> + jerr_str = json_object_new_string(perror_str);
> + if (jerr_str)
> + json_object_array_add(jobj, jerr_str);
> + }
> +
> + json_object_object_add(jbus, "injectable_protocol_errors",
> + jobj);
> + }
> +
> return jbus;
> }
>
> diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
> index deebf7f..f824701 100644
> --- a/cxl/lib/libcxl.c
> +++ b/cxl/lib/libcxl.c
> @@ -285,6 +285,11 @@ static char* get_cxl_debugfs_dir(void)
> return debugfs_dir;
> }
>
> +CXL_EXPORT bool cxl_debugfs_exists(struct cxl_ctx *ctx)
> +{
> + return ctx->cxl_debugfs != NULL;
> +}
> +
> /**
> * cxl_new - instantiate a new library context
> * @ctx: context to establish
> @@ -3567,38 +3572,49 @@ cxl_protocol_error_get_str(struct cxl_protocol_error
> *perror)
> return perror->string;
> }
>
> -CXL_EXPORT int cxl_dport_protocol_error_inject(struct cxl_dport *dport,
> - unsigned int error)
> +CXL_EXPORT char *cxl_dport_get_einj_path(struct cxl_dport *dport)
> {
> struct cxl_ctx *ctx = dport->port->ctx;
> - char buf[32] = { 0 };
> size_t path_len, len;
> char *path;
> int rc;
>
> - if (!ctx->cxl_debugfs)
> - return -ENOENT;
> -
> path_len = strlen(ctx->cxl_debugfs) + 100;
> path = calloc(path_len, sizeof(char));
> if (!path)
> - return -ENOMEM;
> + return NULL;
>
> len = snprintf(path, path_len, "%s/%s/einj_inject", ctx->cxl_debugfs,
> cxl_dport_get_devname(dport));
> if (len >= path_len) {
> err(ctx, "%s: buffer too small\n",
> cxl_dport_get_devname(dport));
> free(path);
> - return -ENOMEM;
> + return NULL;
> }
>
> rc = access(path, F_OK);
> if (rc) {
> err(ctx, "failed to access %s: %s\n", path, strerror(errno));
> free(path);
> - return -errno;
> + return NULL;
> }
>
> + return path;
> +}
> +
> +CXL_EXPORT int cxl_dport_protocol_error_inject(struct cxl_dport *dport,
> + unsigned int error)
> +{
> + struct cxl_ctx *ctx = dport->port->ctx;
> + char buf[32] = { 0 };
> + char *path;
> + size_t len;
> + int rc;
> +
> + path = cxl_dport_get_einj_path(dport);
> + if (!path)
> + return -ENOENT;
> +
> len = snprintf(buf, sizeof(buf), "0x%x\n", error);
> if (len >= sizeof(buf)) {
> err(ctx, "%s: buffer too small\n",
> cxl_dport_get_devname(dport));
> diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
> index c636edb..ebca543 100644
> --- a/cxl/lib/libcxl.sym
> +++ b/cxl/lib/libcxl.sym
> @@ -308,8 +308,10 @@ global:
> cxl_protocol_error_get_next;
> cxl_protocol_error_get_num;
> cxl_protocol_error_get_str;
> + cxl_dport_get_einj_path;
> cxl_dport_protocol_error_inject;
> cxl_memdev_has_poison_injection;
> cxl_memdev_inject_poison;
> cxl_memdev_clear_poison;
> + cxl_debugfs_exists;
> } LIBCXL_10;
> diff --git a/cxl/libcxl.h b/cxl/libcxl.h
> index 4d035f0..e390aca 100644
> --- a/cxl/libcxl.h
> +++ b/cxl/libcxl.h
> @@ -32,6 +32,7 @@ void cxl_set_userdata(struct cxl_ctx *ctx, void *userdata);
> 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);
> +bool cxl_debugfs_exists(struct cxl_ctx *ctx);
>
> enum cxl_fwl_status {
> CXL_FWL_STATUS_UNKNOWN,
> @@ -507,6 +508,7 @@ struct cxl_protocol_error *
> cxl_protocol_error_get_next(struct cxl_protocol_error *perror);
> unsigned int cxl_protocol_error_get_num(struct cxl_protocol_error *perror);
> const char *cxl_protocol_error_get_str(struct cxl_protocol_error *perror);
> +char *cxl_dport_get_einj_path(struct cxl_dport *dport);
> int cxl_dport_protocol_error_inject(struct cxl_dport *dport,
> unsigned int error);
>