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