anitazha / rpms / ndctl

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