anitazha / rpms / ndctl

Forked from rpms/ndctl 2 years ago
Clone
Jeff Moyer 2c91dc
From fef3f05ca8cdfd8d783162042d5cf20325c8b64b Mon Sep 17 00:00:00 2001
Jeff Moyer 2c91dc
From: Dan Williams <dan.j.williams@intel.com>
Jeff Moyer 2c91dc
Date: Sun, 23 Jan 2022 16:53:18 -0800
Jeff Moyer 2c91dc
Subject: [PATCH 103/217] cxl/list: Add port enumeration
Jeff Moyer 2c91dc
Jeff Moyer 2c91dc
Between a cxl_bus (root port) and an endpoint there can be an arbitrary
Jeff Moyer 2c91dc
level of switches. Add enumeration for these ports at each level of the
Jeff Moyer 2c91dc
hierarchy.
Jeff Moyer 2c91dc
Jeff Moyer 2c91dc
However, given the CXL root ports are also "ports" infer that if the port
Jeff Moyer 2c91dc
filter argument is the word "root" or "root%d" then include root ports in
Jeff Moyer 2c91dc
the listing. The keyword "switch" is also provided to filter only the ports
Jeff Moyer 2c91dc
beneath the root that are not endpoint ports.
Jeff Moyer 2c91dc
Jeff Moyer 2c91dc
Link: https://lore.kernel.org/r/164298559854.3021641.17724828997703051001.stgit@dwillia2-desk3.amr.corp.intel.com
Jeff Moyer 2c91dc
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Jeff Moyer 2c91dc
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
Jeff Moyer 2c91dc
---
Jeff Moyer 2c91dc
 .clang-format                    |   1 +
Jeff Moyer 2c91dc
 Documentation/cxl/cxl-list.txt   |  24 ++++
Jeff Moyer 2c91dc
 Documentation/cxl/lib/libcxl.txt |  42 ++++++
Jeff Moyer 2c91dc
 cxl/filter.c                     | 224 ++++++++++++++++++++++++++++++-
Jeff Moyer 2c91dc
 cxl/filter.h                     |   4 +
Jeff Moyer 2c91dc
 cxl/json.c                       |  23 ++++
Jeff Moyer 2c91dc
 cxl/json.h                       |   3 +
Jeff Moyer 2c91dc
 cxl/lib/libcxl.c                 | 160 +++++++++++++++++++++-
Jeff Moyer 2c91dc
 cxl/lib/libcxl.sym               |  12 ++
Jeff Moyer 2c91dc
 cxl/lib/private.h                |  11 ++
Jeff Moyer 2c91dc
 cxl/libcxl.h                     |  19 +++
Jeff Moyer 2c91dc
 cxl/list.c                       |  17 ++-
Jeff Moyer 2c91dc
 12 files changed, 534 insertions(+), 6 deletions(-)
Jeff Moyer 2c91dc
Jeff Moyer 2c91dc
diff --git a/.clang-format b/.clang-format
Jeff Moyer 2c91dc
index 1154c76..391cd34 100644
Jeff Moyer 2c91dc
--- a/.clang-format
Jeff Moyer 2c91dc
+++ b/.clang-format
Jeff Moyer 2c91dc
@@ -79,6 +79,7 @@ ExperimentalAutoDetectBinPacking: false
Jeff Moyer 2c91dc
 ForEachMacros:
Jeff Moyer 2c91dc
   - 'cxl_memdev_foreach'
Jeff Moyer 2c91dc
   - 'cxl_bus_foreach'
Jeff Moyer 2c91dc
+  - 'cxl_port_foreach'
Jeff Moyer 2c91dc
   - 'daxctl_dev_foreach'
Jeff Moyer 2c91dc
   - 'daxctl_mapping_foreach'
Jeff Moyer 2c91dc
   - 'daxctl_region_foreach'
Jeff Moyer 2c91dc
diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt
Jeff Moyer 2c91dc
index be131ae..3076deb 100644
Jeff Moyer 2c91dc
--- a/Documentation/cxl/cxl-list.txt
Jeff Moyer 2c91dc
+++ b/Documentation/cxl/cxl-list.txt
Jeff Moyer 2c91dc
@@ -176,6 +176,30 @@ OPTIONS
Jeff Moyer 2c91dc
 	names to filter the listing. The supported provider names are "ACPI.CXL"
Jeff Moyer 2c91dc
 	and "cxl_test".
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
+-P::
Jeff Moyer 2c91dc
+--ports::
Jeff Moyer 2c91dc
+	Include port objects (CXL / PCIe root ports + Upstream Switch Ports) in
Jeff Moyer 2c91dc
+	the listing.
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+-p::
Jeff Moyer 2c91dc
+--port=::
Jeff Moyer 2c91dc
+	Specify CXL Port device name(s), device id(s), and or port type
Jeff Moyer 2c91dc
+	names to filter the listing. The supported port type names are "root"
Jeff Moyer 2c91dc
+	and "switch". Note that since a bus object is also a port, the following
Jeff Moyer 2c91dc
+	two syntaxes are equivalent:
Jeff Moyer 2c91dc
+----
Jeff Moyer 2c91dc
+# cxl list -B
Jeff Moyer 2c91dc
+# cxl list -P -p root
Jeff Moyer 2c91dc
+----
Jeff Moyer 2c91dc
+	By default, only 'switch' ports are listed.
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+-S::
Jeff Moyer 2c91dc
+--single::
Jeff Moyer 2c91dc
+	Specify whether the listing should emit all the objects that are
Jeff Moyer 2c91dc
+	descendants of a port that matches the port filter, or only direct
Jeff Moyer 2c91dc
+	descendants of the individual ports that match the filter. By default
Jeff Moyer 2c91dc
+	all descendant objects are listed.
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
 include::human-option.txt[]
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 include::verbose-option.txt[]
Jeff Moyer 2c91dc
diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt
Jeff Moyer 2c91dc
index 84af66a..804e9ca 100644
Jeff Moyer 2c91dc
--- a/Documentation/cxl/lib/libcxl.txt
Jeff Moyer 2c91dc
+++ b/Documentation/cxl/lib/libcxl.txt
Jeff Moyer 2c91dc
@@ -164,6 +164,48 @@ discovery order. The possible provider names are 'ACPI.CXL' and
Jeff Moyer 2c91dc
 the kernel device names that are subject to change based on discovery
Jeff Moyer 2c91dc
 order.
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
+PORTS
Jeff Moyer 2c91dc
+-----
Jeff Moyer 2c91dc
+CXL ports track the PCIe hierarchy between a platform firmware CXL root
Jeff Moyer 2c91dc
+object, through CXL / PCIe Host Bridges, CXL / PCIe Root Ports, and CXL
Jeff Moyer 2c91dc
+/ PCIe Switch Ports.
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+=== PORT: Enumeration
Jeff Moyer 2c91dc
+----
Jeff Moyer 2c91dc
+struct cxl_port *cxl_bus_get_port(struct cxl_bus *bus);
Jeff Moyer 2c91dc
+struct cxl_port *cxl_port_get_first(struct cxl_port *parent);
Jeff Moyer 2c91dc
+struct cxl_port *cxl_port_get_next(struct cxl_port *port);
Jeff Moyer 2c91dc
+struct cxl_port *cxl_port_get_parent(struct cxl_port *port);
Jeff Moyer 2c91dc
+struct cxl_bus *cxl_port_get_bus(struct cxl_port *port);
Jeff Moyer 2c91dc
+struct cxl_ctx *cxl_port_get_ctx(struct cxl_port *port);
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+#define cxl_port_foreach(parent, port)                                      \
Jeff Moyer 2c91dc
+       for (port = cxl_port_get_first(parent); port != NULL;                \
Jeff Moyer 2c91dc
+            port = cxl_port_get_next(port))
Jeff Moyer 2c91dc
+----
Jeff Moyer 2c91dc
+A bus object encapsulates a CXL port object. Use cxl_bus_get_port() to
Jeff Moyer 2c91dc
+use generic port APIs on root objects.
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+Ports are hierarchical. All but the a root object have another CXL port
Jeff Moyer 2c91dc
+as a parent object retrievable via cxl_port_get_parent().
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+The root port of a hiearchy can be retrieved via any port instance in
Jeff Moyer 2c91dc
+that hierarchy via cxl_port_get_bus().
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+=== PORT: Attributes
Jeff Moyer 2c91dc
+----
Jeff Moyer 2c91dc
+const char *cxl_port_get_devname(struct cxl_port *port);
Jeff Moyer 2c91dc
+int cxl_port_get_id(struct cxl_port *port);
Jeff Moyer 2c91dc
+int cxl_port_is_enabled(struct cxl_port *port);
Jeff Moyer 2c91dc
+bool cxl_port_is_root(struct cxl_port *port);
Jeff Moyer 2c91dc
+bool cxl_port_is_switch(struct cxl_port *port);
Jeff Moyer 2c91dc
+----
Jeff Moyer 2c91dc
+The port type is communicated via cxl_port_is_<type>(). An 'enabled' port
Jeff Moyer 2c91dc
+is one that has succeeded in discovering the CXL component registers in
Jeff Moyer 2c91dc
+the host device and has enumerated its downstream ports. In order for a
Jeff Moyer 2c91dc
+memdev to be enabled for CXL memory operation all CXL ports in its
Jeff Moyer 2c91dc
+ancestry must also be enabled.
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
 include::../../copyright.txt[]
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 SEE ALSO
Jeff Moyer 2c91dc
diff --git a/cxl/filter.c b/cxl/filter.c
Jeff Moyer 2c91dc
index 5f4844b..8b79db3 100644
Jeff Moyer 2c91dc
--- a/cxl/filter.c
Jeff Moyer 2c91dc
+++ b/cxl/filter.c
Jeff Moyer 2c91dc
@@ -21,6 +21,101 @@ static const char *which_sep(const char *filter)
Jeff Moyer 2c91dc
 	return " ";
Jeff Moyer 2c91dc
 }
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
+bool cxl_filter_has(const char *__filter, const char *needle)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	char *filter, *save;
Jeff Moyer 2c91dc
+	const char *arg;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	if (!needle)
Jeff Moyer 2c91dc
+		return true;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	if (!__filter)
Jeff Moyer 2c91dc
+		return false;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	filter = strdup(__filter);
Jeff Moyer 2c91dc
+	if (!filter)
Jeff Moyer 2c91dc
+		return false;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	for (arg = strtok_r(filter, which_sep(__filter), &save); arg;
Jeff Moyer 2c91dc
+	     arg = strtok_r(NULL, which_sep(__filter), &save))
Jeff Moyer 2c91dc
+		if (strstr(arg, needle))
Jeff Moyer 2c91dc
+			break;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	free(filter);
Jeff Moyer 2c91dc
+	if (arg)
Jeff Moyer 2c91dc
+		return true;
Jeff Moyer 2c91dc
+	return false;
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+static struct cxl_port *__util_cxl_port_filter(struct cxl_port *port,
Jeff Moyer 2c91dc
+					     const char *__ident)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	char *ident, *save;
Jeff Moyer 2c91dc
+	const char *arg;
Jeff Moyer 2c91dc
+	int port_id;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	if (!__ident)
Jeff Moyer 2c91dc
+		return port;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	ident = strdup(__ident);
Jeff Moyer 2c91dc
+	if (!ident)
Jeff Moyer 2c91dc
+		return NULL;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	for (arg = strtok_r(ident, which_sep(__ident), &save); arg;
Jeff Moyer 2c91dc
+	     arg = strtok_r(NULL, which_sep(__ident), &save)) {
Jeff Moyer 2c91dc
+		if (strcmp(arg, "all") == 0)
Jeff Moyer 2c91dc
+			break;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+		if (strcmp(arg, "root") == 0 && cxl_port_is_root(port))
Jeff Moyer 2c91dc
+			break;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+		if (strcmp(arg, "switch") == 0 && cxl_port_is_switch(port))
Jeff Moyer 2c91dc
+			break;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+		if ((sscanf(arg, "%d", &port_id) == 1 ||
Jeff Moyer 2c91dc
+		     sscanf(arg, "port%d", &port_id) == 1) &&
Jeff Moyer 2c91dc
+		    cxl_port_get_id(port) == port_id)
Jeff Moyer 2c91dc
+			break;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+		if (strcmp(arg, cxl_port_get_devname(port)) == 0)
Jeff Moyer 2c91dc
+			break;
Jeff Moyer 2c91dc
+	}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	free(ident);
Jeff Moyer 2c91dc
+	if (arg)
Jeff Moyer 2c91dc
+		return port;
Jeff Moyer 2c91dc
+	return NULL;
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+enum cxl_port_filter_mode {
Jeff Moyer 2c91dc
+	CXL_PF_SINGLE,
Jeff Moyer 2c91dc
+	CXL_PF_ANCESTRY,
Jeff Moyer 2c91dc
+};
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+static enum cxl_port_filter_mode pf_mode(struct cxl_filter_params *p)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	if (p->single)
Jeff Moyer 2c91dc
+		return CXL_PF_SINGLE;
Jeff Moyer 2c91dc
+	return CXL_PF_ANCESTRY;
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+static struct cxl_port *util_cxl_port_filter(struct cxl_port *port,
Jeff Moyer 2c91dc
+					     const char *ident,
Jeff Moyer 2c91dc
+					     enum cxl_port_filter_mode mode)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	struct cxl_port *iter = port;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	while (iter) {
Jeff Moyer 2c91dc
+		if (__util_cxl_port_filter(iter, ident))
Jeff Moyer 2c91dc
+			return port;
Jeff Moyer 2c91dc
+		if (mode == CXL_PF_SINGLE)
Jeff Moyer 2c91dc
+			return NULL;
Jeff Moyer 2c91dc
+		iter = cxl_port_get_parent(iter);
Jeff Moyer 2c91dc
+	}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	return NULL;
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
 static struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus,
