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

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