Blame SOURCES/0117-cxl-list-Extend-decoder-objects-with-target-informat.patch

e0018b
From 1279d1989ef77085d214a193c1458b624039c612 Mon Sep 17 00:00:00 2001
e0018b
From: Dan Williams <dan.j.williams@intel.com>
e0018b
Date: Sun, 23 Jan 2022 16:54:34 -0800
e0018b
Subject: [PATCH 117/217] cxl/list: Extend decoder objects with target
e0018b
 information
e0018b
e0018b
A target combines information about a dport along with its position in the
e0018b
intereleave order. With targets enumerated decoders can also be filtered be
e0018b
memory devices by seeing which decoders have a dport in the memory-device's
e0018b
ancestry.
e0018b
e0018b
$ cxl list -D -d 3.1 -T -u
e0018b
{
e0018b
  "decoder":"decoder3.1",
e0018b
  "resource":"0x8030000000",
e0018b
  "size":"512.00 MiB (536.87 MB)",
e0018b
  "volatile_capable":true,
e0018b
  "nr_targets":2,
e0018b
  "targets":[
e0018b
    {
e0018b
      "target":"cxl_host_bridge.1",
e0018b
      "position":1,
e0018b
      "id":"0x1"
e0018b
    },
e0018b
    {
e0018b
      "target":"cxl_host_bridge.0",
e0018b
      "position":0,
e0018b
      "id":"0"
e0018b
    }
e0018b
  ]
e0018b
}
e0018b
e0018b
Link: https://lore.kernel.org/r/164298567435.3021641.3771899644901785666.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   |   8 ++-
e0018b
 Documentation/cxl/lib/libcxl.txt |  58 ++++++++++++++++
e0018b
 cxl/filter.c                     |  25 +++++++
e0018b
 cxl/filter.h                     |   1 +
e0018b
 cxl/json.c                       |  46 +++++++++++++
e0018b
 cxl/lib/libcxl.c                 | 115 +++++++++++++++++++++++++++++++
e0018b
 cxl/lib/libcxl.sym               |  10 +++
e0018b
 cxl/libcxl.h                     |  19 +++++
e0018b
 cxl/list.c                       |   2 +
e0018b
 10 files changed, 283 insertions(+), 2 deletions(-)
e0018b
e0018b
diff --git a/.clang-format b/.clang-format
e0018b
index 16e28ac..47fb657 100644
e0018b
--- a/.clang-format
e0018b
+++ b/.clang-format
e0018b
@@ -81,6 +81,7 @@ ForEachMacros:
e0018b
   - 'cxl_bus_foreach'
e0018b
   - 'cxl_port_foreach'
e0018b
   - 'cxl_decoder_foreach'
e0018b
+  - 'cxl_target_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 84872b9..20ff2cb 100644
e0018b
--- a/Documentation/cxl/cxl-list.txt
e0018b
+++ b/Documentation/cxl/cxl-list.txt
e0018b
@@ -44,8 +44,8 @@ would only list objects that are beneath port10 AND map mem0, mem1, OR
e0018b
 mem2.
e0018b
 
e0018b
 Given that many topology queries seek to answer questions relative to a
e0018b
-given memdev, buses, ports, and endpoints can be filtered by one or more
e0018b
-memdevs. For example:
e0018b
+given memdev, buses, ports, endpoints, and decoders can be filtered by
e0018b
+one or more memdevs. For example:
e0018b
 ----
e0018b
 # cxl list -P -p switch,endpoint -m mem0
e0018b
 [
e0018b
@@ -270,6 +270,10 @@ OPTIONS
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
+-T::
e0018b
+--targets::
e0018b
+	Extend decoder listings with downstream port target information.
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 73af3d0..bd92fef 100644
e0018b
--- a/Documentation/cxl/lib/libcxl.txt
e0018b
+++ b/Documentation/cxl/lib/libcxl.txt
e0018b
@@ -300,6 +300,7 @@ device-local-physical-address).
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
+struct cxl_decoder *cxl_target_get_decoder(struct cxl_target *target);
e0018b
 