Jeff Moyer 2c91dc
 					   const char *__ident)
Jeff Moyer 2c91dc
 {
Jeff Moyer 2c91dc
@@ -58,6 +153,31 @@ static struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus,
Jeff Moyer 2c91dc
 	return NULL;
Jeff Moyer 2c91dc
 }
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
+static struct cxl_port *util_cxl_port_filter_by_bus(struct cxl_port *port,
Jeff Moyer 2c91dc
+						    const char *__ident)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	struct cxl_ctx *ctx = cxl_port_get_ctx(port);
Jeff Moyer 2c91dc
+	struct cxl_bus *bus;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	if (!__ident)
Jeff Moyer 2c91dc
+		return port;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	if (cxl_port_is_root(port)) {
Jeff Moyer 2c91dc
+		bus = cxl_port_to_bus(port);
Jeff Moyer 2c91dc
+		bus = util_cxl_bus_filter(bus, __ident);
Jeff Moyer 2c91dc
+		return bus ? port : NULL;
Jeff Moyer 2c91dc
+	}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	cxl_bus_foreach(ctx, bus) {
Jeff Moyer 2c91dc
+		if (!util_cxl_bus_filter(bus, __ident))
Jeff Moyer 2c91dc
+			continue;
Jeff Moyer 2c91dc
+		if (bus == cxl_port_get_bus(port))
Jeff Moyer 2c91dc
+			return port;
Jeff Moyer 2c91dc
+	}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	return NULL;
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
 static struct cxl_memdev *
Jeff Moyer 2c91dc
 util_cxl_memdev_serial_filter(struct cxl_memdev *memdev, const char *__serials)
Jeff Moyer 2c91dc
 {
Jeff Moyer 2c91dc
@@ -169,10 +289,82 @@ static void splice_array(struct cxl_filter_params *p, struct json_object *jobjs,
Jeff Moyer 2c91dc
 	json_object_put(jobjs);
Jeff Moyer 2c91dc
 }
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
+static bool cond_add_put_array(struct json_object *jobj, const char *key,
Jeff Moyer 2c91dc
+			       struct json_object *array)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	if (jobj && array && json_object_array_length(array) > 0) {
Jeff Moyer 2c91dc
+		json_object_object_add(jobj, key, array);
Jeff Moyer 2c91dc
+		return true;
Jeff Moyer 2c91dc
+	} else {
Jeff Moyer 2c91dc
+		json_object_put(array);
Jeff Moyer 2c91dc
+		return false;
Jeff Moyer 2c91dc
+	}
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+static bool cond_add_put_array_suffix(struct json_object *jobj, const char *key,
Jeff Moyer 2c91dc
+				      const char *suffix,
Jeff Moyer 2c91dc
+				      struct json_object *array)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	char *name;
Jeff Moyer 2c91dc
+	bool rc;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	if (asprintf(&name, "%s:%s", key, suffix) < 0)
Jeff Moyer 2c91dc
+		return false;
Jeff Moyer 2c91dc
+	rc = cond_add_put_array(jobj, name, array);
Jeff Moyer 2c91dc
+	free(name);
Jeff Moyer 2c91dc
+	return rc;
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+static struct json_object *pick_array(struct json_object *child,
Jeff Moyer 2c91dc
+				      struct json_object *container)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	if (child)
Jeff Moyer 2c91dc
+		return child;
Jeff Moyer 2c91dc
+	if (container)
Jeff Moyer 2c91dc
+		return container;
Jeff Moyer 2c91dc
+	return NULL;
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+static void walk_child_ports(struct cxl_port *parent_port,
Jeff Moyer 2c91dc
+			     struct cxl_filter_params *p,
Jeff Moyer 2c91dc
+			     struct json_object *jports,
Jeff Moyer 2c91dc
+			     unsigned long flags)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	struct cxl_port *port;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	cxl_port_foreach(parent_port, port) {
Jeff Moyer 2c91dc
+		const char *devname = cxl_port_get_devname(port);
Jeff Moyer 2c91dc
+		struct json_object *jport = NULL;
Jeff Moyer 2c91dc
+		struct json_object *jchildports = NULL;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+		if (!util_cxl_port_filter(port, p->port_filter, pf_mode(p)))
Jeff Moyer 2c91dc
+			goto walk_children;
Jeff Moyer 2c91dc
+		if (!util_cxl_port_filter_by_bus(port, p->bus_filter))
Jeff Moyer 2c91dc
+			goto walk_children;
Jeff Moyer 2c91dc
+		if (!p->idle && !cxl_port_is_enabled(port))
Jeff Moyer 2c91dc
+			continue;
Jeff Moyer 2c91dc
+		if (p->ports)
Jeff Moyer 2c91dc
+			jport = util_cxl_port_to_json(port, flags);
Jeff Moyer 2c91dc
+		if (!jport)
Jeff Moyer 2c91dc
+			continue;
Jeff Moyer 2c91dc
+		json_object_array_add(jports, jport);
Jeff Moyer 2c91dc
+		jchildports = json_object_new_array();
Jeff Moyer 2c91dc
+		if (!jchildports) {
Jeff Moyer 2c91dc
+			err(p, "%s: failed to enumerate child ports\n",
Jeff Moyer 2c91dc
+			    devname);
Jeff Moyer 2c91dc
+			continue;
Jeff Moyer 2c91dc
+		}
Jeff Moyer 2c91dc
+walk_children:
Jeff Moyer 2c91dc
+		walk_child_ports(port, p, pick_array(jchildports, jports),
Jeff Moyer 2c91dc
+				 flags);
Jeff Moyer 2c91dc
+		cond_add_put_array_suffix(jport, "ports", devname, jchildports);
Jeff Moyer 2c91dc
+	}
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
 int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
Jeff Moyer 2c91dc
 {
Jeff Moyer 2c91dc
+	struct json_object *jdevs = NULL, *jbuses = NULL, *jports = NULL;
Jeff Moyer 2c91dc
 	struct json_object *jplatform = json_object_new_array();
Jeff Moyer 2c91dc
-	struct json_object *jdevs = NULL, *jbuses = NULL;
Jeff Moyer 2c91dc
 	unsigned long flags = params_to_flags(p);
Jeff Moyer 2c91dc
 	struct cxl_memdev *memdev;
Jeff Moyer 2c91dc
 	int top_level_objs = 0;
Jeff Moyer 2c91dc
@@ -191,6 +383,10 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
Jeff Moyer 2c91dc
 	if (!jbuses)
Jeff Moyer 2c91dc
 		goto err;
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
+	jports = json_object_new_array();
Jeff Moyer 2c91dc
+	if (!jports)
Jeff Moyer 2c91dc
+		goto err;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
 	cxl_memdev_foreach(ctx, memdev) {
Jeff Moyer 2c91dc
 		struct json_object *jdev;
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
@@ -208,10 +404,15 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
Jeff Moyer 2c91dc
 	}
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 	cxl_bus_foreach(ctx, bus) {
Jeff Moyer 2c91dc
-		struct json_object *jbus;
Jeff Moyer 2c91dc
+		struct json_object *jbus = NULL;
Jeff Moyer 2c91dc
+		struct json_object *jchildports = NULL;
Jeff Moyer 2c91dc
+		struct cxl_port *port = cxl_bus_get_port(bus);
Jeff Moyer 2c91dc
+		const char *devname = cxl_bus_get_devname(bus);
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 		if (!util_cxl_bus_filter(bus, p->bus_filter))
Jeff Moyer 2c91dc
-			continue;
Jeff Moyer 2c91dc
+			goto walk_children;
Jeff Moyer 2c91dc
+		if (!util_cxl_port_filter(port, p->port_filter, pf_mode(p)))
Jeff Moyer 2c91dc
+			goto walk_children;
Jeff Moyer 2c91dc
 		if (p->buses) {
Jeff Moyer 2c91dc
 			jbus = util_cxl_bus_to_json(bus, flags);
Jeff Moyer 2c91dc
 			if (!jbus) {
Jeff Moyer 2c91dc
@@ -219,16 +420,32 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
Jeff Moyer 2c91dc
 				continue;
Jeff Moyer 2c91dc
 			}
Jeff Moyer 2c91dc
 			json_object_array_add(jbuses, jbus);
Jeff Moyer 2c91dc
+			if (p->ports) {
Jeff Moyer 2c91dc
+				jchildports = json_object_new_array();
Jeff Moyer 2c91dc
+				if (!jchildports) {
Jeff Moyer 2c91dc
+					err(p,
Jeff Moyer 2c91dc
+					    "%s: failed to enumerate child ports\n",
Jeff Moyer 2c91dc
+					    devname);
Jeff Moyer 2c91dc
+					continue;
Jeff Moyer 2c91dc
+				}
Jeff Moyer 2c91dc
+			}
Jeff Moyer 2c91dc
 		}
Jeff Moyer 2c91dc
+walk_children:
Jeff Moyer 2c91dc
+		walk_child_ports(port, p, pick_array(jchildports, jports),
Jeff Moyer 2c91dc
+				 flags);
Jeff Moyer 2c91dc
+		cond_add_put_array_suffix(jbus, "ports", devname, jchildports);
Jeff Moyer 2c91dc
 	}
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 	if (json_object_array_length(jdevs))
Jeff Moyer 2c91dc
 		top_level_objs++;
Jeff Moyer 2c91dc
 	if (json_object_array_length(jbuses))
Jeff Moyer 2c91dc
 		top_level_objs++;
Jeff Moyer 2c91dc
+	if (json_object_array_length(jports))
Jeff Moyer 2c91dc
+		top_level_objs++;
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 	splice_array(p, jdevs, jplatform, "anon memdevs", top_level_objs > 1);
Jeff Moyer 2c91dc
 	splice_array(p, jbuses, jplatform, "buses", top_level_objs > 1);
Jeff Moyer 2c91dc
+	splice_array(p, jports, jplatform, "ports", top_level_objs > 1);
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 	util_display_json_array(stdout, jplatform, flags);
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
@@ -236,6 +453,7 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
Jeff Moyer 2c91dc
 err:
Jeff Moyer 2c91dc
 	json_object_put(jdevs);
Jeff Moyer 2c91dc
 	json_object_put(jbuses);
Jeff Moyer 2c91dc
+	json_object_put(jports);
Jeff Moyer 2c91dc
 	json_object_put(jplatform);
Jeff Moyer 2c91dc
 	return -ENOMEM;
Jeff Moyer 2c91dc
 }
Jeff Moyer 2c91dc
diff --git a/cxl/filter.h b/cxl/filter.h
Jeff Moyer 2c91dc
index d41e757..0d83304 100644
Jeff Moyer 2c91dc
--- a/cxl/filter.h
Jeff Moyer 2c91dc
+++ b/cxl/filter.h
Jeff Moyer 2c91dc
@@ -10,7 +10,10 @@ struct cxl_filter_params {
Jeff Moyer 2c91dc
 	const char *memdev_filter;
Jeff Moyer 2c91dc
 	const char *serial_filter;
Jeff Moyer 2c91dc
 	const char *bus_filter;
Jeff Moyer 2c91dc
+	const char *port_filter;
Jeff Moyer 2c91dc
+	bool single;
Jeff Moyer 2c91dc
 	bool memdevs;
Jeff Moyer 2c91dc
+	bool ports;
Jeff Moyer 2c91dc
 	bool buses;
Jeff Moyer 2c91dc
 	bool idle;
Jeff Moyer 2c91dc
 	bool human;
Jeff Moyer 2c91dc
@@ -22,4 +25,5 @@ struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev,
Jeff Moyer 2c91dc
 					  const char *__ident,
Jeff Moyer 2c91dc
 					  const char *serials);
Jeff Moyer 2c91dc
 int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *param);
