new file mode 100644
@@ -0,0 +1,289 @@
+From 83d60a4bd13d7dafed1f9eaf28bdf8fddb5a676d Mon Sep 17 00:00:00 2001
+From: Davidlohr Bueso <dave@stgolabs.net>
+Date: Mon, 30 Sep 2024 13:09:38 -0700
+Subject: [PATCH v2 -ndctl] cxl/memdev: Introduce sanitize-memdev functionality
+
+Add a new cxl_memdev_sanitize() to libcxl to support triggering memory
+device sanitation, in either Sanitize and/or Secure Erase, per the
+CXL 3.0 spec.
+
+This is analogous to 'ndctl sanitize-dimm'.
+
+Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
+---
+
+Changes from v1:
+ - remove '-s' option and allow sanitize and secure erase to
+ be exclusive. (Vishal)
+ - update cxl-sanitize test cases (Alison)
+ - update wait-sanitize manpage to include sanitize-memdev cmd
+
+ Documentation/cxl/cxl-sanitize-memdev.txt | 52 +++++++++++++++++++++++
+ Documentation/cxl/cxl-wait-sanitize.txt | 1 +
+ Documentation/cxl/meson.build | 1 +
+ cxl/builtin.h | 1 +
+ cxl/cxl.c | 1 +
+ cxl/lib/libcxl.c | 15 +++++++
+ cxl/lib/libcxl.sym | 1 +
+ cxl/libcxl.h | 1 +
+ cxl/memdev.c | 41 ++++++++++++++++++
+ test/cxl-sanitize.sh | 4 +-
+ 10 files changed, 116 insertions(+), 2 deletions(-)
+ create mode 100644 Documentation/cxl/cxl-sanitize-memdev.txt
+
+diff --git a/Documentation/cxl/cxl-sanitize-memdev.txt b/Documentation/cxl/cxl-sanitize-memdev.txt
+new file mode 100644
+index 000000000000..7a7c9a79b19f
+--- /dev/null
++++ b/Documentation/cxl/cxl-sanitize-memdev.txt
+@@ -0,0 +1,52 @@
++// SPDX-License-Identifier: GPL-2.0
++
++cxl-sanitize-memdev(1)
++======================
++
++NAME
++----
++cxl-sanitize-memdev - Perform a cryptographic destruction or sanitization
++of the contents of the given memdev(s).
++
++SYNOPSIS
++--------
++[verse]
++'cxl sanitize-memdev <mem0> [<mem1>..<memN>] [<options>]'
++
++DESCRIPTION
++-----------
++The 'sanitize-memdev' command performs two different methods of sanitization,
++per the CXL 3.0+ specification. The default is 'sanitize', but additionally,
++a 'secure-erase' option is available. It is required that the memdev be
++disabled before sanitizing, such that the device cannot be actively decoding
++any HPA ranges at the time.
++
++A device Sanitize is meant to securely re-purpose or decommission it. This
++is done by ensuring that all user data and meta data, whether it resides
++in persistent capacity, volatile capacity, or the label storage area,
++is made permanently unavailable by whatever means is appropriate for
++the media type. This sanitization request is merely submitted to the
++kernel, and the completion is asynchronous. Depending on the medium and
++capacity, sanitize may take tens of minutes to many hours. Subsequently,
++'cxl wait-sanitize’ can be used to wait for the memdevs that are under
++the sanitization.
++
++OPTIONS
++-------
++
++include::bus-option.txt[]
++
++-e::
++--secure-erase::
++ Erase user data by changing the media encryption keys for all user
++ data areas of the device.
++
++include::verbose-option.txt[]
++
++include::../copyright.txt[]
++
++SEE ALSO
++--------
++linkcxl:cxl-wait-sanitize[1],
++linkcxl:cxl-disable-memdev[1],
++linkcxl:cxl-list[1],
+diff --git a/Documentation/cxl/cxl-wait-sanitize.txt b/Documentation/cxl/cxl-wait-sanitize.txt
+index e8f2044e4882..5e895808cf36 100644
+--- a/Documentation/cxl/cxl-wait-sanitize.txt
++++ b/Documentation/cxl/cxl-wait-sanitize.txt
+@@ -42,3 +42,4 @@ include::../copyright.txt[]
+ SEE ALSO
+ --------
+ linkcxl:cxl-list[1],
++linkcxl:cxl-sanitize-memdev[1],
+diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build
+index 8085c1c2c87e..99e6ee782a1c 100644
+--- a/Documentation/cxl/meson.build
++++ b/Documentation/cxl/meson.build
+@@ -49,6 +49,7 @@ cxl_manpages = [
+ 'cxl-monitor.txt',
+ 'cxl-update-firmware.txt',
+ 'cxl-set-alert-config.txt',
++ 'cxl-sanitize-memdev.txt',
+ 'cxl-wait-sanitize.txt',
+ ]
+
+diff --git a/cxl/builtin.h b/cxl/builtin.h
+index c483f301e5e0..29c8ad2a0ad9 100644
+--- a/cxl/builtin.h
++++ b/cxl/builtin.h
+@@ -16,6 +16,7 @@ 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_set_alert_config(int argc, const char **argv, struct cxl_ctx *ctx);
++int cmd_sanitize_memdev(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);
+diff --git a/cxl/cxl.c b/cxl/cxl.c
+index 16436671dc53..9c9f217c5a93 100644
+--- a/cxl/cxl.c
++++ b/cxl/cxl.c
+@@ -80,6 +80,7 @@ static struct cmd_struct commands[] = {
+ { "disable-region", .c_fn = cmd_disable_region },
+ { "destroy-region", .c_fn = cmd_destroy_region },
+ { "monitor", .c_fn = cmd_monitor },
++ { "sanitize-memdev", .c_fn = cmd_sanitize_memdev },
+ };
+
+ int main(int argc, const char **argv)
+diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
+index 91eedd1c4688..4f44bf1b6185 100644
+--- a/cxl/lib/libcxl.c
++++ b/cxl/lib/libcxl.c
+@@ -1414,6 +1414,21 @@ CXL_EXPORT int cxl_memdev_get_id(struct cxl_memdev *memdev)
+ return memdev->id;
+ }
+
++CXL_EXPORT int cxl_memdev_sanitize(struct cxl_memdev *memdev, char *op)
++{
++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
++ char *path = memdev->dev_buf;
++ int len = memdev->buf_len;
++
++ if (snprintf(path, len,
++ "%s/security/%s", memdev->dev_path, op) >= len) {
++ err(ctx, "%s: buffer too small!\n",
++ cxl_memdev_get_devname(memdev));
++ return -ERANGE;
++ }
++ return sysfs_write_attr(ctx, path, "1\n");
++}
++
+ CXL_EXPORT int cxl_memdev_wait_sanitize(struct cxl_memdev *memdev,
+ int timeout_ms)
+ {
+diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
+index 304d7fa735d4..89a4c63cb874 100644
+--- a/cxl/lib/libcxl.sym
++++ b/cxl/lib/libcxl.sym
+@@ -281,4 +281,5 @@ global:
+ cxl_memdev_get_ram_qos_class;
+ cxl_region_qos_class_mismatch;
+ cxl_port_decoders_committed;
++ cxl_memdev_sanitize;
+ } LIBCXL_6;
+diff --git a/cxl/libcxl.h b/cxl/libcxl.h
+index fc6dd0085440..a722bab8a65b 100644
+--- a/cxl/libcxl.h
++++ b/cxl/libcxl.h
+@@ -79,6 +79,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_sanitize(struct cxl_memdev *memdev, char *op);
+ int cxl_memdev_wait_sanitize(struct cxl_memdev *memdev, int timeout_ms);
+
+ /* ABI spelling mistakes are forever */
+diff --git a/cxl/memdev.c b/cxl/memdev.c
+index 6e44d1578d03..00040d64a37f 100644
+--- a/cxl/memdev.c
++++ b/cxl/memdev.c
+@@ -35,6 +35,8 @@ static struct parameters {
+ bool align;
+ bool cancel;
+ bool wait;
++ bool sanitize;
++ bool secure_erase;
+ const char *type;
+ const char *size;
+ const char *decoder_filter;
+@@ -160,6 +162,10 @@ OPT_STRING('\0', "pmem-err-alert", \
+ ¶m.corrected_pmem_err_alert, "'on' or 'off'", \
+ "enable or disable corrected pmem error warning alert")
+
++#define SANITIZE_OPTIONS() \
++OPT_BOOLEAN('e', "secure-erase", ¶m.secure_erase, \
++ "Secure Erase a memdev")
++
+ #define WAIT_SANITIZE_OPTIONS() \
+ OPT_INTEGER('t', "timeout", ¶m.timeout, \
+ "time in milliseconds to wait for overwrite completion (default: infinite)")
+@@ -226,6 +232,12 @@ static const struct option set_alert_options[] = {
+ OPT_END(),
+ };
+
++static const struct option sanitize_options[] = {
++ BASE_OPTIONS(),
++ SANITIZE_OPTIONS(),
++ OPT_END(),
++};
++
+ static const struct option wait_sanitize_options[] = {
+ BASE_OPTIONS(),
+ WAIT_SANITIZE_OPTIONS(),
+@@ -772,6 +784,22 @@ out_err:
+ return rc;
+ }
+
++static int action_sanitize_memdev(struct cxl_memdev *memdev,
++ struct action_context *actx)
++{
++ int rc = 0;
++
++ if (cxl_memdev_is_enabled(memdev))
++ return -EBUSY;
++
++ if (param.secure_erase)
++ rc = cxl_memdev_sanitize(memdev, "erase");
++ else
++ rc = cxl_memdev_sanitize(memdev, "sanitize");
++
++ return rc;
++}
++
+ static int action_wait_sanitize(struct cxl_memdev *memdev,
+ struct action_context *actx)
+ {
+@@ -1228,6 +1256,19 @@ int cmd_set_alert_config(int argc, const char **argv, struct cxl_ctx *ctx)
+ return count >= 0 ? 0 : EXIT_FAILURE;
+ }
+
++int cmd_sanitize_memdev(int argc, const char **argv, struct cxl_ctx *ctx)
++{
++ int count = memdev_action(
++ argc, argv, ctx, action_sanitize_memdev, sanitize_options,
++ "cxl sanitize-memdev <mem0> [<mem1>..<memn>] [<options>]");
++
++ log_info(&ml, "sanitize %s on %d mem device%s\n",
++ count >= 0 ? "completed/started" : "failed",
++ count >= 0 ? count : 0, count > 1 ? "s" : "");
++
++ return count >= 0 ? 0 : EXIT_FAILURE;
++}
++
+ int cmd_wait_sanitize(int argc, const char **argv, struct cxl_ctx *ctx)
+ {
+ int count = memdev_action(
+diff --git a/test/cxl-sanitize.sh b/test/cxl-sanitize.sh
+index 9c161014ccb7..8c5027ab9f48 100644
+--- a/test/cxl-sanitize.sh
++++ b/test/cxl-sanitize.sh
+@@ -45,7 +45,7 @@ count=${#active_mem[@]}
+ set_timeout ${active_mem[0]} 2000
+
+ # sanitize with an active memdev should fail
+-echo 1 > /sys/bus/cxl/devices/${active_mem[0]}/security/sanitize && err $LINENO
++"$CXL" sanitize-memdev ${active_mem[0]} && err $LINENO
+
+ # find an inactive mem
+ inactive=""
+@@ -67,7 +67,7 @@ done
+ # secounds
+ set_timeout $inactive 3000
+ start=$SECONDS
+-echo 1 > /sys/bus/cxl/devices/${inactive}/security/sanitize &
++"$CXL" sanitize-memdev $inactive || err $LINENO
+ "$CXL" wait-sanitize $inactive || err $LINENO
+ ((SECONDS > start + 2)) || err $LINENO
+
+--
+2.46.1
+
@@ -42,3 +42,4 @@ include::../copyright.txt[]
SEE ALSO
--------
linkcxl:cxl-list[1],
+linkcxl:cxl-sanitize-memdev[1],
@@ -49,6 +49,7 @@ cxl_manpages = [
'cxl-monitor.txt',
'cxl-update-firmware.txt',
'cxl-set-alert-config.txt',
+ 'cxl-sanitize-memdev.txt',
'cxl-wait-sanitize.txt',
]
@@ -16,6 +16,7 @@ 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_set_alert_config(int argc, const char **argv, struct cxl_ctx *ctx);
+int cmd_sanitize_memdev(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);
@@ -80,6 +80,7 @@ static struct cmd_struct commands[] = {
{ "disable-region", .c_fn = cmd_disable_region },
{ "destroy-region", .c_fn = cmd_destroy_region },
{ "monitor", .c_fn = cmd_monitor },
+ { "sanitize-memdev", .c_fn = cmd_sanitize_memdev },
};
int main(int argc, const char **argv)
@@ -1414,6 +1414,21 @@ CXL_EXPORT int cxl_memdev_get_id(struct cxl_memdev *memdev)
return memdev->id;
}
+CXL_EXPORT int cxl_memdev_sanitize(struct cxl_memdev *memdev, char *op)
+{
+ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
+ char *path = memdev->dev_buf;
+ int len = memdev->buf_len;
+
+ if (snprintf(path, len,
+ "%s/security/%s", memdev->dev_path, op) >= len) {
+ err(ctx, "%s: buffer too small!\n",
+ cxl_memdev_get_devname(memdev));
+ return -ERANGE;
+ }
+ return sysfs_write_attr(ctx, path, "1\n");
+}
+
CXL_EXPORT int cxl_memdev_wait_sanitize(struct cxl_memdev *memdev,
int timeout_ms)
{
@@ -287,4 +287,5 @@ LIBECXL_8 {
global:
cxl_memdev_trigger_poison_list;
cxl_region_trigger_poison_list;
+ cxl_memdev_sanitize;
} LIBCXL_7;
@@ -79,6 +79,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_sanitize(struct cxl_memdev *memdev, char *op);
int cxl_memdev_wait_sanitize(struct cxl_memdev *memdev, int timeout_ms);
/* ABI spelling mistakes are forever */
@@ -35,6 +35,8 @@ static struct parameters {
bool align;
bool cancel;
bool wait;
+ bool sanitize;
+ bool secure_erase;
const char *type;
const char *size;
const char *decoder_filter;
@@ -160,6 +162,10 @@ OPT_STRING('\0', "pmem-err-alert", \
¶m.corrected_pmem_err_alert, "'on' or 'off'", \
"enable or disable corrected pmem error warning alert")
+#define SANITIZE_OPTIONS() \
+OPT_BOOLEAN('e', "secure-erase", ¶m.secure_erase, \
+ "Secure Erase a memdev")
+
#define WAIT_SANITIZE_OPTIONS() \
OPT_INTEGER('t', "timeout", ¶m.timeout, \
"time in milliseconds to wait for overwrite completion (default: infinite)")
@@ -226,6 +232,12 @@ static const struct option set_alert_options[] = {
OPT_END(),
};
+static const struct option sanitize_options[] = {
+ BASE_OPTIONS(),
+ SANITIZE_OPTIONS(),
+ OPT_END(),
+};
+
static const struct option wait_sanitize_options[] = {
BASE_OPTIONS(),
WAIT_SANITIZE_OPTIONS(),
@@ -772,6 +784,19 @@ out_err:
return rc;
}
+static int action_sanitize_memdev(struct cxl_memdev *memdev,
+ struct action_context *actx)
+{
+ int rc = 0;
+
+ if (param.secure_erase)
+ rc = cxl_memdev_sanitize(memdev, "erase");
+ else
+ rc = cxl_memdev_sanitize(memdev, "sanitize");
+
+ return rc;
+}
+
static int action_wait_sanitize(struct cxl_memdev *memdev,
struct action_context *actx)
{
@@ -1228,6 +1253,19 @@ int cmd_set_alert_config(int argc, const char **argv, struct cxl_ctx *ctx)
return count >= 0 ? 0 : EXIT_FAILURE;
}
+int cmd_sanitize_memdev(int argc, const char **argv, struct cxl_ctx *ctx)
+{
+ int count = memdev_action(
+ argc, argv, ctx, action_sanitize_memdev, sanitize_options,
+ "cxl sanitize-memdev <mem0> [<mem1>..<memn>] [<options>]");
+
+ log_info(&ml, "sanitize %s on %d mem device%s\n",
+ count >= 0 ? "completed/started" : "failed",
+ count >= 0 ? count : 0, count > 1 ? "s" : "");
+
+ return count >= 0 ? 0 : EXIT_FAILURE;
+}
+
int cmd_wait_sanitize(int argc, const char **argv, struct cxl_ctx *ctx)
{
int count = memdev_action(
@@ -45,7 +45,7 @@ count=${#active_mem[@]}
set_timeout ${active_mem[0]} 2000
# sanitize with an active memdev should fail
-echo 1 > /sys/bus/cxl/devices/${active_mem[0]}/security/sanitize && err $LINENO
+"$CXL" sanitize-memdev ${active_mem[0]} && err $LINENO
# find an inactive mem
inactive=""
@@ -67,7 +67,7 @@ done
# secounds
set_timeout $inactive 3000
start=$SECONDS
-echo 1 > /sys/bus/cxl/devices/${inactive}/security/sanitize &
+"$CXL" sanitize-memdev $inactive || err $LINENO
"$CXL" wait-sanitize $inactive || err $LINENO
((SECONDS > start + 2)) || err $LINENO
Add a new cxl_memdev_sanitize() to libcxl to support triggering memory device sanitation, in either Sanitize and/or Secure Erase, per the CXL 3.0 spec. This is analogous to 'ndctl sanitize-dimm'. Signed-off-by: Davidlohr Bueso <dave@stgolabs.net> --- Changes from v1: - remove '-s' option and allow sanitize and secure erase to be exclusive. (Vishal) - update cxl-sanitize test cases (Alison) - update wait-sanitize manpage to include sanitize-memdev cmd ...roduce-sanitize-memdev-functionality.patch | 289 ++++++++++++++++++ Documentation/cxl/cxl-wait-sanitize.txt | 1 + Documentation/cxl/meson.build | 1 + cxl/builtin.h | 1 + cxl/cxl.c | 1 + cxl/lib/libcxl.c | 15 + cxl/lib/libcxl.sym | 1 + cxl/libcxl.h | 1 + cxl/memdev.c | 38 +++ test/cxl-sanitize.sh | 4 +- 10 files changed, 350 insertions(+), 2 deletions(-) create mode 100644 0001-cxl-memdev-Introduce-sanitize-memdev-functionality.patch