e0018b
 #define cxl_decoder_foreach(port, decoder)                                  \
e0018b
        for (decoder = cxl_decoder_get_first(port); decoder != NULL;         \
e0018b
@@ -314,6 +315,7 @@ 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
+int cxl_decoder_get_nr_targets(struct cxl_decoder *decoder);
e0018b
 
e0018b
 enum cxl_decoder_target_type {
e0018b
        CXL_DECODER_TTYPE_UNKNOWN,
e0018b
@@ -352,6 +354,62 @@ 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
+==== TARGETS
e0018b
+A root or switch level decoder takes an SPA (system-physical-address) as
e0018b
+input and routes it to a downstream port. Which downstream port depends
e0018b
+on the downstream port's position in the interleave. A 'struct
e0018b
+cxl_target' object represents the properties of a given downstream port
e0018b
+relative to its interleave configuration.
e0018b
+
e0018b
+===== TARGET: Enumeration
e0018b
+----
e0018b
+struct cxl_target *cxl_decoder_get_target_by_memdev(struct cxl_decoder *decoder,
e0018b
+                                                   struct cxl_memdev *memdev);
e0018b
+struct cxl_target *
e0018b
+cxl_decoder_get_target_by_position(struct cxl_decoder *decoder, int position);
e0018b
+struct cxl_target *cxl_target_get_first(struct cxl_decoder *decoder);
e0018b
+struct cxl_target *cxl_target_get_next(struct cxl_target *target);
e0018b
+
e0018b
+#define cxl_target_foreach(decoder, target)                                   \
e0018b
+       for (target = cxl_target_get_first(decoder); target != NULL;           \
e0018b
+            target = cxl_target_get_next(target))
e0018b
+----
e0018b
+Target objects can only be enumerated if the decoder has been
e0018b
+configured, for switch decoders. For root decoders they are always
e0018b
+available since the root decoder target mapping is static. The
e0018b
+cxl_decoder_get_target_by_memdev() helper walks the topology to validate
e0018b
+if the given memory device is capable of receiving cycles from this
e0018b
+upstream decoder. It does not validate if the memory device is currently
e0018b
+configured to participate in that decode.
e0018b
+
e0018b
+===== TARGET: Attributes
e0018b
+----
e0018b
+int cxl_target_get_position(struct cxl_target *target);
e0018b
+unsigned long cxl_target_get_id(struct cxl_target *target);
e0018b
+const char *cxl_target_get_devname(struct cxl_target *target);
e0018b
+bool cxl_target_maps_memdev(struct cxl_target *target,
e0018b
+                           struct cxl_memdev *memdev);
e0018b
+----
e0018b
+The position of a decoder along with the interleave granularity dictate
e0018b
+which address in the decoder's resource range map to which port.
e0018b
+
e0018b
+The target id is an identifier that the CXL port uses to reference this
e0018b
+downstream port. For CXL / PCIe downstream switch ports the id is
e0018b
+defined by the PCIe Link Capability Port Number field. For root decoders
e0018b
+the id is specified by platform firmware specific mechanism. For
e0018b
+ACPI.CXL defined root ports the id comes from the CEDT.CHBS / ACPI0016
e0018b
+_UID.
e0018b
+
e0018b
+The device name of a target is the name of the host device for the
e0018b
+downstream port. For CXL / PCIe downstream ports the devname is
e0018b
+downstream switch port PCI device. For CXL root ports the devname is a
e0018b
+platform firmware object for the host bridge like a ACPI0016 device
e0018b
+instance.
e0018b
+
e0018b
+The cxl_target_maps_memdev() helper is the companion of
e0018b
+cxl_decoder_get_target_by_memdev() to determine which downstream ports /
e0018b
+targets are capable of mapping which memdevs.
e0018b
+
e0018b
 include::../../copyright.txt[]
e0018b
 
e0018b
 SEE ALSO
e0018b
diff --git a/cxl/filter.c b/cxl/filter.c
e0018b
index dc052f6..05ede91 100644
e0018b
--- a/cxl/filter.c
e0018b
+++ b/cxl/filter.c
e0018b
@@ -421,6 +421,26 @@ static struct cxl_decoder *util_cxl_decoder_filter(struct cxl_decoder *decoder,
e0018b
 	return NULL;
e0018b
 }
e0018b
 
e0018b
+static struct cxl_decoder *
e0018b
+util_cxl_decoder_filter_by_memdev(struct cxl_decoder *decoder,
e0018b
+				  const char *ident, const char *serial)
e0018b
+{
e0018b
+	struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder);
e0018b
+	struct cxl_memdev *memdev;
e0018b
+
e0018b
+	if (!ident && !serial)
e0018b
+		return decoder;
e0018b
+
e0018b
+	cxl_memdev_foreach(ctx, memdev) {
e0018b
+		if (!util_cxl_memdev_filter(memdev, ident, serial))
e0018b
+			continue;
e0018b
+		if (cxl_decoder_get_target_by_memdev(decoder, memdev))
e0018b
+			return decoder;
e0018b
+	}
e0018b
+
e0018b
+	return NULL;
e0018b
+}
e0018b
+
e0018b
 static unsigned long params_to_flags(struct cxl_filter_params *param)
e0018b
 {
e0018b
 	unsigned long flags = 0;
e0018b
@@ -431,6 +451,8 @@ static unsigned long params_to_flags(struct cxl_filter_params *param)
e0018b
 		flags |= UTIL_JSON_HUMAN;
e0018b
 	if (param->health)
e0018b
 		flags |= UTIL_JSON_HEALTH;
e0018b
+	if (param->targets)
e0018b
+		flags |= UTIL_JSON_TARGETS;
e0018b
 	return flags;
e0018b
 }
e0018b
 
e0018b
@@ -521,6 +543,9 @@ static void walk_decoders(struct cxl_port *port, struct cxl_filter_params *p,
e0018b
 		if (!util_cxl_decoder_filter_by_port(decoder, p->port_filter,
e0018b
 						     pf_mode(p)))
e0018b
 			continue;
e0018b
+		if (!util_cxl_decoder_filter_by_memdev(
e0018b
+			    decoder, p->memdev_filter, p->serial_filter))
e0018b
+			continue;
e0018b
 		if (!p->idle && cxl_decoder_get_size(decoder) == 0)
e0018b
 			continue;
e0018b
 		jdecoder = util_cxl_decoder_to_json(decoder, flags);
e0018b
diff --git a/cxl/filter.h b/cxl/filter.h
e0018b
index 5d7bf45..6fd469f 100644
e0018b
--- a/cxl/filter.h
e0018b
+++ b/cxl/filter.h
e0018b
@@ -16,6 +16,7 @@ struct cxl_filter_params {
e0018b
 	bool single;
e0018b
 	bool endpoints;
e0018b
 	bool decoders;
e0018b
+	bool targets;
e0018b
 	bool memdevs;
e0018b
 	bool ports;
e0018b
 	bool buses;
e0018b
diff --git a/cxl/json.c b/cxl/json.c
e0018b
index 548bc52..3a37909 100644
e0018b
--- a/cxl/json.c
e0018b
+++ b/cxl/json.c
e0018b
@@ -268,6 +268,8 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder,
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
+	struct json_object *jtargets;
e0018b
+	struct cxl_target *target;
e0018b
 	u64 val;
e0018b
 
e0018b
 	jdecoder = json_object_new_object();
e0018b
@@ -321,7 +323,51 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder,
e0018b
 					       jobj);
e0018b
 	}
e0018b
 
e0018b
+	/* Endpoints don't have targets, they *are* targets */
e0018b
+	if (cxl_port_is_endpoint(port))
e0018b
+		return jdecoder;
e0018b
+
e0018b
+	val = cxl_decoder_get_nr_targets(decoder);
e0018b
+	jobj = json_object_new_int(val);
e0018b
+	if (jobj)
e0018b
+		json_object_object_add(jdecoder, "nr_targets", jobj);
e0018b
+
e0018b
+	if (!(flags & UTIL_JSON_TARGETS) ||
e0018b
+	    !cxl_decoder_get_nr_targets(decoder))
e0018b
+		return jdecoder;
e0018b
+
e0018b
+	jtargets = json_object_new_array();
e0018b
+	if (!jtargets)
e0018b
+		return jdecoder;
e0018b
+
e0018b
+	cxl_target_foreach(decoder, target) {
e0018b
+		struct json_object *jtarget = json_object_new_object();
e0018b
+
e0018b
+		if (!jtarget)
e0018b
+			continue;
e0018b
+
e0018b
+		devname = cxl_target_get_devname(target);
e0018b
+		jobj = json_object_new_string(devname);
e0018b
+		if (jobj)
e0018b
+			json_object_object_add(jtarget, "target", jobj);
e0018b
+
e0018b
+		val = cxl_target_get_position(target);
e0018b
+		jobj = json_object_new_int(val);
e0018b
+		if (jobj)
e0018b
+			json_object_object_add(jtarget, "position", jobj);
e0018b
+
e0018b
+		val = cxl_target_get_id(target);
e0018b
+		jobj = util_json_object_hex(val, flags);
e0018b
+		if (jobj)
e0018b
+			json_object_object_add(jtarget, "id", jobj);
e0018b
+
e0018b
+		json_object_array_add(jtargets, jtarget);
e0018b
+	}
e0018b
+
e0018b
+	json_object_object_add(jdecoder, "targets", jtargets);
e0018b
+
e0018b
 	return jdecoder;
e0018b
+
e0018b
 }
