new file mode 100644
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0
+
+cxl-wait-sanitize(1)
+======================
+
+NAME
+----
+cxl-wait-sanitize - wait for a sanitize operation to complete
+
+SYNOPSIS
+--------
+[verse]
+'cxl wait-sanitize <mem0> [<mem1>..<memN>] [<options>]'
+
+DESCRIPTION
+-----------
+
+A sanitize take several seconds to complete. Block and wait for the
+sanitize operation to complete.
+
+EXAMPLE
+-------
+----
+# cxl wait-sanitize mem0
+sanitize completed on 1 mem device
+----
+
+OPTIONS
+-------
+
+include::bus-option.txt[]
+
+-t::
+--timeout::
+ Milliseconds to wait before timing out and returning. Defaults
+ to 5000.
+
+include::verbose-option.txt[]
+
+include::../copyright.txt[]
+
+SEE ALSO
+--------
+linkcxl:cxl-list[1],
@@ -72,6 +72,7 @@ const char *cxl_memdev_get_firmware_version(struct cxl_memdev *memdev);
size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev);
int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev);
int cxl_memdev_get_numa_node(struct cxl_memdev *memdev);
+int cxl_memdev_wait_sanitize(struct cxl_memdev *memdev);
----
A memdev is given a kernel device name of the form "mem%d" where an id
@@ -93,6 +94,10 @@ device.
cxl_memdev_get_numa_node() returns the affinitized CPU node number if
available or -1 otherwise.
+cxl_memdev_wait_sanitize() if a sanitize operation is in-flight when
+this is called the program will block until the sanitize operation
+completes or the wait times out.
+
=== MEMDEV: Control
----
int cxl_memdev_disable_invalidate(struct cxl_memdev *memdev);
@@ -47,6 +47,7 @@ cxl_manpages = [
'cxl-destroy-region.txt',
'cxl-monitor.txt',
'cxl-update-firmware.txt',
+ 'cxl-wait-sanitize.txt',
]
foreach man : cxl_manpages
@@ -15,6 +15,7 @@ int cmd_enable_memdev(int argc, const char **argv, struct cxl_ctx *ctx);
int cmd_reserve_dpa(int argc, const char **argv, struct cxl_ctx *ctx);
int cmd_free_dpa(int argc, const char **argv, struct cxl_ctx *ctx);
int cmd_update_fw(int argc, const char **argv, struct cxl_ctx *ctx);
+int cmd_wait_sanitize(int argc, const char **argv, struct cxl_ctx *ctx);
int cmd_disable_port(int argc, const char **argv, struct cxl_ctx *ctx);
int cmd_enable_port(int argc, const char **argv, struct cxl_ctx *ctx);
int cmd_set_partition(int argc, const char **argv, struct cxl_ctx *ctx);
@@ -69,6 +69,7 @@ static struct cmd_struct commands[] = {
{ "reserve-dpa", .c_fn = cmd_reserve_dpa },
{ "free-dpa", .c_fn = cmd_free_dpa },
{ "update-firmware", .c_fn = cmd_update_fw },
+ { "wait-sanitize", .c_fn = cmd_wait_sanitize },
{ "disable-port", .c_fn = cmd_disable_port },
{ "enable-port", .c_fn = cmd_enable_port },
{ "set-partition", .c_fn = cmd_set_partition },
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: LGPL-2.1
// Copyright (C) 2020-2021, Intel Corporation. All rights reserved.
+#include <poll.h>
#include <stdio.h>
#include <errno.h>
#include <limits.h>
@@ -1371,6 +1372,68 @@ CXL_EXPORT int cxl_memdev_get_id(struct cxl_memdev *memdev)
return memdev->id;
}
+CXL_EXPORT int cxl_memdev_wait_sanitize(struct cxl_memdev *memdev,
+ int timeout_ms)
+{
+ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
+ const char *devname = cxl_memdev_get_devname(memdev);
+ char *path = memdev->dev_buf;
+ int len = memdev->buf_len;
+ struct pollfd fds = { 0 };
+ int fd = 0, rc;
+ char buf[9];
+
+ if (snprintf(path, len, "%s/security/state", memdev->dev_path) >= len) {
+ err(ctx, "%s: buffer too small!\n", devname);
+ return -ERANGE;
+ }
+
+ fd = open(path, O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ /* device does not support security operations */
+ if (errno == ENOENT)
+ return 0;
+ rc = -errno;
+ err(ctx, "%s: %s\n", path, strerror(errno));
+ return rc;
+ }
+ memset(&fds, 0, sizeof(fds));
+ fds.fd = fd;
+
+ rc = pread(fd, buf, sizeof(buf), 0);
+ if (rc < 0) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* skipping if not currently sanitizing */
+ if (strncmp(buf, "sanitize", 8) != 0) {
+ rc = 0;
+ goto out;
+ }
+
+ rc = poll(&fds, 1, timeout_ms);
+ if (rc == 0) {
+ dbg(ctx, "%s: sanitize timeout\n", devname);
+ rc = -ETIMEDOUT;
+ } else if (rc < 0) {
+ err(ctx, "%s: sanitize poll error: %s\n", devname, strerror(errno));
+ rc = -errno;
+ } else {
+ dbg(ctx, "%s: sanitize wake\n", devname);
+
+ rc = pread(fd, buf, sizeof(buf), 0);
+ if (rc < 0 || strncmp(buf, "sanitize", 8) == 0) {
+ err(ctx, "%s: sanitize wake error\n", devname);
+ rc = -ENXIO;
+ } else
+ rc = 0;
+ }
+out:
+ close(fd);
+ return rc;
+}
+
CXL_EXPORT unsigned long long cxl_memdev_get_serial(struct cxl_memdev *memdev)
{
return memdev->serial;
@@ -264,3 +264,8 @@ global:
cxl_memdev_update_fw;
cxl_memdev_cancel_fw_update;
} LIBCXL_5;
+
+LIBCXL_7 {
+global:
+ cxl_memdev_wait_sanitize;
+} LIBCXL_6;
@@ -77,6 +77,7 @@ bool cxl_memdev_fw_update_in_progress(struct cxl_memdev *memdev);
size_t cxl_memdev_fw_update_get_remaining(struct cxl_memdev *memdev);
int cxl_memdev_update_fw(struct cxl_memdev *memdev, const char *fw_path);
int cxl_memdev_cancel_fw_update(struct cxl_memdev *memdev);
+int cxl_memdev_wait_sanitize(struct cxl_memdev *memdev, int timeout_ms);
/* ABI spelling mistakes are forever */
static inline const char *cxl_memdev_get_firmware_version(
@@ -38,7 +38,10 @@ static struct parameters {
const char *type;
const char *size;
const char *decoder_filter;
-} param;
+ int timeout;
+} param = {
+ .timeout = -1,
+};
static struct log_ctx ml;
@@ -99,6 +102,10 @@ OPT_BOOLEAN('c', "cancel", ¶m.cancel, \
OPT_BOOLEAN('w', "wait", ¶m.wait, \
"wait for firmware update to complete before returning")
+#define WAIT_SANITIZE_OPTIONS() \
+OPT_INTEGER('t', "timeout", ¶m.timeout, \
+ "time in milliseconds to wait for overwrite completion (default: infinite)")
+
static const struct option read_options[] = {
BASE_OPTIONS(),
LABEL_OPTIONS(),
@@ -155,6 +162,12 @@ static const struct option update_fw_options[] = {
OPT_END(),
};
+static const struct option wait_sanitize_options[] = {
+ BASE_OPTIONS(),
+ WAIT_SANITIZE_OPTIONS(),
+ OPT_END(),
+};
+
enum reserve_dpa_mode {
DPA_ALLOC,
DPA_FREE,
@@ -673,6 +686,12 @@ out_err:
return rc;
}
+static int action_wait_sanitize(struct cxl_memdev *memdev,
+ struct action_context *actx)
+{
+ return cxl_memdev_wait_sanitize(memdev, param.timeout);
+}
+
static int action_update_fw(struct cxl_memdev *memdev,
struct action_context *actx)
{
@@ -968,3 +987,16 @@ int cmd_update_fw(int argc, const char **argv, struct cxl_ctx *ctx)
return count >= 0 ? 0 : EXIT_FAILURE;
}
+
+int cmd_wait_sanitize(int argc, const char **argv, struct cxl_ctx *ctx)
+{
+ int count = memdev_action(
+ argc, argv, ctx, action_wait_sanitize, wait_sanitize_options,
+ "cxl wait-sanitize <mem0> [<mem1>..<memn>] [<options>]");
+
+ log_info(&ml, "wait sanitize %s on %d mem device%s\n",
+ count >= 0 ? "completed" : "failed", count >= 0 ? count : 0,
+ count > 1 ? "s" : "");
+
+ return count >= 0 ? 0 : EXIT_FAILURE;
+}
In support of a regression test for the kernel's sanitize completion notifier, add a "wait-sanitize" command and supporting infrastructure. Cc: Davidlohr Bueso <dave@stgolabs.net> Signed-off-by: Dan Williams <dan.j.williams@intel.com> --- Documentation/cxl/cxl-wait-sanitize.txt | 44 ++++++++++++++++++++++ Documentation/cxl/lib/libcxl.txt | 5 ++ Documentation/cxl/meson.build | 1 cxl/builtin.h | 1 cxl/cxl.c | 1 cxl/lib/libcxl.c | 63 +++++++++++++++++++++++++++++++ cxl/lib/libcxl.sym | 5 ++ cxl/libcxl.h | 1 cxl/memdev.c | 34 ++++++++++++++++- 9 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 Documentation/cxl/cxl-wait-sanitize.txt