From 8a35aa8fd3e1db06228329a0ca900ce246ca329e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 28 Apr 2022 15:10:27 -0700 Subject: [PATCH 159/217] cxl/bus: Add bus disable support Route requests to disable the root back to unbinding the platform firmware device, ACPI0017 for ACPI.CXL platforms. Link: https://lore.kernel.org/r/165118382738.1676208.16851880881648171660.stgit@dwillia2-desk3.amr.corp.intel.com Signed-off-by: Dan Williams Signed-off-by: Vishal Verma --- Documentation/cxl/cxl-disable-bus.txt | 37 ++++++ Documentation/cxl/lib/libcxl.txt | 12 ++ Documentation/cxl/meson.build | 1 + cxl/builtin.h | 1 + cxl/bus.c | 159 ++++++++++++++++++++++++++ cxl/cxl.c | 1 + cxl/filter.c | 3 +- cxl/filter.h | 1 + cxl/lib/libcxl.c | 15 +++ cxl/lib/libcxl.sym | 1 + cxl/libcxl.h | 1 + cxl/meson.build | 1 + 12 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 Documentation/cxl/cxl-disable-bus.txt create mode 100644 cxl/bus.c diff --git a/Documentation/cxl/cxl-disable-bus.txt b/Documentation/cxl/cxl-disable-bus.txt new file mode 100644 index 0000000..65f695c --- /dev/null +++ b/Documentation/cxl/cxl-disable-bus.txt @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 + +cxl-disable-bus(1) +=================== + +NAME +---- +cxl-disable-bus - Shutdown an entire tree of CXL devices + +SYNOPSIS +-------- +[verse] +'cxl disable-bus' [..] [] + +For test and debug scenarios, disable a CXL bus and any associated +memory devices from CXL.mem operations. + +OPTIONS +------- +-f:: +--force:: + DANGEROUS: Override the safety measure that blocks attempts to disable a + bus if the tool determines a descendent memdev is in active usage. + Recall that CXL memory ranges might have been established by platform + firmware and disabling an active device is akin to force removing memory + from a running system. + +--debug:: + If the cxl tool was built with debug disabled, turn on debug + messages. + + +include::../copyright.txt[] + +SEE ALSO +-------- +linkcxl:cxl-disable-port[1] diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt index 7b223cb..f8f0e66 100644 --- a/Documentation/cxl/lib/libcxl.txt +++ b/Documentation/cxl/lib/libcxl.txt @@ -216,6 +216,18 @@ discovery order. The possible provider names are 'ACPI.CXL' and the kernel device names that are subject to change based on discovery order. +=== BUS: Control +---- +int cxl_bus_disable_invalidate(struct cxl_bus *bus); +---- + +An entire CXL topology can be torn down with this API. Like other +_invalidate APIs callers must assume that all library objects have been +freed. This one goes one step further and also frees the @bus argument. +This may crash the system and is only useful in kernel driver +development scenarios. + + PORTS ----- CXL ports track the PCIe hierarchy between a platform firmware CXL root diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build index e927644..974a5a4 100644 --- a/Documentation/cxl/meson.build +++ b/Documentation/cxl/meson.build @@ -34,6 +34,7 @@ cxl_manpages = [ 'cxl-disable-memdev.txt', 'cxl-enable-port.txt', 'cxl-disable-port.txt', + 'cxl-disable-bus.txt', 'cxl-set-partition.txt', ] diff --git a/cxl/builtin.h b/cxl/builtin.h index 7bbad98..a437bc3 100644 --- a/cxl/builtin.h +++ b/cxl/builtin.h @@ -15,4 +15,5 @@ int cmd_enable_memdev(int argc, const char **argv, struct cxl_ctx *ctx); int cmd_disable_port(int argc, const char **argv, struct cxl_ctx *ctx); int cmd_enable_port(int argc, const char **argv, struct cxl_ctx *ctx); int cmd_set_partition(int argc, const char **argv, struct cxl_ctx *ctx); +int cmd_disable_bus(int argc, const char **argv, struct cxl_ctx *ctx); #endif /* _CXL_BUILTIN_H_ */ diff --git a/cxl/bus.c b/cxl/bus.c new file mode 100644 index 0000000..3321295 --- /dev/null +++ b/cxl/bus.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2020-2022 Intel Corporation. All rights reserved. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "filter.h" + +static struct parameters { + bool debug; + bool force; +} param; + +static struct log_ctx bl; + +#define BASE_OPTIONS() \ +OPT_BOOLEAN(0, "debug", ¶m.debug, "turn on debug") + +#define DISABLE_OPTIONS() \ +OPT_BOOLEAN('f', "force", ¶m.force, \ + "DANGEROUS: override active memdev safety checks") + +static const struct option disable_options[] = { + BASE_OPTIONS(), + DISABLE_OPTIONS(), + OPT_END(), +}; + +static int action_disable(struct cxl_bus *bus) +{ + const char *devname = cxl_bus_get_devname(bus); + struct cxl_ctx *ctx = cxl_bus_get_ctx(bus); + struct cxl_memdev *memdev; + int active_memdevs = 0; + + cxl_memdev_foreach(ctx, memdev) + if (bus == cxl_memdev_get_bus(memdev)) + active_memdevs++; + + if (active_memdevs && !param.force) { + /* + * TODO: actually detect rather than assume active just + * because the memdev is enabled + */ + log_err(&bl, + "%s hosts %d memdev%s which %s part of an active region\n", + devname, active_memdevs, active_memdevs > 1 ? "s" : "", + active_memdevs > 1 ? "are" : "is"); + log_err(&bl, + "See 'cxl list -M -b %s' to see impacted device%s\n", + devname, active_memdevs > 1 ? "s" : ""); + return -EBUSY; + } + + return cxl_bus_disable_invalidate(bus); +} + +static struct cxl_bus *find_cxl_bus(struct cxl_ctx *ctx, const char *ident) +{ + struct cxl_bus *bus; + + cxl_bus_foreach(ctx, bus) + if (util_cxl_bus_filter(bus, ident)) + return bus; + return NULL; +} + +static int bus_action(int argc, const char **argv, struct cxl_ctx *ctx, + int (*action)(struct cxl_bus *bus), + const struct option *options, const char *usage) +{ + int i, rc = 0, count = 0, err = 0; + const char * const u[] = { + usage, + NULL + }; + unsigned long id; + + log_init(&bl, "cxl bus", "CXL_PORT_LOG"); + argc = parse_options(argc, argv, options, u, 0); + + if (argc == 0) + usage_with_options(u, options); + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "all") == 0) { + argv[0] = "all"; + argc = 1; + break; + } + + if (sscanf(argv[i], "root%lu", &id) == 1) + continue; + if (sscanf(argv[i], "%lu", &id) == 1) + continue; + + log_err(&bl, "'%s' is not a valid bus identifer\n", argv[i]); + err++; + } + + if (err == argc) { + usage_with_options(u, options); + return -EINVAL; + } + + if (param.debug) { + cxl_set_log_priority(ctx, LOG_DEBUG); + bl.log_priority = LOG_DEBUG; + } else + bl.log_priority = LOG_INFO; + + rc = 0; + err = 0; + count = 0; + + for (i = 0; i < argc; i++) { + struct cxl_bus *bus; + + bus = find_cxl_bus(ctx, argv[i]); + if (!bus) { + log_dbg(&bl, "bus: %s not found\n", argv[i]); + continue; + } + + log_dbg(&bl, "run action on bus: %s\n", + cxl_bus_get_devname(bus)); + rc = action(bus); + if (rc == 0) + count++; + else if (rc && !err) + err = rc; + } + rc = err; + + /* + * count if some actions succeeded, 0 if none were attempted, + * negative error code otherwise. + */ + if (count > 0) + return count; + return rc; +} + + int cmd_disable_bus(int argc, const char **argv, struct cxl_ctx *ctx) + { + int count = bus_action( + argc, argv, ctx, action_disable, disable_options, + "cxl disable-bus [..] []"); + + log_info(&bl, "disabled %d bus%s\n", count >= 0 ? count : 0, + count > 1 ? "s" : ""); + return count >= 0 ? 0 : EXIT_FAILURE; + } diff --git a/cxl/cxl.c b/cxl/cxl.c index ab4bbec..aa4ce61 100644 --- a/cxl/cxl.c +++ b/cxl/cxl.c @@ -69,6 +69,7 @@ static struct cmd_struct commands[] = { { "disable-port", .c_fn = cmd_disable_port }, { "enable-port", .c_fn = cmd_enable_port }, { "set-partition", .c_fn = cmd_set_partition }, + { "disable-bus", .c_fn = cmd_disable_bus }, }; int main(int argc, const char **argv) diff --git a/cxl/filter.c b/cxl/filter.c index b339642..c6ab9eb 100644 --- a/cxl/filter.c +++ b/cxl/filter.c @@ -176,8 +176,7 @@ util_cxl_decoder_filter_by_port(struct cxl_decoder *decoder, const char *ident, return NULL; } -static struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus, - const char *__ident) +struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus, const char *__ident) { char *ident, *save; const char *arg; diff --git a/cxl/filter.h b/cxl/filter.h index 697b777..9557943 100644 --- a/cxl/filter.h +++ b/cxl/filter.h @@ -41,6 +41,7 @@ enum cxl_port_filter_mode { struct cxl_port *util_cxl_port_filter(struct cxl_port *port, const char *ident, enum cxl_port_filter_mode mode); +struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus, const char *__ident); struct cxl_endpoint *util_cxl_endpoint_filter(struct cxl_endpoint *endpoint, const char *__ident); struct cxl_target *util_cxl_target_filter_by_memdev(struct cxl_target *target, diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c index 59e1644..0e8dd20 100644 --- a/cxl/lib/libcxl.c +++ b/cxl/lib/libcxl.c @@ -556,6 +556,21 @@ static void bus_invalidate(struct cxl_bus *bus) cxl_flush(ctx); } +CXL_EXPORT int cxl_bus_disable_invalidate(struct cxl_bus *bus) +{ + struct cxl_ctx *ctx = cxl_bus_get_ctx(bus); + struct cxl_port *port = cxl_bus_get_port(bus); + int rc; + + rc = util_unbind(port->uport, ctx); + if (rc) + return rc; + + free_bus(bus, &ctx->buses); + cxl_flush(ctx); + return 0; +} + CXL_EXPORT int cxl_memdev_disable_invalidate(struct cxl_memdev *memdev) { struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym index aab1112..dffcb60 100644 --- a/cxl/lib/libcxl.sym +++ b/cxl/lib/libcxl.sym @@ -86,6 +86,7 @@ global: cxl_bus_get_id; cxl_bus_get_port; cxl_bus_get_ctx; + cxl_bus_disable_invalidate; cxl_port_get_first; cxl_port_get_next; cxl_port_get_devname; diff --git a/cxl/libcxl.h b/cxl/libcxl.h index 0063d31..0007f4d 100644 --- a/cxl/libcxl.h +++ b/cxl/libcxl.h @@ -73,6 +73,7 @@ const char *cxl_bus_get_devname(struct cxl_bus *bus); int cxl_bus_get_id(struct cxl_bus *bus); struct cxl_port *cxl_bus_get_port(struct cxl_bus *bus); struct cxl_ctx *cxl_bus_get_ctx(struct cxl_bus *bus); +int cxl_bus_disable_invalidate(struct cxl_bus *bus); #define cxl_bus_foreach(ctx, bus) \ for (bus = cxl_bus_get_first(ctx); bus != NULL; \ diff --git a/cxl/meson.build b/cxl/meson.build index 671c8e1..d63dcb1 100644 --- a/cxl/meson.build +++ b/cxl/meson.build @@ -2,6 +2,7 @@ cxl_src = [ 'cxl.c', 'list.c', 'port.c', + 'bus.c', 'memdev.c', 'json.c', 'filter.c', -- 2.27.0