e0018b
 
e0018b
 static struct json_object *__util_cxl_port_to_json(struct cxl_port *port,
e0018b
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
e0018b
index 5e30923..877f42c 100644
e0018b
--- a/cxl/lib/libcxl.c
e0018b
+++ b/cxl/lib/libcxl.c
e0018b
@@ -67,10 +67,22 @@ static void free_memdev(struct cxl_memdev *memdev, struct list_head *head)
e0018b
 	free(memdev);
e0018b
 }
e0018b
 
e0018b
+static void free_target(struct cxl_target *target, struct list_head *head)
e0018b
+{
e0018b
+	if (head)
e0018b
+		list_del_from(head, &target->list);
e0018b
+	free(target->dev_path);
e0018b
+	free(target);
e0018b
+}
e0018b
+
e0018b
 static void free_decoder(struct cxl_decoder *decoder, struct list_head *head)
e0018b
 {
e0018b
+	struct cxl_target *target, *_t;
e0018b
+
e0018b
 	if (head)
e0018b
 		list_del_from(head, &decoder->list);
e0018b
+	list_for_each_safe(&decoder->targets, target, _t, list)
e0018b
+		free_target(target, &decoder->targets);
e0018b
 	free(decoder->dev_buf);
e0018b
 	free(decoder->dev_path);
e0018b
 	free(decoder);
e0018b
@@ -856,6 +868,7 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
e0018b
 	struct cxl_port *port = parent;
e0018b
 	struct cxl_ctx *ctx = cxl_port_get_ctx(port);
e0018b
 	char buf[SYSFS_ATTR_SIZE];
e0018b
+	char *target_id, *save;
e0018b
 	size_t i;
e0018b
 
e0018b
 	dbg(ctx, "%s: base: \'%s\'\n", devname, cxldecoder_base);
e0018b
@@ -870,6 +883,7 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
e0018b
 	decoder->id = id;
e0018b
 	decoder->ctx = ctx;
e0018b
 	decoder->port = port;
e0018b
+	list_head_init(&decoder->targets);
e0018b
 
e0018b
 	decoder->dev_path = strdup(cxldecoder_base);
e0018b
 	if (!decoder->dev_path)
e0018b
@@ -935,6 +949,36 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
e0018b
 	}
