You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
735 lines
21 KiB
735 lines
21 KiB
2 years ago
|
From 1cfb7570369ae6bed832bde908435d38fa990f9d Mon Sep 17 00:00:00 2001
|
||
|
From: Dan Williams <dan.j.williams@intel.com>
|
||
|
Date: Sun, 23 Jan 2022 16:55:01 -0800
|
||
|
Subject: [PATCH 122/217] cxl/port: Add {disable,enable}-port command
|
||
|
|
||
|
The {disable,enable}-port commands are used for debugging port enumeration
|
||
|
corner cases and testing the kernel CXL device hotplug implementation.
|
||
|
|
||
|
In addition to unbinding the port from its driver, which also kicks of
|
||
|
unregistration of descendent ports, the disable operation also flushes the
|
||
|
kernels delayed workqueue for memory device removal.
|
||
|
|
||
|
Link: https://lore.kernel.org/r/164298570117.3021641.14546710754812021284.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-port.txt | 46 +++++
|
||
|
Documentation/cxl/cxl-enable-port.txt | 43 +++++
|
||
|
Documentation/cxl/lib/libcxl.txt | 11 ++
|
||
|
Documentation/cxl/meson.build | 2 +
|
||
|
cxl/builtin.h | 2 +
|
||
|
cxl/cxl.c | 2 +
|
||
|
cxl/filter.c | 21 +-
|
||
|
cxl/filter.h | 13 ++
|
||
|
cxl/lib/libcxl.c | 95 +++++++++-
|
||
|
cxl/lib/libcxl.sym | 3 +
|
||
|
cxl/libcxl.h | 3 +
|
||
|
cxl/meson.build | 1 +
|
||
|
cxl/port.c | 253 +++++++++++++++++++++++++
|
||
|
13 files changed, 471 insertions(+), 24 deletions(-)
|
||
|
create mode 100644 Documentation/cxl/cxl-disable-port.txt
|
||
|
create mode 100644 Documentation/cxl/cxl-enable-port.txt
|
||
|
create mode 100644 cxl/port.c
|
||
|
|
||
|
diff --git a/Documentation/cxl/cxl-disable-port.txt b/Documentation/cxl/cxl-disable-port.txt
|
||
|
new file mode 100644
|
||
|
index 0000000..de13c07
|
||
|
--- /dev/null
|
||
|
+++ b/Documentation/cxl/cxl-disable-port.txt
|
||
|
@@ -0,0 +1,46 @@
|
||
|
+// SPDX-License-Identifier: GPL-2.0
|
||
|
+
|
||
|
+cxl-disable-port(1)
|
||
|
+===================
|
||
|
+
|
||
|
+NAME
|
||
|
+----
|
||
|
+cxl-disable-port - activate / hot-add a given CXL port
|
||
|
+
|
||
|
+SYNOPSIS
|
||
|
+--------
|
||
|
+[verse]
|
||
|
+'cxl disable-port' <port0> [<port1>..<portN>] [<options>]
|
||
|
+
|
||
|
+For test and debug scenarios, disable a CXL port and any memory devices
|
||
|
+dependent on this port being active for CXL.mem operation.
|
||
|
+
|
||
|
+OPTIONS
|
||
|
+-------
|
||
|
+-e::
|
||
|
+--endpoint::
|
||
|
+ Toggle from treating the port arguments as Switch Port identifiers to
|
||
|
+ Endpoint Port identifiers.
|
||
|
+
|
||
|
+
|
||
|
+-f::
|
||
|
+--force::
|
||
|
+ DANGEROUS: Override the safety measure that blocks attempts to disable a
|
||
|
+ port 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.
|
||
|
+
|
||
|
+ Toggle from treating the port arguments as Switch Port identifiers to
|
||
|
+ Endpoint Port identifiers.
|
||
|
+
|
||
|
+--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/cxl-enable-port.txt b/Documentation/cxl/cxl-enable-port.txt
|
||
|
new file mode 100644
|
||
|
index 0000000..9a37cef
|
||
|
--- /dev/null
|
||
|
+++ b/Documentation/cxl/cxl-enable-port.txt
|
||
|
@@ -0,0 +1,43 @@
|
||
|
+// SPDX-License-Identifier: GPL-2.0
|
||
|
+
|
||
|
+cxl-enable-port(1)
|
||
|
+==================
|
||
|
+
|
||
|
+NAME
|
||
|
+----
|
||
|
+cxl-enable-port - activate / hot-add a given CXL port
|
||
|
+
|
||
|
+SYNOPSIS
|
||
|
+--------
|
||
|
+[verse]
|
||
|
+'cxl enable-port' <port0> [<port1>..<portN>] [<options>]
|
||
|
+
|
||
|
+A port typically autoenables at initial device discovery. However, if it
|
||
|
+was manually disabled this command can trigger the kernel to activate it
|
||
|
+again. This involves detecting the state of the HDM (Host Managed Device
|
||
|
+Memory) Decoders and validating that CXL.mem is enabled for each port in
|
||
|
+the device's hierarchy.
|
||
|
+
|
||
|
+OPTIONS
|
||
|
+-------
|
||
|
+-e::
|
||
|
+--endpoint::
|
||
|
+ Toggle from treating the port arguments as Switch Port identifiers to
|
||
|
+ Endpoint Port identifiers.
|
||
|
+
|
||
|
+-m::
|
||
|
+--enable-memdevs::
|
||
|
+ Try to enable descendant memdevs after enabling the port. Recall that a
|
||
|
+ memdev is only enabled after all CXL ports in its device topology
|
||
|
+ ancestry are enabled.
|
||
|
+
|
||
|
+--debug::
|
||
|
+ If the cxl tool was built with debug enabled, 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 a0fcee9..27eb29e 100644
|
||
|
--- a/Documentation/cxl/lib/libcxl.txt
|
||
|
+++ b/Documentation/cxl/lib/libcxl.txt
|
||
|
@@ -247,6 +247,16 @@ Port, or Switch Upstream Port with CXL capabilities.
|
||
|
The cxl_port_foreach_all() helper does a depth first iteration of all
|
||
|
ports beneath the 'top' port argument.
|
||
|
|
||
|
+=== PORT: Control
|
||
|
+---
|
||
|
+int cxl_port_disable_invalidate(struct cxl_port *port);
|
||
|
+int cxl_port_enable(struct cxl_port *port);
|
||
|
+---
|
||
|
+cxl_port_disable_invalidate() is a violent operation that disables
|
||
|
+entire sub-tree of CXL Memory Device and Ports, only use it for test /
|
||
|
+debug scenarios, or ensuring that all impacted devices are deactivated
|
||
|
+first.
|
||
|
+
|
||
|
=== PORT: Attributes
|
||
|
----
|
||
|
const char *cxl_port_get_devname(struct cxl_port *port);
|
||
|
@@ -315,6 +325,7 @@ struct cxl_port *cxl_endpoint_get_parent(struct cxl_endpoint *endpoint);
|
||
|
struct cxl_port *cxl_endpoint_get_port(struct cxl_endpoint *endpoint);
|
||
|
const char *cxl_endpoint_get_host(struct cxl_endpoint *endpoint);
|
||
|
struct cxl_endpoint *cxl_memdev_get_endpoint(struct cxl_memdev *memdev);
|
||
|
+struct cxl_endpoint *cxl_port_to_endpoint(struct cxl_port *port);
|
||
|
|
||
|
#define cxl_endpoint_foreach(port, endpoint) \
|
||
|
for (endpoint = cxl_endpoint_get_first(port); endpoint != NULL; \
|
||
|
diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build
|
||
|
index 7618c97..96f4666 100644
|
||
|
--- a/Documentation/cxl/meson.build
|
||
|
+++ b/Documentation/cxl/meson.build
|
||
|
@@ -32,6 +32,8 @@ cxl_manpages = [
|
||
|
'cxl-zero-labels.txt',
|
||
|
'cxl-enable-memdev.txt',
|
||
|
'cxl-disable-memdev.txt',
|
||
|
+ 'cxl-enable-port.txt',
|
||
|
+ 'cxl-disable-port.txt',
|
||
|
]
|
||
|
|
||
|
foreach man : cxl_manpages
|
||
|
diff --git a/cxl/builtin.h b/cxl/builtin.h
|
||
|
index 621c85c..3123d5e 100644
|
||
|
--- a/cxl/builtin.h
|
||
|
+++ b/cxl/builtin.h
|
||
|
@@ -12,4 +12,6 @@ int cmd_init_labels(int argc, const char **argv, struct cxl_ctx *ctx);
|
||
|
int cmd_check_labels(int argc, const char **argv, struct cxl_ctx *ctx);
|
||
|
int cmd_disable_memdev(int argc, const char **argv, struct cxl_ctx *ctx);
|
||
|
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);
|
||
|
#endif /* _CXL_BUILTIN_H_ */
|
||
|
diff --git a/cxl/cxl.c b/cxl/cxl.c
|
||
|
index 78d2e9a..c20c569 100644
|
||
|
--- a/cxl/cxl.c
|
||
|
+++ b/cxl/cxl.c
|
||
|
@@ -66,6 +66,8 @@ static struct cmd_struct commands[] = {
|
||
|
{ "write-labels", .c_fn = cmd_write_labels },
|
||
|
{ "disable-memdev", .c_fn = cmd_disable_memdev },
|
||
|
{ "enable-memdev", .c_fn = cmd_enable_memdev },
|
||
|
+ { "disable-port", .c_fn = cmd_disable_port },
|
||
|
+ { "enable-port", .c_fn = cmd_enable_port },
|
||
|
};
|
||
|
|
||
|
int main(int argc, const char **argv)
|
||
|
diff --git a/cxl/filter.c b/cxl/filter.c
|
||
|
index c691edf..f6a23b7 100644
|
||
|
--- a/cxl/filter.c
|
||
|
+++ b/cxl/filter.c
|
||
|
@@ -47,8 +47,8 @@ bool cxl_filter_has(const char *__filter, const char *needle)
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
-static struct cxl_endpoint *
|
||
|
-util_cxl_endpoint_filter(struct cxl_endpoint *endpoint, const char *__ident)
|
||
|
+struct cxl_endpoint *util_cxl_endpoint_filter(struct cxl_endpoint *endpoint,
|
||
|
+ const char *__ident)
|
||
|
{
|
||
|
char *ident, *save;
|
||
|
const char *arg;
|
||
|
@@ -124,11 +124,6 @@ static struct cxl_port *__util_cxl_port_filter(struct cxl_port *port,
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
-enum cxl_port_filter_mode {
|
||
|
- CXL_PF_SINGLE,
|
||
|
- CXL_PF_ANCESTRY,
|
||
|
-};
|
||
|
-
|
||
|
static enum cxl_port_filter_mode pf_mode(struct cxl_filter_params *p)
|
||
|
{
|
||
|
if (p->single)
|
||
|
@@ -136,9 +131,8 @@ static enum cxl_port_filter_mode pf_mode(struct cxl_filter_params *p)
|
||
|
return CXL_PF_ANCESTRY;
|
||
|
}
|
||
|
|
||
|
-static struct cxl_port *util_cxl_port_filter(struct cxl_port *port,
|
||
|
- const char *ident,
|
||
|
- enum cxl_port_filter_mode mode)
|
||
|
+struct cxl_port *util_cxl_port_filter(struct cxl_port *port, const char *ident,
|
||
|
+ enum cxl_port_filter_mode mode)
|
||
|
{
|
||
|
struct cxl_port *iter = port;
|
||
|
|
||
|
@@ -358,9 +352,9 @@ util_cxl_endpoint_filter_by_memdev(struct cxl_endpoint *endpoint,
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
-static struct cxl_port *util_cxl_port_filter_by_memdev(struct cxl_port *port,
|
||
|
- const char *ident,
|
||
|
- const char *serial)
|
||
|
+struct cxl_port *util_cxl_port_filter_by_memdev(struct cxl_port *port,
|
||
|
+ const char *ident,
|
||
|
+ const char *serial)
|
||
|
{
|
||
|
struct cxl_ctx *ctx = cxl_port_get_ctx(port);
|
||
|
struct cxl_memdev *memdev;
|
||
|
@@ -958,7 +952,6 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
-
|
||
|
}
|
||
|
walk_children:
|
||
|
dbg(p, "walk decoders\n");
|
||
|
diff --git a/cxl/filter.h b/cxl/filter.h
|
||
|
index 6fd469f..850df70 100644
|
||
|
--- a/cxl/filter.h
|
||
|
+++ b/cxl/filter.h
|
||
|
@@ -29,6 +29,19 @@ struct cxl_filter_params {
|
||
|
struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev,
|
||
|
const char *__ident,
|
||
|
const char *serials);
|
||
|
+struct cxl_port *util_cxl_port_filter_by_memdev(struct cxl_port *port,
|
||
|
+ const char *ident,
|
||
|
+ const char *serial);
|
||
|
+
|
||
|
+enum cxl_port_filter_mode {
|
||
|
+ CXL_PF_SINGLE,
|
||
|
+ CXL_PF_ANCESTRY,
|
||
|
+};
|
||
|
+
|
||
|
+struct cxl_port *util_cxl_port_filter(struct cxl_port *port, const char *ident,
|
||
|
+ enum cxl_port_filter_mode mode);
|
||
|
+struct cxl_endpoint *util_cxl_endpoint_filter(struct cxl_endpoint *endpoint,
|
||
|
+ const char *__ident);
|
||
|
int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *param);
|
||
|
bool cxl_filter_has(const char *needle, const char *__filter);
|
||
|
#endif /* _CXL_UTIL_FILTER_H_ */
|
||
|
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
|
||
|
index dcfc826..1a7dccb 100644
|
||
|
--- a/cxl/lib/libcxl.c
|
||
|
+++ b/cxl/lib/libcxl.c
|
||
|
@@ -258,6 +258,11 @@ CXL_EXPORT void cxl_unref(struct cxl_ctx *ctx)
|
||
|
free(ctx);
|
||
|
}
|
||
|
|
||
|
+static int cxl_flush(struct cxl_ctx *ctx)
|
||
|
+{
|
||
|
+ return sysfs_write_attr(ctx, "/sys/bus/cxl/flush", "1\n");
|
||
|
+}
|
||
|
+
|
||
|
/**
|
||
|
* cxl_set_log_fn - override default log routine
|
||
|
* @ctx: cxl library context
|
||
|
@@ -530,11 +535,31 @@ CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev
|
||
|
return memdev->firmware_version;
|
||
|
}
|
||
|
|
||
|
+static void bus_invalidate(struct cxl_bus *bus)
|
||
|
+{
|
||
|
+ struct cxl_ctx *ctx = cxl_bus_get_ctx(bus);
|
||
|
+ struct cxl_port *bus_port, *port, *_p;
|
||
|
+ struct cxl_memdev *memdev;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Something happend to cause the state of all ports to be
|
||
|
+ * indeterminate, delete them all and start over.
|
||
|
+ */
|
||
|
+ cxl_memdev_foreach(ctx, memdev)
|
||
|
+ if (cxl_memdev_get_bus(memdev) == bus)
|
||
|
+ memdev->endpoint = NULL;
|
||
|
+
|
||
|
+ bus_port = cxl_bus_get_port(bus);
|
||
|
+ list_for_each_safe(&bus_port->child_ports, port, _p, list)
|
||
|
+ free_port(port, &bus_port->child_ports);
|
||
|
+ bus_port->ports_init = 0;
|
||
|
+ cxl_flush(ctx);
|
||
|
+}
|
||
|
+
|
||
|
CXL_EXPORT int cxl_memdev_disable_invalidate(struct cxl_memdev *memdev)
|
||
|
{
|
||
|
struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
|
||
|
const char *devname = cxl_memdev_get_devname(memdev);
|
||
|
- struct cxl_port *port, *_p, *bus_port;
|
||
|
struct cxl_bus *bus;
|
||
|
|
||
|
if (!cxl_memdev_is_enabled(memdev))
|
||
|
@@ -553,15 +578,7 @@ CXL_EXPORT int cxl_memdev_disable_invalidate(struct cxl_memdev *memdev)
|
||
|
return -EBUSY;
|
||
|
}
|
||
|
|
||
|
- /*
|
||
|
- * The state of all ports is now indeterminate, delete them all
|
||
|
- * and start over.
|
||
|
- */
|
||
|
- bus_port = cxl_bus_get_port(bus);
|
||
|
- list_for_each_safe(&bus_port->child_ports, port, _p, list)
|
||
|
- free_port(port, &bus_port->child_ports);
|
||
|
- bus_port->ports_init = 0;
|
||
|
- memdev->endpoint = NULL;
|
||
|
+ bus_invalidate(bus);
|
||
|
|
||
|
dbg(ctx, "%s: disabled\n", devname);
|
||
|
|
||
|
@@ -1352,6 +1369,57 @@ CXL_EXPORT int cxl_port_is_enabled(struct cxl_port *port)
|
||
|
return is_enabled(path);
|
||
|
}
|
||
|
|
||
|
+CXL_EXPORT int cxl_port_disable_invalidate(struct cxl_port *port)
|
||
|
+{
|
||
|
+ const char *devname = cxl_port_get_devname(port);
|
||
|
+ struct cxl_bus *bus = cxl_port_get_bus(port);
|
||
|
+ struct cxl_ctx *ctx = cxl_port_get_ctx(port);
|
||
|
+
|
||
|
+ if (cxl_port_is_root(port)) {
|
||
|
+ err(ctx, "%s: can not be disabled through this interface\n",
|
||
|
+ devname);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!bus) {
|
||
|
+ err(ctx, "%s: failed to invalidate\n", devname);
|
||
|
+ return -ENXIO;
|
||
|
+ }
|
||
|
+
|
||
|
+ util_unbind(port->dev_path, ctx);
|
||
|
+
|
||
|
+ if (cxl_port_is_enabled(port)) {
|
||
|
+ err(ctx, "%s: failed to disable\n", devname);
|
||
|
+ return -EBUSY;
|
||
|
+ }
|
||
|
+
|
||
|
+ dbg(ctx, "%s: disabled\n", devname);
|
||
|
+
|
||
|
+ bus_invalidate(bus);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+CXL_EXPORT int cxl_port_enable(struct cxl_port *port)
|
||
|
+{
|
||
|
+ struct cxl_ctx *ctx = cxl_port_get_ctx(port);
|
||
|
+ const char *devname = cxl_port_get_devname(port);
|
||
|
+
|
||
|
+ if (cxl_port_is_enabled(port))
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ util_bind(devname, port->module, "cxl", ctx);
|
||
|
+
|
||
|
+ if (!cxl_port_is_enabled(port)) {
|
||
|
+ err(ctx, "%s: failed to enable\n", devname);
|
||
|
+ return -ENXIO;
|
||
|
+ }
|
||
|
+
|
||
|
+ dbg(ctx, "%s: enabled\n", devname);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
CXL_EXPORT struct cxl_bus *cxl_port_to_bus(struct cxl_port *port)
|
||
|
{
|
||
|
if (!cxl_port_is_root(port))
|
||
|
@@ -1359,6 +1427,13 @@ CXL_EXPORT struct cxl_bus *cxl_port_to_bus(struct cxl_port *port)
|
||
|
return container_of(port, struct cxl_bus, port);
|
||
|
}
|
||
|
|
||
|
+CXL_EXPORT struct cxl_endpoint *cxl_port_to_endpoint(struct cxl_port *port)
|
||
|
+{
|
||
|
+ if (!cxl_port_is_endpoint(port))
|
||
|
+ return NULL;
|
||
|
+ return container_of(port, struct cxl_endpoint, port);
|
||
|
+}
|
||
|
+
|
||
|
static void *add_cxl_dport(void *parent, int id, const char *cxldport_base)
|
||
|
{
|
||
|
const char *devname = devpath_to_devname(cxldport_base);
|
||
|
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
|
||
|
index 2c8358e..67c7fd5 100644
|
||
|
--- a/cxl/lib/libcxl.sym
|
||
|
+++ b/cxl/lib/libcxl.sym
|
||
|
@@ -97,11 +97,14 @@ global:
|
||
|
cxl_port_is_switch;
|
||
|
cxl_port_to_bus;
|
||
|
cxl_port_is_endpoint;
|
||
|
+ cxl_port_to_endpoint;
|
||
|
cxl_port_get_bus;
|
||
|
cxl_port_get_host;
|
||
|
cxl_port_get_bus;
|
||
|
cxl_port_hosts_memdev;
|
||
|
cxl_port_get_nr_dports;
|
||
|
+ cxl_port_disable_invalidate;
|
||
|
+ cxl_port_enable;
|
||
|
cxl_port_get_next_all;
|
||
|
cxl_endpoint_get_first;
|
||
|
cxl_endpoint_get_next;
|
||
|
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
|
||
|
index c8d07bb..1aac396 100644
|
||
|
--- a/cxl/libcxl.h
|
||
|
+++ b/cxl/libcxl.h
|
||
|
@@ -90,10 +90,13 @@ bool cxl_port_is_root(struct cxl_port *port);
|
||
|
bool cxl_port_is_switch(struct cxl_port *port);
|
||
|
struct cxl_bus *cxl_port_to_bus(struct cxl_port *port);
|
||
|
bool cxl_port_is_endpoint(struct cxl_port *port);
|
||
|
+struct cxl_endpoint *cxl_port_to_endpoint(struct cxl_port *port);
|
||
|
struct cxl_bus *cxl_port_get_bus(struct cxl_port *port);
|
||
|
const char *cxl_port_get_host(struct cxl_port *port);
|
||
|
bool cxl_port_hosts_memdev(struct cxl_port *port, struct cxl_memdev *memdev);
|
||
|
int cxl_port_get_nr_dports(struct cxl_port *port);
|
||
|
+int cxl_port_disable_invalidate(struct cxl_port *port);
|
||
|
+int cxl_port_enable(struct cxl_port *port);
|
||
|
struct cxl_port *cxl_port_get_next_all(struct cxl_port *port,
|
||
|
const struct cxl_port *top);
|
||
|
|
||
|
diff --git a/cxl/meson.build b/cxl/meson.build
|
||
|
index fc7ee71..87cfea7 100644
|
||
|
--- a/cxl/meson.build
|
||
|
+++ b/cxl/meson.build
|
||
|
@@ -1,6 +1,7 @@
|
||
|
cxl_src = [
|
||
|
'cxl.c',
|
||
|
'list.c',
|
||
|
+ 'port.c',
|
||
|
'memdev.c',
|
||
|
'../util/json.c',
|
||
|
'../util/log.c',
|
||
|
diff --git a/cxl/port.c b/cxl/port.c
|
||
|
new file mode 100644
|
||
|
index 0000000..46a8f32
|
||
|
--- /dev/null
|
||
|
+++ b/cxl/port.c
|
||
|
@@ -0,0 +1,253 @@
|
||
|
+// 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;
|
||
|
+ bool memdevs;
|
||
|
+ bool endpoint;
|
||
|
+} param;
|
||
|
+
|
||
|
+static struct log_ctx pl;
|
||
|
+
|
||
|
+#define BASE_OPTIONS() \
|
||
|
+OPT_BOOLEAN(0, "debug", ¶m.debug, "turn on debug"), \
|
||
|
+OPT_BOOLEAN('e', "endpoint", ¶m.endpoint, \
|
||
|
+ "target endpoints instead of switch ports")
|
||
|
+
|
||
|
+#define ENABLE_OPTIONS() \
|
||
|
+OPT_BOOLEAN('m', "enable-memdevs", ¶m.memdevs, \
|
||
|
+ "enable downstream memdev(s)")
|
||
|
+
|
||
|
+#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 const struct option enable_options[] = {
|
||
|
+ BASE_OPTIONS(),
|
||
|
+ ENABLE_OPTIONS(),
|
||
|
+ OPT_END(),
|
||
|
+};
|
||
|
+
|
||
|
+static int action_disable(struct cxl_port *port)
|
||
|
+{
|
||
|
+ const char *devname = cxl_port_get_devname(port);
|
||
|
+ struct cxl_ctx *ctx = cxl_port_get_ctx(port);
|
||
|
+ struct cxl_memdev *memdev;
|
||
|
+ int active_memdevs = 0;
|
||
|
+
|
||
|
+ if (!cxl_port_is_enabled(port)) {
|
||
|
+ log_dbg(&pl, "%s already disabled\n", devname);
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (param.endpoint) {
|
||
|
+ struct cxl_endpoint *endpoint = cxl_port_to_endpoint(port);
|
||
|
+
|
||
|
+ if (cxl_endpoint_get_memdev(endpoint))
|
||
|
+ active_memdevs++;
|
||
|
+ }
|
||
|
+
|
||
|
+ cxl_memdev_foreach(ctx, memdev) {
|
||
|
+ if (!cxl_port_get_dport_by_memdev(port, memdev))
|
||
|
+ continue;
|
||
|
+ if (cxl_memdev_is_enabled(memdev))
|
||
|
+ active_memdevs++;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (active_memdevs && !param.force) {
|
||
|
+ /*
|
||
|
+ * TODO: actually detect rather than assume active just
|
||
|
+ * because the memdev is enabled
|
||
|
+ */
|
||
|
+ log_err(&pl,
|
||
|
+ "%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(&pl,
|
||
|
+ "See 'cxl list -M -p %s' to see impacted device%s\n",
|
||
|
+ devname, active_memdevs > 1 ? "s" : "");
|
||
|
+ return -EBUSY;
|
||
|
+ }
|
||
|
+
|
||
|
+ return cxl_port_disable_invalidate(port);
|
||
|
+}
|
||
|
+
|
||
|
+static int action_enable(struct cxl_port *port)
|
||
|
+{
|
||
|
+ struct cxl_ctx *ctx = cxl_port_get_ctx(port);
|
||
|
+ struct cxl_memdev *memdev;
|
||
|
+ int rc;
|
||
|
+
|
||
|
+ rc = cxl_port_enable(port);
|
||
|
+ if (rc || !param.memdevs)
|
||
|
+ return rc;
|
||
|
+
|
||
|
+ cxl_memdev_foreach(ctx, memdev)
|
||
|
+ if (cxl_port_get_dport_by_memdev(port, memdev))
|
||
|
+ cxl_memdev_enable(memdev);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static struct cxl_port *find_cxl_port(struct cxl_ctx *ctx, const char *ident)
|
||
|
+{
|
||
|
+ struct cxl_bus *bus;
|
||
|
+ struct cxl_port *port;
|
||
|
+
|
||
|
+ cxl_bus_foreach(ctx, bus)
|
||
|
+ cxl_port_foreach_all(cxl_bus_get_port(bus), port)
|
||
|
+ if (util_cxl_port_filter(port, ident, CXL_PF_SINGLE))
|
||
|
+ return port;
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
+static struct cxl_endpoint *find_cxl_endpoint(struct cxl_ctx *ctx,
|
||
|
+ const char *ident)
|
||
|
+{
|
||
|
+ struct cxl_bus *bus;
|
||
|
+ struct cxl_port *port;
|
||
|
+ struct cxl_endpoint *endpoint;
|
||
|
+
|
||
|
+ cxl_bus_foreach(ctx, bus)
|
||
|
+ cxl_port_foreach_all(cxl_bus_get_port(bus), port)
|
||
|
+ cxl_endpoint_foreach(port, endpoint)
|
||
|
+ if (util_cxl_endpoint_filter(endpoint, ident))
|
||
|
+ return endpoint;
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+static int port_action(int argc, const char **argv, struct cxl_ctx *ctx,
|
||
|
+ int (*action)(struct cxl_port *port),
|
||
|
+ 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(&pl, "cxl port", "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++) {
|
||
|
+ const char *fmt;
|
||
|
+
|
||
|
+ if (strcmp(argv[i], "all") == 0) {
|
||
|
+ argc = 1;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (param.endpoint)
|
||
|
+ fmt = "endpoint%lu";
|
||
|
+ else
|
||
|
+ fmt = "port%lu";
|
||
|
+
|
||
|
+ if (sscanf(argv[i], fmt, &id) == 1)
|
||
|
+ continue;
|
||
|
+ if (sscanf(argv[i], "%lu", &id) == 1)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ log_err(&pl, "'%s' is not a valid %s identifer\n", argv[i],
|
||
|
+ param.endpoint ? "endpoint" : "port");
|
||
|
+ err++;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (err == argc) {
|
||
|
+ usage_with_options(u, options);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (param.debug) {
|
||
|
+ cxl_set_log_priority(ctx, LOG_DEBUG);
|
||
|
+ pl.log_priority = LOG_DEBUG;
|
||
|
+ } else
|
||
|
+ pl.log_priority = LOG_INFO;
|
||
|
+
|
||
|
+ rc = 0;
|
||
|
+ err = 0;
|
||
|
+ count = 0;
|
||
|
+
|
||
|
+ for (i = 0; i < argc; i++) {
|
||
|
+ struct cxl_port *port;
|
||
|
+
|
||
|
+ if (param.endpoint) {
|
||
|
+ struct cxl_endpoint *endpoint;
|
||
|
+
|
||
|
+ endpoint = find_cxl_endpoint(ctx, argv[i]);
|
||
|
+ if (!endpoint) {
|
||
|
+ log_dbg(&pl, "endpoint: %s not found\n",
|
||
|
+ argv[i]);
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+ port = cxl_endpoint_get_port(endpoint);
|
||
|
+ } else {
|
||
|
+ port = find_cxl_port(ctx, argv[i]);
|
||
|
+ if (!port) {
|
||
|
+ log_dbg(&pl, "port: %s not found\n", argv[i]);
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ log_dbg(&pl, "run action on port: %s\n",
|
||
|
+ cxl_port_get_devname(port));
|
||
|
+ rc = action(port);
|
||
|
+ 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_port(int argc, const char **argv, struct cxl_ctx *ctx)
|
||
|
+ {
|
||
|
+ int count = port_action(
|
||
|
+ argc, argv, ctx, action_disable, disable_options,
|
||
|
+ "cxl disable-port <port0> [<port1>..<portN>] [<options>]");
|
||
|
+
|
||
|
+ log_info(&pl, "disabled %d port%s\n", count >= 0 ? count : 0,
|
||
|
+ count > 1 ? "s" : "");
|
||
|
+ return count >= 0 ? 0 : EXIT_FAILURE;
|
||
|
+ }
|
||
|
+
|
||
|
+ int cmd_enable_port(int argc, const char **argv, struct cxl_ctx *ctx)
|
||
|
+ {
|
||
|
+ int count = port_action(
|
||
|
+ argc, argv, ctx, action_enable, enable_options,
|
||
|
+ "cxl enable-port <port0> [<port1>..<portN>] [<options>]");
|
||
|
+
|
||
|
+ log_info(&pl, "enabled %d port%s\n", count >= 0 ? count : 0,
|
||
|
+ count > 1 ? "s" : "");
|
||
|
+ return count >= 0 ? 0 : EXIT_FAILURE;
|
||
|
+ }
|
||
|
--
|
||
|
2.27.0
|
||
|
|