Jeff Moyer 2c91dc
+bool cxl_filter_has(const char *needle, const char *__filter);
Jeff Moyer 2c91dc
 #endif /* _CXL_UTIL_FILTER_H_ */
Jeff Moyer 2c91dc
diff --git a/cxl/json.c b/cxl/json.c
Jeff Moyer 2c91dc
index a584594..d9f864e 100644
Jeff Moyer 2c91dc
--- a/cxl/json.c
Jeff Moyer 2c91dc
+++ b/cxl/json.c
Jeff Moyer 2c91dc
@@ -242,3 +242,26 @@ struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus,
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 	return jbus;
Jeff Moyer 2c91dc
 }
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+struct json_object *util_cxl_port_to_json(struct cxl_port *port,
Jeff Moyer 2c91dc
+					  unsigned long flags)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	const char *devname = cxl_port_get_devname(port);
Jeff Moyer 2c91dc
+	struct json_object *jport, *jobj;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	jport = json_object_new_object();
Jeff Moyer 2c91dc
+	if (!jport)
Jeff Moyer 2c91dc
+		return NULL;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	jobj = json_object_new_string(devname);
Jeff Moyer 2c91dc
+	if (jobj)
Jeff Moyer 2c91dc
+		json_object_object_add(jport, "port", jobj);
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	if (!cxl_port_is_enabled(port)) {
Jeff Moyer 2c91dc
+		jobj = json_object_new_string("disabled");
Jeff Moyer 2c91dc
+		if (jobj)
Jeff Moyer 2c91dc
+			json_object_object_add(jport, "state", jobj);
Jeff Moyer 2c91dc
+	}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	return jport;
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
diff --git a/cxl/json.h b/cxl/json.h
Jeff Moyer 2c91dc
index 4abf6e5..36653db 100644
Jeff Moyer 2c91dc
--- a/cxl/json.h
Jeff Moyer 2c91dc
+++ b/cxl/json.h
Jeff Moyer 2c91dc
@@ -8,4 +8,7 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev,
Jeff Moyer 2c91dc
 struct cxl_bus;
Jeff Moyer 2c91dc
 struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus,
Jeff Moyer 2c91dc
 					 unsigned long flags);
Jeff Moyer 2c91dc
+struct cxl_port;
Jeff Moyer 2c91dc
+struct json_object *util_cxl_port_to_json(struct cxl_port *port,
Jeff Moyer 2c91dc
+					  unsigned long flags);
Jeff Moyer 2c91dc
 #endif /* __CXL_UTIL_JSON_H__ */
Jeff Moyer 2c91dc
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
Jeff Moyer 2c91dc
index 8548a45..03eff3c 100644
Jeff Moyer 2c91dc
--- a/cxl/lib/libcxl.c
Jeff Moyer 2c91dc
+++ b/cxl/lib/libcxl.c
Jeff Moyer 2c91dc
@@ -66,15 +66,27 @@ static void free_memdev(struct cxl_memdev *memdev, struct list_head *head)
Jeff Moyer 2c91dc
 	free(memdev);
Jeff Moyer 2c91dc
 }
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
+static void free_port(struct cxl_port *port, struct list_head *head);
Jeff Moyer 2c91dc
 static void __free_port(struct cxl_port *port, struct list_head *head)
Jeff Moyer 2c91dc
 {
Jeff Moyer 2c91dc
+	struct cxl_port *child, *_c;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
 	if (head)
Jeff Moyer 2c91dc
 		list_del_from(head, &port->list);
Jeff Moyer 2c91dc
+	list_for_each_safe(&port->child_ports, child, _c, list)
Jeff Moyer 2c91dc
+		free_port(child, &port->child_ports);
Jeff Moyer 2c91dc
+	kmod_module_unref(port->module);
Jeff Moyer 2c91dc
 	free(port->dev_buf);
Jeff Moyer 2c91dc
 	free(port->dev_path);
Jeff Moyer 2c91dc
 	free(port->uport);
Jeff Moyer 2c91dc
 }
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
+static void free_port(struct cxl_port *port, struct list_head *head)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	__free_port(port, head);
Jeff Moyer 2c91dc
+	free(port);
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
 static void free_bus(struct cxl_bus *bus, struct list_head *head)
Jeff Moyer 2c91dc
 {
Jeff Moyer 2c91dc
 	__free_port(&bus->port, head);
Jeff Moyer 2c91dc
@@ -471,10 +483,12 @@ CXL_EXPORT int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev)
Jeff Moyer 2c91dc
 	return is_enabled(path);