e0018b
 	}
e0018b
 
e0018b
+	sprintf(path, "%s/target_list", cxldecoder_base);
e0018b
+	if (sysfs_read_attr(ctx, path, buf) < 0)
e0018b
+		buf[0] = '\0';
e0018b
+
e0018b
+	for (i = 0, target_id = strtok_r(buf, ",", &save); target_id;
e0018b
+	     target_id = strtok_r(NULL, ",", &save), i++) {
e0018b
+		int did = strtoul(target_id, NULL, 0);
e0018b
+		struct cxl_target *target = calloc(1, sizeof(*target));
e0018b
+
e0018b
+		if (!target)
e0018b
+			break;
e0018b
+
e0018b
+		target->id = did;
e0018b
+		target->position = i;
e0018b
+		target->decoder = decoder;
e0018b
+		sprintf(port->dev_buf, "%s/dport%d", port->dev_path, did);
e0018b
+		target->dev_path = realpath(port->dev_buf, NULL);
e0018b
+		if (!target->dev_path) {
e0018b
+			free(target);
e0018b
+			break;
e0018b
+		}
e0018b
+		dbg(ctx, "%s: target%ld %s\n", devname, i, target->dev_path);
e0018b
+		list_add(&decoder->targets, &target->list);
e0018b
+	}
e0018b
+
e0018b
+	if (target_id)
e0018b
+		err(ctx, "%s: failed to parse target%ld\n",
e0018b
+		    devpath_to_devname(cxldecoder_base), i);
e0018b
+	decoder->nr_targets = i;
e0018b
+
e0018b
 	cxl_decoder_foreach(port, decoder_dup)
e0018b
 		if (decoder_dup->id == decoder->id) {
e0018b
 			free_decoder(decoder, NULL);
e0018b
@@ -1044,11 +1088,82 @@ CXL_EXPORT bool cxl_decoder_is_locked(struct cxl_decoder *decoder)
e0018b
 	return decoder->locked;
e0018b
 }
e0018b
 
e0018b
+CXL_EXPORT int cxl_decoder_get_nr_targets(struct cxl_decoder *decoder)
e0018b
+{
e0018b
+	return decoder->nr_targets;
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
+CXL_EXPORT struct cxl_target *cxl_target_get_first(struct cxl_decoder *decoder)
e0018b
+{
e0018b
+	return list_top(&decoder->targets, struct cxl_target, list);
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT struct cxl_decoder *cxl_target_get_decoder(struct cxl_target *target)
e0018b
+{
e0018b
+	return target->decoder;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT struct cxl_target *cxl_target_get_next(struct cxl_target *target)
e0018b
+{
e0018b
+	struct cxl_decoder *decoder = cxl_target_get_decoder(target);
e0018b
+
e0018b
+	return list_next(&decoder->targets, target, list);
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT const char *cxl_target_get_devname(struct cxl_target *target)
e0018b
+{
e0018b
+	return devpath_to_devname(target->dev_path);
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT unsigned long cxl_target_get_id(struct cxl_target *target)
e0018b
+{
e0018b
+	return target->id;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT int cxl_target_get_position(struct cxl_target *target)
e0018b
+{
e0018b
+	return target->position;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT bool cxl_target_maps_memdev(struct cxl_target *target,
e0018b
+					struct cxl_memdev *memdev)
e0018b
+{
e0018b
+	struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
e0018b
+
e0018b
+	dbg(ctx, "memdev: %s target: %s\n", memdev->host_path,
e0018b
+	    target->dev_path);
e0018b
+
e0018b
+	return !!strstr(memdev->host_path, target->dev_path);
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT struct cxl_target *
e0018b
+cxl_decoder_get_target_by_memdev(struct cxl_decoder *decoder,
e0018b
+				 struct cxl_memdev *memdev)
e0018b
+{
e0018b
+	struct cxl_target *target;
e0018b
+
e0018b
+	cxl_target_foreach(decoder, target)
e0018b
+		if (cxl_target_maps_memdev(target, memdev))
e0018b
+			return target;
e0018b
+	return NULL;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT struct cxl_target *
e0018b
+cxl_decoder_get_target_by_position(struct cxl_decoder *decoder, int position)
e0018b
+{
e0018b
+	struct cxl_target *target;
e0018b
+
e0018b
+	cxl_target_foreach(decoder, target)
e0018b
+		if (target->position == position)
e0018b
+			return target;
e0018b
+	return NULL;
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 22babb7..cb33180 100644
e0018b
--- a/cxl/lib/libcxl.sym
e0018b
+++ b/cxl/lib/libcxl.sym
e0018b
@@ -125,10 +125,20 @@ global:
e0018b
 	cxl_decoder_get_resource;
e0018b
 	cxl_decoder_get_size;
e0018b
 	cxl_decoder_get_devname;
e0018b
+	cxl_decoder_get_target_by_memdev;
e0018b
+	cxl_decoder_get_target_by_position;
e0018b
+	cxl_decoder_get_nr_targets;
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
+	cxl_target_get_first;
e0018b
+	cxl_target_get_next;
e0018b
+	cxl_target_get_decoder;
e0018b
+	cxl_target_get_position;
e0018b
+	cxl_target_get_id;
e0018b
+	cxl_target_get_devname;
e0018b
+	cxl_target_maps_memdev;
e0018b
 } LIBCXL_1;
e0018b
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
e0018b
index 439ed93..abda0e5 100644
e0018b
--- a/cxl/libcxl.h
e0018b
+++ b/cxl/libcxl.h
e0018b
@@ -104,6 +104,11 @@ 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_target *cxl_decoder_get_target_by_memdev(struct cxl_decoder *decoder,
e0018b
+						    struct cxl_memdev *memdev);
e0018b
+struct cxl_target *
e0018b
+cxl_decoder_get_target_by_position(struct cxl_decoder *decoder, int position);
e0018b
+int cxl_decoder_get_nr_targets(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
@@ -126,6 +131,20 @@ bool cxl_decoder_is_locked(struct cxl_decoder *decoder);
e0018b
 	for (decoder = cxl_decoder_get_first(port); decoder != NULL;           \
e0018b
 	     decoder = cxl_decoder_get_next(decoder))
e0018b
 
e0018b
+struct cxl_target;
e0018b
+struct cxl_target *cxl_target_get_first(struct cxl_decoder *decoder);
e0018b
+struct cxl_target *cxl_target_get_next(struct cxl_target *target);
e0018b
+struct cxl_decoder *cxl_target_get_decoder(struct cxl_target *target);
e0018b
+int cxl_target_get_position(struct cxl_target *target);
e0018b
+unsigned long cxl_target_get_id(struct cxl_target *target);
e0018b
+const char *cxl_target_get_devname(struct cxl_target *target);
e0018b
+bool cxl_target_maps_memdev(struct cxl_target *target,
e0018b
+			    struct cxl_memdev *memdev);
e0018b
+
e0018b
+#define cxl_target_foreach(decoder, target)                                    \
e0018b
+	for (target = cxl_target_get_first(decoder); target != NULL;           \
e0018b
+	     target = cxl_target_get_next(target))
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 d70192a..27c963a 100644
e0018b
--- a/cxl/list.c
e0018b
+++ b/cxl/list.c
e0018b
@@ -41,6 +41,8 @@ static const struct option options[] = {
e0018b
 		   "filter by CXL decoder device name(s) / class"),
e0018b
 	OPT_BOOLEAN('D', "decoders", &param.decoders,
e0018b
 		    "include CXL decoder info"),
e0018b
+	OPT_BOOLEAN('T', "targets", &param.targets,
e0018b
+		    "include CXL target data with decoders"),
e0018b
 	OPT_BOOLEAN('i', "idle", &param.idle, "include disabled devices"),
e0018b
 	OPT_BOOLEAN('u', "human", &param.human,
e0018b
 		    "use human friendly number formats "),
e0018b
-- 
e0018b
2.27.0
e0018b