anitazha / rpms / ndctl

Forked from rpms/ndctl 2 years ago
Clone
e0018b
From e2d5cbab4a4b530b679172ae7ca59cc506d1d4cc Mon Sep 17 00:00:00 2001
e0018b
From: Alison Schofield <alison.schofield@intel.com>
e0018b
Date: Tue, 22 Feb 2022 11:56:08 -0800
e0018b
Subject: [PATCH 137/217] cxl: add command 'cxl set-partition'
e0018b
e0018b
CXL devices may support both volatile and persistent memory capacity.
e0018b
The amount of device capacity set aside for each type is typically
e0018b
established at the factory, but some devices also allow for dynamic
e0018b
re-partitioning. Add a command for this purpose.
e0018b
e0018b
 usage: cxl set-partition <mem0> [<mem1>..<memN>] [<options>]
e0018b
e0018b
    -v, --verbose         turn on debug
e0018b
    -S, --serial          use serial numbers to id memdevs
e0018b
    -t, --type <type>     'pmem' or 'volatile' (Default: 'pmem')
e0018b
    -s, --size <size>     size in bytes (Default: all available capacity)
e0018b
    -a, --align           auto-align --size per device's requirement
e0018b
e0018b
Link: https://lore.kernel.org/r/e7accc7ba93def81b48304cf5fb483345757410d.1645558189.git.alison.schofield@intel.com
e0018b
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
e0018b
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
e0018b
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
e0018b
---
e0018b
 Documentation/cxl/cxl-set-partition.txt |  68 ++++++++
e0018b
 Documentation/cxl/meson.build           |   1 +
e0018b
 cxl/builtin.h                           |   1 +
e0018b
 cxl/cxl.c                               |   1 +
e0018b
 cxl/memdev.c                            | 206 ++++++++++++++++++++++++
e0018b
 5 files changed, 277 insertions(+)
e0018b
 create mode 100644 Documentation/cxl/cxl-set-partition.txt
e0018b
e0018b
diff --git a/Documentation/cxl/cxl-set-partition.txt b/Documentation/cxl/cxl-set-partition.txt
e0018b
new file mode 100644
e0018b
index 0000000..1e548af
e0018b
--- /dev/null
e0018b
+++ b/Documentation/cxl/cxl-set-partition.txt
e0018b
@@ -0,0 +1,68 @@
e0018b
+// SPDX-License-Identifier: GPL-2.0
e0018b
+
e0018b
+cxl-set-partition(1)
e0018b
+====================
e0018b
+
e0018b
+NAME
e0018b
+----
e0018b
+cxl-set-partition - set the partitioning between volatile and persistent capacity on a CXL memdev
e0018b
+
e0018b
+SYNOPSIS
e0018b
+--------
e0018b
+[verse]
e0018b
+'cxl set-partition <mem0> [ [<mem1>..<memN>] [<options>]'
e0018b
+
e0018b
+DESCRIPTION
e0018b
+-----------
e0018b
+CXL devices may support both volatile and persistent memory capacity.
e0018b
+The amount of device capacity set aside for each type is typically
e0018b
+established at the factory, but some devices also allow for dynamic
e0018b
+re-partitioning.
e0018b
+
e0018b
+Use this command to partition a device into volatile and persistent
e0018b
+capacity. The change in partitioning becomes the “next” configuration,
e0018b
+to become active on the next device reset.
e0018b
+
e0018b
+Use "cxl list -m <memdev> -I" to examine the partitioning capabilities
e0018b
+of a device. A partition_alignment_size value of zero means there is
e0018b
+no available capacity and therefore the partitions cannot be changed.
e0018b
+
e0018b
+Using this command to change the size of the persistent capacity shall
e0018b
+result in the loss of data stored.
e0018b
+
e0018b
+OPTIONS
e0018b
+-------
e0018b
+<memory device(s)>::
e0018b
+include::memdev-option.txt[]
e0018b
+
e0018b
+-t::
e0018b
+--type=::
e0018b
+	Type of partition, 'pmem' or 'volatile', to modify.
e0018b
+	Default: 'pmem'
e0018b
+
e0018b
+-s::
e0018b
+--size=::
e0018b
+	Size of the <type> partition in bytes. Size must align to the
e0018b
+	devices alignment requirement. Use 'cxl list -m <memdev> -I'
e0018b
+	to find 'partition_alignment_size', or, use the --align option.
e0018b
+	Default: All available capacity is assigned to <type>.
e0018b
+
e0018b
+-a::
e0018b
+--align::
e0018b
+	Select this option to allow the automatic alignment of --size
e0018b
+	to meet device alignment requirements. When using this option,
e0018b
+	specify the minimum --size of the --type partition needed. When
e0018b
+	this option is omitted, the command fails if --size is not
e0018b
+	properly aligned. Use 'cxl list -m <memdev> -I' to examine the
e0018b
+	partition_alignment_size.
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-list[1],
e0018b
+CXL-2.0 8.2.9.5.2
e0018b
diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build
e0018b
index 96f4666..e927644 100644
e0018b
--- a/Documentation/cxl/meson.build
e0018b
+++ b/Documentation/cxl/meson.build
e0018b
@@ -34,6 +34,7 @@ cxl_manpages = [
e0018b
   'cxl-disable-memdev.txt',
e0018b
   'cxl-enable-port.txt',
e0018b
   'cxl-disable-port.txt',
e0018b
+  'cxl-set-partition.txt',
e0018b
 ]
