@@ -82,6 +82,7 @@ ForEachMacros:
- 'cxl_port_foreach'
- 'cxl_decoder_foreach'
- 'cxl_target_foreach'
+ - 'cxl_dport_foreach'
- 'cxl_endpoint_foreach'
- 'daxctl_dev_foreach'
- 'daxctl_mapping_foreach'
@@ -272,7 +272,23 @@ OPTIONS
-T::
--targets::
- Extend decoder listings with downstream port target information.
+ Extend decoder listings with downstream port target information, and /
+ or port and bus listings with the downstream port information.
+----
+# cxl list -BTu -b ACPI.CXL
+{
+ "bus":"root0",
+ "provider":"ACPI.CXL",
+ "nr_dports":1,
+ "dports":[
+ {
+ "dport":"ACPI0016:00",
+ "alias":"pci0000:34",
+ "id":"0"
+ }
+ ]
+}
+----
--debug::
If the cxl tool was built with debug enabled, turn on debug
@@ -245,6 +245,7 @@ bool cxl_port_is_root(struct cxl_port *port);
bool cxl_port_is_switch(struct cxl_port *port);
bool cxl_port_is_endpoint(struct cxl_port *port);
bool cxl_port_hosts_memdev(struct cxl_port *port, struct cxl_memdev *memdev);
+int cxl_port_get_nr_dports(struct cxl_port *port);
----
The port type is communicated via cxl_port_is_<type>(). An 'enabled' port
is one that has succeeded in discovering the CXL component registers in
@@ -256,6 +257,32 @@ of intervening switch ports, and a terminal endpoint port.
cxl_port_hosts_memdev() returns true if the port's host appears in the
memdev host's device topology ancestry.
+==== DPORTS
+A CXL dport object represents a CXL / PCIe Switch Downstream Port, or a
+CXL / PCIe host bridge.
+
+===== DPORT: Enumeration
+----
+struct cxl_dport *cxl_dport_get_first(struct cxl_port *port);
+struct cxl_dport *cxl_dport_get_next(struct cxl_dport *dport);
+
+#define cxl_dport_foreach(port, dport) \
+ for (dport = cxl_dport_get_first(port); dport != NULL; \
+ dport = cxl_dport_get_next(dport))
+
+----
+
+===== DPORT: Attributes
+----
+const char *cxl_dport_get_devname(struct cxl_dport *dport);
+const char *cxl_dport_get_physical_node(struct cxl_dport *dport);
+int cxl_dport_get_id(struct cxl_dport *dport);
+----
+The id of a dport is the hardware idenfifier used by an upstream port to
+reference a downstream port. The physical node of a dport is only
+available for platform firmware defined downstream ports and alias the
+companion object, like a PCI host bridge, in the PCI device hierarchy.
+
ENDPOINTS
---------
CXL endpoint objects encapsulate the set of host-managed device-memory
@@ -241,6 +241,58 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev,
return jdev;
}
+static struct json_object *util_cxl_dports_to_json(struct json_object *jport,
+ struct cxl_port *port,
+ unsigned long flags)
+{
+ struct json_object *jobj, *jdports;
+ struct cxl_dport *dport;
+ int val;
+
+ val = cxl_port_get_nr_dports(port);
+ if (!val || !(flags & UTIL_JSON_TARGETS))
+ return jport;
+
+ jobj = json_object_new_int(val);
+ if (jobj)
+ json_object_object_add(jport, "nr_dports", jobj);
+
+ jdports = json_object_new_array();
+ if (!jdports)
+ return jport;
+
+ cxl_dport_foreach(port, dport) {
+ struct json_object *jdport;
+ const char *phys_node;
+
+ jdport = json_object_new_object();
+ if (!jdport)
+ continue;
+
+ jobj = json_object_new_string(cxl_dport_get_devname(dport));
+ if (jobj)
+ json_object_object_add(jdport, "dport", jobj);
+
+ phys_node = cxl_dport_get_physical_node(dport);
+ if (phys_node) {
+ jobj = json_object_new_string(phys_node);
+ if (jobj)
+ json_object_object_add(jdport, "alias", jobj);
+ }
+
+ val = cxl_dport_get_id(dport);
+ jobj = util_json_object_hex(val, flags);
+ if (jobj)
+ json_object_object_add(jdport, "id", jobj);
+
+ json_object_array_add(jdports, jdport);
+ }
+
+ json_object_object_add(jport, "dports", jdports);
+
+ return jport;
+}
+
struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus,
unsigned long flags)
{
@@ -259,7 +311,7 @@ struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus,
if (jobj)
json_object_object_add(jbus, "provider", jobj);
- return jbus;
+ return util_cxl_dports_to_json(jbus, cxl_bus_get_port(bus), flags);
}
struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder,
@@ -403,7 +455,7 @@ static struct json_object *__util_cxl_port_to_json(struct cxl_port *port,
json_object_object_add(jport, "state", jobj);
}
- return jport;
+ return util_cxl_dports_to_json(jport, port, flags);
}
struct json_object *util_cxl_port_to_json(struct cxl_port *port,
@@ -89,13 +89,24 @@ static void free_decoder(struct cxl_decoder *decoder, struct list_head *head)
free(decoder);
}
+static void free_dport(struct cxl_dport *dport, struct list_head *head)
+{
+ if (head)
+ list_del_from(head, &dport->list);
+ free(dport->dev_buf);
+ free(dport->dev_path);
+ free(dport->phys_path);
+ free(dport);
+}
+
static void free_port(struct cxl_port *port, struct list_head *head);
static void free_endpoint(struct cxl_endpoint *endpoint, struct list_head *head);
static void __free_port(struct cxl_port *port, struct list_head *head)
{
- struct cxl_port *child, *_c;
struct cxl_endpoint *endpoint, *_e;
struct cxl_decoder *decoder, *_d;
+ struct cxl_dport *dport, *_dp;
+ struct cxl_port *child, *_c;
if (head)
list_del_from(head, &port->list);
@@ -105,6 +116,8 @@ static void __free_port(struct cxl_port *port, struct list_head *head)
free_endpoint(endpoint, &port->endpoints);
list_for_each_safe(&port->decoders, decoder, _d, list)
free_decoder(decoder, &port->decoders);
+ list_for_each_safe(&port->dports, dport, _dp, list)
+ free_dport(dport , &port->dports);
kmod_module_unref(port->module);
free(port->dev_buf);
free(port->dev_path);
@@ -701,6 +714,7 @@ static int cxl_port_init(struct cxl_port *port, struct cxl_port *parent_port,
list_head_init(&port->child_ports);
list_head_init(&port->endpoints);
list_head_init(&port->decoders);
+ list_head_init(&port->dports);
port->dev_path = strdup(cxlport_base);
if (!port->dev_path)
@@ -1332,6 +1346,99 @@ CXL_EXPORT struct cxl_bus *cxl_port_to_bus(struct cxl_port *port)
return container_of(port, struct cxl_bus, port);
}
+static void *add_cxl_dport(void *parent, int id, const char *cxldport_base)
+{
+ const char *devname = devpath_to_devname(cxldport_base);
+ struct cxl_dport *dport, *dport_dup;
+ struct cxl_port *port = parent;
+ struct cxl_ctx *ctx = cxl_port_get_ctx(port);
+
+ dbg(ctx, "%s: base: \'%s\'\n", devname, cxldport_base);
+
+ dport = calloc(1, sizeof(*dport));
+ if (!dport)
+ return NULL;
+
+ dport->id = id;
+ dport->port = port;
+
+ dport->dev_path = realpath(cxldport_base, NULL);
+ if (!dport->dev_path)
+ goto err;
+
+ dport->dev_buf = calloc(1, strlen(cxldport_base) + 50);
+ if (!dport->dev_buf)
+ goto err;
+ dport->buf_len = strlen(cxldport_base) + 50;
+
+ sprintf(dport->dev_buf, "%s/physical_node", cxldport_base);
+ dport->phys_path = realpath(dport->dev_buf, NULL);
+
+ cxl_dport_foreach(port, dport_dup)
+ if (dport_dup->id == dport->id) {
+ free_dport(dport, NULL);
+ return dport_dup;
+ }
+
+ port->nr_dports++;
+ list_add(&port->dports, &dport->list);
+ return dport;
+
+err:
+ free_dport(dport, NULL);
+ return NULL;
+}
+
+static void cxl_dports_init(struct cxl_port *port)
+{
+ struct cxl_ctx *ctx = cxl_port_get_ctx(port);
+
+ if (port->dports_init)
+ return;
+
+ port->dports_init = 1;
+
+ sysfs_device_parse(ctx, port->dev_path, "dport", port, add_cxl_dport);
+}
+
+CXL_EXPORT int cxl_port_get_nr_dports(struct cxl_port *port)
+{
+ if (!port->dports_init)
+ cxl_dports_init(port);
+ return port->nr_dports;
+}
+
+CXL_EXPORT struct cxl_dport *cxl_dport_get_first(struct cxl_port *port)
+{
+ cxl_dports_init(port);
+
+ return list_top(&port->dports, struct cxl_dport, list);
+}
+
+CXL_EXPORT struct cxl_dport *cxl_dport_get_next(struct cxl_dport *dport)
+{
+ struct cxl_port *port = dport->port;
+
+ return list_next(&port->dports, dport, list);
+}
+
+CXL_EXPORT const char *cxl_dport_get_devname(struct cxl_dport *dport)
+{
+ return devpath_to_devname(dport->dev_path);
+}
+
+CXL_EXPORT const char *cxl_dport_get_physical_node(struct cxl_dport *dport)
+{
+ if (!dport->phys_path)
+ return NULL;
+ return devpath_to_devname(dport->phys_path);
+}
+
+CXL_EXPORT int cxl_dport_get_id(struct cxl_dport *dport)
+{
+ return dport->id;
+}
+
static void *add_cxl_bus(void *parent, int id, const char *cxlbus_base)
{
const char *devname = devpath_to_devname(cxlbus_base);
@@ -101,6 +101,8 @@ global:
cxl_port_get_host;
cxl_port_get_bus;
cxl_port_hosts_memdev;
+ cxl_port_get_nr_dports;
+ cxl_port_get_next_all;
cxl_endpoint_get_first;
cxl_endpoint_get_next;
cxl_endpoint_get_devname;
@@ -142,4 +144,9 @@ global:
cxl_target_get_devname;
cxl_target_maps_memdev;
cxl_target_get_physical_node;
+ cxl_dport_get_first;
+ cxl_dport_get_next;
+ cxl_dport_get_devname;
+ cxl_dport_get_physical_node;
+ cxl_dport_get_id;
} LIBCXL_1;
@@ -38,6 +38,16 @@ struct cxl_memdev {
struct cxl_endpoint *endpoint;
};
+struct cxl_dport {
+ int id;
+ void *dev_buf;
+ size_t buf_len;
+ char *dev_path;
+ char *phys_path;
+ struct cxl_port *port;
+ struct list_node list;
+};
+
enum cxl_port_type {
CXL_PORT_ROOT,
CXL_PORT_SWITCH,
@@ -53,6 +63,8 @@ struct cxl_port {
int ports_init;
int endpoints_init;
int decoders_init;
+ int dports_init;
+ int nr_dports;
struct cxl_ctx *ctx;
struct cxl_bus *bus;
enum cxl_port_type type;
@@ -62,6 +74,7 @@ struct cxl_port {
struct list_head child_ports;
struct list_head endpoints;
struct list_head decoders;
+ struct list_head dports;
};
struct cxl_bus {
@@ -93,11 +93,23 @@ bool cxl_port_is_endpoint(struct cxl_port *port);
struct cxl_bus *cxl_port_get_bus(struct cxl_port *port);
const char *cxl_port_get_host(struct cxl_port *port);
bool cxl_port_hosts_memdev(struct cxl_port *port, struct cxl_memdev *memdev);
+int cxl_port_get_nr_dports(struct cxl_port *port);
#define cxl_port_foreach(parent, port) \
for (port = cxl_port_get_first(parent); port != NULL; \
port = cxl_port_get_next(port))
+struct cxl_dport;
+struct cxl_dport *cxl_dport_get_first(struct cxl_port *port);
+struct cxl_dport *cxl_dport_get_next(struct cxl_dport *dport);
+const char *cxl_dport_get_devname(struct cxl_dport *dport);
+const char *cxl_dport_get_physical_node(struct cxl_dport *dport);
+int cxl_dport_get_id(struct cxl_dport *dport);
+
+#define cxl_dport_foreach(port, dport) \
+ for (dport = cxl_dport_get_first(port); dport != NULL; \
+ dport = cxl_dport_get_next(dport))
+
struct cxl_decoder;
struct cxl_decoder *cxl_decoder_get_first(struct cxl_port *port);
struct cxl_decoder *cxl_decoder_get_next(struct cxl_decoder *decoder);
@@ -42,7 +42,7 @@ static const struct option options[] = {
OPT_BOOLEAN('D', "decoders", ¶m.decoders,
"include CXL decoder info"),
OPT_BOOLEAN('T', "targets", ¶m.targets,
- "include CXL target data with decoders"),
+ "include CXL target data with decoders or ports"),
OPT_BOOLEAN('i', "idle", ¶m.idle, "include disabled devices"),
OPT_BOOLEAN('u', "human", ¶m.human,
"use human friendly number formats "),
It is useful to be able to dump the dport-id to host-device-name. Rather than introduce a new option, just interpret --target as "list dports" for port objects. $ cxl list -BTu -b ACPI.CXL { "bus":"root0", "provider":"ACPI.CXL", "nr_dports":1, "dports":[ { "dport":"ACPI0016:00", "alias":"pci0000:34", "id":"0" } ] } Signed-off-by: Dan Williams <dan.j.williams@intel.com> --- .clang-format | 1 Documentation/cxl/cxl-list.txt | 18 ++++++ Documentation/cxl/lib/libcxl.txt | 27 +++++++++ cxl/json.c | 56 +++++++++++++++++++- cxl/lib/libcxl.c | 109 ++++++++++++++++++++++++++++++++++++++ cxl/lib/libcxl.sym | 7 ++ cxl/lib/private.h | 13 +++++ cxl/libcxl.h | 12 ++++ cxl/list.c | 2 - 9 files changed, 240 insertions(+), 5 deletions(-)