From: Ben Cheatham <Benjamin.Cheatham@amd.com>
To: <nvdimm@lists.linux.dev>, <alison.schofield@intel.com>,
<dave.jiang@intel.com>
Cc: <linux-cxl@vger.kernel.org>, <benjamin.cheatham@amd.com>
Subject: [PATCH 2/7] libcxl: Add CXL protocol errors
Date: Fri, 9 Jan 2026 10:07:15 -0600 [thread overview]
Message-ID: <20260109160720.1823-3-Benjamin.Cheatham@amd.com> (raw)
In-Reply-To: <20260109160720.1823-1-Benjamin.Cheatham@amd.com>
The v6.11 Linux kernel adds CXL protocl (CXL.cache & CXL.mem) error
injection for platforms that implement the error types as according to
the v6.5+ ACPI specification. The interface for injecting these errors
are provided by the kernel under the CXL debugfs. The relevant files in
the interface are the einj_types file, which provides the available CXL
error types for injection, and the einj_inject file, which injects the
error into a CXL VH root port or CXL RCH downstream port.
Add a library API to retrieve the CXL error types and inject them. This
API will be used in a later commit by the 'cxl-inject-error' and
'cxl-list' commands.
Signed-off-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
---
cxl/lib/libcxl.c | 194 +++++++++++++++++++++++++++++++++++++++++++++
cxl/lib/libcxl.sym | 5 ++
cxl/lib/private.h | 14 ++++
cxl/libcxl.h | 13 +++
4 files changed, 226 insertions(+)
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
index 6b7e92c..27ff037 100644
--- a/cxl/lib/libcxl.c
+++ b/cxl/lib/libcxl.c
@@ -48,11 +48,13 @@ struct cxl_ctx {
void *userdata;
int memdevs_init;
int buses_init;
+ int perrors_init;
unsigned long timeout;
struct udev *udev;
struct udev_queue *udev_queue;
struct list_head memdevs;
struct list_head buses;
+ struct list_head perrors;
struct kmod_ctx *kmod_ctx;
struct daxctl_ctx *daxctl_ctx;
void *private_data;
@@ -207,6 +209,14 @@ static void free_bus(struct cxl_bus *bus, struct list_head *head)
free(bus);
}
+static void free_protocol_error(struct cxl_protocol_error *perror,
+ struct list_head *head)
+{
+ if (head)
+ list_del_from(head, &perror->list);
+ free(perror);
+}
+
/**
* cxl_get_userdata - retrieve stored data pointer from library context
* @ctx: cxl library context
@@ -325,6 +335,7 @@ CXL_EXPORT int cxl_new(struct cxl_ctx **ctx)
*ctx = c;
list_head_init(&c->memdevs);
list_head_init(&c->buses);
+ list_head_init(&c->perrors);
c->kmod_ctx = kmod_ctx;
c->daxctl_ctx = daxctl_ctx;
c->udev = udev;
@@ -366,6 +377,7 @@ CXL_EXPORT struct cxl_ctx *cxl_ref(struct cxl_ctx *ctx)
*/
CXL_EXPORT void cxl_unref(struct cxl_ctx *ctx)
{
+ struct cxl_protocol_error *perror, *_p;
struct cxl_memdev *memdev, *_d;
struct cxl_bus *bus, *_b;
@@ -381,6 +393,9 @@ CXL_EXPORT void cxl_unref(struct cxl_ctx *ctx)
list_for_each_safe(&ctx->buses, bus, _b, port.list)
free_bus(bus, &ctx->buses);
+ list_for_each_safe(&ctx->perrors, perror, _p, list)
+ free_protocol_error(perror, &ctx->perrors);
+
udev_queue_unref(ctx->udev_queue);
udev_unref(ctx->udev);
kmod_unref(ctx->kmod_ctx);
@@ -3423,6 +3438,185 @@ CXL_EXPORT int cxl_port_decoders_committed(struct cxl_port *port)
return port->decoders_committed;
}
+const struct cxl_protocol_error cxl_protocol_errors[] = {
+ CXL_PROTOCOL_ERROR(0x1000, "cache-correctable"),
+ CXL_PROTOCOL_ERROR(0x2000, "cache-uncorrectable"),
+ CXL_PROTOCOL_ERROR(0x4000, "cache-fatal"),
+ CXL_PROTOCOL_ERROR(0x8000, "mem-correctable"),
+ CXL_PROTOCOL_ERROR(0x10000, "mem-uncorrectable"),
+ CXL_PROTOCOL_ERROR(0x20000, "mem-fatal")
+};
+
+static struct cxl_protocol_error *create_cxl_protocol_error(struct cxl_ctx *ctx,
+ unsigned int n)
+{
+ struct cxl_protocol_error *perror;
+
+ for (unsigned long i = 0; i < ARRAY_SIZE(cxl_protocol_errors); i++) {
+ if (n != cxl_protocol_errors[i].num)
+ continue;
+
+ perror = calloc(1, sizeof(*perror));
+ if (!perror)
+ return NULL;
+
+ *perror = cxl_protocol_errors[i];
+ perror->ctx = ctx;
+ return perror;
+ }
+
+ return NULL;
+}
+
+static void cxl_add_protocol_errors(struct cxl_ctx *ctx)
+{
+ struct cxl_protocol_error *perror;
+ char buf[SYSFS_ATTR_SIZE];
+ char *path, *num, *save;
+ size_t path_len, len;
+ unsigned long n;
+ int rc = 0;
+
+ if (!ctx->cxl_debugfs)
+ return;
+
+ path_len = strlen(ctx->cxl_debugfs) + 100;
+ path = calloc(1, path_len);
+ if (!path)
+ return;
+
+ len = snprintf(path, path_len, "%s/einj_types", ctx->cxl_debugfs);
+ if (len >= path_len) {
+ err(ctx, "Buffer too small\n");
+ goto err;
+ }
+
+ rc = access(path, F_OK);
+ if (rc) {
+ err(ctx, "failed to access %s: %s\n", path, strerror(errno));
+ goto err;
+ }
+
+ rc = sysfs_read_attr(ctx, path, buf);
+ if (rc) {
+ err(ctx, "failed to read %s: %s\n", path, strerror(-rc));
+ goto err;
+ }
+
+ /*
+ * The format of the output of the einj_types attr is:
+ * <Error number in hex 1> <Error name 1>
+ * <Error number in hex 2> <Error name 2>
+ * ...
+ *
+ * We only need the number, so parse that and skip the rest of
+ * the line.
+ */
+ num = strtok_r(buf, " \n", &save);
+ while (num) {
+ n = strtoul(num, NULL, 16);
+ perror = create_cxl_protocol_error(ctx, n);
+ if (perror)
+ list_add_tail(&ctx->perrors, &perror->list);
+
+ num = strtok_r(NULL, "\n", &save);
+ if (!num)
+ break;
+
+ num = strtok_r(NULL, " \n", &save);
+ }
+
+err:
+ free(path);
+}
+
+static void cxl_protocol_errors_init(struct cxl_ctx *ctx)
+{
+ if (ctx->perrors_init)
+ return;
+
+ ctx->perrors_init = 1;
+ cxl_add_protocol_errors(ctx);
+}
+
+CXL_EXPORT struct cxl_protocol_error *
+cxl_protocol_error_get_first(struct cxl_ctx *ctx)
+{
+ cxl_protocol_errors_init(ctx);
+
+ return list_top(&ctx->perrors, struct cxl_protocol_error, list);
+}
+
+CXL_EXPORT struct cxl_protocol_error *
+cxl_protocol_error_get_next(struct cxl_protocol_error *perror)
+{
+ struct cxl_ctx *ctx = perror->ctx;
+
+ return list_next(&ctx->perrors, perror, list);
+}
+
+CXL_EXPORT unsigned int
+cxl_protocol_error_get_num(struct cxl_protocol_error *perror)
+{
+ return perror->num;
+}
+
+CXL_EXPORT const char *
+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)
+{
+ 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;
+
+ 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;
+ }
+
+ rc = access(path, F_OK);
+ if (rc) {
+ err(ctx, "failed to access %s: %s\n", path, strerror(errno));
+ free(path);
+ return -errno;
+ }
+
+ 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));
+ free(path);
+ return -ENOMEM;
+ }
+
+ rc = sysfs_write_attr(ctx, path, buf);
+ if (rc) {
+ err(ctx, "failed to write %s: %s\n", path, strerror(-rc));
+ free(path);
+ return -errno;
+ }
+
+ free(path);
+ return 0;
+}
+
static void *add_cxl_bus(void *parent, int id, const char *cxlbus_base)
{
const char *devname = devpath_to_devname(cxlbus_base);
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
index 36a93c3..c683b83 100644
--- a/cxl/lib/libcxl.sym
+++ b/cxl/lib/libcxl.sym
@@ -304,4 +304,9 @@ global:
LIBCXL_11 {
global:
cxl_region_get_extended_linear_cache_size;
+ cxl_protocol_error_get_first;
+ cxl_protocol_error_get_next;
+ cxl_protocol_error_get_num;
+ cxl_protocol_error_get_str;
+ cxl_dport_protocol_error_inject;
} LIBCXL_10;
diff --git a/cxl/lib/private.h b/cxl/lib/private.h
index 542cdb7..582eebf 100644
--- a/cxl/lib/private.h
+++ b/cxl/lib/private.h
@@ -108,6 +108,20 @@ struct cxl_port {
struct list_head dports;
};
+struct cxl_protocol_error {
+ unsigned int num;
+ const char *string;
+ struct cxl_ctx *ctx;
+ struct list_node list;
+};
+
+#define CXL_PROTOCOL_ERROR(n, str) \
+ ((struct cxl_protocol_error){ \
+ .num = (n), \
+ .string = (str), \
+ .ctx = NULL, \
+ })
+
struct cxl_bus {
struct cxl_port port;
};
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
index 9371aac..faef62e 100644
--- a/cxl/libcxl.h
+++ b/cxl/libcxl.h
@@ -498,6 +498,19 @@ int cxl_cmd_alert_config_set_enable_alert_actions(struct cxl_cmd *cmd,
int enable);
struct cxl_cmd *cxl_cmd_new_set_alert_config(struct cxl_memdev *memdev);
+struct cxl_protocol_error;
+struct cxl_protocol_error *cxl_protocol_error_get_first(struct cxl_ctx *ctx);
+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);
+int cxl_dport_protocol_error_inject(struct cxl_dport *dport,
+ unsigned int error);
+
+#define cxl_protocol_error_foreach(ctx, perror) \
+ for (perror = cxl_protocol_error_get_first(ctx); perror != NULL; \
+ perror = cxl_protocol_error_get_next(perror))
+
#ifdef __cplusplus
} /* extern "C" */
#endif
--
2.52.0
next prev parent reply other threads:[~2026-01-09 16:07 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-09 16:07 [ndctl PATCH v6 0/7] Add error injection support Ben Cheatham
2026-01-09 16:07 ` [PATCH 1/7] libcxl: Add debugfs path to CXL context Ben Cheatham
2026-01-09 17:43 ` Dave Jiang
2026-01-09 16:07 ` Ben Cheatham [this message]
2026-01-09 17:54 ` [PATCH 2/7] libcxl: Add CXL protocol errors Dave Jiang
2026-01-12 17:20 ` Cheatham, Benjamin
2026-01-09 16:07 ` [PATCH 3/7] libcxl: Add poison injection support Ben Cheatham
2026-01-09 18:03 ` Dave Jiang
2026-01-12 17:20 ` Cheatham, Benjamin
2026-01-09 16:07 ` [PATCH 4/7] cxl: Add inject-error command Ben Cheatham
2026-01-09 21:53 ` Dave Jiang
2026-01-12 17:20 ` Cheatham, Benjamin
2026-01-09 16:07 ` [PATCH 5/7] cxl: Add clear-error command Ben Cheatham
2026-01-09 22:12 ` Dave Jiang
2026-01-09 16:07 ` [PATCH 6/7] cxl/list: Add injectable errors in output Ben Cheatham
2026-01-09 22:17 ` Dave Jiang
2026-01-09 16:07 ` [PATCH 7/7] Documentation: Add docs for inject/clear-error commands Ben Cheatham
2026-01-09 22:25 ` Dave Jiang
-- strict thread matches above, loose matches on Subject: below --
2026-01-22 20:37 [ndctl PATCH v7 0/7] Add error injection support Ben Cheatham
2026-01-22 20:37 ` [PATCH 2/7] libcxl: Add CXL protocol errors Ben Cheatham
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260109160720.1823-3-Benjamin.Cheatham@amd.com \
--to=benjamin.cheatham@amd.com \
--cc=alison.schofield@intel.com \
--cc=dave.jiang@intel.com \
--cc=linux-cxl@vger.kernel.org \
--cc=nvdimm@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox