Blame SOURCES/0115-cxl-memdev-Enable-disable-support.patch

26ccd9
From 782694f9aeff6e146cfd00b31822995790546175 Mon Sep 17 00:00:00 2001
26ccd9
From: Dan Williams <dan.j.williams@intel.com>
26ccd9
Date: Sun, 23 Jan 2022 16:54:22 -0800
26ccd9
Subject: [PATCH 115/217] cxl/memdev: Enable / disable support
26ccd9
26ccd9
Introduce the 'cxl {enable,disable}-memdev' commands. When a memdev is
26ccd9
disabled the ports in the topology may be unregistered. CXL memory regions
26ccd9
require each endpoint in the interleave to attach to the cxl_mem driver
26ccd9
before regions are activated.
26ccd9
26ccd9
Note that this starts out with the deliberate bug that it has false
26ccd9
positive detection of active memdevs. The fix for that bug requires kernel
26ccd9
support to detect the device's active participation in a region, until then
26ccd9
require all disable attempts to specify the --force override. This way
26ccd9
there are never any releases of cxl-cli that lack disable-memdev safety.
26ccd9
26ccd9
Link: https://lore.kernel.org/r/164298566245.3021641.12696907310209056878.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-memdev.txt | 37 +++++++++++++++
26ccd9
 Documentation/cxl/cxl-enable-memdev.txt  | 34 ++++++++++++++
26ccd9
 Documentation/cxl/lib/libcxl.txt         | 23 +++++++++
26ccd9
 Documentation/cxl/meson.build            |  2 +
26ccd9
 cxl/builtin.h                            |  2 +
26ccd9
 cxl/cxl.c                                |  2 +
26ccd9
 cxl/lib/libcxl.c                         | 58 +++++++++++++++++++++++
26ccd9
 cxl/lib/libcxl.sym                       |  2 +
26ccd9
 cxl/libcxl.h                             |  2 +
26ccd9
 cxl/memdev.c                             | 60 +++++++++++++++++++++++-
26ccd9
 10 files changed, 221 insertions(+), 1 deletion(-)
26ccd9
 create mode 100644 Documentation/cxl/cxl-disable-memdev.txt
26ccd9
 create mode 100644 Documentation/cxl/cxl-enable-memdev.txt
26ccd9
26ccd9
diff --git a/Documentation/cxl/cxl-disable-memdev.txt b/Documentation/cxl/cxl-disable-memdev.txt
26ccd9
new file mode 100644
26ccd9
index 0000000..edd5385
26ccd9
--- /dev/null
26ccd9
+++ b/Documentation/cxl/cxl-disable-memdev.txt
26ccd9
@@ -0,0 +1,37 @@
26ccd9
+// SPDX-License-Identifier: GPL-2.0
26ccd9
+
26ccd9
+cxl-disable-memdev(1)
26ccd9
+=====================
26ccd9
+
26ccd9
+NAME
26ccd9
+----
26ccd9
+cxl-disable-memdev - deactivate / hot-remove a given CXL memdev
26ccd9
+
26ccd9
+SYNOPSIS
26ccd9
+--------
26ccd9
+[verse]
26ccd9
+'cxl disable-memdev' <mem0> [<mem1>..<memN>] [<options>]
26ccd9
+
26ccd9
+
26ccd9
+OPTIONS
26ccd9
+-------
26ccd9
+<memory device(s)>::
26ccd9
+include::memdev-option.txt[]
26ccd9
+
26ccd9
+-f::
26ccd9
+--force::
26ccd9
+	DANGEROUS: Override the safety measure that blocks attempts to disable
26ccd9
+	a device if the tool determines the memdev is in active usage. Recall
26ccd9
+	that CXL memory ranges might have been established by platform
26ccd9
+	firmware and disabling an active device is akin to force removing
26ccd9
+	memory from a running system.
26ccd9
+
26ccd9
+-v::
26ccd9
+	Turn on verbose debug messages in the library (if libcxl was built with
26ccd9
+	logging and debug enabled).
26ccd9
+
26ccd9
+include::../copyright.txt[]
26ccd9
+
26ccd9
+SEE ALSO
26ccd9
+--------
26ccd9
+linkcxl:cxl-enable-memdev[1]
26ccd9
diff --git a/Documentation/cxl/cxl-enable-memdev.txt b/Documentation/cxl/cxl-enable-memdev.txt
26ccd9
new file mode 100644
26ccd9
index 0000000..088d5e0
26ccd9
--- /dev/null
26ccd9
+++ b/Documentation/cxl/cxl-enable-memdev.txt
26ccd9
@@ -0,0 +1,34 @@
26ccd9
+// SPDX-License-Identifier: GPL-2.0
26ccd9
+
26ccd9
+cxl-enable-memdev(1)
26ccd9
+====================
26ccd9
+
26ccd9
+NAME
26ccd9
+----
26ccd9
+cxl-enable-memdev - activate / hot-add a given CXL memdev
26ccd9
+
26ccd9
+SYNOPSIS
26ccd9
+--------
26ccd9
+[verse]
26ccd9
+'cxl enable-memdev' <mem0> [<mem1>..<memN>] [<options>]
26ccd9
+
26ccd9
+A memdev typically autoenables at initial device discovery. However, if
26ccd9
+it was manually disabled this command can trigger the kernel to activate
26ccd9
+it again. This involves detecting the state of the HDM (Host Managed
26ccd9
+Device Memory) Decoders and validating that CXL.mem is enabled for each
26ccd9
+port in the device's hierarchy.
26ccd9
+
26ccd9
+OPTIONS
26ccd9
+-------
26ccd9
+<memory device(s)>::
26ccd9
+include::memdev-option.txt[]
26ccd9
+
26ccd9
+-v::
26ccd9
+	Turn on verbose debug messages in the library (if libcxl was built with
26ccd9
+	logging and debug enabled).
26ccd9
+
26ccd9
+include::../copyright.txt[]
26ccd9
+
26ccd9
+SEE ALSO
26ccd9
+--------
26ccd9
+linkcxl:cxl-disable-memdev[1]
26ccd9
diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt
26ccd9
index de88d19..49edb71 100644
26ccd9
--- a/Documentation/cxl/lib/libcxl.txt
26ccd9
+++ b/Documentation/cxl/lib/libcxl.txt
26ccd9
@@ -93,6 +93,29 @@ device.
26ccd9
 cxl_memdev_get_numa_node() returns the affinitized CPU node number if
26ccd9
 available or -1 otherwise.
26ccd9
 
26ccd9
+=== MEMDEV: Control
26ccd9
+----
26ccd9
+int cxl_memdev_disable_invalidate(struct cxl_memdev *memdev);
26ccd9
+int cxl_memdev_enable(struct cxl_memdev *memdev);
26ccd9
+----
26ccd9
+When a memory device is disabled it unregisters its associated endpoints
26ccd9
+and potentially intervening switch ports if there are no other memdevs
26ccd9
+pinning that port active. That means that any existing port objects that
26ccd9
+the library has previously returned are in valid and need to be re-read.
26ccd9
+Callers must be careful to re-retrieve port objects after
26ccd9
+cxl_memdev_disable_invalidate(). Any usage of a previously obtained port
26ccd9
+object after a cxl_memdev_disable_invalidate() call is a use-after-free
26ccd9
+programming error. It follows that after cxl_memdev_enable() new ports
26ccd9
+may appear in the topology that were not previously enumerable.
26ccd9
+
26ccd9
+NOTE: cxl_memdev_disable_invalidate() will force disable the memdev
26ccd9
+regardless of whether the memory provided by the device is in active use
26ccd9
+by the operating system. Callers take responisbility for assuring that
26ccd9
+it is safe to disable the memory device. Otherwise, this call can be as
26ccd9
+destructive as ripping a DIMM out of a running system. Like all other
26ccd9
+libcxl calls that mutate the system state or divulge security sensitive
26ccd9
+information this call requires root / CAP_SYS_ADMIN.
26ccd9
+
26ccd9
 === MEMDEV: Commands
26ccd9
 ----
26ccd9
 struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, int opcode);
26ccd9
diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build
26ccd9
index 0a6346b..7618c97 100644
26ccd9
--- a/Documentation/cxl/meson.build
26ccd9
+++ b/Documentation/cxl/meson.build
26ccd9
@@ -30,6 +30,8 @@ cxl_manpages = [
26ccd9
   'cxl-read-labels.txt',
26ccd9
   'cxl-write-labels.txt',
26ccd9
   'cxl-zero-labels.txt',
26ccd9
+  'cxl-enable-memdev.txt',
26ccd9
+  'cxl-disable-memdev.txt',
26ccd9
 ]