e0018b
 
e0018b
 foreach man : cxl_manpages
e0018b
diff --git a/cxl/builtin.h b/cxl/builtin.h
e0018b
index 3123d5e..7bbad98 100644
e0018b
--- a/cxl/builtin.h
e0018b
+++ b/cxl/builtin.h
e0018b
@@ -14,4 +14,5 @@ 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
 int cmd_disable_port(int argc, const char **argv, struct cxl_ctx *ctx);
e0018b
 int cmd_enable_port(int argc, const char **argv, struct cxl_ctx *ctx);
e0018b
+int cmd_set_partition(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 c20c569..ab4bbec 100644
e0018b
--- a/cxl/cxl.c
e0018b
+++ b/cxl/cxl.c
e0018b
@@ -68,6 +68,7 @@ static struct cmd_struct commands[] = {
e0018b
 	{ "enable-memdev", .c_fn = cmd_enable_memdev },
e0018b
 	{ "disable-port", .c_fn = cmd_disable_port },
e0018b
 	{ "enable-port", .c_fn = cmd_enable_port },
e0018b
+	{ "set-partition", .c_fn = cmd_set_partition },
e0018b
 };
e0018b
 
e0018b
 int main(int argc, const char **argv)
e0018b
diff --git a/cxl/memdev.c b/cxl/memdev.c
e0018b
index 90b33e1..91d914d 100644
e0018b
--- a/cxl/memdev.c
e0018b
+++ b/cxl/memdev.c
e0018b
@@ -6,11 +6,14 @@
e0018b
 #include <unistd.h>
e0018b
 #include <limits.h>
e0018b
 #include <util/log.h>
e0018b
+#include <util/json.h>
e0018b
+#include <util/size.h>
e0018b
 #include <cxl/libcxl.h>
e0018b
 #include <util/parse-options.h>
e0018b
 #include <ccan/minmax/minmax.h>
e0018b
 #include <ccan/array_size/array_size.h>
e0018b
 
e0018b
+#include "json.h"
e0018b
 #include "filter.h"
e0018b
 
e0018b
 struct action_context {
e0018b
@@ -26,10 +29,18 @@ static struct parameters {
e0018b
 	bool verbose;
e0018b
 	bool serial;
e0018b
 	bool force;
e0018b
+	bool align;
e0018b
+	const char *type;
e0018b
+	const char *size;
e0018b
 } param;
e0018b
 
e0018b
 static struct log_ctx ml;
e0018b
 
e0018b
+enum cxl_setpart_type {
e0018b
+	CXL_SETPART_PMEM,
e0018b
+	CXL_SETPART_VOLATILE,
e0018b
+};
e0018b
+
e0018b
 #define BASE_OPTIONS() \
e0018b
 OPT_BOOLEAN('v',"verbose", &param.verbose, "turn on debug"), \
e0018b
 OPT_BOOLEAN('S', "serial", &param.serial, "use serial numbers to id memdevs")
e0018b
@@ -51,6 +62,14 @@ OPT_UINTEGER('O', "offset", &param.offset, \
e0018b
 OPT_BOOLEAN('f', "force", &param.force,                                \
e0018b
 	    "DANGEROUS: override active memdev safety checks")
e0018b
 
e0018b
+#define SET_PARTITION_OPTIONS() \
e0018b
+OPT_STRING('t', "type",  &param.type, "type",			\
e0018b
+	"'pmem' or 'volatile' (Default: 'pmem')"),		\
e0018b
+OPT_STRING('s', "size",  &param.size, "size",			\
e0018b
+	"size in bytes (Default: all available capacity)"),	\
e0018b
+OPT_BOOLEAN('a', "align",  &param.align,			\
e0018b
+	"auto-align --size per device's requirement")
e0018b
+
e0018b
 static const struct option read_options[] = {
e0018b
 	BASE_OPTIONS(),
e0018b
 	LABEL_OPTIONS(),
e0018b
@@ -82,6 +101,12 @@ static const struct option enable_options[] = {
e0018b
 	OPT_END(),
e0018b
 };
e0018b
 
e0018b
+static const struct option set_partition_options[] = {
e0018b
+	BASE_OPTIONS(),
e0018b
+	SET_PARTITION_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
@@ -209,6 +234,176 @@ out:
e0018b
 	return rc;
e0018b
 }
e0018b
 
e0018b
+static unsigned long long
e0018b
+partition_align(const char *devname, enum cxl_setpart_type type,
e0018b
+		unsigned long long volatile_size, unsigned long long alignment,
e0018b
+		unsigned long long available)
e0018b
+{
e0018b
+	if (IS_ALIGNED(volatile_size, alignment))
e0018b
+		return volatile_size;
e0018b
+
e0018b
+	if (!param.align) {
e0018b
+		log_err(&ml, "%s: size %lld is not partition aligned %lld\n",
e0018b
+			devname, volatile_size, alignment);
e0018b
+		return ULLONG_MAX;
e0018b
+	}
e0018b
+
e0018b
+	/* Align based on partition type to fulfill users size request */
e0018b
+	if (type == CXL_SETPART_PMEM)
e0018b
+		volatile_size = ALIGN_DOWN(volatile_size, alignment);
e0018b
+	else
e0018b
+		volatile_size = ALIGN(volatile_size, alignment);
e0018b
+
e0018b
+	/* Fail if the align pushes size over the available limit. */
e0018b
+	if (volatile_size > available) {
e0018b
+		log_err(&ml, "%s: aligned partition size %lld exceeds available size %lld\n",
e0018b
+			devname, volatile_size, available);
e0018b
+		volatile_size = ULLONG_MAX;
e0018b
+	}
e0018b
+
e0018b
+	return volatile_size;
e0018b
+}
e0018b
+
e0018b
+static unsigned long long
e0018b
+param_size_to_volatile_size(const char *devname, enum cxl_setpart_type type,
e0018b
+		unsigned long long size, unsigned long long available)
e0018b
+{
e0018b
+	/* User omits size option. Apply all available capacity to type. */
e0018b
+	if (size == ULLONG_MAX) {
e0018b
+		if (type == CXL_SETPART_PMEM)
e0018b
+			return 0;
e0018b
+		return available;
e0018b
+	}
e0018b
+
e0018b
+	/* User includes a size option. Apply it to type */
e0018b
+	if (size > available) {
e0018b
+		log_err(&ml, "%s: %lld exceeds available capacity %lld\n",
e0018b
+			devname, size, available);
e0018b
+			return ULLONG_MAX;
e0018b
+	}
e0018b
+	if (type == CXL_SETPART_PMEM)
e0018b
+		return available - size;
e0018b
+	return size;
e0018b
+}
e0018b
+
e0018b
+/*
e0018b
+ * Return the volatile_size to use in the CXL set paritition
e0018b
+ * command, or ULLONG_MAX if unable to validate the partition
e0018b
+ * request.
e0018b
+ */
e0018b
+static unsigned long long
e0018b
+validate_partition(struct cxl_memdev *memdev, enum cxl_setpart_type type,
e0018b
+		unsigned long long size)
e0018b
+{
e0018b
+	unsigned long long total_cap, volatile_only, persistent_only;
e0018b
+	const char *devname = cxl_memdev_get_devname(memdev);
e0018b
+	unsigned long long volatile_size = ULLONG_MAX;
e0018b
+	unsigned long long available, alignment;
e0018b
+	struct cxl_cmd *cmd;
e0018b
+	int rc;
e0018b
+
e0018b
+	cmd = cxl_cmd_new_identify(memdev);
e0018b
+	if (!cmd)
e0018b
+		return ULLONG_MAX;
e0018b
+	rc = cxl_cmd_submit(cmd);
e0018b
+	if (rc < 0)
e0018b
+		goto out;
e0018b
+	rc = cxl_cmd_get_mbox_status(cmd);
e0018b
+	if (rc != 0)
e0018b
+		goto out;
e0018b
+
e0018b
+	alignment = cxl_cmd_identify_get_partition_align(cmd);
e0018b
+	if (alignment == 0) {
e0018b
+		log_err(&ml, "%s: no available capacity\n", devname);
e0018b
+		goto out;
e0018b
+	}
e0018b
+
e0018b
+	/* Calculate the actual available capacity */
e0018b
+	total_cap = cxl_cmd_identify_get_total_size(cmd);
e0018b
+	volatile_only = cxl_cmd_identify_get_volatile_only_size(cmd);
e0018b
+	persistent_only = cxl_cmd_identify_get_persistent_only_size(cmd);
e0018b
+	available = total_cap - volatile_only - persistent_only;
e0018b
+
e0018b
+	/* Translate the users size request into an aligned volatile_size */
e0018b
+	volatile_size = param_size_to_volatile_size(devname, type, size,
e0018b
+				available);
e0018b
+	if (volatile_size == ULLONG_MAX)
e0018b
+		goto out;
e0018b
+
e0018b
+	volatile_size = partition_align(devname, type, volatile_size, alignment,
e0018b
+				available);
e0018b
+
e0018b
+out:
e0018b
+	cxl_cmd_unref(cmd);
e0018b
+	return volatile_size;
e0018b
+}
e0018b
+
e0018b
+static int action_setpartition(struct cxl_memdev *memdev,
e0018b
+		struct action_context *actx)
e0018b
+{
e0018b
+	const char *devname = cxl_memdev_get_devname(memdev);
e0018b
+	enum cxl_setpart_type type = CXL_SETPART_PMEM;
e0018b
+	unsigned long long size = ULLONG_MAX;
e0018b
+	struct json_object *jmemdev;
e0018b
+	struct cxl_cmd *cmd;
e0018b
+	int rc;
e0018b
+
e0018b
+	if (param.type) {
e0018b
+		if (strcmp(param.type, "pmem") == 0)
e0018b
+			/* default */;
e0018b
+		else if (strcmp(param.type, "volatile") == 0)
e0018b
+			type = CXL_SETPART_VOLATILE;
e0018b
+		else {
e0018b
+			log_err(&ml, "invalid type '%s'\n", param.type);
e0018b
+			return -EINVAL;
e0018b
+		}
e0018b
+	}
e0018b
+
e0018b
+	if (param.size) {
e0018b
+		size = parse_size64(param.size);
e0018b
+		if (size == ULLONG_MAX) {
e0018b
+			log_err(&ml, "%s: failed to parse size option '%s'\n",
e0018b
+			devname, param.size);
e0018b
+			return -EINVAL;
e0018b
+		}
e0018b
+	}
e0018b
+
e0018b
+	size = validate_partition(memdev, type, size);
e0018b
+	if (size == ULLONG_MAX)
e0018b
+		return -EINVAL;
e0018b
+
e0018b
+	cmd = cxl_cmd_new_set_partition(memdev, size);
e0018b
+	if (!cmd) {
e0018b
+		rc = -ENXIO;
e0018b
+		goto out_err;
e0018b
+	}
e0018b
+
e0018b
+	rc = cxl_cmd_submit(cmd);
e0018b
+	if (rc < 0) {
e0018b
+		log_err(&ml, "cmd submission failed: %s\n", strerror(-rc));
e0018b
+		goto out_cmd;
e0018b
+	}
e0018b
+
e0018b
+	rc = cxl_cmd_get_mbox_status(cmd);
e0018b
+	if (rc != 0) {
e0018b
+		log_err(&ml, "%s: mbox status: %d\n", __func__, rc);
e0018b
+		rc = -ENXIO;
e0018b
+	}
e0018b
+
e0018b
+out_cmd:
e0018b
+	cxl_cmd_unref(cmd);
e0018b
+out_err:
e0018b
+	if (rc)
e0018b
+		log_err(&ml, "%s error: %s\n", devname, strerror(-rc));
e0018b
+
e0018b
+	jmemdev = util_cxl_memdev_to_json(memdev, UTIL_JSON_PARTITION);
e0018b
+	if (jmemdev)
e0018b
+		printf("%s\n", json_object_to_json_string_ext(jmemdev,
e0018b
+		       JSON_C_TO_STRING_PRETTY));
e0018b
+
e0018b
+	return rc;
e0018b
+}
e0018b
+
e0018b
 static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx,
e0018b
 			 int (*action)(struct cxl_memdev *memdev,
e0018b
 				       struct action_context *actx),
e0018b
@@ -398,3 +593,14 @@ int cmd_enable_memdev(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_set_partition(int argc, const char **argv, struct cxl_ctx *ctx)
e0018b
+{
e0018b
+	int count = memdev_action(argc, argv, ctx, action_setpartition,
e0018b
+			set_partition_options,
e0018b
+			"cxl set-partition <mem0> [<mem1>..<memN>] [<options>]");
e0018b
+	log_info(&ml, "set_partition %d mem%s\n", count >= 0 ? count : 0,
e0018b
+		 count > 1 ? "s" : "");
e0018b
+
e0018b
+	return count >= 0 ? 0 : EXIT_FAILURE;
e0018b
+}
e0018b
-- 
e0018b
2.27.0
e0018b