2c91dc1bcd
This includes support for the CXL commands, and adds the following packages: cxl-cli, cxl-devel, cxl-libs. Resolves: rhbz#2132167
385 lines
11 KiB
Diff
385 lines
11 KiB
Diff
From 8a35aa8fd3e1db06228329a0ca900ce246ca329e Mon Sep 17 00:00:00 2001
|
|
From: Dan Williams <dan.j.williams@intel.com>
|
|
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 <dan.j.williams@intel.com>
|
|
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
|
|
---
|
|
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' <root0> [<root1>..<rootN>] [<options>]
|
|
+
|
|
+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 <stdio.h>
|
|
+#include <errno.h>
|
|
+#include <stdlib.h>
|
|
+#include <unistd.h>
|
|
+#include <limits.h>
|
|
+#include <util/log.h>
|
|
+#include <cxl/libcxl.h>
|
|
+#include <util/parse-options.h>
|
|
+#include <ccan/minmax/minmax.h>
|
|
+#include <ccan/array_size/array_size.h>
|
|
+
|
|
+#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 <bus0> [<bus1>..<busN>] [<options>]");
|
|
+
|
|
+ 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
|
|
|