On 10/23/25 1:15 PM, Cheatham, Benjamin wrote:
> On 10/22/2025 12:06 PM, Dave Jiang wrote:
>>
>>
>> On 10/21/25 11:31 AM, Ben Cheatham wrote:
>>> 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 <[email protected]>
>>> ---
>>> cxl/builtin.h | 1 +
>>> cxl/cxl.c | 1 +
>>> cxl/inject-error.c | 195 +++++++++++++++++++++++++++++++++++++++++++++
>>> cxl/meson.build | 1 +
>>> 4 files changed, 198 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..c48ea69
>>> --- /dev/null
>>> +++ b/cxl/inject-error.c
>>> @@ -0,0 +1,195 @@
>>> +// 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;
>>
>> Would it be worthwhile to create a util_cxl_dport_filter()?
>>
>
> Yeah probably. I'll make one for the next revision.
>
>>> + }
>>> +
>>> + 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));
>>
>> log_info() maybe?
>
> I think I had it as log_info() before, but I don't think it was making it's
> way to
> the console. I think I wanted the console output because I personally don't
> like running
> silent commands. Not a great reason, so I'm fine with changing it if that's
> the preferred
> way.
>
Alison,
Do you have a preference?
DJ
>>
>>> + return 0;
>>> +}
>>> +
>>> +static int poison_action(struct cxl_ctx *ctx, const char *filter,
>>> + const char *addr)
>>> +{
>>> + struct cxl_memdev *memdev;
>>> + size_t a;
>>
>> Maybe rename 'addr' to 'addr_str' and rename 'a' to 'addr'
>>
>
> Sure.
>
>>> + 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);
>>> +
>>
>> unnecessary blank line> + if (rc)
>
> Will remove!
>
>>> + 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);
>>
>> log_info() maybe?
>
> Same thing as above.
>
> Thanks,
> Ben
>
>>
>> DJ
>>