Add the 'cxl-inject-error' command. This command will provide CXL protocol error injection for CXL VH root ports and CXL RCH downstream ports, as well as poison injection for CXL memory devices.
Signed-off-by: Ben Cheatham <benjamin.cheat...@amd.com> --- cxl/builtin.h | 1 + cxl/cxl.c | 1 + cxl/inject-error.c | 196 +++++++++++++++++++++++++++++++++++++++++++++ cxl/meson.build | 1 + 4 files changed, 199 insertions(+) create mode 100644 cxl/inject-error.c diff --git a/cxl/builtin.h b/cxl/builtin.h index c483f30..e82fcb5 100644 --- a/cxl/builtin.h +++ b/cxl/builtin.h @@ -25,6 +25,7 @@ int cmd_create_region(int argc, const char **argv, struct cxl_ctx *ctx); int cmd_enable_region(int argc, const char **argv, struct cxl_ctx *ctx); int cmd_disable_region(int argc, const char **argv, struct cxl_ctx *ctx); int cmd_destroy_region(int argc, const char **argv, struct cxl_ctx *ctx); +int cmd_inject_error(int argc, const char **argv, struct cxl_ctx *ctx); #ifdef ENABLE_LIBTRACEFS int cmd_monitor(int argc, const char **argv, struct cxl_ctx *ctx); #else diff --git a/cxl/cxl.c b/cxl/cxl.c index 1643667..a98bd6b 100644 --- a/cxl/cxl.c +++ b/cxl/cxl.c @@ -80,6 +80,7 @@ static struct cmd_struct commands[] = { { "disable-region", .c_fn = cmd_disable_region }, { "destroy-region", .c_fn = cmd_destroy_region }, { "monitor", .c_fn = cmd_monitor }, + { "inject-error", .c_fn = cmd_inject_error }, }; int main(int argc, const char **argv) diff --git a/cxl/inject-error.c b/cxl/inject-error.c new file mode 100644 index 0000000..bc46f82 --- /dev/null +++ b/cxl/inject-error.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 AMD. All rights reserved. */ +#include <util/parse-options.h> +#include <cxl/libcxl.h> +#include <cxl/filter.h> +#include <util/log.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <limits.h> + +#define EINJ_TYPES_BUF_SIZE 512 + +static bool debug; + +static struct inject_params { + const char *type; + const char *address; +} inj_param; + +static const struct option inject_options[] = { + OPT_STRING('t', "type", &inj_param.type, "Error type", + "Error type to inject into <device>"), + OPT_STRING('a', "address", &inj_param.address, "Address for poison injection", + "Device physical address for poison injection in hex or decimal"), +#ifdef ENABLE_DEBUG + OPT_BOOLEAN(0, "debug", &debug, "turn on debug output"), +#endif + OPT_END(), +}; + +static struct log_ctx iel; + +static struct cxl_protocol_error *find_cxl_proto_err(struct cxl_ctx *ctx, + const char *type) +{ + struct cxl_protocol_error *perror; + + cxl_protocol_error_foreach(ctx, perror) { + if (strcmp(type, cxl_protocol_error_get_str(perror)) == 0) + return perror; + } + + log_err(&iel, "Invalid CXL protocol error type: %s\n", type); + return NULL; +} + +static struct cxl_dport *find_cxl_dport(struct cxl_ctx *ctx, const char *devname) +{ + struct cxl_port *port, *top; + struct cxl_dport *dport; + struct cxl_bus *bus; + + cxl_bus_foreach(ctx, bus) { + top = cxl_bus_get_port(bus); + + cxl_port_foreach_all(top, port) + cxl_dport_foreach(port, dport) + if (!strcmp(devname, + cxl_dport_get_devname(dport))) + return dport; + } + + log_err(&iel, "Downstream port \"%s\" not found\n", devname); + return NULL; +} + +static struct cxl_memdev *find_cxl_memdev(struct cxl_ctx *ctx, + const char *filter) +{ + struct cxl_memdev *memdev; + + cxl_memdev_foreach(ctx, memdev) { + if (util_cxl_memdev_filter(memdev, filter, NULL)) + return memdev; + } + + log_err(&iel, "Memdev \"%s\" not found\n", filter); + return NULL; +} + +static int inject_proto_err(struct cxl_ctx *ctx, const char *devname, + struct cxl_protocol_error *perror) +{ + struct cxl_dport *dport; + int rc; + + if (!devname) { + log_err(&iel, "No downstream port specified for injection\n"); + return -EINVAL; + } + + dport = find_cxl_dport(ctx, devname); + if (!dport) + return -ENODEV; + + rc = cxl_dport_protocol_error_inject(dport, + cxl_protocol_error_get_num(perror)); + if (rc) + return rc; + + printf("injected %s protocol error.\n", + cxl_protocol_error_get_str(perror)); + return 0; +} + +static int poison_action(struct cxl_ctx *ctx, const char *filter, + const char *addr) +{ + struct cxl_memdev *memdev; + size_t a; + int rc; + + memdev = find_cxl_memdev(ctx, filter); + if (!memdev) + return -ENODEV; + + if (!cxl_memdev_has_poison_injection(memdev)) { + log_err(&iel, "%s does not support error injection\n", + cxl_memdev_get_devname(memdev)); + return -EINVAL; + } + + if (!addr) { + log_err(&iel, "no address provided\n"); + return -EINVAL; + } + + a = strtoull(addr, NULL, 0); + if (a == ULLONG_MAX && errno == ERANGE) { + log_err(&iel, "invalid address %s", addr); + return -EINVAL; + } + + rc = cxl_memdev_inject_poison(memdev, a); + + if (rc) + log_err(&iel, "failed to inject poison at %s:%s: %s\n", + cxl_memdev_get_devname(memdev), addr, strerror(-rc)); + else + printf("poison injected at %s:%s\n", + cxl_memdev_get_devname(memdev), addr); + + return rc; +} + +static int inject_action(int argc, const char **argv, struct cxl_ctx *ctx, + const struct option *options, const char *usage) +{ + struct cxl_protocol_error *perr; + const char * const u[] = { + usage, + NULL + }; + int rc = -EINVAL; + + log_init(&iel, "cxl inject-error", "CXL_INJECT_LOG"); + argc = parse_options(argc, argv, options, u, 0); + + if (debug) { + cxl_set_log_priority(ctx, LOG_DEBUG); + iel.log_priority = LOG_DEBUG; + } else { + iel.log_priority = LOG_INFO; + } + + if (argc != 1) { + usage_with_options(u, options); + return rc; + } + + if (strcmp(inj_param.type, "poison") == 0) { + rc = poison_action(ctx, argv[0], inj_param.address); + return rc; + } + + perr = find_cxl_proto_err(ctx, inj_param.type); + if (perr) { + rc = inject_proto_err(ctx, argv[0], perr); + if (rc) + log_err(&iel, "Failed to inject error: %d\n", rc); + } + + log_err(&iel, "Invalid error type %s", inj_param.type); + return rc; +} + +int cmd_inject_error(int argc, const char **argv, struct cxl_ctx *ctx) +{ + int rc = inject_action(argc, argv, ctx, inject_options, + "inject-error <device> [<options>]"); + + return rc ? EXIT_FAILURE : EXIT_SUCCESS; +} + diff --git a/cxl/meson.build b/cxl/meson.build index e4d1683..29918e4 100644 --- a/cxl/meson.build +++ b/cxl/meson.build @@ -7,6 +7,7 @@ cxl_src = [ 'memdev.c', 'json.c', 'filter.c', + 'inject-error.c', '../daxctl/json.c', '../daxctl/filter.c', ] -- 2.34.1