|
|
e0018b |
From 21b089025178442baa7b59823a7fd264b4c075a8 Mon Sep 17 00:00:00 2001
|
|
|
e0018b |
From: Vishal Verma <vishal.l.verma@intel.com>
|
|
|
e0018b |
Date: Mon, 15 Aug 2022 13:22:10 -0600
|
|
|
e0018b |
Subject: [PATCH 195/217] cxl: add a 'create-region' command
|
|
|
e0018b |
|
|
|
e0018b |
Add a 'create-region' command to cxl-cli that walks the platform's CXL
|
|
|
e0018b |
hierarchy to find an appropriate root decoder based on any options
|
|
|
e0018b |
provided, and uses libcxl APIs to create a 'region' that is comprehended
|
|
|
e0018b |
by libnvdimm and ndctl.
|
|
|
e0018b |
|
|
|
e0018b |
Link: https://lore.kernel.org/r/20220815192214.545800-8-vishal.l.verma@intel.com
|
|
|
e0018b |
Cc: Dan Williams <dan.j.williams@intel.com>
|
|
|
e0018b |
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
|
|
|
e0018b |
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
|
|
|
e0018b |
---
|
|
|
e0018b |
Documentation/cxl/bus-option.txt | 5 +
|
|
|
e0018b |
Documentation/cxl/cxl-create-region.txt | 112 +++++
|
|
|
e0018b |
Documentation/cxl/meson.build | 2 +
|
|
|
e0018b |
Documentation/cxl/region-description.txt | 7 +
|
|
|
e0018b |
cxl/builtin.h | 1 +
|
|
|
e0018b |
cxl/cxl.c | 1 +
|
|
|
e0018b |
cxl/filter.h | 4 +-
|
|
|
e0018b |
cxl/json.c | 9 +
|
|
|
e0018b |
cxl/meson.build | 1 +
|
|
|
e0018b |
cxl/region.c | 550 +++++++++++++++++++++++
|
|
|
e0018b |
10 files changed, 691 insertions(+), 1 deletion(-)
|
|
|
e0018b |
create mode 100644 Documentation/cxl/bus-option.txt
|
|
|
e0018b |
create mode 100644 Documentation/cxl/cxl-create-region.txt
|
|
|
e0018b |
create mode 100644 Documentation/cxl/region-description.txt
|
|
|
e0018b |
create mode 100644 cxl/region.c
|
|
|
e0018b |
|
|
|
e0018b |
diff --git a/Documentation/cxl/bus-option.txt b/Documentation/cxl/bus-option.txt
|
|
|
e0018b |
new file mode 100644
|
|
|
e0018b |
index 0000000..02e2f08
|
|
|
e0018b |
--- /dev/null
|
|
|
e0018b |
+++ b/Documentation/cxl/bus-option.txt
|
|
|
e0018b |
@@ -0,0 +1,5 @@
|
|
|
e0018b |
+// SPDX-License-Identifier: GPL-2.0
|
|
|
e0018b |
+
|
|
|
e0018b |
+-b::
|
|
|
e0018b |
+--bus=::
|
|
|
e0018b |
+ Restrict the operation to the specified bus.
|
|
|
e0018b |
diff --git a/Documentation/cxl/cxl-create-region.txt b/Documentation/cxl/cxl-create-region.txt
|
|
|
e0018b |
new file mode 100644
|
|
|
e0018b |
index 0000000..6b740d5
|
|
|
e0018b |
--- /dev/null
|
|
|
e0018b |
+++ b/Documentation/cxl/cxl-create-region.txt
|
|
|
e0018b |
@@ -0,0 +1,112 @@
|
|
|
e0018b |
+// SPDX-License-Identifier: GPL-2.0
|
|
|
e0018b |
+
|
|
|
e0018b |
+cxl-create-region(1)
|
|
|
e0018b |
+====================
|
|
|
e0018b |
+
|
|
|
e0018b |
+NAME
|
|
|
e0018b |
+----
|
|
|
e0018b |
+cxl-create-region - Assemble a CXL region by setting up attributes of its
|
|
|
e0018b |
+constituent CXL memdevs.
|
|
|
e0018b |
+
|
|
|
e0018b |
+SYNOPSIS
|
|
|
e0018b |
+--------
|
|
|
e0018b |
+[verse]
|
|
|
e0018b |
+'cxl create-region [<options>]'
|
|
|
e0018b |
+
|
|
|
e0018b |
+include::region-description.txt[]
|
|
|
e0018b |
+
|
|
|
e0018b |
+For create-region, a size can optionally be specified, but if not, the maximum
|
|
|
e0018b |
+possible size for each memdev will be used up to the available decode capacity
|
|
|
e0018b |
+in the system for the given memory type. For persistent regions a UUID can
|
|
|
e0018b |
+optionally be specified, but if not, one will be generated.
|
|
|
e0018b |
+
|
|
|
e0018b |
+If the region-creation operation is successful, a region object will be
|
|
|
e0018b |
+emitted on stdout in JSON format (see examples). If the specified arguments
|
|
|
e0018b |
+cannot be satisfied with a legal configuration, then an appropriate error will
|
|
|
e0018b |
+be emitted on stderr.
|
|
|
e0018b |
+
|
|
|
e0018b |
+EXAMPLE
|
|
|
e0018b |
+-------
|
|
|
e0018b |
+----
|
|
|
e0018b |
+#cxl create - region - m - d decoder0 .1 - w 2 - g 1024 mem0 mem1
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ "region":"region0",
|
|
|
e0018b |
+ "resource":"0xc90000000",
|
|
|
e0018b |
+ "size":"512.00 MiB (536.87 MB)",
|
|
|
e0018b |
+ "interleave_ways":2,
|
|
|
e0018b |
+ "interleave_granularity":1024,
|
|
|
e0018b |
+ "mappings":[
|
|
|
e0018b |
+ {
|
|
|
e0018b |
+ "position":1,
|
|
|
e0018b |
+ "decoder":"decoder4.0"
|
|
|
e0018b |
+ },
|
|
|
e0018b |
+ {
|
|
|
e0018b |
+ "position":0,
|
|
|
e0018b |
+ "decoder":"decoder3.0"
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ ]
|
|
|
e0018b |
+}
|
|
|
e0018b |
+created 1 region
|
|
|
e0018b |
+----
|
|
|
e0018b |
+
|
|
|
e0018b |
+OPTIONS
|
|
|
e0018b |
+-------
|
|
|
e0018b |
+<target(s)>::
|
|
|
e0018b |
+The CXL targets that should be used to form the region. The number of
|
|
|
e0018b |
+'target' arguments must match the '--ways' option (if provided). The
|
|
|
e0018b |
+targets are memdev names such as 'mem0', 'mem1' etc.
|
|
|
e0018b |
+
|
|
|
e0018b |
+include::bus-option.txt[]
|
|
|
e0018b |
+
|
|
|
e0018b |
+-m::
|
|
|
e0018b |
+--memdevs::
|
|
|
e0018b |
+ Indicate that the non-option arguments for 'target(s)' refer to memdev
|
|
|
e0018b |
+ names. Currently this is the only option supported, and must be
|
|
|
e0018b |
+ specified.
|
|
|
e0018b |
+
|
|
|
e0018b |
+-s::
|
|
|
e0018b |
+--size=::
|
|
|
e0018b |
+ Specify the total size for the new region. This is optional, and by
|
|
|
e0018b |
+ default, the maximum possible size will be used. The maximum possible
|
|
|
e0018b |
+ size is gated by both the contiguous free HPA space remaining in the
|
|
|
e0018b |
+ root decoder, and the available DPA space in the component memdevs.
|
|
|
e0018b |
+
|
|
|
e0018b |
+-t::
|
|
|
e0018b |
+--type=::
|
|
|
e0018b |
+ Specify the region type - 'pmem' or 'ram'. Defaults to 'pmem'.
|
|
|
e0018b |
+
|
|
|
e0018b |
+-U::
|
|
|
e0018b |
+--uuid=::
|
|
|
e0018b |
+ Specify a UUID for the new region. This shouldn't usually need to be
|
|
|
e0018b |
+ specified, as one will be generated by default.
|
|
|
e0018b |
+
|
|
|
e0018b |
+-w::
|
|
|
e0018b |
+--ways=::
|
|
|
e0018b |
+ The number of interleave ways for the new region's interleave. This
|
|
|
e0018b |
+ should be equal to the number of memdevs specified in --memdevs, if
|
|
|
e0018b |
+ --memdevs is being supplied. If --ways is not specified, it will be
|
|
|
e0018b |
+ determined based on the number of memdev targets provided.
|
|
|
e0018b |
+
|
|
|
e0018b |
+-g::
|
|
|
e0018b |
+--granularity=::
|
|
|
e0018b |
+ The interleave granularity for the new region. Must match the selected
|
|
|
e0018b |
+ root decoder's (if provided) granularity. If the root decoder is
|
|
|
e0018b |
+ interleaved across more than one host-bridge then this value must match
|
|
|
e0018b |
+ that granularity. Otherwise, for non-interleaved decode windows, any
|
|
|
e0018b |
+ granularity can be specified as long as all devices support that setting.
|
|
|
e0018b |
+
|
|
|
e0018b |
+-d::
|
|
|
e0018b |
+--decoder=::
|
|
|
e0018b |
+ The root decoder that the region should be created under. If not
|
|
|
e0018b |
+ supplied, the first cross-host bridge (if available), decoder that
|
|
|
e0018b |
+ supports the largest interleave will be chosen.
|
|
|
e0018b |
+
|
|
|
e0018b |
+include::human-option.txt[]
|
|
|
e0018b |
+
|
|
|
e0018b |
+include::debug-option.txt[]
|
|
|
e0018b |
+
|
|
|
e0018b |
+include::../copyright.txt[]
|
|
|
e0018b |
+
|
|
|
e0018b |
+SEE ALSO
|
|
|
e0018b |
+--------
|
|
|
e0018b |
+linkcxl:cxl-list[1],
|
|
|
e0018b |
diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build
|
|
|
e0018b |
index 423be90..340cdee 100644
|
|
|
e0018b |
--- a/Documentation/cxl/meson.build
|
|
|
e0018b |
+++ b/Documentation/cxl/meson.build
|
|
|
e0018b |
@@ -23,6 +23,7 @@ filedeps = [
|
|
|
e0018b |
'memdev-option.txt',
|
|
|
e0018b |
'labels-options.txt',
|
|
|
e0018b |
'debug-option.txt',
|
|
|
e0018b |
+ 'region-description.txt',
|
|
|
e0018b |
]
|
|
|
e0018b |
|
|
|
e0018b |
cxl_manpages = [
|
|
|
e0018b |
@@ -39,6 +40,7 @@ cxl_manpages = [
|
|
|
e0018b |
'cxl-set-partition.txt',
|
|
|
e0018b |
'cxl-reserve-dpa.txt',
|
|
|
e0018b |
'cxl-free-dpa.txt',
|
|
|
e0018b |
+ 'cxl-create-region.txt',
|
|
|
e0018b |
]
|
|
|
e0018b |
|
|
|
e0018b |
foreach man : cxl_manpages
|
|
|
e0018b |
diff --git a/Documentation/cxl/region-description.txt b/Documentation/cxl/region-description.txt
|
|
|
e0018b |
new file mode 100644
|
|
|
e0018b |
index 0000000..d7e3077
|
|
|
e0018b |
--- /dev/null
|
|
|
e0018b |
+++ b/Documentation/cxl/region-description.txt
|
|
|
e0018b |
@@ -0,0 +1,7 @@
|
|
|
e0018b |
+// SPDX-License-Identifier: GPL-2.0
|
|
|
e0018b |
+
|
|
|
e0018b |
+DESCRIPTION
|
|
|
e0018b |
+-----------
|
|
|
e0018b |
+A CXL region is composed of one or more slices of CXL memdevs, with configurable
|
|
|
e0018b |
+interleave settings - both the number of interleave ways, and the interleave
|
|
|
e0018b |
+granularity.
|
|
|
e0018b |
diff --git a/cxl/builtin.h b/cxl/builtin.h
|
|
|
e0018b |
index 9e6fc62..843bada 100644
|
|
|
e0018b |
--- a/cxl/builtin.h
|
|
|
e0018b |
+++ b/cxl/builtin.h
|
|
|
e0018b |
@@ -18,4 +18,5 @@ 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 |
int cmd_disable_bus(int argc, const char **argv, struct cxl_ctx *ctx);
|
|
|
e0018b |
+int cmd_create_region(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 ef4cda9..f0afcfe 100644
|
|
|
e0018b |
--- a/cxl/cxl.c
|
|
|
e0018b |
+++ b/cxl/cxl.c
|
|
|
e0018b |
@@ -72,6 +72,7 @@ static struct cmd_struct commands[] = {
|
|
|
e0018b |
{ "enable-port", .c_fn = cmd_enable_port },
|
|
|
e0018b |
{ "set-partition", .c_fn = cmd_set_partition },
|
|
|
e0018b |
{ "disable-bus", .c_fn = cmd_disable_bus },
|
|
|
e0018b |
+ { "create-region", .c_fn = cmd_create_region },
|
|
|
e0018b |
};
|
|
|
e0018b |
|
|
|
e0018b |
int main(int argc, const char **argv)
|
|
|
e0018b |
diff --git a/cxl/filter.h b/cxl/filter.h
|
|
|
e0018b |
index 609433c..d22d8b1 100644
|
|
|
e0018b |
--- a/cxl/filter.h
|
|
|
e0018b |
+++ b/cxl/filter.h
|
|
|
e0018b |
@@ -35,8 +35,10 @@ struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev,
|
|
|
e0018b |
struct cxl_port *util_cxl_port_filter_by_memdev(struct cxl_port *port,
|
|
|
e0018b |
const char *ident,
|
|
|
e0018b |
const char *serial);
|
|
|
e0018b |
-struct cxl_region *util_cxl_region_filter(struct cxl_region *region,
|
|
|
e0018b |
+struct cxl_decoder *util_cxl_decoder_filter(struct cxl_decoder *decoder,
|
|
|
e0018b |
const char *__ident);
|
|
|
e0018b |
+struct cxl_region *util_cxl_region_filter(struct cxl_region *region,
|
|
|
e0018b |
+ const char *__ident);
|
|
|
e0018b |
|
|
|
e0018b |
enum cxl_port_filter_mode {
|
|
|
e0018b |
CXL_PF_SINGLE,
|
|
|
e0018b |
diff --git a/cxl/json.c b/cxl/json.c
|
|
|
e0018b |
index 70cf286..9dc99df 100644
|
|
|
e0018b |
--- a/cxl/json.c
|
|
|
e0018b |
+++ b/cxl/json.c
|
|
|
e0018b |
@@ -547,6 +547,7 @@ void util_cxl_mappings_append_json(struct json_object *jregion,
|
|
|
e0018b |
cxl_mapping_foreach(region, mapping) {
|
|
|
e0018b |
struct json_object *jmapping;
|
|
|
e0018b |
struct cxl_decoder *decoder;
|
|
|
e0018b |
+ struct cxl_memdev *memdev;
|
|
|
e0018b |
|
|
|
e0018b |
jmapping = json_object_new_object();
|
|
|
e0018b |
if (!jmapping)
|
|
|
e0018b |
@@ -564,6 +565,14 @@ void util_cxl_mappings_append_json(struct json_object *jregion,
|
|
|
e0018b |
if (!decoder)
|
|
|
e0018b |
continue;
|
|
|
e0018b |
|
|
|
e0018b |
+ memdev = cxl_decoder_get_memdev(decoder);
|
|
|
e0018b |
+ if (memdev) {
|
|
|
e0018b |
+ devname = cxl_memdev_get_devname(memdev);
|
|
|
e0018b |
+ jobj = json_object_new_string(devname);
|
|
|
e0018b |
+ if (jobj)
|
|
|
e0018b |
+ json_object_object_add(jmapping, "memdev", jobj);
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
devname = cxl_decoder_get_devname(decoder);
|
|
|
e0018b |
jobj = json_object_new_string(devname);
|
|
|
e0018b |
if (jobj)
|
|
|
e0018b |
diff --git a/cxl/meson.build b/cxl/meson.build
|
|
|
e0018b |
index d63dcb1..f2474aa 100644
|
|
|
e0018b |
--- a/cxl/meson.build
|
|
|
e0018b |
+++ b/cxl/meson.build
|
|
|
e0018b |
@@ -3,6 +3,7 @@ cxl_src = [
|
|
|
e0018b |
'list.c',
|
|
|
e0018b |
'port.c',
|
|
|
e0018b |
'bus.c',
|
|
|
e0018b |
+ 'region.c',
|
|
|
e0018b |
'memdev.c',
|
|
|
e0018b |
'json.c',
|
|
|
e0018b |
'filter.c',
|
|
|
e0018b |
diff --git a/cxl/region.c b/cxl/region.c
|
|
|
e0018b |
new file mode 100644
|
|
|
e0018b |
index 0000000..2791ac9
|
|
|
e0018b |
--- /dev/null
|
|
|
e0018b |
+++ b/cxl/region.c
|
|
|
e0018b |
@@ -0,0 +1,550 @@
|
|
|
e0018b |
+// SPDX-License-Identifier: GPL-2.0
|
|
|
e0018b |
+/* Copyright (C) 2020-2022 Intel Corporation. All rights reserved. */
|
|
|
e0018b |
+#include <stdio.h>
|
|
|
e0018b |
+#include <errno.h>
|
|
|
e0018b |
+#include <stdlib.h>
|
|
|
e0018b |
+#include <unistd.h>
|
|
|
e0018b |
+#include <limits.h>
|
|
|
e0018b |
+#include <util/log.h>
|
|
|
e0018b |
+#include <uuid/uuid.h>
|
|
|
e0018b |
+#include <util/json.h>
|
|
|
e0018b |
+#include <util/size.h>
|
|
|
e0018b |
+#include <cxl/libcxl.h>
|
|
|
e0018b |
+#include <json-c/json.h>
|
|
|
e0018b |
+#include <util/parse-options.h>
|
|
|
e0018b |
+#include <ccan/minmax/minmax.h>
|
|
|
e0018b |
+#include <ccan/short_types/short_types.h>
|
|
|
e0018b |
+
|
|
|
e0018b |
+#include "filter.h"
|
|
|
e0018b |
+#include "json.h"
|
|
|
e0018b |
+
|
|
|
e0018b |
+static struct region_params {
|
|
|
e0018b |
+ const char *bus;
|
|
|
e0018b |
+ const char *size;
|
|
|
e0018b |
+ const char *ways;
|
|
|
e0018b |
+ const char *granularity;
|
|
|
e0018b |
+ const char *type;
|
|
|
e0018b |
+ const char *root_decoder;
|
|
|
e0018b |
+ const char *region;
|
|
|
e0018b |
+ bool memdevs;
|
|
|
e0018b |
+ bool force;
|
|
|
e0018b |
+ bool human;
|
|
|
e0018b |
+ bool debug;
|
|
|
e0018b |
+} param;
|
|
|
e0018b |
+
|
|
|
e0018b |
+struct parsed_params {
|
|
|
e0018b |
+ u64 size;
|
|
|
e0018b |
+ u64 ep_min_size;
|
|
|
e0018b |
+ unsigned int ways;
|
|
|
e0018b |
+ unsigned int granularity;
|
|
|
e0018b |
+ const char **targets;
|
|
|
e0018b |
+ int num_targets;
|
|
|
e0018b |
+ struct cxl_decoder *root_decoder;
|
|
|
e0018b |
+ enum cxl_decoder_mode mode;
|
|
|
e0018b |
+};
|
|
|
e0018b |
+
|
|
|
e0018b |
+enum region_actions {
|
|
|
e0018b |
+ ACTION_CREATE,
|
|
|
e0018b |
+};
|
|
|
e0018b |
+
|
|
|
e0018b |
+static struct log_ctx rl;
|
|
|
e0018b |
+
|
|
|
e0018b |
+#define BASE_OPTIONS() \
|
|
|
e0018b |
+OPT_STRING('b', "bus", ¶m.bus, "bus name", \
|
|
|
e0018b |
+ "Limit operation to the specified bus"), \
|
|
|
e0018b |
+OPT_STRING('d', "decoder", ¶m.root_decoder, "root decoder name", \
|
|
|
e0018b |
+ "Limit to / use the specified root decoder"), \
|
|
|
e0018b |
+OPT_BOOLEAN(0, "debug", ¶m.debug, "turn on debug")
|
|
|
e0018b |
+
|
|
|
e0018b |
+#define CREATE_OPTIONS() \
|
|
|
e0018b |
+OPT_STRING('s', "size", ¶m.size, \
|
|
|
e0018b |
+ "size in bytes or with a K/M/G etc. suffix", \
|
|
|
e0018b |
+ "total size desired for the resulting region."), \
|
|
|
e0018b |
+OPT_STRING('w', "ways", ¶m.ways, \
|
|
|
e0018b |
+ "number of interleave ways", \
|
|
|
e0018b |
+ "number of memdevs participating in the regions interleave set"), \
|
|
|
e0018b |
+OPT_STRING('g', "granularity", \
|
|
|
e0018b |
+ ¶m.granularity, "interleave granularity", \
|
|
|
e0018b |
+ "granularity of the interleave set"), \
|
|
|
e0018b |
+OPT_STRING('t', "type", ¶m.type, \
|
|
|
e0018b |
+ "region type", "region type - 'pmem' or 'ram'"), \
|
|
|
e0018b |
+OPT_BOOLEAN('m', "memdevs", ¶m.memdevs, \
|
|
|
e0018b |
+ "non-option arguments are memdevs"), \
|
|
|
e0018b |
+OPT_BOOLEAN('u', "human", ¶m.human, "use human friendly number formats")
|
|
|
e0018b |
+
|
|
|
e0018b |
+static const struct option create_options[] = {
|
|
|
e0018b |
+ BASE_OPTIONS(),
|
|
|
e0018b |
+ CREATE_OPTIONS(),
|
|
|
e0018b |
+ OPT_END(),
|
|
|
e0018b |
+};
|
|
|
e0018b |
+
|
|
|
e0018b |
+
|
|
|
e0018b |
+
|
|
|
e0018b |
+static int parse_create_options(int argc, const char **argv,
|
|
|
e0018b |
+ struct parsed_params *p)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ int i;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (!param.root_decoder) {
|
|
|
e0018b |
+ log_err(&rl, "no root decoder specified\n");
|
|
|
e0018b |
+ return -EINVAL;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (param.type) {
|
|
|
e0018b |
+ p->mode = cxl_decoder_mode_from_ident(param.type);
|
|
|
e0018b |
+ if (p->mode == CXL_DECODER_MODE_NONE) {
|
|
|
e0018b |
+ log_err(&rl, "unsupported type: %s\n", param.type);
|
|
|
e0018b |
+ return -EINVAL;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ } else {
|
|
|
e0018b |
+ p->mode = CXL_DECODER_MODE_PMEM;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (param.size) {
|
|
|
e0018b |
+ p->size = parse_size64(param.size);
|
|
|
e0018b |
+ if (p->size == ULLONG_MAX) {
|
|
|
e0018b |
+ log_err(&rl, "Invalid size: %s\n", param.size);
|
|
|
e0018b |
+ return -EINVAL;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (param.ways) {
|
|
|
e0018b |
+ unsigned long ways = strtoul(param.ways, NULL, 0);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (ways == ULONG_MAX || (int)ways <= 0) {
|
|
|
e0018b |
+ log_err(&rl, "Invalid interleave ways: %s\n",
|
|
|
e0018b |
+ param.ways);
|
|
|
e0018b |
+ return -EINVAL;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ p->ways = ways;
|
|
|
e0018b |
+ } else if (argc) {
|
|
|
e0018b |
+ p->ways = argc;
|
|
|
e0018b |
+ } else {
|
|
|
e0018b |
+ log_err(&rl,
|
|
|
e0018b |
+ "couldn't determine interleave ways from options or arguments\n");
|
|
|
e0018b |
+ return -EINVAL;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (param.granularity) {
|
|
|
e0018b |
+ unsigned long granularity = strtoul(param.granularity, NULL, 0);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (granularity == ULONG_MAX || (int)granularity <= 0) {
|
|
|
e0018b |
+ log_err(&rl, "Invalid interleave granularity: %s\n",
|
|
|
e0018b |
+ param.granularity);
|
|
|
e0018b |
+ return -EINVAL;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ p->granularity = granularity;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (argc > (int)p->ways) {
|
|
|
e0018b |
+ for (i = p->ways; i < argc; i++)
|
|
|
e0018b |
+ log_err(&rl, "extra argument: %s\n", p->targets[i]);
|
|
|
e0018b |
+ return -EINVAL;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (argc < (int)p->ways) {
|
|
|
e0018b |
+ log_err(&rl,
|
|
|
e0018b |
+ "too few target arguments (%d) for interleave ways (%u)\n",
|
|
|
e0018b |
+ argc, p->ways);
|
|
|
e0018b |
+ return -EINVAL;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (p->size && p->ways) {
|
|
|
e0018b |
+ if (p->size % p->ways) {
|
|
|
e0018b |
+ log_err(&rl,
|
|
|
e0018b |
+ "size (%lu) is not an integral multiple of interleave-ways (%u)\n",
|
|
|
e0018b |
+ p->size, p->ways);
|
|
|
e0018b |
+ return -EINVAL;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ /*
|
|
|
e0018b |
+ * For all practical purposes, -m is the default target type, but
|
|
|
e0018b |
+ * hold off on actively making that decision until a second target
|
|
|
e0018b |
+ * option is available.
|
|
|
e0018b |
+ */
|
|
|
e0018b |
+ if (!param.memdevs) {
|
|
|
e0018b |
+ log_err(&rl,
|
|
|
e0018b |
+ "must specify option for target object types (-m)\n");
|
|
|
e0018b |
+ return -EINVAL;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ return 0;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+static int parse_region_options(int argc, const char **argv,
|
|
|
e0018b |
+ struct cxl_ctx *ctx, enum region_actions action,
|
|
|
e0018b |
+ const struct option *options,
|
|
|
e0018b |
+ struct parsed_params *p, const char *usage)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ const char * const u[] = {
|
|
|
e0018b |
+ usage,
|
|
|
e0018b |
+ NULL
|
|
|
e0018b |
+ };
|
|
|
e0018b |
+
|
|
|
e0018b |
+ argc = parse_options(argc, argv, options, u, 0);
|
|
|
e0018b |
+ p->targets = argv;
|
|
|
e0018b |
+ p->num_targets = argc;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (param.debug) {
|
|
|
e0018b |
+ cxl_set_log_priority(ctx, LOG_DEBUG);
|
|
|
e0018b |
+ rl.log_priority = LOG_DEBUG;
|
|
|
e0018b |
+ } else
|
|
|
e0018b |
+ rl.log_priority = LOG_INFO;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ switch(action) {
|
|
|
e0018b |
+ case ACTION_CREATE:
|
|
|
e0018b |
+ return parse_create_options(argc, argv, p);
|
|
|
e0018b |
+ default:
|
|
|
e0018b |
+ return 0;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+/**
|
|
|
e0018b |
+ * validate_memdev() - match memdev with the target provided,
|
|
|
e0018b |
+ * and determine its size contribution
|
|
|
e0018b |
+ * @memdev: cxl_memdev being tested for a match against the named target
|
|
|
e0018b |
+ * @target: target memdev
|
|
|
e0018b |
+ * @p: params structure
|
|
|
e0018b |
+ *
|
|
|
e0018b |
+ * This is called for each memdev in the system, and only returns 'true' if
|
|
|
e0018b |
+ * the memdev name matches the target argument being tested. Additionally,
|
|
|
e0018b |
+ * it sets an ep_min_size attribute that always contains the size of the
|
|
|
e0018b |
+ * smallest target in the provided list. This is used during the automatic
|
|
|
e0018b |
+ * size determination later, to ensure that all targets contribute equally
|
|
|
e0018b |
+ * to the region in case of unevenly sized memdevs.
|
|
|
e0018b |
+ */
|
|
|
e0018b |
+static bool validate_memdev(struct cxl_memdev *memdev, const char *target,
|
|
|
e0018b |
+ struct parsed_params *p)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ const char *devname = cxl_memdev_get_devname(memdev);
|
|
|
e0018b |
+ u64 size;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (strcmp(devname, target) != 0)
|
|
|
e0018b |
+ return false;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ size = cxl_memdev_get_pmem_size(memdev);
|
|
|
e0018b |
+ if (!p->ep_min_size)
|
|
|
e0018b |
+ p->ep_min_size = size;
|
|
|
e0018b |
+ else
|
|
|
e0018b |
+ p->ep_min_size = min(p->ep_min_size, size);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ return true;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+static int validate_config_memdevs(struct cxl_ctx *ctx, struct parsed_params *p)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ unsigned int i, matched = 0;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ for (i = 0; i < p->ways; i++) {
|
|
|
e0018b |
+ struct cxl_memdev *memdev;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ cxl_memdev_foreach(ctx, memdev)
|
|
|
e0018b |
+ if (validate_memdev(memdev, p->targets[i], p))
|
|
|
e0018b |
+ matched++;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ if (matched != p->ways) {
|
|
|
e0018b |
+ log_err(&rl,
|
|
|
e0018b |
+ "one or more memdevs not found in CXL topology\n");
|
|
|
e0018b |
+ return -ENXIO;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ return 0;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+static int validate_decoder(struct cxl_decoder *decoder,
|
|
|
e0018b |
+ struct parsed_params *p)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ const char *devname = cxl_decoder_get_devname(decoder);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ switch(p->mode) {
|
|
|
e0018b |
+ case CXL_DECODER_MODE_RAM:
|
|
|
e0018b |
+ if (!cxl_decoder_is_volatile_capable(decoder)) {
|
|
|
e0018b |
+ log_err(&rl, "%s is not volatile capable\n", devname);
|
|
|
e0018b |
+ return -EINVAL;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ break;
|
|
|
e0018b |
+ case CXL_DECODER_MODE_PMEM:
|
|
|
e0018b |
+ if (!cxl_decoder_is_pmem_capable(decoder)) {
|
|
|
e0018b |
+ log_err(&rl, "%s is not pmem capable\n", devname);
|
|
|
e0018b |
+ return -EINVAL;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ break;
|
|
|
e0018b |
+ default:
|
|
|
e0018b |
+ log_err(&rl, "unknown type: %s\n", param.type);
|
|
|
e0018b |
+ return -EINVAL;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ /* TODO check if the interleave config is possible under this decoder */
|
|
|
e0018b |
+
|
|
|
e0018b |
+ return 0;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+static int create_region_validate_config(struct cxl_ctx *ctx,
|
|
|
e0018b |
+ struct parsed_params *p)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ struct cxl_bus *bus;
|
|
|
e0018b |
+ int rc;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ cxl_bus_foreach(ctx, bus) {
|
|
|
e0018b |
+ struct cxl_decoder *decoder;
|
|
|
e0018b |
+ struct cxl_port *port;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (!util_cxl_bus_filter(bus, param.bus))
|
|
|
e0018b |
+ continue;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ port = cxl_bus_get_port(bus);
|
|
|
e0018b |
+ if (!cxl_port_is_root(port))
|
|
|
e0018b |
+ continue;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ cxl_decoder_foreach (port, decoder) {
|
|
|
e0018b |
+ if (util_cxl_decoder_filter(decoder,
|
|
|
e0018b |
+ param.root_decoder)) {
|
|
|
e0018b |
+ p->root_decoder = decoder;
|
|
|
e0018b |
+ goto found;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+found:
|
|
|
e0018b |
+ if (p->root_decoder == NULL) {
|
|
|
e0018b |
+ log_err(&rl, "%s not found in CXL topology\n",
|
|
|
e0018b |
+ param.root_decoder);
|
|
|
e0018b |
+ return -ENXIO;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ rc = validate_decoder(p->root_decoder, p);
|
|
|
e0018b |
+ if (rc)
|
|
|
e0018b |
+ return rc;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ return validate_config_memdevs(ctx, p);
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+static struct cxl_decoder *
|
|
|
e0018b |
+cxl_memdev_target_find_decoder(struct cxl_ctx *ctx, const char *memdev_name)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ struct cxl_endpoint *ep = NULL;
|
|
|
e0018b |
+ struct cxl_decoder *decoder;
|
|
|
e0018b |
+ struct cxl_memdev *memdev;
|
|
|
e0018b |
+ struct cxl_port *port;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ cxl_memdev_foreach(ctx, memdev) {
|
|
|
e0018b |
+ const char *devname = cxl_memdev_get_devname(memdev);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (strcmp(devname, memdev_name) != 0)
|
|
|
e0018b |
+ continue;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ ep = cxl_memdev_get_endpoint(memdev);
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (!ep) {
|
|
|
e0018b |
+ log_err(&rl, "could not get an endpoint for %s\n",
|
|
|
e0018b |
+ memdev_name);
|
|
|
e0018b |
+ return NULL;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ port = cxl_endpoint_get_port(ep);
|
|
|
e0018b |
+ if (!port) {
|
|
|
e0018b |
+ log_err(&rl, "could not get a port for %s\n",
|
|
|
e0018b |
+ memdev_name);
|
|
|
e0018b |
+ return NULL;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ cxl_decoder_foreach(port, decoder)
|
|
|
e0018b |
+ if (cxl_decoder_get_size(decoder) == 0)
|
|
|
e0018b |
+ return decoder;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ log_err(&rl, "could not get a free decoder for %s\n", memdev_name);
|
|
|
e0018b |
+ return NULL;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+#define try(prefix, op, dev, p) \
|
|
|
e0018b |
+do { \
|
|
|
e0018b |
+ int __rc = prefix##_##op(dev, p); \
|
|
|
e0018b |
+ if (__rc) { \
|
|
|
e0018b |
+ log_err(&rl, "%s: " #op " failed: %s\n", \
|
|
|
e0018b |
+ prefix##_get_devname(dev), \
|
|
|
e0018b |
+ strerror(abs(__rc))); \
|
|
|
e0018b |
+ rc = __rc; \
|
|
|
e0018b |
+ goto err_delete; \
|
|
|
e0018b |
+ } \
|
|
|
e0018b |
+} while (0)
|
|
|
e0018b |
+
|
|
|
e0018b |
+static int cxl_region_determine_granularity(struct cxl_region *region,
|
|
|
e0018b |
+ struct parsed_params *p)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ const char *devname = cxl_region_get_devname(region);
|
|
|
e0018b |
+ unsigned int granularity, ways;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ /* Default granularity will be the root decoder's granularity */
|
|
|
e0018b |
+ granularity = cxl_decoder_get_interleave_granularity(p->root_decoder);
|
|
|
e0018b |
+ if (granularity == 0 || granularity == UINT_MAX) {
|
|
|
e0018b |
+ log_err(&rl, "%s: unable to determine root decoder granularity\n",
|
|
|
e0018b |
+ devname);
|
|
|
e0018b |
+ return -ENXIO;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ /* If no user-supplied granularity, just use the default */
|
|
|
e0018b |
+ if (!p->granularity)
|
|
|
e0018b |
+ return granularity;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ ways = cxl_decoder_get_interleave_ways(p->root_decoder);
|
|
|
e0018b |
+ if (ways == 0 || ways == UINT_MAX) {
|
|
|
e0018b |
+ log_err(&rl, "%s: unable to determine root decoder ways\n",
|
|
|
e0018b |
+ devname);
|
|
|
e0018b |
+ return -ENXIO;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ /* For ways == 1, any user-supplied granularity is fine */
|
|
|
e0018b |
+ if (ways == 1)
|
|
|
e0018b |
+ return p->granularity;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ /*
|
|
|
e0018b |
+ * For ways > 1, only allow the same granularity as the selected
|
|
|
e0018b |
+ * root decoder
|
|
|
e0018b |
+ */
|
|
|
e0018b |
+ if (p->granularity == granularity)
|
|
|
e0018b |
+ return granularity;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ log_err(&rl,
|
|
|
e0018b |
+ "%s: For an x%d root, only root decoder granularity (%d) permitted\n",
|
|
|
e0018b |
+ devname, ways, granularity);
|
|
|
e0018b |
+ return -EINVAL;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+static int create_region(struct cxl_ctx *ctx, int *count,
|
|
|
e0018b |
+ struct parsed_params *p)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ unsigned long flags = UTIL_JSON_TARGETS;
|
|
|
e0018b |
+ struct json_object *jregion;
|
|
|
e0018b |
+ unsigned int i, granularity;
|
|
|
e0018b |
+ struct cxl_region *region;
|
|
|
e0018b |
+ const char *devname;
|
|
|
e0018b |
+ uuid_t uuid;
|
|
|
e0018b |
+ u64 size;
|
|
|
e0018b |
+ int rc;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ rc = create_region_validate_config(ctx, p);
|
|
|
e0018b |
+ if (rc)
|
|
|
e0018b |
+ return rc;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (p->size) {
|
|
|
e0018b |
+ size = p->size;
|
|
|
e0018b |
+ } else if (p->ep_min_size) {
|
|
|
e0018b |
+ size = p->ep_min_size * p->ways;
|
|
|
e0018b |
+ } else {
|
|
|
e0018b |
+ log_err(&rl, "%s: unable to determine region size\n", __func__);
|
|
|
e0018b |
+ return -ENXIO;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (p->mode == CXL_DECODER_MODE_PMEM) {
|
|
|
e0018b |
+ region = cxl_decoder_create_pmem_region(p->root_decoder);
|
|
|
e0018b |
+ if (!region) {
|
|
|
e0018b |
+ log_err(&rl, "failed to create region under %s\n",
|
|
|
e0018b |
+ param.root_decoder);
|
|
|
e0018b |
+ return -ENXIO;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ } else {
|
|
|
e0018b |
+ log_err(&rl, "region type '%s' not supported yet\n",
|
|
|
e0018b |
+ param.type);
|
|
|
e0018b |
+ return -EOPNOTSUPP;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ devname = cxl_region_get_devname(region);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ rc = cxl_region_determine_granularity(region, p);
|
|
|
e0018b |
+ if (rc < 0)
|
|
|
e0018b |
+ goto err_delete;
|
|
|
e0018b |
+ granularity = rc;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ uuid_generate(uuid);
|
|
|
e0018b |
+ try(cxl_region, set_interleave_granularity, region, granularity);
|
|
|
e0018b |
+ try(cxl_region, set_interleave_ways, region, p->ways);
|
|
|
e0018b |
+ try(cxl_region, set_uuid, region, uuid);
|
|
|
e0018b |
+ try(cxl_region, set_size, region, size);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ for (i = 0; i < p->ways; i++) {
|
|
|
e0018b |
+ struct cxl_decoder *ep_decoder = NULL;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ ep_decoder = cxl_memdev_target_find_decoder(ctx, p->targets[i]);
|
|
|
e0018b |
+ if (!ep_decoder) {
|
|
|
e0018b |
+ rc = -ENXIO;
|
|
|
e0018b |
+ goto err_delete;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ if (cxl_decoder_get_mode(ep_decoder) != p->mode) {
|
|
|
e0018b |
+ /*
|
|
|
e0018b |
+ * The memdev_target_find_decoder() helper returns a free
|
|
|
e0018b |
+ * decoder whose size has been checked for 0.
|
|
|
e0018b |
+ * Thus it is safe to change the mode here if needed.
|
|
|
e0018b |
+ */
|
|
|
e0018b |
+ try(cxl_decoder, set_dpa_size, ep_decoder, 0);
|
|
|
e0018b |
+ try(cxl_decoder, set_mode, ep_decoder, p->mode);
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ try(cxl_decoder, set_dpa_size, ep_decoder, size/p->ways);
|
|
|
e0018b |
+ rc = cxl_region_set_target(region, i, ep_decoder);
|
|
|
e0018b |
+ if (rc) {
|
|
|
e0018b |
+ log_err(&rl, "%s: failed to set target%d to %s\n",
|
|
|
e0018b |
+ devname, i, p->targets[i]);
|
|
|
e0018b |
+ goto err_delete;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ rc = cxl_region_decode_commit(region);
|
|
|
e0018b |
+ if (rc) {
|
|
|
e0018b |
+ log_err(&rl, "%s: failed to commit decode: %s\n", devname,
|
|
|
e0018b |
+ strerror(-rc));
|
|
|
e0018b |
+ goto err_delete;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ rc = cxl_region_enable(region);
|
|
|
e0018b |
+ if (rc) {
|
|
|
e0018b |
+ log_err(&rl, "%s: failed to enable: %s\n", devname,
|
|
|
e0018b |
+ strerror(-rc));
|
|
|
e0018b |
+ goto err_delete;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ *count = 1;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (isatty(1))
|
|
|
e0018b |
+ flags |= UTIL_JSON_HUMAN;
|
|
|
e0018b |
+ jregion = util_cxl_region_to_json(region, flags);
|
|
|
e0018b |
+ if (jregion)
|
|
|
e0018b |
+ printf("%s\n", json_object_to_json_string_ext(jregion,
|
|
|
e0018b |
+ JSON_C_TO_STRING_PRETTY));
|
|
|
e0018b |
+
|
|
|
e0018b |
+ return 0;
|
|
|
e0018b |
+
|
|
|
e0018b |
+err_delete:
|
|
|
e0018b |
+ cxl_region_delete(region);
|
|
|
e0018b |
+ return rc;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+static int region_action(int argc, const char **argv, struct cxl_ctx *ctx,
|
|
|
e0018b |
+ enum region_actions action,
|
|
|
e0018b |
+ const struct option *options, struct parsed_params *p,
|
|
|
e0018b |
+ int *count, const char *u)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ int rc = -ENXIO;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ log_init(&rl, "cxl region", "CXL_REGION_LOG");
|
|
|
e0018b |
+ rc = parse_region_options(argc, argv, ctx, action, options, p, u);
|
|
|
e0018b |
+ if (rc)
|
|
|
e0018b |
+ return rc;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (action == ACTION_CREATE)
|
|
|
e0018b |
+ return create_region(ctx, count, p);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ return rc;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+int cmd_create_region(int argc, const char **argv, struct cxl_ctx *ctx)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ const char *u = "cxl create-region <target0> ... [<options>]";
|
|
|
e0018b |
+ struct parsed_params p = { 0 };
|
|
|
e0018b |
+ int rc, count = 0;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ rc = region_action(argc, argv, ctx, ACTION_CREATE, create_options, &p,
|
|
|
e0018b |
+ &count, u);
|
|
|
e0018b |
+ log_info(&rl, "created %d region%s\n", count, count == 1 ? "" : "s");
|
|
|
e0018b |
+ return rc == 0 ? 0 : EXIT_FAILURE;
|
|
|
e0018b |
+}
|
|
|
e0018b |
--
|
|
|
e0018b |
2.27.0
|
|
|
e0018b |
|