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