26ccd9
 
26ccd9
 foreach man : cxl_manpages
26ccd9
diff --git a/cxl/builtin.h b/cxl/builtin.h
26ccd9
index 78eca6e..621c85c 100644
26ccd9
--- a/cxl/builtin.h
26ccd9
+++ b/cxl/builtin.h
26ccd9
@@ -10,4 +10,6 @@ int cmd_read_labels(int argc, const char **argv, struct cxl_ctx *ctx);
26ccd9
 int cmd_zero_labels(int argc, const char **argv, struct cxl_ctx *ctx);
26ccd9
 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
 #endif /* _CXL_BUILTIN_H_ */
26ccd9
diff --git a/cxl/cxl.c b/cxl/cxl.c
26ccd9
index 4b1661d..78d2e9a 100644
26ccd9
--- a/cxl/cxl.c
26ccd9
+++ b/cxl/cxl.c
26ccd9
@@ -64,6 +64,8 @@ static struct cmd_struct commands[] = {
26ccd9
 	{ "zero-labels", .c_fn = cmd_zero_labels },
26ccd9
 	{ "read-labels", .c_fn = cmd_read_labels },
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
 };
26ccd9
 
26ccd9
 int main(int argc, const char **argv)
26ccd9
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
26ccd9
index 14c7db8..2fdaf71 100644
26ccd9
--- a/cxl/lib/libcxl.c
26ccd9
+++ b/cxl/lib/libcxl.c
26ccd9
@@ -500,6 +500,64 @@ CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev
26ccd9
 	return memdev->firmware_version;
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
+		return 0;
26ccd9
+
26ccd9
+	bus = cxl_memdev_get_bus(memdev);
26ccd9
+	if (!bus) {
26ccd9
+		err(ctx, "%s: failed to invalidate\n", devname);
26ccd9
+		return -ENXIO;
26ccd9
+	}
26ccd9
+
26ccd9
+	util_unbind(memdev->dev_path, ctx);
26ccd9
+
26ccd9
+	if (cxl_memdev_is_enabled(memdev)) {
26ccd9
+		err(ctx, "%s: failed to disable\n", devname);
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
+
26ccd9
+	dbg(ctx, "%s: disabled\n", devname);
26ccd9
+
26ccd9
+	return 0;
26ccd9
+}
26ccd9
+
26ccd9
+CXL_EXPORT int cxl_memdev_enable(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
+
26ccd9
+	if (cxl_memdev_is_enabled(memdev))
26ccd9
+		return 0;
26ccd9
+
26ccd9
+	util_bind(devname, memdev->module, "cxl", ctx);
26ccd9
+
26ccd9
+	if (!cxl_memdev_is_enabled(memdev)) {
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
 static struct cxl_endpoint *cxl_port_find_endpoint(struct cxl_port *parent_port,
26ccd9
 						   struct cxl_memdev *memdev)
26ccd9
 {
26ccd9
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
26ccd9
index b13a2d6..f235e99 100644
26ccd9
--- a/cxl/lib/libcxl.sym
26ccd9
+++ b/cxl/lib/libcxl.sym
26ccd9
@@ -115,4 +115,6 @@ global:
26ccd9
 	cxl_memdev_get_endpoint;
26ccd9
 	cxl_memdev_is_enabled;
26ccd9
 	cxl_memdev_get_bus;
26ccd9
+	cxl_memdev_disable_invalidate;
26ccd9
+	cxl_memdev_enable;
26ccd9
 } LIBCXL_1;
26ccd9
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
26ccd9
index be656ed..53f68dd 100644
26ccd9
--- a/cxl/libcxl.h
26ccd9
+++ b/cxl/libcxl.h
26ccd9
@@ -48,6 +48,8 @@ unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev);
26ccd9
 unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev);
26ccd9
 const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev);
26ccd9
 size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev);
26ccd9
+int cxl_memdev_disable_invalidate(struct cxl_memdev *memdev);
26ccd9
+int cxl_memdev_enable(struct cxl_memdev *memdev);
26ccd9
 struct cxl_endpoint;
26ccd9
 struct cxl_endpoint *cxl_memdev_get_endpoint(struct cxl_memdev *memdev);
26ccd9
 int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev);
26ccd9
diff --git a/cxl/memdev.c b/cxl/memdev.c
26ccd9
index ef5343a..90b33e1 100644
26ccd9
--- a/cxl/memdev.c
26ccd9
+++ b/cxl/memdev.c
26ccd9
@@ -25,13 +25,14 @@ static struct parameters {
26ccd9
 	unsigned offset;
26ccd9
 	bool verbose;
26ccd9
 	bool serial;
26ccd9
+	bool force;
26ccd9
 } param;
26ccd9
 
26ccd9
 static struct log_ctx ml;
26ccd9
 
26ccd9
 #define BASE_OPTIONS() \
26ccd9
 OPT_BOOLEAN('v',"verbose", &param.verbose, "turn on debug"), \
26ccd9
-OPT_BOOLEAN('S', "serial", &param.serial, "user serials numbers to id memdevs")
26ccd9
+OPT_BOOLEAN('S', "serial", &param.serial, "use serial numbers to id memdevs")
26ccd9
 
26ccd9
 #define READ_OPTIONS() \
26ccd9
 OPT_STRING('o', "output", &param.outfile, "output-file", \
26ccd9
@@ -46,6 +47,10 @@ OPT_UINTEGER('s', "size", &param.len, "number of label bytes to operate"), \
26ccd9
 OPT_UINTEGER('O', "offset", &param.offset, \
26ccd9
 	"offset into the label area to start operation")
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 read_options[] = {
26ccd9
 	BASE_OPTIONS(),
26ccd9
 	LABEL_OPTIONS(),
26ccd9
@@ -66,6 +71,37 @@ static const struct option zero_options[] = {
26ccd9
 	OPT_END(),
26ccd9
 };
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
+	OPT_END(),
26ccd9
+};
26ccd9
+
26ccd9
+static int action_disable(struct cxl_memdev *memdev, struct action_context *actx)
26ccd9
+{
26ccd9
+	if (!cxl_memdev_is_enabled(memdev))
26ccd9
+		return 0;
26ccd9
+
26ccd9
+	if (!param.force) {
26ccd9
+		/* TODO: actually detect rather than assume active */
26ccd9
+		log_err(&ml, "%s is part of an active region\n",
26ccd9
+			cxl_memdev_get_devname(memdev));
26ccd9
+		return -EBUSY;
26ccd9
+	}
26ccd9
+
26ccd9
+	return cxl_memdev_disable_invalidate(memdev);
26ccd9
+}
26ccd9
+
26ccd9
+static int action_enable(struct cxl_memdev *memdev, struct action_context *actx)
26ccd9
+{
26ccd9
+	return cxl_memdev_enable(memdev);
26ccd9
+}
26ccd9
+
26ccd9
 static int action_zero(struct cxl_memdev *memdev, struct action_context *actx)
26ccd9
 {
26ccd9
 	size_t size;
26ccd9
@@ -340,3 +376,25 @@ int cmd_zero_labels(int argc, const char **argv, struct cxl_ctx *ctx)
26ccd9
 		 count > 1 ? "s" : "");
26ccd9
 	return count >= 0 ? 0 : EXIT_FAILURE;
26ccd9
 }
26ccd9
+
26ccd9
+int cmd_disable_memdev(int argc, const char **argv, struct cxl_ctx *ctx)
26ccd9
+{
26ccd9
+	int count = memdev_action(
26ccd9
+		argc, argv, ctx, action_disable, disable_options,
26ccd9
+		"cxl disable-memdev <mem0> [<mem1>..<memN>] [<options>]");
26ccd9
+
26ccd9
+	log_info(&ml, "disabled %d mem%s\n", count >= 0 ? count : 0,
26ccd9
+		 count > 1 ? "s" : "");
26ccd9
+	return count >= 0 ? 0 : EXIT_FAILURE;
26ccd9
+}
26ccd9
+
26ccd9
+int cmd_enable_memdev(int argc, const char **argv, struct cxl_ctx *ctx)
26ccd9
+{
26ccd9
+	int count = memdev_action(
26ccd9
+		argc, argv, ctx, action_enable, enable_options,
26ccd9
+		"cxl enable-memdev <mem0> [<mem1>..<memN>] [<options>]");
26ccd9
+
26ccd9
+	log_info(&ml, "enabled %d mem%s\n", count >= 0 ? count : 0,
26ccd9
+		 count > 1 ? "s" : "");
26ccd9
+	return count >= 0 ? 0 : EXIT_FAILURE;
26ccd9
+}
26ccd9
-- 
26ccd9
2.27.0
26ccd9