anitazha / rpms / ndctl

Forked from rpms/ndctl a year ago
Clone

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

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", &param.debug, "turn on debug"),                \
e0018b
+OPT_BOOLEAN('e', "endpoint", &param.endpoint,                          \
e0018b
+	    "target endpoints instead of switch ports")
e0018b
+
e0018b
+#define ENABLE_OPTIONS()                                               \
e0018b
+OPT_BOOLEAN('m', "enable-memdevs", &param.memdevs,                   \
e0018b
+	    "enable downstream memdev(s)")
e0018b
+
e0018b
+#define DISABLE_OPTIONS()                                              \
e0018b
+OPT_BOOLEAN('f', "force", &param.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