From: Ben Cheatham <Benjamin.Cheatham@amd.com>
To: <nvdimm@lists.linux.dev>
Cc: <linux-cxl@vger.kernel.org>, <benjamin.cheatham@amd.com>,
<alison.schofield@intel.com>
Subject: [PATCH 2/6] libcxl: Add CXL protocol errors
Date: Thu, 24 Apr 2025 16:23:57 -0500 [thread overview]
Message-ID: <20250424212401.14789-3-Benjamin.Cheatham@amd.com> (raw)
In-Reply-To: <20250424212401.14789-1-Benjamin.Cheatham@amd.com>
The v6.11 kernel adds CXL protocol (CXL.cache/CXL.mem) error injection
for platforms that implement the v6.5+ ACPI specification. These errors
are reported by the kernel through the einj_types file and injected
through the einj_inject file under the relevant CXL RCH dport or VH root
port.
Add a library API to retreive the CXL error types and inject them. This
API will be used in a later commit by the cxl inject-error and list
commands.
Signed-off-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
---
cxl/lib/libcxl.c | 166 +++++++++++++++++++++++++++++++++++++++++++++
cxl/lib/libcxl.sym | 5 ++
cxl/lib/private.h | 14 ++++
cxl/libcxl.h | 13 ++++
4 files changed, 198 insertions(+)
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
index e86d00f..408b2a3 100644
--- a/cxl/lib/libcxl.c
+++ b/cxl/lib/libcxl.c
@@ -46,11 +46,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;
@@ -204,6 +206,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
@@ -290,6 +300,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;
@@ -331,6 +342,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;
@@ -346,6 +358,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);
@@ -3272,6 +3287,157 @@ CXL_EXPORT void cxl_set_debugfs(struct cxl_ctx *ctx, const char *debugfs)
ctx->debugfs = debugfs;
}
+const struct cxl_protocol_error cxl_protocol_errors[] = {
+ CXL_PROTOCOL_ERROR(12, "cache-correctable"),
+ CXL_PROTOCOL_ERROR(13, "cache-uncorrectable"),
+ CXL_PROTOCOL_ERROR(14, "cache-fatal"),
+ CXL_PROTOCOL_ERROR(15, "mem-correctable"),
+ CXL_PROTOCOL_ERROR(16, "mem-uncorrectable"),
+ CXL_PROTOCOL_ERROR(17, "mem-fatal")
+};
+
+static struct cxl_protocol_error *create_cxl_protocol_error(struct cxl_ctx *ctx,
+ unsigned long n)
+{
+ struct cxl_protocol_error *perror;
+
+ for (unsigned long i = 0; i < ARRAY_SIZE(cxl_protocol_errors); i++) {
+ if (n != BIT(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)
+{
+ size_t path_len = strlen(ctx->debugfs) + 30;
+ struct cxl_protocol_error *perror;
+ char *path, *num, *save;
+ unsigned long n;
+ char buf[512];
+ int rc = 0;
+
+ path = calloc(1, path_len);
+ if (!path)
+ return;
+
+ snprintf(path, path_len, "%s/cxl/einj_types", ctx->debugfs);
+ rc = access(path, F_OK);
+ if (rc) {
+ err(ctx, "failed to access %s: %s\n", path, strerror(-rc));
+ 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(&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 long
+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 long error)
+{
+ struct cxl_ctx *ctx = dport->port->ctx;
+ unsigned long path_len = strlen(ctx->debugfs) + 100;
+ char buf[32] = { 0 };
+ char *path;
+ int rc;
+
+ path = calloc(path_len, sizeof(char));
+ if (!path)
+ return -ENOMEM;
+
+ snprintf(path, path_len, "%s/cxl/%s/einj_inject", ctx->debugfs,
+ cxl_dport_get_devname(dport));
+ rc = access(path, F_OK);
+ if (rc) {
+ err(ctx, "failed to access %s: %s\n", path, strerror(-rc));
+ free(path);
+ return rc;
+ }
+
+ snprintf(buf, sizeof(buf), "0x%lx\n", error);
+ rc = sysfs_write_attr(ctx, path, buf);
+ if (rc)
+ err(ctx, "failed to write %s: %s\n", path, strerror(-rc));
+
+ free(path);
+ return rc;
+}
+
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 61553c0..a0ab86d 100644
--- a/cxl/lib/libcxl.sym
+++ b/cxl/lib/libcxl.sym
@@ -291,4 +291,9 @@ global:
LIBCXL_9 {
global:
cxl_set_debugfs;
+ 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;
} LIBECXL_8;
diff --git a/cxl/lib/private.h b/cxl/lib/private.h
index b6cd910..85806ac 100644
--- a/cxl/lib/private.h
+++ b/cxl/lib/private.h
@@ -102,6 +102,20 @@ struct cxl_port {
struct list_head dports;
};
+struct cxl_protocol_error {
+ unsigned long 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 f3f11ad..f8b2aff 100644
--- a/cxl/libcxl.h
+++ b/cxl/libcxl.h
@@ -487,6 +487,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 long 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 long 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.34.1
next prev parent reply other threads:[~2025-04-24 21:24 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-04-24 21:23 [ndctl PATCH 0/6] Add error injection support Ben Cheatham
2025-04-24 21:23 ` [PATCH 1/6] libcxl: Add debugfs path to CXL context Ben Cheatham
2025-04-24 21:23 ` Ben Cheatham [this message]
2025-04-24 21:23 ` [PATCH 3/6] libcxl: Add poison injection functions Ben Cheatham
2025-04-24 21:23 ` [PATCH 4/6] cxl/list: Add debugfs option Ben Cheatham
2025-04-24 21:24 ` [PATCH 5/6] cxl/list: Add injectable-errors option Ben Cheatham
2025-04-24 21:24 ` [PATCH 6/6] cxl: Add inject-error command Ben Cheatham
2025-04-29 2:35 ` [ndctl PATCH 0/6] Add error injection support Alison Schofield
2025-04-29 20:01 ` Ben Cheatham
2025-04-30 0:53 ` Alison Schofield
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=20250424212401.14789-3-Benjamin.Cheatham@amd.com \
--to=benjamin.cheatham@amd.com \
--cc=alison.schofield@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.