|
|
e0018b |
From 46564977afb733a92526c688fe106e19b139ddfc Mon Sep 17 00:00:00 2001
|
|
|
e0018b |
From: Dan Williams <dan.j.williams@intel.com>
|
|
|
e0018b |
Date: Sun, 23 Jan 2022 16:54:27 -0800
|
|
|
e0018b |
Subject: [PATCH 116/217] cxl/list: Add decoder support
|
|
|
e0018b |
|
|
|
e0018b |
Decoder objects exist at each level of a CXL port topology and map a
|
|
|
e0018b |
physical address range a set of interleaved ports at each level of the
|
|
|
e0018b |
hierarchy. Typically end users mostly care about the root-level decoders
|
|
|
e0018b |
which enumerate the potential CXL address space in the system, and the
|
|
|
e0018b |
endpoint decoders that indicate which address ranges a given device
|
|
|
e0018b |
contributes resources. Intermediate switch-level decoders are typically
|
|
|
e0018b |
only useful for debugging decode problems.
|
|
|
e0018b |
|
|
|
e0018b |
$ cxl list -D -d 3.1 -u
|
|
|
e0018b |
{
|
|
|
e0018b |
"decoder":"decoder3.1",
|
|
|
e0018b |
"resource":"0x8030000000",
|
|
|
e0018b |
"size":"512.00 MiB (536.87 MB)",
|
|
|
e0018b |
"volatile_capable":true
|
|
|
e0018b |
}
|
|
|
e0018b |
|
|
|
e0018b |
Link: https://lore.kernel.org/r/164298566760.3021641.3999006903066004615.stgit@dwillia2-desk3.amr.corp.intel.com
|
|
|
e0018b |
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
|
|
|
e0018b |
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
|
|
|
e0018b |
---
|
|
|
e0018b |
.clang-format | 1 +
|
|
|
e0018b |
Documentation/cxl/cxl-list.txt | 18 ++-
|
|
|
e0018b |
Documentation/cxl/lib/libcxl.txt | 70 ++++++++-
|
|
|
e0018b |
cxl/filter.c | 194 +++++++++++++++++++++++--
|
|
|
e0018b |
cxl/filter.h | 2 +
|
|
|
e0018b |
cxl/json.c | 62 ++++++++
|
|
|
e0018b |
cxl/json.h | 2 +
|
|
|
e0018b |
cxl/lib/libcxl.c | 239 ++++++++++++++++++++++++++++---
|
|
|
e0018b |
cxl/lib/libcxl.sym | 14 ++
|
|
|
e0018b |
cxl/lib/private.h | 31 +++-
|
|
|
e0018b |
cxl/libcxl.h | 28 ++++
|
|
|
e0018b |
cxl/list.c | 9 +-
|
|
|
e0018b |
util/json.h | 1 +
|
|
|
e0018b |
13 files changed, 643 insertions(+), 28 deletions(-)
|
|
|
e0018b |
|
|
|
e0018b |
diff --git a/.clang-format b/.clang-format
|
|
|
e0018b |
index 106bc5e..16e28ac 100644
|
|
|
e0018b |
--- a/.clang-format
|
|
|
e0018b |
+++ b/.clang-format
|
|
|
e0018b |
@@ -80,6 +80,7 @@ ForEachMacros:
|
|
|
e0018b |
- 'cxl_memdev_foreach'
|
|
|
e0018b |
- 'cxl_bus_foreach'
|
|
|
e0018b |
- 'cxl_port_foreach'
|
|
|
e0018b |
+ - 'cxl_decoder_foreach'
|
|
|
e0018b |
- 'cxl_endpoint_foreach'
|
|
|
e0018b |
- 'daxctl_dev_foreach'
|
|
|
e0018b |
- 'daxctl_mapping_foreach'
|
|
|
e0018b |
diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt
|
|
|
e0018b |
index bac27c7..84872b9 100644
|
|
|
e0018b |
--- a/Documentation/cxl/cxl-list.txt
|
|
|
e0018b |
+++ b/Documentation/cxl/cxl-list.txt
|
|
|
e0018b |
@@ -5,7 +5,7 @@ cxl-list(1)
|
|
|
e0018b |
|
|
|
e0018b |
NAME
|
|
|
e0018b |
----
|
|
|
e0018b |
-cxl-list - List CXL capable memory devices, and their attributes in json.
|
|
|
e0018b |
+cxl-list - List platform CXL objects, and their attributes, in json.
|
|
|
e0018b |
|
|
|
e0018b |
SYNOPSIS
|
|
|
e0018b |
--------
|
|
|
e0018b |
@@ -29,6 +29,10 @@ The potential top-level array names and their nesting properties are:
|
|
|
e0018b |
"endpoints":: nest under ports or buses (if ports are not emitted)
|
|
|
e0018b |
"memdevs":: nest under endpoints or ports (if endpoints are not
|
|
|
e0018b |
emitted) or buses (if endpoints and ports are not emitted)
|
|
|
e0018b |
+"root decoders":: nest under buses
|
|
|
e0018b |
+"port decoders":: nest under ports, or buses (if ports are not emitted)
|
|
|
e0018b |
+"endpoint decoders":: nest under endpoints, or ports (if endpoints are
|
|
|
e0018b |
+ not emitted) or buses (if endpoints and ports are not emitted)
|
|
|
e0018b |
|
|
|
e0018b |
Filters can by specifed as either a single identidier, a space separated
|
|
|
e0018b |
quoted string, or a comma separated list. When multiple filter
|
|
|
e0018b |
@@ -254,6 +258,18 @@ OPTIONS
|
|
|
e0018b |
Specify CXL endpoint device name(s), or device id(s) to filter
|
|
|
e0018b |
the emitted endpoint(s).
|
|
|
e0018b |
|
|
|
e0018b |
+-D::
|
|
|
e0018b |
+--decoders::
|
|
|
e0018b |
+ Include decoder objects (CXL Memory decode capability instances
|
|
|
e0018b |
+ in buses, ports, and endpoints) in the listing.
|
|
|
e0018b |
+
|
|
|
e0018b |
+-d::
|
|
|
e0018b |
+--decoder::
|
|
|
e0018b |
+ Specify CXL decoder device name(s), device id(s), or decoder type names
|
|
|
e0018b |
+ to filter the emitted decoder(s). The format for a decoder name is
|
|
|
e0018b |
+ "decoder<port_id>.<instance_id>". The possible decoder type names are
|
|
|
e0018b |
+ "root", "switch", or "endpoint", similar to the port filter syntax.
|
|
|
e0018b |
+
|
|
|
e0018b |
--debug::
|
|
|
e0018b |
If the cxl tool was built with debug enabled, turn on debug
|
|
|
e0018b |
messages.
|
|
|
e0018b |
diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt
|
|
|
e0018b |
index 49edb71..73af3d0 100644
|
|
|
e0018b |
--- a/Documentation/cxl/lib/libcxl.txt
|
|
|
e0018b |
+++ b/Documentation/cxl/lib/libcxl.txt
|
|
|
e0018b |
@@ -179,6 +179,7 @@ struct cxl_bus *cxl_bus_get_first(struct cxl_ctx *ctx);
|
|
|
e0018b |
struct cxl_bus *cxl_bus_get_next(struct cxl_bus *bus);
|
|
|
e0018b |
struct cxl_ctx *cxl_bus_get_ctx(struct cxl_bus *bus);
|
|
|
e0018b |
struct cxl_bus *cxl_memdev_get_bus(struct cxl_memdev *memdev);
|
|
|
e0018b |
+struct cxl_bus *cxl_port_get_bus(struct cxl_port *port);
|
|
|
e0018b |
struct cxl_bus *cxl_endpoint_get_bus(struct cxl_endpoint *endpoint);
|
|
|
e0018b |
|
|
|
e0018b |
#define cxl_bus_foreach(ctx, bus) \
|
|
|
e0018b |
@@ -215,9 +216,9 @@ struct cxl_port *cxl_bus_get_port(struct cxl_bus *bus);
|
|
|
e0018b |
struct cxl_port *cxl_port_get_first(struct cxl_port *parent);
|
|
|
e0018b |
struct cxl_port *cxl_port_get_next(struct cxl_port *port);
|
|
|
e0018b |
struct cxl_port *cxl_port_get_parent(struct cxl_port *port);
|
|
|
e0018b |
-struct cxl_bus *cxl_port_get_bus(struct cxl_port *port);
|
|
|
e0018b |
struct cxl_ctx *cxl_port_get_ctx(struct cxl_port *port);
|
|
|
e0018b |
const char *cxl_port_get_host(struct cxl_port *port);
|
|
|
e0018b |
+struct cxl_port *cxl_decoder_get_port(struct cxl_decoder *decoder);
|
|
|
e0018b |
|
|
|
e0018b |
#define cxl_port_foreach(parent, port) \
|
|
|
e0018b |
for (port = cxl_port_get_first(parent); port != NULL; \
|
|
|
e0018b |
@@ -284,6 +285,73 @@ int cxl_endpoint_get_id(struct cxl_endpoint *endpoint);
|
|
|
e0018b |
int cxl_endpoint_is_enabled(struct cxl_endpoint *endpoint);
|
|
|
e0018b |
----
|
|
|
e0018b |
|
|
|
e0018b |
+DECODERS
|
|
|
e0018b |
+--------
|
|
|
e0018b |
+Decoder objects are associated with the "HDM Decoder Capability"
|
|
|
e0018b |
+published in Port devices and CXL capable PCIe endpoints. The kernel
|
|
|
e0018b |
+additionally models platform firmware described CXL memory ranges (like
|
|
|
e0018b |
+the ACPI CEDT.CFMWS) as static decoder objects. They route System
|
|
|
e0018b |
+Physical Addresses through a port topology to an endpoint decoder that
|
|
|
e0018b |
+does the final translation from SPA to DPA (system-physical-address to
|
|
|
e0018b |
+device-local-physical-address).
|
|
|
e0018b |
+
|
|
|
e0018b |
+=== DECODER: Enumeration
|
|
|
e0018b |
+----
|
|
|
e0018b |
+struct cxl_decoder *cxl_decoder_get_first(struct cxl_port *port);
|
|
|
e0018b |
+struct cxl_decoder *cxl_decoder_get_next(struct cxl_decoder *decoder);
|
|
|
e0018b |
+struct cxl_ctx *cxl_decoder_get_ctx(struct cxl_decoder *decoder);
|
|
|
e0018b |
+
|
|
|
e0018b |
+#define cxl_decoder_foreach(port, decoder) \
|
|
|
e0018b |
+ for (decoder = cxl_decoder_get_first(port); decoder != NULL; \
|
|
|
e0018b |
+ decoder = cxl_decoder_get_next(decoder))
|
|
|
e0018b |
+----
|
|
|
e0018b |
+The definition of a CXL port in libcxl is an object that hosts one or
|
|
|
e0018b |
+more CXL decoder objects.
|
|
|
e0018b |
+
|
|
|
e0018b |
+=== DECODER: Attributes
|
|
|
e0018b |
+----
|
|
|
e0018b |
+unsigned long long cxl_decoder_get_resource(struct cxl_decoder *decoder);
|
|
|
e0018b |
+unsigned long long cxl_decoder_get_size(struct cxl_decoder *decoder);
|
|
|
e0018b |
+const char *cxl_decoder_get_devname(struct cxl_decoder *decoder);
|
|
|
e0018b |
+int cxl_decoder_get_id(struct cxl_decoder *decoder);
|
|
|
e0018b |
+
|
|
|
e0018b |
+enum cxl_decoder_target_type {
|
|
|
e0018b |
+ CXL_DECODER_TTYPE_UNKNOWN,
|
|
|
e0018b |
+ CXL_DECODER_TTYPE_EXPANDER,
|
|
|
e0018b |
+ CXL_DECODER_TTYPE_ACCELERATOR,
|
|
|
e0018b |
+};
|
|
|
e0018b |
+
|
|
|
e0018b |
+cxl_decoder_get_target_type(struct cxl_decoder *decoder);
|
|
|
e0018b |
+bool cxl_decoder_is_pmem_capable(struct cxl_decoder *decoder);
|
|
|
e0018b |
+bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder);
|
|
|
e0018b |
+bool cxl_decoder_is_mem_capable(struct cxl_decoder *decoder);
|
|
|
e0018b |
+bool cxl_decoder_is_accelmem_capable(struct cxl_decoder *decoder);
|
|
|
e0018b |
+bool cxl_decoder_is_locked(struct cxl_decoder *decoder);
|
|
|
e0018b |
+----
|
|
|
e0018b |
+The kernel protects the enumeration of the physical address layout of
|
|
|
e0018b |
+the system. Without CAP_SYS_ADMIN cxl_decoder_get_resource() returns
|
|
|
e0018b |
+ULLONG_MAX to indicate that the address information was not retrievable.
|
|
|
e0018b |
+Otherwise, cxl_decoder_get_resource() returns the currently programmed
|
|
|
e0018b |
+value of the base of the decoder's decode range. A zero-sized decoder
|
|
|
e0018b |
+indicates a disabled decoder.
|
|
|
e0018b |
+
|
|
|
e0018b |
+Root level decoders only support limited set of memory types in their
|
|
|
e0018b |
+address range. The cxl_decoder_is_<memtype>_capable() helpers identify
|
|
|
e0018b |
+what is supported. Switch level decoders, in contrast are capable of
|
|
|
e0018b |
+routing any memory type, i.e. they just forward along the memory type
|
|
|
e0018b |
+support from their parent port. Endpoint decoders follow the
|
|
|
e0018b |
+capabilities of their host memory device.
|
|
|
e0018b |
+
|
|
|
e0018b |
+The capabilities of a decoder are not to be confused with their type /
|
|
|
e0018b |
+mode. The type ultimately depends on the endpoint. For example an
|
|
|
e0018b |
+accelerator requires all decoders in its ancestry to be set to
|
|
|
e0018b |
+CXL_DECODER_TTYPE_ACCELERATOR, and conversely plain memory expander
|
|
|
e0018b |
+devices require CXL_DECODER_TTYPE_EXPANDER.
|
|
|
e0018b |
+
|
|
|
e0018b |
+Platform firmware may setup the CXL decode hierarchy before the OS
|
|
|
e0018b |
+boots, and may additionally require that the OS not change the decode
|
|
|
e0018b |
+settings. This property is indicated by the cxl_decoder_is_locked() API.
|
|
|
e0018b |
+
|
|
|
e0018b |
include::../../copyright.txt[]
|
|
|
e0018b |
|
|
|
e0018b |
SEE ALSO
|
|
|
e0018b |
diff --git a/cxl/filter.c b/cxl/filter.c
|
|
|
e0018b |
index 6dc61a1..dc052f6 100644
|
|
|
e0018b |
--- a/cxl/filter.c
|
|
|
e0018b |
+++ b/cxl/filter.c
|
|
|
e0018b |
@@ -171,6 +171,17 @@ util_cxl_endpoint_filter_by_port(struct cxl_endpoint *endpoint,
|
|
|
e0018b |
return NULL;
|
|
|
e0018b |
}
|
|
|
e0018b |
|
|
|
e0018b |
+static struct cxl_decoder *
|
|
|
e0018b |
+util_cxl_decoder_filter_by_port(struct cxl_decoder *decoder, const char *ident,
|
|
|
e0018b |
+ enum cxl_port_filter_mode mode)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ struct cxl_port *port = cxl_decoder_get_port(decoder);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (util_cxl_port_filter(port, ident, mode))
|
|
|
e0018b |
+ return decoder;
|
|
|
e0018b |
+ return NULL;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
static struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus,
|
|
|
e0018b |
const char *__ident)
|
|
|
e0018b |
{
|
|
|
e0018b |
@@ -233,6 +244,16 @@ static struct cxl_port *util_cxl_port_filter_by_bus(struct cxl_port *port,
|
|
|
e0018b |
return NULL;
|
|
|
e0018b |
}
|
|
|
e0018b |
|
|
|
e0018b |
+static struct cxl_decoder *
|
|
|
e0018b |
+util_cxl_decoder_filter_by_bus(struct cxl_decoder *decoder, const char *__ident)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ struct cxl_port *port = cxl_decoder_get_port(decoder);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (!util_cxl_port_filter_by_bus(port, __ident))
|
|
|
e0018b |
+ return NULL;
|
|
|
e0018b |
+ return decoder;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
static struct cxl_memdev *
|
|
|
e0018b |
util_cxl_memdev_serial_filter(struct cxl_memdev *memdev, const char *__serials)
|
|
|
e0018b |
{
|
|
|
e0018b |
@@ -357,6 +378,49 @@ static struct cxl_port *util_cxl_port_filter_by_memdev(struct cxl_port *port,
|
|
|
e0018b |
return NULL;
|
|
|
e0018b |
}
|
|
|
e0018b |
|
|
|
e0018b |
+static struct cxl_decoder *util_cxl_decoder_filter(struct cxl_decoder *decoder,
|
|
|
e0018b |
+ const char *__ident)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ struct cxl_port *port = cxl_decoder_get_port(decoder);
|
|
|
e0018b |
+ int pid, did;
|
|
|
e0018b |
+ char *ident, *save;
|
|
|
e0018b |
+ const char *name;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (!__ident)
|
|
|
e0018b |
+ return decoder;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ ident = strdup(__ident);
|
|
|
e0018b |
+ if (!ident)
|
|
|
e0018b |
+ return NULL;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ for (name = strtok_r(ident, which_sep(__ident), &save); name;
|
|
|
e0018b |
+ name = strtok_r(NULL, which_sep(__ident), &save)) {
|
|
|
e0018b |
+ if (strcmp(name, "all") == 0)
|
|
|
e0018b |
+ break;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (strcmp(name, "root") == 0 && cxl_port_is_root(port))
|
|
|
e0018b |
+ break;
|
|
|
e0018b |
+ if (strcmp(name, "switch") == 0 && cxl_port_is_switch(port))
|
|
|
e0018b |
+ break;
|
|
|
e0018b |
+ if (strcmp(name, "endpoint") == 0 && cxl_port_is_endpoint(port))
|
|
|
e0018b |
+ break;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if ((sscanf(name, "%d.%d", &pid, &did) == 2 ||
|
|
|
e0018b |
+ sscanf(name, "decoder%d.%d", &pid, &did) == 2) &&
|
|
|
e0018b |
+ cxl_port_get_id(port) == pid &&
|
|
|
e0018b |
+ cxl_decoder_get_id(decoder) == did)
|
|
|
e0018b |
+ break;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (strcmp(name, cxl_decoder_get_devname(decoder)) == 0)
|
|
|
e0018b |
+ break;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ free(ident);
|
|
|
e0018b |
+ if (name)
|
|
|
e0018b |
+ return decoder;
|
|
|
e0018b |
+ return NULL;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
static unsigned long params_to_flags(struct cxl_filter_params *param)
|
|
|
e0018b |
{
|
|
|
e0018b |
unsigned long flags = 0;
|
|
|
e0018b |
@@ -440,15 +504,44 @@ static struct json_object *pick_array(struct json_object *child,
|
|
|
e0018b |
return NULL;
|
|
|
e0018b |
}
|
|
|
e0018b |
|
|
|
e0018b |
+static void walk_decoders(struct cxl_port *port, struct cxl_filter_params *p,
|
|
|
e0018b |
+ struct json_object *jdecoders, unsigned long flags)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ struct cxl_decoder *decoder;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ cxl_decoder_foreach(port, decoder) {
|
|
|
e0018b |
+ struct json_object *jdecoder;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (!p->decoders)
|
|
|
e0018b |
+ continue;
|
|
|
e0018b |
+ if (!util_cxl_decoder_filter(decoder, p->decoder_filter))
|
|
|
e0018b |
+ continue;
|
|
|
e0018b |
+ if (!util_cxl_decoder_filter_by_bus(decoder, p->bus_filter))
|
|
|
e0018b |
+ continue;
|
|
|
e0018b |
+ if (!util_cxl_decoder_filter_by_port(decoder, p->port_filter,
|
|
|
e0018b |
+ pf_mode(p)))
|
|
|
e0018b |
+ continue;
|
|
|
e0018b |
+ if (!p->idle && cxl_decoder_get_size(decoder) == 0)
|
|
|
e0018b |
+ continue;
|
|
|
e0018b |
+ jdecoder = util_cxl_decoder_to_json(decoder, flags);
|
|
|
e0018b |
+ if (!decoder) {
|
|
|
e0018b |
+ dbg(p, "decoder object allocation failure\n");
|
|
|
e0018b |
+ continue;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ json_object_array_add(jdecoders, jdecoder);
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
static void walk_endpoints(struct cxl_port *port, struct cxl_filter_params *p,
|
|
|
e0018b |
struct json_object *jeps, struct json_object *jdevs,
|
|
|
e0018b |
- unsigned long flags)
|
|
|
e0018b |
+ struct json_object *jdecoders, unsigned long flags)
|
|
|
e0018b |
{
|
|
|
e0018b |
struct cxl_endpoint *endpoint;
|
|
|
e0018b |
|
|
|
e0018b |
cxl_endpoint_foreach(port, endpoint) {
|
|
|
e0018b |
struct cxl_port *ep_port = cxl_endpoint_get_port(endpoint);
|
|
|
e0018b |
const char *devname = cxl_endpoint_get_devname(endpoint);
|
|
|
e0018b |
+ struct json_object *jchilddecoders = NULL;
|
|
|
e0018b |
struct json_object *jendpoint = NULL;
|
|
|
e0018b |
struct cxl_memdev *memdev;
|
|
|
e0018b |
|
|
|
e0018b |
@@ -495,14 +588,31 @@ static void walk_endpoints(struct cxl_port *port, struct cxl_filter_params *p,
|
|
|
e0018b |
else
|
|
|
e0018b |
json_object_array_add(jdevs, jobj);
|
|
|
e0018b |
}
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (p->decoders && p->endpoints) {
|
|
|
e0018b |
+ jchilddecoders = json_object_new_array();
|
|
|
e0018b |
+ if (!jchilddecoders) {
|
|
|
e0018b |
+ err(p,
|
|
|
e0018b |
+ "%s: failed to enumerate child decoders\n",
|
|
|
e0018b |
+ devname);
|
|
|
e0018b |
+ continue;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (!p->decoders)
|
|
|
e0018b |
+ continue;
|
|
|
e0018b |
+ walk_decoders(cxl_endpoint_get_port(endpoint), p,
|
|
|
e0018b |
+ pick_array(jchilddecoders, jdecoders), flags);
|
|
|
e0018b |
+ cond_add_put_array_suffix(jendpoint, "decoders", devname,
|
|
|
e0018b |
+ jchilddecoders);
|
|
|
e0018b |
}
|
|
|
e0018b |
}
|
|
|
e0018b |
|
|
|
e0018b |
-static void walk_child_ports(struct cxl_port *parent_port,
|
|
|
e0018b |
- struct cxl_filter_params *p,
|
|
|
e0018b |
- struct json_object *jports,
|
|
|
e0018b |
- struct json_object *jeps,
|
|
|
e0018b |
- struct json_object *jdevs, unsigned long flags)
|
|
|
e0018b |
+static void
|
|
|
e0018b |
+walk_child_ports(struct cxl_port *parent_port, struct cxl_filter_params *p,
|
|
|
e0018b |
+ struct json_object *jports, struct json_object *jportdecoders,
|
|
|
e0018b |
+ struct json_object *jeps, struct json_object *jepdecoders,
|
|
|
e0018b |
+ struct json_object *jdevs, unsigned long flags)
|
|
|
e0018b |
{
|
|
|
e0018b |
struct cxl_port *port;
|
|
|
e0018b |
|
|
|
e0018b |
@@ -512,6 +622,7 @@ static void walk_child_ports(struct cxl_port *parent_port,
|
|
|
e0018b |
struct json_object *jchilddevs = NULL;
|
|
|
e0018b |
struct json_object *jchildports = NULL;
|
|
|
e0018b |
struct json_object *jchildeps = NULL;
|
|
|
e0018b |
+ struct json_object *jchilddecoders = NULL;
|
|
|
e0018b |
|
|
|
e0018b |
if (!util_cxl_port_filter_by_memdev(port, p->memdev_filter,
|
|
|
e0018b |
p->serial_filter))
|
|
|
e0018b |
@@ -555,19 +666,37 @@ static void walk_child_ports(struct cxl_port *parent_port,
|
|
|
e0018b |
continue;
|
|
|
e0018b |
}
|
|
|
e0018b |
}
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (p->decoders) {
|
|
|
e0018b |
+ jchilddecoders = json_object_new_array();
|
|
|
e0018b |
+ if (!jchilddecoders) {
|
|
|
e0018b |
+ err(p,
|
|
|
e0018b |
+ "%s: failed to enumerate child decoders\n",
|
|
|
e0018b |
+ devname);
|
|
|
e0018b |
+ continue;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ }
|
|
|
e0018b |
}
|
|
|
e0018b |
|
|
|
e0018b |
walk_children:
|
|
|
e0018b |
- if (p->endpoints || p->memdevs)
|
|
|
e0018b |
+ if (p->endpoints || p->memdevs || p->decoders)
|
|
|
e0018b |
walk_endpoints(port, p, pick_array(jchildeps, jeps),
|
|
|
e0018b |
- pick_array(jchilddevs, jdevs), flags);
|
|
|
e0018b |
+ pick_array(jchilddevs, jdevs),
|
|
|
e0018b |
+ pick_array(jchilddecoders, jepdecoders),
|
|
|
e0018b |
+ flags);
|
|
|
e0018b |
|
|
|
e0018b |
+ walk_decoders(port, p,
|
|
|
e0018b |
+ pick_array(jchilddecoders, jportdecoders), flags);
|
|
|
e0018b |
walk_child_ports(port, p, pick_array(jchildports, jports),
|
|
|
e0018b |
+ pick_array(jchilddecoders, jportdecoders),
|
|
|
e0018b |
pick_array(jchildeps, jeps),
|
|
|
e0018b |
+ pick_array(jchilddecoders, jepdecoders),
|
|
|
e0018b |
pick_array(jchilddevs, jdevs), flags);
|
|
|
e0018b |
cond_add_put_array_suffix(jport, "ports", devname, jchildports);
|
|
|
e0018b |
cond_add_put_array_suffix(jport, "endpoints", devname,
|
|
|
e0018b |
jchildeps);
|
|
|
e0018b |
+ cond_add_put_array_suffix(jport, "decoders", devname,
|
|
|
e0018b |
+ jchilddecoders);
|
|
|
e0018b |
cond_add_put_array_suffix(jport, "memdevs", devname,
|
|
|
e0018b |
jchilddevs);
|
|
|
e0018b |
}
|
|
|
e0018b |
@@ -578,6 +707,9 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
|
|
|
e0018b |
struct json_object *jdevs = NULL, *jbuses = NULL, *jports = NULL;
|
|
|
e0018b |
struct json_object *jplatform = json_object_new_array();
|
|
|
e0018b |
unsigned long flags = params_to_flags(p);
|
|
|
e0018b |
+ struct json_object *jportdecoders = NULL;
|
|
|
e0018b |
+ struct json_object *jbusdecoders = NULL;
|
|
|
e0018b |
+ struct json_object *jepdecoders = NULL;
|
|
|
e0018b |
struct json_object *janondevs = NULL;
|
|
|
e0018b |
struct json_object *jeps = NULL;
|
|
|
e0018b |
struct cxl_memdev *memdev;
|
|
|
e0018b |
@@ -609,6 +741,18 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
|
|
|
e0018b |
if (!jdevs)
|
|
|
e0018b |
goto err;
|
|
|
e0018b |
|
|
|
e0018b |
+ jbusdecoders = json_object_new_array();
|
|
|
e0018b |
+ if (!jbusdecoders)
|
|
|
e0018b |
+ goto err;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ jportdecoders = json_object_new_array();
|
|
|
e0018b |
+ if (!jportdecoders)
|
|
|
e0018b |
+ goto err;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ jepdecoders = json_object_new_array();
|
|
|
e0018b |
+ if (!jepdecoders)
|
|
|
e0018b |
+ goto err;
|
|
|
e0018b |
+
|
|
|
e0018b |
dbg(p, "walk memdevs\n");
|
|
|
e0018b |
cxl_memdev_foreach(ctx, memdev) {
|
|
|
e0018b |
struct json_object *janondev;
|
|
|
e0018b |
@@ -633,6 +777,7 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
|
|
|
e0018b |
dbg(p, "walk buses\n");
|
|
|
e0018b |
cxl_bus_foreach(ctx, bus) {
|
|
|
e0018b |
struct json_object *jbus = NULL;
|
|
|
e0018b |
+ struct json_object *jchilddecoders = NULL;
|
|
|
e0018b |
struct json_object *jchildports = NULL;
|
|
|
e0018b |
struct json_object *jchilddevs = NULL;
|
|
|
e0018b |
struct json_object *jchildeps = NULL;
|
|
|
e0018b |
@@ -681,15 +826,33 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
|
|
|
e0018b |
continue;
|
|
|
e0018b |
}
|
|
|
e0018b |
}
|
|
|
e0018b |
+ if (p->decoders) {
|
|
|
e0018b |
+ jchilddecoders = json_object_new_array();
|
|
|
e0018b |
+ if (!jchilddecoders) {
|
|
|
e0018b |
+ err(p,
|
|
|
e0018b |
+ "%s: failed to enumerate child decoders\n",
|
|
|
e0018b |
+ devname);
|
|
|
e0018b |
+ continue;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
}
|
|
|
e0018b |
walk_children:
|
|
|
e0018b |
+ dbg(p, "walk decoders\n");
|
|
|
e0018b |
+ walk_decoders(port, p, pick_array(jchilddecoders, jbusdecoders),
|
|
|
e0018b |
+ flags);
|
|
|
e0018b |
+
|
|
|
e0018b |
dbg(p, "walk ports\n");
|
|
|
e0018b |
walk_child_ports(port, p, pick_array(jchildports, jports),
|
|
|
e0018b |
+ pick_array(jchilddecoders, jportdecoders),
|
|
|
e0018b |
pick_array(jchildeps, jeps),
|
|
|
e0018b |
+ pick_array(jchilddecoders, jepdecoders),
|
|
|
e0018b |
pick_array(jchilddevs, jdevs), flags);
|
|
|
e0018b |
cond_add_put_array_suffix(jbus, "ports", devname, jchildports);
|
|
|
e0018b |
cond_add_put_array_suffix(jbus, "endpoints", devname,
|
|
|
e0018b |
jchildeps);
|
|
|
e0018b |
+ cond_add_put_array_suffix(jbus, "decoders", devname,
|
|
|
e0018b |
+ jchilddecoders);
|
|
|
e0018b |
cond_add_put_array_suffix(jbus, "memdevs", devname, jchilddevs);
|
|
|
e0018b |
}
|
|
|
e0018b |
|
|
|
e0018b |
@@ -703,12 +866,24 @@ walk_children:
|
|
|
e0018b |
top_level_objs++;
|
|
|
e0018b |
if (json_object_array_length(jdevs))
|
|
|
e0018b |
top_level_objs++;
|
|
|
e0018b |
+ if (json_object_array_length(jbusdecoders))
|
|
|
e0018b |
+ top_level_objs++;
|
|
|
e0018b |
+ if (json_object_array_length(jportdecoders))
|
|
|
e0018b |
+ top_level_objs++;
|
|
|
e0018b |
+ if (json_object_array_length(jepdecoders))
|
|
|
e0018b |
+ top_level_objs++;
|
|
|
e0018b |
|
|
|
e0018b |
splice_array(p, janondevs, jplatform, "anon memdevs", top_level_objs > 1);
|
|
|
e0018b |
splice_array(p, jbuses, jplatform, "buses", top_level_objs > 1);
|
|
|
e0018b |
splice_array(p, jports, jplatform, "ports", top_level_objs > 1);
|
|
|
e0018b |
splice_array(p, jeps, jplatform, "endpoints", top_level_objs > 1);
|
|
|
e0018b |
splice_array(p, jdevs, jplatform, "memdevs", top_level_objs > 1);
|
|
|
e0018b |
+ splice_array(p, jbusdecoders, jplatform, "root decoders",
|
|
|
e0018b |
+ top_level_objs > 1);
|
|
|
e0018b |
+ splice_array(p, jportdecoders, jplatform, "port decoders",
|
|
|
e0018b |
+ top_level_objs > 1);
|
|
|
e0018b |
+ splice_array(p, jepdecoders, jplatform, "endpoint decoders",
|
|
|
e0018b |
+ top_level_objs > 1);
|
|
|
e0018b |
|
|
|
e0018b |
util_display_json_array(stdout, jplatform, flags);
|
|
|
e0018b |
|
|
|
e0018b |
@@ -719,6 +894,9 @@ err:
|
|
|
e0018b |
json_object_put(jports);
|
|
|
e0018b |
json_object_put(jeps);
|
|
|
e0018b |
json_object_put(jdevs);
|
|
|
e0018b |
+ json_object_put(jbusdecoders);
|
|
|
e0018b |
+ json_object_put(jportdecoders);
|
|
|
e0018b |
+ json_object_put(jepdecoders);
|
|
|
e0018b |
json_object_put(jplatform);
|
|
|
e0018b |
return -ENOMEM;
|
|
|
e0018b |
}
|
|
|
e0018b |
diff --git a/cxl/filter.h b/cxl/filter.h
|
|
|
e0018b |
index bbd341c..5d7bf45 100644
|
|
|
e0018b |
--- a/cxl/filter.h
|
|
|
e0018b |
+++ b/cxl/filter.h
|
|
|
e0018b |
@@ -12,8 +12,10 @@ struct cxl_filter_params {
|
|
|
e0018b |
const char *bus_filter;
|
|
|
e0018b |
const char *port_filter;
|
|
|
e0018b |
const char *endpoint_filter;
|
|
|
e0018b |
+ const char *decoder_filter;
|
|
|
e0018b |
bool single;
|
|
|
e0018b |
bool endpoints;
|
|
|
e0018b |
+ bool decoders;
|
|
|
e0018b |
bool memdevs;
|
|
|
e0018b |
bool ports;
|
|
|
e0018b |
bool buses;
|
|
|
e0018b |
diff --git a/cxl/json.c b/cxl/json.c
|
|
|
e0018b |
index 51918d6..548bc52 100644
|
|
|
e0018b |
--- a/cxl/json.c
|
|
|
e0018b |
+++ b/cxl/json.c
|
|
|
e0018b |
@@ -262,6 +262,68 @@ struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus,
|
|
|
e0018b |
return jbus;
|
|
|
e0018b |
}
|
|
|
e0018b |
|
|
|
e0018b |
+struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder,
|
|
|
e0018b |
+ unsigned long flags)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ const char *devname = cxl_decoder_get_devname(decoder);
|
|
|
e0018b |
+ struct cxl_port *port = cxl_decoder_get_port(decoder);
|
|
|
e0018b |
+ struct json_object *jdecoder, *jobj;
|
|
|
e0018b |
+ u64 val;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ jdecoder = json_object_new_object();
|
|
|
e0018b |
+ if (!jdecoder)
|
|
|
e0018b |
+ return NULL;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ jobj = json_object_new_string(devname);
|
|
|
e0018b |
+ if (jobj)
|
|
|
e0018b |
+ json_object_object_add(jdecoder, "decoder", jobj);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ val = cxl_decoder_get_resource(decoder);
|
|
|
e0018b |
+ if (val < ULLONG_MAX) {
|
|
|
e0018b |
+ jobj = util_json_object_hex(val, flags);
|
|
|
e0018b |
+ if (jobj)
|
|
|
e0018b |
+ json_object_object_add(jdecoder, "resource", jobj);
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ val = cxl_decoder_get_size(decoder);
|
|
|
e0018b |
+ if (val < ULLONG_MAX) {
|
|
|
e0018b |
+ jobj = util_json_object_size(val, flags);
|
|
|
e0018b |
+ if (jobj)
|
|
|
e0018b |
+ json_object_object_add(jdecoder, "size", jobj);
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (val == 0) {
|
|
|
e0018b |
+ jobj = json_object_new_string("disabled");
|
|
|
e0018b |
+ if (jobj)
|
|
|
e0018b |
+ json_object_object_add(jdecoder, "state", jobj);
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (cxl_port_is_root(port) && cxl_decoder_is_mem_capable(decoder)) {
|
|
|
e0018b |
+ if (cxl_decoder_is_pmem_capable(decoder)) {
|
|
|
e0018b |
+ jobj = json_object_new_boolean(true);
|
|
|
e0018b |
+ if (jobj)
|
|
|
e0018b |
+ json_object_object_add(jdecoder, "pmem_capable",
|
|
|
e0018b |
+ jobj);
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ if (cxl_decoder_is_volatile_capable(decoder)) {
|
|
|
e0018b |
+ jobj = json_object_new_boolean(true);
|
|
|
e0018b |
+ if (jobj)
|
|
|
e0018b |
+ json_object_object_add(
|
|
|
e0018b |
+ jdecoder, "volatile_capable", jobj);
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (cxl_port_is_root(port) &&
|
|
|
e0018b |
+ cxl_decoder_is_accelmem_capable(decoder)) {
|
|
|
e0018b |
+ jobj = json_object_new_boolean(true);
|
|
|
e0018b |
+ if (jobj)
|
|
|
e0018b |
+ json_object_object_add(jdecoder, "accelmem_capable",
|
|
|
e0018b |
+ jobj);
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ return jdecoder;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
static struct json_object *__util_cxl_port_to_json(struct cxl_port *port,
|
|
|
e0018b |
const char *name_key,
|
|
|
e0018b |
unsigned long flags)
|
|
|
e0018b |
diff --git a/cxl/json.h b/cxl/json.h
|
|
|
e0018b |
index 8f45190..fcca2e6 100644
|
|
|
e0018b |
--- a/cxl/json.h
|
|
|
e0018b |
+++ b/cxl/json.h
|
|
|
e0018b |
@@ -13,4 +13,6 @@ struct json_object *util_cxl_port_to_json(struct cxl_port *port,
|
|
|
e0018b |
unsigned long flags);
|
|
|
e0018b |
struct json_object *util_cxl_endpoint_to_json(struct cxl_endpoint *endpoint,
|
|
|
e0018b |
unsigned long flags);
|
|
|
e0018b |
+struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder,
|
|
|
e0018b |
+ unsigned long flags);
|
|
|
e0018b |
#endif /* __CXL_UTIL_JSON_H__ */
|
|
|
e0018b |
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
|
|
|
e0018b |
index 2fdaf71..5e30923 100644
|
|
|
e0018b |
--- a/cxl/lib/libcxl.c
|
|
|
e0018b |
+++ b/cxl/lib/libcxl.c
|
|
|
e0018b |
@@ -63,16 +63,26 @@ static void free_memdev(struct cxl_memdev *memdev, struct list_head *head)
|
|
|
e0018b |
free(memdev->firmware_version);
|
|
|
e0018b |
free(memdev->dev_buf);
|
|
|
e0018b |
free(memdev->dev_path);
|
|
|
e0018b |
- free(memdev->host);
|
|
|
e0018b |
+ free(memdev->host_path);
|
|
|
e0018b |
free(memdev);
|
|
|
e0018b |
}
|
|
|
e0018b |
|
|
|
e0018b |
+static void free_decoder(struct cxl_decoder *decoder, struct list_head *head)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ if (head)
|
|
|
e0018b |
+ list_del_from(head, &decoder->list);
|
|
|
e0018b |
+ free(decoder->dev_buf);
|
|
|
e0018b |
+ free(decoder->dev_path);
|
|
|
e0018b |
+ free(decoder);
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
static void free_port(struct cxl_port *port, struct list_head *head);
|
|
|
e0018b |
static void free_endpoint(struct cxl_endpoint *endpoint, struct list_head *head);
|
|
|
e0018b |
static void __free_port(struct cxl_port *port, struct list_head *head)
|
|
|
e0018b |
{
|
|
|
e0018b |
struct cxl_port *child, *_c;
|
|
|
e0018b |
struct cxl_endpoint *endpoint, *_e;
|
|
|
e0018b |
+ struct cxl_decoder *decoder, *_d;
|
|
|
e0018b |
|
|
|
e0018b |
if (head)
|
|
|
e0018b |
list_del_from(head, &port->list);
|
|
|
e0018b |
@@ -80,6 +90,8 @@ static void __free_port(struct cxl_port *port, struct list_head *head)
|
|
|
e0018b |
free_port(child, &port->child_ports);
|
|
|
e0018b |
list_for_each_safe(&port->endpoints, endpoint, _e, port.list)
|
|
|
e0018b |
free_endpoint(endpoint, &port->endpoints);
|
|
|
e0018b |
+ list_for_each_safe(&port->decoders, decoder, _d, list)
|
|
|
e0018b |
+ free_decoder(decoder, &port->decoders);
|
|
|
e0018b |
kmod_module_unref(port->module);
|
|
|
e0018b |
free(port->dev_buf);
|
|
|
e0018b |
free(port->dev_path);
|
|
|
e0018b |
@@ -298,9 +310,9 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
|
|
|
e0018b |
char *path = calloc(1, strlen(cxlmem_base) + 100);
|
|
|
e0018b |
struct cxl_ctx *ctx = parent;
|
|
|
e0018b |
struct cxl_memdev *memdev, *memdev_dup;
|
|
|
e0018b |
- char *host, *rpath = NULL;
|
|
|
e0018b |
char buf[SYSFS_ATTR_SIZE];
|
|
|
e0018b |
struct stat st;
|
|
|
e0018b |
+ char *host;
|
|
|
e0018b |
|
|
|
e0018b |
if (!path)
|
|
|
e0018b |
return NULL;
|
|
|
e0018b |
@@ -358,21 +370,13 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
|
|
|
e0018b |
if (!memdev->dev_path)
|
|
|
e0018b |
goto err_read;
|
|
|
e0018b |
|
|
|
e0018b |
- rpath = realpath(cxlmem_base, NULL);
|
|
|
e0018b |
- if (!rpath)
|
|
|
e0018b |
+ memdev->host_path = realpath(cxlmem_base, NULL);
|
|
|
e0018b |
+ if (!memdev->host_path)
|
|
|
e0018b |
goto err_read;
|
|
|
e0018b |
- host = strrchr(rpath, '/');
|
|
|
e0018b |
- if (host) {
|
|
|
e0018b |
- host[0] = '\0';
|
|
|
e0018b |
- host = strrchr(rpath, '/');
|
|
|
e0018b |
- }
|
|
|
e0018b |
+ host = strrchr(memdev->host_path, '/');
|
|
|
e0018b |
if (!host)
|
|
|
e0018b |
goto err_read;
|
|
|
e0018b |
- memdev->host = strdup(host + 1);
|
|
|
e0018b |
- if (!memdev->host)
|
|
|
e0018b |
- goto err_read;
|
|
|
e0018b |
- free(rpath);
|
|
|
e0018b |
- rpath = NULL;
|
|
|
e0018b |
+ host[0] = '\0';
|
|
|
e0018b |
|
|
|
e0018b |
sprintf(path, "%s/firmware_version", cxlmem_base);
|
|
|
e0018b |
if (sysfs_read_attr(ctx, path, buf) < 0)
|
|
|
e0018b |
@@ -404,8 +408,8 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
|
|
|
e0018b |
free(memdev->firmware_version);
|
|
|
e0018b |
free(memdev->dev_buf);
|
|
|
e0018b |
free(memdev->dev_path);
|
|
|
e0018b |
+ free(memdev->host_path);
|
|
|
e0018b |
free(memdev);
|
|
|
e0018b |
- free(rpath);
|
|
|
e0018b |
err_dev:
|
|
|
e0018b |
free(path);
|
|
|
e0018b |
return NULL;
|
|
|
e0018b |
@@ -463,7 +467,7 @@ CXL_EXPORT const char *cxl_memdev_get_devname(struct cxl_memdev *memdev)
|
|
|
e0018b |
|
|
|
e0018b |
CXL_EXPORT const char *cxl_memdev_get_host(struct cxl_memdev *memdev)
|
|
|
e0018b |
{
|
|
|
e0018b |
- return memdev->host;
|
|
|
e0018b |
+ return devpath_to_devname(memdev->host_path);
|
|
|
e0018b |
}
|
|
|
e0018b |
|
|
|
e0018b |
CXL_EXPORT struct cxl_bus *cxl_memdev_get_bus(struct cxl_memdev *memdev)
|
|
|
e0018b |
@@ -679,9 +683,11 @@ static int cxl_port_init(struct cxl_port *port, struct cxl_port *parent_port,
|
|
|
e0018b |
port->ctx = ctx;
|
|
|
e0018b |
port->type = type;
|
|
|
e0018b |
port->parent = parent_port;
|
|
|
e0018b |
+ port->type = type;
|
|
|
e0018b |
|
|
|
e0018b |
list_head_init(&port->child_ports);
|
|
|
e0018b |
list_head_init(&port->endpoints);
|
|
|
e0018b |
+ list_head_init(&port->decoders);
|
|
|
e0018b |
|
|
|
e0018b |
port->dev_path = strdup(cxlport_base);
|
|
|
e0018b |
if (!port->dev_path)
|
|
|
e0018b |
@@ -842,6 +848,207 @@ cxl_endpoint_get_memdev(struct cxl_endpoint *endpoint)
|
|
|
e0018b |
return NULL;
|
|
|
e0018b |
}
|
|
|
e0018b |
|
|
|
e0018b |
+static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ const char *devname = devpath_to_devname(cxldecoder_base);
|
|
|
e0018b |
+ char *path = calloc(1, strlen(cxldecoder_base) + 100);
|
|
|
e0018b |
+ struct cxl_decoder *decoder, *decoder_dup;
|
|
|
e0018b |
+ struct cxl_port *port = parent;
|
|
|
e0018b |
+ struct cxl_ctx *ctx = cxl_port_get_ctx(port);
|
|
|
e0018b |
+ char buf[SYSFS_ATTR_SIZE];
|
|
|
e0018b |
+ size_t i;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ dbg(ctx, "%s: base: \'%s\'\n", devname, cxldecoder_base);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (!path)
|
|
|
e0018b |
+ return NULL;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ decoder = calloc(1, sizeof(*decoder));
|
|
|
e0018b |
+ if (!decoder)
|
|
|
e0018b |
+ goto err;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ decoder->id = id;
|
|
|
e0018b |
+ decoder->ctx = ctx;
|
|
|
e0018b |
+ decoder->port = port;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ decoder->dev_path = strdup(cxldecoder_base);
|
|
|
e0018b |
+ if (!decoder->dev_path)
|
|
|
e0018b |
+ goto err;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ decoder->dev_buf = calloc(1, strlen(cxldecoder_base) + 50);
|
|
|
e0018b |
+ if (!decoder->dev_buf)
|
|
|
e0018b |
+ goto err;
|
|
|
e0018b |
+ decoder->buf_len = strlen(cxldecoder_base) + 50;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ sprintf(path, "%s/start", cxldecoder_base);
|
|
|
e0018b |
+ if (sysfs_read_attr(ctx, path, buf) < 0)
|
|
|
e0018b |
+ decoder->start = ULLONG_MAX;
|
|
|
e0018b |
+ else
|
|
|
e0018b |
+ decoder->start = strtoull(buf, NULL, 0);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ sprintf(path, "%s/size", cxldecoder_base);
|
|
|
e0018b |
+ if (sysfs_read_attr(ctx, path, buf) < 0)
|
|
|
e0018b |
+ decoder->size = ULLONG_MAX;
|
|
|
e0018b |
+ else
|
|
|
e0018b |
+ decoder->size = strtoull(buf, NULL, 0);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ switch (port->type) {
|
|
|
e0018b |
+ case CXL_PORT_SWITCH:
|
|
|
e0018b |
+ case CXL_PORT_ENDPOINT:
|
|
|
e0018b |
+ decoder->pmem_capable = true;
|
|
|
e0018b |
+ decoder->volatile_capable = true;
|
|
|
e0018b |
+ decoder->mem_capable = true;
|
|
|
e0018b |
+ decoder->accelmem_capable = true;
|
|
|
e0018b |
+ sprintf(path, "%s/locked", cxldecoder_base);
|
|
|
e0018b |
+ if (sysfs_read_attr(ctx, path, buf) == 0)
|
|
|
e0018b |
+ decoder->locked = !!strtoul(buf, NULL, 0);
|
|
|
e0018b |
+ sprintf(path, "%s/target_type", cxldecoder_base);
|
|
|
e0018b |
+ if (sysfs_read_attr(ctx, path, buf) == 0) {
|
|
|
e0018b |
+ if (strcmp(buf, "accelerator") == 0)
|
|
|
e0018b |
+ decoder->target_type =
|
|
|
e0018b |
+ CXL_DECODER_TTYPE_ACCELERATOR;
|
|
|
e0018b |
+ if (strcmp(buf, "expander") == 0)
|
|
|
e0018b |
+ decoder->target_type =
|
|
|
e0018b |
+ CXL_DECODER_TTYPE_EXPANDER;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ break;
|
|
|
e0018b |
+ case CXL_PORT_ROOT: {
|
|
|
e0018b |
+ struct cxl_decoder_flag {
|
|
|
e0018b |
+ char *name;
|
|
|
e0018b |
+ bool *flag;
|
|
|
e0018b |
+ } flags[] = {
|
|
|
e0018b |
+ { "cap_type2", &decoder->accelmem_capable },
|
|
|
e0018b |
+ { "cap_type3", &decoder->mem_capable },
|
|
|
e0018b |
+ { "cap_ram", &decoder->volatile_capable },
|
|
|
e0018b |
+ { "cap_pmem", &decoder->pmem_capable },
|
|
|
e0018b |
+ { "locked", &decoder->locked },
|
|
|
e0018b |
+ };
|
|
|
e0018b |
+
|
|
|
e0018b |
+ for (i = 0; i < ARRAY_SIZE(flags); i++) {
|
|
|
e0018b |
+ struct cxl_decoder_flag *flag = &flags[i];
|
|
|
e0018b |
+
|
|
|
e0018b |
+ sprintf(path, "%s/%s", cxldecoder_base, flag->name);
|
|
|
e0018b |
+ if (sysfs_read_attr(ctx, path, buf) == 0)
|
|
|
e0018b |
+ *(flag->flag) = !!strtoul(buf, NULL, 0);
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ break;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ cxl_decoder_foreach(port, decoder_dup)
|
|
|
e0018b |
+ if (decoder_dup->id == decoder->id) {
|
|
|
e0018b |
+ free_decoder(decoder, NULL);
|
|
|
e0018b |
+ return decoder_dup;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ list_add(&port->decoders, &decoder->list);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ return decoder;
|
|
|
e0018b |
+err:
|
|
|
e0018b |
+ free(decoder->dev_path);
|
|
|
e0018b |
+ free(decoder->dev_buf);
|
|
|
e0018b |
+ free(decoder);
|
|
|
e0018b |
+ free(path);
|
|
|
e0018b |
+ return NULL;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+static void cxl_decoders_init(struct cxl_port *port)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ struct cxl_ctx *ctx = cxl_port_get_ctx(port);
|
|
|
e0018b |
+ char *decoder_fmt;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (port->decoders_init)
|
|
|
e0018b |
+ return;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ if (asprintf(&decoder_fmt, "decoder%d.", cxl_port_get_id(port)) < 0) {
|
|
|
e0018b |
+ err(ctx, "%s: failed to add decoder(s)\n",
|
|
|
e0018b |
+ cxl_port_get_devname(port));
|
|
|
e0018b |
+ return;
|
|
|
e0018b |
+ }
|
|
|
e0018b |
+
|
|
|
e0018b |
+ port->decoders_init = 1;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ sysfs_device_parse(ctx, port->dev_path, decoder_fmt, port,
|
|
|
e0018b |
+ add_cxl_decoder);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ free(decoder_fmt);
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+CXL_EXPORT struct cxl_decoder *cxl_decoder_get_first(struct cxl_port *port)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ cxl_decoders_init(port);
|
|
|
e0018b |
+
|
|
|
e0018b |
+ return list_top(&port->decoders, struct cxl_decoder, list);
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+CXL_EXPORT struct cxl_decoder *cxl_decoder_get_next(struct cxl_decoder *decoder)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ struct cxl_port *port = decoder->port;
|
|
|
e0018b |
+
|
|
|
e0018b |
+ return list_next(&port->decoders, decoder, list);
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+CXL_EXPORT struct cxl_ctx *cxl_decoder_get_ctx(struct cxl_decoder *decoder)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ return decoder->ctx;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+CXL_EXPORT int cxl_decoder_get_id(struct cxl_decoder *decoder)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ return decoder->id;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+CXL_EXPORT struct cxl_port *cxl_decoder_get_port(struct cxl_decoder *decoder)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ return decoder->port;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+CXL_EXPORT unsigned long long cxl_decoder_get_resource(struct cxl_decoder *decoder)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ return decoder->start;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+CXL_EXPORT unsigned long long cxl_decoder_get_size(struct cxl_decoder *decoder)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ return decoder->size;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+CXL_EXPORT enum cxl_decoder_target_type
|
|
|
e0018b |
+cxl_decoder_get_target_type(struct cxl_decoder *decoder)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ return decoder->target_type;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+CXL_EXPORT bool cxl_decoder_is_pmem_capable(struct cxl_decoder *decoder)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ return decoder->pmem_capable;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+CXL_EXPORT bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ return decoder->volatile_capable;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+CXL_EXPORT bool cxl_decoder_is_mem_capable(struct cxl_decoder *decoder)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ return decoder->mem_capable;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+CXL_EXPORT bool cxl_decoder_is_accelmem_capable(struct cxl_decoder *decoder)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ return decoder->accelmem_capable;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+CXL_EXPORT bool cxl_decoder_is_locked(struct cxl_decoder *decoder)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ return decoder->locked;
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
+CXL_EXPORT const char *cxl_decoder_get_devname(struct cxl_decoder *decoder)
|
|
|
e0018b |
+{
|
|
|
e0018b |
+ return devpath_to_devname(decoder->dev_path);
|
|
|
e0018b |
+}
|
|
|
e0018b |
+
|
|
|
e0018b |
static void *add_cxl_port(void *parent, int id, const char *cxlport_base)
|
|
|
e0018b |
{
|
|
|
e0018b |
const char *devname = devpath_to_devname(cxlport_base);
|
|
|
e0018b |
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
|
|
|
e0018b |
index f235e99..22babb7 100644
|
|
|
e0018b |
--- a/cxl/lib/libcxl.sym
|
|
|
e0018b |
+++ b/cxl/lib/libcxl.sym
|
|
|
e0018b |
@@ -117,4 +117,18 @@ global:
|
|
|
e0018b |
cxl_memdev_get_bus;
|
|
|
e0018b |
cxl_memdev_disable_invalidate;
|
|
|
e0018b |
cxl_memdev_enable;
|
|
|
e0018b |
+ cxl_decoder_get_first;
|
|
|
e0018b |
+ cxl_decoder_get_next;
|
|
|
e0018b |
+ cxl_decoder_get_ctx;
|
|
|
e0018b |
+ cxl_decoder_get_id;
|
|
|
e0018b |
+ cxl_decoder_get_port;
|
|
|
e0018b |
+ cxl_decoder_get_resource;
|
|
|
e0018b |
+ cxl_decoder_get_size;
|
|
|
e0018b |
+ cxl_decoder_get_devname;
|
|
|
e0018b |
+ cxl_decoder_get_target_type;
|
|
|
e0018b |
+ cxl_decoder_is_pmem_capable;
|
|
|
e0018b |
+ cxl_decoder_is_volatile_capable;
|
|
|
e0018b |
+ cxl_decoder_is_mem_capable;
|
|
|
e0018b |
+ cxl_decoder_is_accelmem_capable;
|
|
|
e0018b |
+ cxl_decoder_is_locked;
|
|
|
e0018b |
} LIBCXL_1;
|
|
|
e0018b |
diff --git a/cxl/lib/private.h b/cxl/lib/private.h
|
|
|
e0018b |
index c00bb36..1743a24 100644
|
|
|
e0018b |
--- a/cxl/lib/private.h
|
|
|
e0018b |
+++ b/cxl/lib/private.h
|
|
|
e0018b |
@@ -23,7 +23,7 @@ struct cxl_memdev {
|
|
|
e0018b |
int numa_node;
|
|
|
e0018b |
void *dev_buf;
|
|
|
e0018b |
size_t buf_len;
|
|
|
e0018b |
- char *host;
|
|
|
e0018b |
+ char *host_path;
|
|
|
e0018b |
char *dev_path;
|
|
|
e0018b |
char *firmware_version;
|
|
|
e0018b |
struct cxl_ctx *ctx;
|
|
|
e0018b |
@@ -52,6 +52,7 @@ struct cxl_port {
|
|
|
e0018b |
char *uport;
|
|
|
e0018b |
int ports_init;
|
|
|
e0018b |
int endpoints_init;
|
|
|
e0018b |
+ int decoders_init;
|
|
|
e0018b |
struct cxl_ctx *ctx;
|
|
|
e0018b |
struct cxl_bus *bus;
|
|
|
e0018b |
enum cxl_port_type type;
|
|
|
e0018b |
@@ -60,6 +61,7 @@ struct cxl_port {
|
|
|
e0018b |
struct list_node list;
|
|
|
e0018b |
struct list_head child_ports;
|
|
|
e0018b |
struct list_head endpoints;
|
|
|
e0018b |
+ struct list_head decoders;
|
|
|
e0018b |
};
|
|
|
e0018b |
|
|
|
e0018b |
struct cxl_bus {
|
|
|
e0018b |
@@ -71,6 +73,33 @@ struct cxl_endpoint {
|
|
|
e0018b |
struct cxl_memdev *memdev;
|
|
|
e0018b |
};
|
|
|
e0018b |
|
|
|
e0018b |
+struct cxl_target {
|
|
|
e0018b |
+ struct list_node list;
|
|
|
e0018b |
+ struct cxl_decoder *decoder;
|
|
|
e0018b |
+ char *dev_path;
|
|
|
e0018b |
+ int id, position;
|
|
|
e0018b |
+};
|
|
|
e0018b |
+
|
|
|
e0018b |
+struct cxl_decoder {
|
|
|
e0018b |
+ struct cxl_port *port;
|
|
|
e0018b |
+ struct list_node list;
|
|
|
e0018b |
+ struct cxl_ctx *ctx;
|
|
|
e0018b |
+ u64 start;
|
|
|
e0018b |
+ u64 size;
|
|
|
e0018b |
+ void *dev_buf;
|
|
|
e0018b |
+ size_t buf_len;
|
|
|
e0018b |
+ char *dev_path;
|
|
|
e0018b |
+ int nr_targets;
|
|
|
e0018b |
+ int id;
|
|
|
e0018b |
+ bool pmem_capable;
|
|
|
e0018b |
+ bool volatile_capable;
|
|
|
e0018b |
+ bool mem_capable;
|
|
|
e0018b |
+ bool accelmem_capable;
|
|
|
e0018b |
+ bool locked;
|
|
|
e0018b |
+ enum cxl_decoder_target_type target_type;
|
|
|
e0018b |
+ struct list_head targets;
|
|
|
e0018b |
+};
|
|
|
e0018b |
+
|
|
|
e0018b |
enum cxl_cmd_query_status {
|
|
|
e0018b |
CXL_CMD_QUERY_NOT_RUN = 0,
|
|
|
e0018b |
CXL_CMD_QUERY_OK,
|
|
|
e0018b |
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
|
|
|
e0018b |
index 53f68dd..439ed93 100644
|
|
|
e0018b |
--- a/cxl/libcxl.h
|
|
|
e0018b |
+++ b/cxl/libcxl.h
|
|
|
e0018b |
@@ -98,6 +98,34 @@ bool cxl_port_hosts_memdev(struct cxl_port *port, struct cxl_memdev *memdev);
|
|
|
e0018b |
for (port = cxl_port_get_first(parent); port != NULL; \
|
|
|
e0018b |
port = cxl_port_get_next(port))
|
|
|
e0018b |
|
|
|
e0018b |
+struct cxl_decoder;
|
|
|
e0018b |
+struct cxl_decoder *cxl_decoder_get_first(struct cxl_port *port);
|
|
|
e0018b |
+struct cxl_decoder *cxl_decoder_get_next(struct cxl_decoder *decoder);
|
|
|
e0018b |
+unsigned long long cxl_decoder_get_resource(struct cxl_decoder *decoder);
|
|
|
e0018b |
+unsigned long long cxl_decoder_get_size(struct cxl_decoder *decoder);
|
|
|
e0018b |
+const char *cxl_decoder_get_devname(struct cxl_decoder *decoder);
|
|
|
e0018b |
+struct cxl_ctx *cxl_decoder_get_ctx(struct cxl_decoder *decoder);
|
|
|
e0018b |
+int cxl_decoder_get_id(struct cxl_decoder *decoder);
|
|
|
e0018b |
+struct cxl_port *cxl_decoder_get_port(struct cxl_decoder *decoder);
|
|
|
e0018b |
+
|
|
|
e0018b |
+enum cxl_decoder_target_type {
|
|
|
e0018b |
+ CXL_DECODER_TTYPE_UNKNOWN,
|
|
|
e0018b |
+ CXL_DECODER_TTYPE_EXPANDER,
|
|
|
e0018b |
+ CXL_DECODER_TTYPE_ACCELERATOR,
|
|
|
e0018b |
+};
|
|
|
e0018b |
+
|
|
|
e0018b |
+enum cxl_decoder_target_type
|
|
|
e0018b |
+cxl_decoder_get_target_type(struct cxl_decoder *decoder);
|
|
|
e0018b |
+bool cxl_decoder_is_pmem_capable(struct cxl_decoder *decoder);
|
|
|
e0018b |
+bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder);
|
|
|
e0018b |
+bool cxl_decoder_is_mem_capable(struct cxl_decoder *decoder);
|
|
|
e0018b |
+bool cxl_decoder_is_accelmem_capable(struct cxl_decoder *decoder);
|
|
|
e0018b |
+bool cxl_decoder_is_locked(struct cxl_decoder *decoder);
|
|
|
e0018b |
+
|
|
|
e0018b |
+#define cxl_decoder_foreach(port, decoder) \
|
|
|
e0018b |
+ for (decoder = cxl_decoder_get_first(port); decoder != NULL; \
|
|
|
e0018b |
+ decoder = cxl_decoder_get_next(decoder))
|
|
|
e0018b |
+
|
|
|
e0018b |
struct cxl_endpoint;
|
|
|
e0018b |
struct cxl_endpoint *cxl_endpoint_get_first(struct cxl_port *parent);
|
|
|
e0018b |
struct cxl_endpoint *cxl_endpoint_get_next(struct cxl_endpoint *endpoint);
|
|
|
e0018b |
diff --git a/cxl/list.c b/cxl/list.c
|
|
|
e0018b |
index b15e01c..d70192a 100644
|
|
|
e0018b |
--- a/cxl/list.c
|
|
|
e0018b |
+++ b/cxl/list.c
|
|
|
e0018b |
@@ -36,6 +36,11 @@ static const struct option options[] = {
|
|
|
e0018b |
"filter by CXL endpoint device name(s)"),
|
|
|
e0018b |
OPT_BOOLEAN('E', "endpoints", ¶m.endpoints,
|
|
|
e0018b |
"include CXL endpoint info"),
|
|
|
e0018b |
+ OPT_STRING('d', "decoder", ¶m.decoder_filter,
|
|
|
e0018b |
+ "decoder device name",
|
|
|
e0018b |
+ "filter by CXL decoder device name(s) / class"),
|
|
|
e0018b |
+ OPT_BOOLEAN('D', "decoders", ¶m.decoders,
|
|
|
e0018b |
+ "include CXL decoder info"),
|
|
|
e0018b |
OPT_BOOLEAN('i', "idle", ¶m.idle, "include disabled devices"),
|
|
|
e0018b |
OPT_BOOLEAN('u', "human", ¶m.human,
|
|
|
e0018b |
"use human friendly number formats "),
|
|
|
e0018b |
@@ -50,7 +55,7 @@ static const struct option options[] = {
|
|
|
e0018b |
static int num_list_flags(void)
|
|
|
e0018b |
{
|
|
|
e0018b |
return !!param.memdevs + !!param.buses + !!param.ports +
|
|
|
e0018b |
- !!param.endpoints;
|
|
|
e0018b |
+ !!param.endpoints + !!param.decoders;
|
|
|
e0018b |
}
|
|
|
e0018b |
|
|
|
e0018b |
int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx)
|
|
|
e0018b |
@@ -82,6 +87,8 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx)
|
|
|
e0018b |
param.ports = true;
|
|
|
e0018b |
if (param.endpoint_filter)
|
|
|
e0018b |
param.endpoints = true;
|
|
|
e0018b |
+ if (param.decoder_filter)
|
|
|
e0018b |
+ param.decoders = true;
|
|
|
e0018b |
if (num_list_flags() == 0) {
|
|
|
e0018b |
/*
|
|
|
e0018b |
* TODO: We likely want to list regions by default if
|
|
|
e0018b |
diff --git a/util/json.h b/util/json.h
|
|
|
e0018b |
index 061f0d4..e026df1 100644
|
|
|
e0018b |
--- a/util/json.h
|
|
|
e0018b |
+++ b/util/json.h
|
|
|
e0018b |
@@ -18,6 +18,7 @@ enum util_json_flags {
|
|
|
e0018b |
UTIL_JSON_FIRMWARE = (1 << 8),
|
|
|
e0018b |
UTIL_JSON_DAX_MAPPINGS = (1 << 9),
|
|
|
e0018b |
UTIL_JSON_HEALTH = (1 << 10),
|
|
|
e0018b |
+ UTIL_JSON_TARGETS = (1 << 11),
|
|
|
e0018b |
};
|
|
|
e0018b |
|
|
|
e0018b |
void util_display_json_array(FILE *f_out, struct json_object *jarray,
|
|
|
e0018b |
--
|
|
|
e0018b |
2.27.0
|
|
|
e0018b |
|