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.cheat...@amd.com>
---
 cxl/lib/libcxl.c   | 174 +++++++++++++++++++++++++++++++++++++++++++++
 cxl/lib/libcxl.sym |   9 +++
 cxl/lib/private.h  |  14 ++++
 cxl/libcxl.h       |  13 ++++
 4 files changed, 210 insertions(+)

diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
index 3e0aa5c..0403fa9 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
@@ -327,6 +337,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;
@@ -368,6 +379,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;
 
@@ -383,6 +395,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);
@@ -3305,6 +3320,165 @@ 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(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)
+{
+       struct cxl_protocol_error *perror;
+       char *path, *num, *save;
+       unsigned long n;
+       size_t path_len;
+       char buf[512];
+       int rc = 0;
+
+       if (!ctx->debugfs)
+               return;
+
+       path_len = strlen(ctx->debugfs) + 100;
+       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;
+       char buf[32] = { 0 };
+       char *path;
+       int rc;
+
+       if (!ctx->debugfs)
+               return -ENOENT;
+
+       path_len = strlen(ctx->debugfs) + 100;
+       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 763151f..61ed0db 100644
--- a/cxl/lib/libcxl.sym
+++ b/cxl/lib/libcxl.sym
@@ -287,3 +287,12 @@ global:
        cxl_memdev_trigger_poison_list;
        cxl_region_trigger_poison_list;
 } LIBCXL_7;
+
+LIBCXL_9 {
+global:
+       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 43c082a..afa076a 100644
--- a/cxl/libcxl.h
+++ b/cxl/libcxl.h
@@ -486,6 +486,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


Reply via email to