Blame SOURCES/0122-cxl-port-Add-disable-enable-port-command.patch

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