Jeff Moyer 2c91dc
 }
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
-static int cxl_port_init(struct cxl_port *port, struct cxl_ctx *ctx, int id,
Jeff Moyer 2c91dc
+static int cxl_port_init(struct cxl_port *port, struct cxl_port *parent_port,
Jeff Moyer 2c91dc
+			 enum cxl_port_type type, struct cxl_ctx *ctx, int id,
Jeff Moyer 2c91dc
 			 const char *cxlport_base)
Jeff Moyer 2c91dc
 {
Jeff Moyer 2c91dc
 	char *path = calloc(1, strlen(cxlport_base) + 100);
Jeff Moyer 2c91dc
+	char buf[SYSFS_ATTR_SIZE];
Jeff Moyer 2c91dc
 	size_t rc;
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 	if (!path)
Jeff Moyer 2c91dc
@@ -482,6 +496,10 @@ static int cxl_port_init(struct cxl_port *port, struct cxl_ctx *ctx, int id,
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 	port->id = id;
Jeff Moyer 2c91dc
 	port->ctx = ctx;
Jeff Moyer 2c91dc
+	port->type = type;
Jeff Moyer 2c91dc
+	port->parent = parent_port;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	list_head_init(&port->child_ports);
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 	port->dev_path = strdup(cxlport_base);
Jeff Moyer 2c91dc
 	if (!port->dev_path)
Jeff Moyer 2c91dc
@@ -499,6 +517,10 @@ static int cxl_port_init(struct cxl_port *port, struct cxl_ctx *ctx, int id,
Jeff Moyer 2c91dc
 	if (!port->uport)
Jeff Moyer 2c91dc
 		goto err;
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
+	sprintf(path, "%s/modalias", cxlport_base);
Jeff Moyer 2c91dc
+	if (sysfs_read_attr(ctx, path, buf) == 0)
Jeff Moyer 2c91dc
+		port->module = util_modalias_to_module(ctx, buf);
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
 	return 0;
Jeff Moyer 2c91dc
 err:
Jeff Moyer 2c91dc
 	free(port->dev_path);
Jeff Moyer 2c91dc
@@ -507,6 +529,135 @@ err:
Jeff Moyer 2c91dc
 	return -ENOMEM;
Jeff Moyer 2c91dc
 }
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
+static void *add_cxl_port(void *parent, int id, const char *cxlport_base)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	const char *devname = devpath_to_devname(cxlport_base);
Jeff Moyer 2c91dc
+	struct cxl_port *port, *port_dup;
Jeff Moyer 2c91dc
+	struct cxl_port *parent_port = parent;
Jeff Moyer 2c91dc
+	struct cxl_ctx *ctx = cxl_port_get_ctx(parent_port);
Jeff Moyer 2c91dc
+	int rc;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	dbg(ctx, "%s: base: \'%s\'\n", devname, cxlport_base);
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	port = calloc(1, sizeof(*port));
Jeff Moyer 2c91dc
+	if (!port)
Jeff Moyer 2c91dc
+		return NULL;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	rc = cxl_port_init(port, parent_port, CXL_PORT_SWITCH, ctx, id,
Jeff Moyer 2c91dc
+			   cxlport_base);
Jeff Moyer 2c91dc
+	if (rc)
Jeff Moyer 2c91dc
+		goto err;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	cxl_port_foreach(parent_port, port_dup)
Jeff Moyer 2c91dc
+		if (port_dup->id == port->id) {
Jeff Moyer 2c91dc
+			free_port(port, NULL);
Jeff Moyer 2c91dc
+			return port_dup;
Jeff Moyer 2c91dc
+		}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	list_add(&parent_port->child_ports, &port->list);
Jeff Moyer 2c91dc
+	return port;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+err:
Jeff Moyer 2c91dc
+	free(port);
Jeff Moyer 2c91dc
+	return NULL;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+static void cxl_ports_init(struct cxl_port *port)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	struct cxl_ctx *ctx = cxl_port_get_ctx(port);
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	if (port->ports_init)
Jeff Moyer 2c91dc
+		return;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	port->ports_init = 1;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	sysfs_device_parse(ctx, port->dev_path, "port", port, add_cxl_port);
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+CXL_EXPORT struct cxl_ctx *cxl_port_get_ctx(struct cxl_port *port)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	return port->ctx;
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+CXL_EXPORT struct cxl_port *cxl_port_get_first(struct cxl_port *port)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	cxl_ports_init(port);
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	return list_top(&port->child_ports, struct cxl_port, list);
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+CXL_EXPORT struct cxl_port *cxl_port_get_next(struct cxl_port *port)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	struct cxl_port *parent_port = port->parent;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	return list_next(&parent_port->child_ports, port, list);
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+CXL_EXPORT const char *cxl_port_get_devname(struct cxl_port *port)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	return devpath_to_devname(port->dev_path);
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+CXL_EXPORT int cxl_port_get_id(struct cxl_port *port)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	return port->id;
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+CXL_EXPORT struct cxl_port *cxl_port_get_parent(struct cxl_port *port)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	return port->parent;
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+CXL_EXPORT bool cxl_port_is_root(struct cxl_port *port)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	return port->type == CXL_PORT_ROOT;
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+CXL_EXPORT bool cxl_port_is_switch(struct cxl_port *port)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	return port->type == CXL_PORT_SWITCH;
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+CXL_EXPORT struct cxl_bus *cxl_port_get_bus(struct cxl_port *port)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	struct cxl_bus *bus;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	if (!cxl_port_is_enabled(port))
Jeff Moyer 2c91dc
+		return NULL;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	if (port->bus)
Jeff Moyer 2c91dc
+		return port->bus;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	while (port->parent)
Jeff Moyer 2c91dc
+		port = port->parent;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	bus = container_of(port, typeof(*bus), port);
Jeff Moyer 2c91dc
+	port->bus = bus;
Jeff Moyer 2c91dc
+	return bus;
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+CXL_EXPORT int cxl_port_is_enabled(struct cxl_port *port)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	struct cxl_ctx *ctx = cxl_port_get_ctx(port);
Jeff Moyer 2c91dc
+	char *path = port->dev_buf;
Jeff Moyer 2c91dc
+	int len = port->buf_len;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	if (snprintf(path, len, "%s/driver", port->dev_path) >= len) {
Jeff Moyer 2c91dc
+		err(ctx, "%s: buffer too small!\n", cxl_port_get_devname(port));
Jeff Moyer 2c91dc
+		return 0;
Jeff Moyer 2c91dc
+	}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+	return is_enabled(path);
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+CXL_EXPORT struct cxl_bus *cxl_port_to_bus(struct cxl_port *port)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	if (!cxl_port_is_root(port))
Jeff Moyer 2c91dc
+		return NULL;
Jeff Moyer 2c91dc
+	return container_of(port, struct cxl_bus, port);
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
 static void *add_cxl_bus(void *parent, int id, const char *cxlbus_base)
Jeff Moyer 2c91dc
 {
Jeff Moyer 2c91dc
 	const char *devname = devpath_to_devname(cxlbus_base);
Jeff Moyer 2c91dc
@@ -522,7 +673,7 @@ static void *add_cxl_bus(void *parent, int id, const char *cxlbus_base)
Jeff Moyer 2c91dc
 		return NULL;
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 	port = &bus->port;
Jeff Moyer 2c91dc
-	rc = cxl_port_init(port, ctx, id, cxlbus_base);
Jeff Moyer 2c91dc
+	rc = cxl_port_init(port, NULL, CXL_PORT_ROOT, ctx, id, cxlbus_base);
Jeff Moyer 2c91dc
 	if (rc)
Jeff Moyer 2c91dc
 		goto err;
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
@@ -579,6 +730,11 @@ CXL_EXPORT int cxl_bus_get_id(struct cxl_bus *bus)
Jeff Moyer 2c91dc
 	return port->id;
Jeff Moyer 2c91dc
 }
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
+CXL_EXPORT struct cxl_port *cxl_bus_get_port(struct cxl_bus *bus)
Jeff Moyer 2c91dc
+{
Jeff Moyer 2c91dc
+	return &bus->port;
Jeff Moyer 2c91dc
+}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
 CXL_EXPORT const char *cxl_bus_get_provider(struct cxl_bus *bus)
Jeff Moyer 2c91dc
 {
Jeff Moyer 2c91dc
 	struct cxl_port *port = &bus->port;
Jeff Moyer 2c91dc
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
Jeff Moyer 2c91dc
index 781ff99..a7e923f 100644
Jeff Moyer 2c91dc
--- a/cxl/lib/libcxl.sym
Jeff Moyer 2c91dc
+++ b/cxl/lib/libcxl.sym
Jeff Moyer 2c91dc
@@ -82,4 +82,16 @@ global:
Jeff Moyer 2c91dc
 	cxl_bus_get_provider;
Jeff Moyer 2c91dc
 	cxl_bus_get_devname;
Jeff Moyer 2c91dc
 	cxl_bus_get_id;
Jeff Moyer 2c91dc
+	cxl_bus_get_port;
Jeff Moyer 2c91dc
+	cxl_port_get_first;
Jeff Moyer 2c91dc
+	cxl_port_get_next;
Jeff Moyer 2c91dc
+	cxl_port_get_devname;
Jeff Moyer 2c91dc
+	cxl_port_get_id;
Jeff Moyer 2c91dc
+	cxl_port_get_ctx;
Jeff Moyer 2c91dc
+	cxl_port_is_enabled;
Jeff Moyer 2c91dc
+	cxl_port_get_parent;
Jeff Moyer 2c91dc
+	cxl_port_is_root;
Jeff Moyer 2c91dc
+	cxl_port_is_switch;
Jeff Moyer 2c91dc
+	cxl_port_to_bus;
Jeff Moyer 2c91dc
+	cxl_port_get_bus;
Jeff Moyer 2c91dc
 } LIBCXL_1;
Jeff Moyer 2c91dc
diff --git a/cxl/lib/private.h b/cxl/lib/private.h
Jeff Moyer 2c91dc
index 0758d05..637f90d 100644
Jeff Moyer 2c91dc
--- a/cxl/lib/private.h
Jeff Moyer 2c91dc
+++ b/cxl/lib/private.h
Jeff Moyer 2c91dc
@@ -34,14 +34,25 @@ struct cxl_memdev {
Jeff Moyer 2c91dc
 	unsigned long long serial;
Jeff Moyer 2c91dc
 };
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
+enum cxl_port_type {
Jeff Moyer 2c91dc
+	CXL_PORT_ROOT,
Jeff Moyer 2c91dc
+	CXL_PORT_SWITCH,
Jeff Moyer 2c91dc
+};
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
 struct cxl_port {
Jeff Moyer 2c91dc
 	int id;
Jeff Moyer 2c91dc
 	void *dev_buf;
Jeff Moyer 2c91dc
 	size_t buf_len;
Jeff Moyer 2c91dc
 	char *dev_path;
Jeff Moyer 2c91dc
 	char *uport;
Jeff Moyer 2c91dc
+	int ports_init;
Jeff Moyer 2c91dc
 	struct cxl_ctx *ctx;
Jeff Moyer 2c91dc
+	struct cxl_bus *bus;
Jeff Moyer 2c91dc
+	enum cxl_port_type type;
Jeff Moyer 2c91dc
+	struct cxl_port *parent;
Jeff Moyer 2c91dc
+	struct kmod_module *module;
Jeff Moyer 2c91dc
 	struct list_node list;
Jeff Moyer 2c91dc
+	struct list_head child_ports;
Jeff Moyer 2c91dc
 };
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 struct cxl_bus {
Jeff Moyer 2c91dc
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
Jeff Moyer 2c91dc
index da66eb2..efbb397 100644
Jeff Moyer 2c91dc
--- a/cxl/libcxl.h
Jeff Moyer 2c91dc
+++ b/cxl/libcxl.h
Jeff Moyer 2c91dc
@@ -5,6 +5,7 @@
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 #include <stdarg.h>
Jeff Moyer 2c91dc
 #include <unistd.h>
Jeff Moyer 2c91dc
+#include <stdbool.h>
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 #ifdef HAVE_UUID
Jeff Moyer 2c91dc
 #include <uuid/uuid.h>
Jeff Moyer 2c91dc
@@ -63,11 +64,29 @@ struct cxl_bus *cxl_bus_get_next(struct cxl_bus *bus);
Jeff Moyer 2c91dc
 const char *cxl_bus_get_provider(struct cxl_bus *bus);
Jeff Moyer 2c91dc
 const char *cxl_bus_get_devname(struct cxl_bus *bus);
Jeff Moyer 2c91dc
 int cxl_bus_get_id(struct cxl_bus *bus);
Jeff Moyer 2c91dc
+struct cxl_port *cxl_bus_get_port(struct cxl_bus *bus);
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 #define cxl_bus_foreach(ctx, bus)                                              \
Jeff Moyer 2c91dc
 	for (bus = cxl_bus_get_first(ctx); bus != NULL;                        \
Jeff Moyer 2c91dc
 	     bus = cxl_bus_get_next(bus))
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
+struct cxl_port;
Jeff Moyer 2c91dc
+struct cxl_port *cxl_port_get_first(struct cxl_port *parent);
Jeff Moyer 2c91dc
+struct cxl_port *cxl_port_get_next(struct cxl_port *port);
Jeff Moyer 2c91dc
+const char *cxl_port_get_devname(struct cxl_port *port);
Jeff Moyer 2c91dc
+int cxl_port_get_id(struct cxl_port *port);
Jeff Moyer 2c91dc
+struct cxl_ctx *cxl_port_get_ctx(struct cxl_port *port);
Jeff Moyer 2c91dc
+int cxl_port_is_enabled(struct cxl_port *port);
Jeff Moyer 2c91dc
+struct cxl_port *cxl_port_get_parent(struct cxl_port *port);
Jeff Moyer 2c91dc
+bool cxl_port_is_root(struct cxl_port *port);
Jeff Moyer 2c91dc
+bool cxl_port_is_switch(struct cxl_port *port);
Jeff Moyer 2c91dc
+struct cxl_bus *cxl_port_to_bus(struct cxl_port *port);
Jeff Moyer 2c91dc
+struct cxl_bus *cxl_port_get_bus(struct cxl_port *port);
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
+#define cxl_port_foreach(parent, port)                                         \
Jeff Moyer 2c91dc
+	for (port = cxl_port_get_first(parent); port != NULL;                  \
Jeff Moyer 2c91dc
+	     port = cxl_port_get_next(port))
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
 struct cxl_cmd;
Jeff Moyer 2c91dc
 const char *cxl_cmd_get_devname(struct cxl_cmd *cmd);
Jeff Moyer 2c91dc
 struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, int opcode);
Jeff Moyer 2c91dc
diff --git a/cxl/list.c b/cxl/list.c
Jeff Moyer 2c91dc
index 9500e61..1ef91b4 100644
Jeff Moyer 2c91dc
--- a/cxl/list.c
Jeff Moyer 2c91dc
+++ b/cxl/list.c
Jeff Moyer 2c91dc
@@ -25,6 +25,11 @@ static const struct option options[] = {
Jeff Moyer 2c91dc
 	OPT_STRING('b', "bus", &param.bus_filter, "bus device name",
Jeff Moyer 2c91dc
 		   "filter by CXL bus device name(s)"),
Jeff Moyer 2c91dc
 	OPT_BOOLEAN('B', "buses", &param.buses, "include CXL bus info"),
Jeff Moyer 2c91dc
+	OPT_STRING('p', "port", &param.port_filter, "port device name",
Jeff Moyer 2c91dc
+		   "filter by CXL port device name(s)"),
Jeff Moyer 2c91dc
+	OPT_BOOLEAN('P', "ports", &param.ports, "include CXL port info"),
Jeff Moyer 2c91dc
+	OPT_BOOLEAN('S', "single", &param.single,
Jeff Moyer 2c91dc
+		    "skip listing descendant objects"),
Jeff Moyer 2c91dc
 	OPT_BOOLEAN('i', "idle", &param.idle, "include disabled devices"),
Jeff Moyer 2c91dc
 	OPT_BOOLEAN('u', "human", &param.human,
Jeff Moyer 2c91dc
 		    "use human friendly number formats "),
Jeff Moyer 2c91dc
@@ -35,7 +40,7 @@ static const struct option options[] = {
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 static int num_list_flags(void)
Jeff Moyer 2c91dc
 {
Jeff Moyer 2c91dc
-       return !!param.memdevs + !!param.buses;
Jeff Moyer 2c91dc
+       return !!param.memdevs + !!param.buses + !!param.ports;
Jeff Moyer 2c91dc
 }
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx)
Jeff Moyer 2c91dc
@@ -53,11 +58,18 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx)
Jeff Moyer 2c91dc
 	if (argc)
Jeff Moyer 2c91dc
 		usage_with_options(u, options);
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
+	if (param.single && !param.port_filter) {
Jeff Moyer 2c91dc
+		error("-S/--single expects a port filter: -p/--port=\n");
Jeff Moyer 2c91dc
+		usage_with_options(u, options);
Jeff Moyer 2c91dc
+	}
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
 	if (num_list_flags() == 0) {
Jeff Moyer 2c91dc
 		if (param.memdev_filter || param.serial_filter)
Jeff Moyer 2c91dc
 			param.memdevs = true;
Jeff Moyer 2c91dc
 		if (param.bus_filter)
Jeff Moyer 2c91dc
 			param.buses = true;
Jeff Moyer 2c91dc
+		if (param.port_filter)
Jeff Moyer 2c91dc
+			param.ports = true;
Jeff Moyer 2c91dc
 		if (num_list_flags() == 0) {
Jeff Moyer 2c91dc
 			/*
Jeff Moyer 2c91dc
 			 * TODO: We likely want to list regions by default if
Jeff Moyer 2c91dc
@@ -73,5 +85,8 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx)
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
 	log_init(&param.ctx, "cxl list", "CXL_LIST_LOG");
Jeff Moyer 2c91dc
 
Jeff Moyer 2c91dc
+	if (cxl_filter_has(param.port_filter, "root") && param.ports)
Jeff Moyer 2c91dc
+		param.buses = true;
Jeff Moyer 2c91dc
+
Jeff Moyer 2c91dc
 	return cxl_filter_walk(ctx, ¶m;;
Jeff Moyer 2c91dc
 }
Jeff Moyer 2c91dc
-- 
Jeff Moyer 2c91dc
2.27.0
Jeff Moyer 2c91dc