Blame SOURCES/0100-cxl-list-Add-bus-objects.patch

26ccd9
From 9dce91c303720a336c55ecdc2e01e423589b85b2 Mon Sep 17 00:00:00 2001
26ccd9
From: Dan Williams <dan.j.williams@intel.com>
26ccd9
Date: Sun, 23 Jan 2022 16:53:02 -0800
26ccd9
Subject: [PATCH 100/217] cxl/list: Add bus objects
26ccd9
26ccd9
A 'struct cxl_bus' represents a CXL.mem domain. It is the root of a
26ccd9
Host-managed Device Memory (HDM) hierarchy. When memory devices are enabled
26ccd9
for CXL operation they appear underneath a bus in a 'cxl list -BM' listing,
26ccd9
otherwise they display as disconnected.
26ccd9
26ccd9
A 'bus' is identical to the kernel's CXL root port object, but given the
26ccd9
confusion between CXL root ports, and PCIe root ports, the 'bus' name is
26ccd9
less ambiguous. It also serves a similar role in the object hierarchy as a
26ccd9
'struct ndctl_bus' object. It is also the case that the "root" name will
26ccd9
appear as the kernel device-name, so the association will be clear.
26ccd9
26ccd9
Link: https://lore.kernel.org/r/164298558278.3021641.16323855851736615358.stgit@dwillia2-desk3.amr.corp.intel.com
26ccd9
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
26ccd9
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
26ccd9
---
26ccd9
 .clang-format                    |   1 +
26ccd9
 Documentation/cxl/cxl-list.txt   |  88 ++++++++++++++++---
26ccd9
 Documentation/cxl/lib/libcxl.txt |  30 +++++++
26ccd9
 cxl/filter.c                     | 117 ++++++++++++++++++++++++-
26ccd9
 cxl/filter.h                     |   2 +
26ccd9
 cxl/json.c                       |  21 +++++
26ccd9
 cxl/json.h                       |   5 +-
26ccd9
 cxl/lib/libcxl.c                 | 142 +++++++++++++++++++++++++++++++
26ccd9
 cxl/lib/libcxl.sym               |   5 ++
26ccd9
 cxl/lib/private.h                |  14 +++
26ccd9
 cxl/libcxl.h                     |  11 +++
26ccd9
 cxl/list.c                       |  19 +++--
26ccd9
 12 files changed, 431 insertions(+), 24 deletions(-)
26ccd9
26ccd9
diff --git a/.clang-format b/.clang-format
26ccd9
index d2e77d0..1154c76 100644
26ccd9
--- a/.clang-format
26ccd9
+++ b/.clang-format
26ccd9
@@ -78,6 +78,7 @@ ExperimentalAutoDetectBinPacking: false
26ccd9
 # 	| sort -u)
26ccd9
 ForEachMacros:
26ccd9
   - 'cxl_memdev_foreach'
26ccd9
+  - 'cxl_bus_foreach'
26ccd9
   - 'daxctl_dev_foreach'
26ccd9
   - 'daxctl_mapping_foreach'
26ccd9
   - 'daxctl_region_foreach'
26ccd9
diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt
26ccd9
index 224c972..be131ae 100644
26ccd9
--- a/Documentation/cxl/cxl-list.txt
26ccd9
+++ b/Documentation/cxl/cxl-list.txt
26ccd9
@@ -15,17 +15,60 @@ SYNOPSIS
26ccd9
 Walk the CXL capable device hierarchy in the system and list all device
26ccd9
 instances along with some of their major attributes.
26ccd9
 
26ccd9
-Options can be specified to limit the output to specific objects.
26ccd9
+Options can be specified to limit the output to specific objects. When a
26ccd9
+single object type is specified the return json object is an array of
26ccd9
+just those objects, when multiple objects types are specified the
26ccd9
+returned the returned object may be an array of arrays with the inner
26ccd9
+array named for the given object type.
26ccd9
+
26ccd9
+Filters can by specifed as either a single identidier, a space separated
26ccd9
+quoted string, or a comma separated list. When multiple filter
26ccd9
+identifiers are specified within a filter string, like "-m
26ccd9
+mem0,mem1,mem2", they are combined as an 'OR' filter.  When multiple
26ccd9
+filter string types are specified, like "-m mem0,mem1,mem2 -p port10",
26ccd9
+they are combined as an 'AND' filter. So, "-m mem0,mem1,mem2 -p port10"
26ccd9
+would only list objects that are beneath port10 AND map mem0, mem1, OR
26ccd9
+mem2.
26ccd9
+
26ccd9
+The --human option in addition to reformatting some fields to more human
26ccd9
+friendly strings also unwraps the array to reduce the number of lines of
26ccd9
+output.
26ccd9
 
26ccd9
 EXAMPLE
26ccd9
 -------
26ccd9
 ----
26ccd9
 # cxl list --memdevs
26ccd9
-{
26ccd9
-  "memdev":"mem0",
26ccd9
-  "pmem_size":268435456,
26ccd9
-  "ram_size":0,
26ccd9
-}
26ccd9
+[
26ccd9
+  {
26ccd9
+    "memdev":"mem0",
26ccd9
+    "pmem_size":268435456,
26ccd9
+    "ram_size":0,
26ccd9
+    "serial":0
26ccd9
+  }
26ccd9
+]
26ccd9
+
26ccd9
+# cxl list -BMu
26ccd9
+[
26ccd9
+  {
26ccd9
+    "anon memdevs":[
26ccd9
+      {
26ccd9
+        "memdev":"mem0",
26ccd9
+        "pmem_size":"256.00 MiB (268.44 MB)",
26ccd9
+        "ram_size":0,
26ccd9
+        "serial":"0"
26ccd9
+      }
26ccd9
+    ]
26ccd9
+  },
26ccd9
+  {
26ccd9
+    "buses":[
26ccd9
+      {
26ccd9
+        "bus":"root0",
26ccd9
+        "provider":"ACPI.CXL"
26ccd9
+      }
26ccd9
+    ]
26ccd9
+  }
26ccd9
+]
26ccd9
+
26ccd9
 ----
26ccd9
 
26ccd9
 OPTIONS
26ccd9
@@ -34,13 +77,6 @@ OPTIONS
26ccd9
 --memdev=::
26ccd9
 	Specify CXL memory device name(s), or device id(s), to filter the listing. For example:
26ccd9
 ----
26ccd9
-# cxl list --memdev=mem0
26ccd9
-{
26ccd9
-  "memdev":"mem0",
26ccd9
-  "pmem_size":268435456,
26ccd9
-  "ram_size":0,
26ccd9
-}
26ccd9
-
26ccd9
 # cxl list -M --memdev="0 mem3 5"
26ccd9
 [
26ccd9
   {
26ccd9
@@ -114,6 +150,32 @@ OPTIONS
26ccd9
 ]
26ccd9
 ----
26ccd9
 
26ccd9
+-B::
26ccd9
+--buses::
26ccd9
+	Include 'bus' / CXL root object(s) in the listing. Typically, on ACPI
26ccd9
+	systems the bus object is a singleton associated with the ACPI0017
26ccd9
+	device, but there are test scenerios where there may be multiple CXL
26ccd9
+	memory hierarchies.
26ccd9
+----
26ccd9
+# cxl list -B
26ccd9
+[
26ccd9
+  {
26ccd9
+    "bus":"root3",
26ccd9
+    "provider":"cxl_test"
26ccd9
+  },
26ccd9
+  {
26ccd9
+    "bus":"root0",
26ccd9
+    "provider":"ACPI.CXL"
26ccd9
+  }
26ccd9
+]
26ccd9
+----
26ccd9
+
26ccd9
+-b::
26ccd9
+--bus=::
26ccd9
+	Specify CXL root device name(s), device id(s), and / or CXL bus provider
26ccd9
+	names to filter the listing. The supported provider names are "ACPI.CXL"
26ccd9
+	and "cxl_test".
26ccd9
+
26ccd9
 include::human-option.txt[]
26ccd9
 
26ccd9
 include::verbose-option.txt[]
26ccd9
diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt
26ccd9
index c127326..84af66a 100644
26ccd9
--- a/Documentation/cxl/lib/libcxl.txt
26ccd9
+++ b/Documentation/cxl/lib/libcxl.txt
26ccd9
@@ -134,6 +134,36 @@ cxl_memdev{read,write,zero}_label() are helpers for marshaling multiple
26ccd9
 label access commands over an arbitrary extent of the device's label
26ccd9
 area.
26ccd9
 
26ccd9
+BUSES
26ccd9
+-----
26ccd9
+The CXL Memory space is CPU and Device coherent. The address ranges that
26ccd9
+support coherent access are described by platform firmware and
26ccd9
+communicated to the operating system via a CXL root object 'struct
26ccd9
+cxl_bus'.
26ccd9
+
26ccd9
+=== BUS: Enumeration
26ccd9
+----
26ccd9
+struct cxl_bus *cxl_bus_get_first(struct cxl_ctx *ctx);
26ccd9
+struct cxl_bus *cxl_bus_get_next(struct cxl_bus *bus);
26ccd9
+
26ccd9
+#define cxl_bus_foreach(ctx, bus)                                           \
26ccd9
+       for (bus = cxl_bus_get_first(ctx); bus != NULL;                      \
26ccd9
+            bus = cxl_bus_get_next(bus))
26ccd9
+----
26ccd9
+
26ccd9
+=== BUS: Attributes
26ccd9
+----
26ccd9
+const char *cxl_bus_get_provider(struct cxl_bus *bus);
26ccd9
+const char *cxl_bus_get_devname(struct cxl_bus *bus);
26ccd9
+int cxl_bus_get_id(struct cxl_bus *bus);
26ccd9
+----
26ccd9
+
26ccd9
+The provider name of a bus is a persistent name that is independent of
26ccd9
+discovery order. The possible provider names are 'ACPI.CXL' and
26ccd9
+'cxl_test'. The devname and id attributes, like other objects, are just
26ccd9
+the kernel device names that are subject to change based on discovery
26ccd9
+order.
26ccd9
+
26ccd9
 include::../../copyright.txt[]
26ccd9
 
26ccd9
 SEE ALSO
26ccd9
diff --git a/cxl/filter.c b/cxl/filter.c
26ccd9
index 26efc65..5f4844b 100644
26ccd9
--- a/cxl/filter.c
26ccd9
+++ b/cxl/filter.c
26ccd9
@@ -1,5 +1,5 @@
26ccd9
 // SPDX-License-Identifier: GPL-2.0
26ccd9
-// Copyright (C) 2015-2020 Intel Corporation. All rights reserved.
26ccd9
+// Copyright (C) 2015-2022 Intel Corporation. All rights reserved.
26ccd9
 #include <errno.h>
26ccd9
 #include <stdio.h>
26ccd9
 #include <string.h>
26ccd9
@@ -21,6 +21,43 @@ static const char *which_sep(const char *filter)
26ccd9
 	return " ";
26ccd9
 }
26ccd9
 
26ccd9
+static struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus,
26ccd9
+					   const char *__ident)
26ccd9
+{
26ccd9
+	char *ident, *save;
26ccd9
+	const char *arg;
26ccd9
+	int bus_id;
26ccd9
+
26ccd9
+	if (!__ident)
26ccd9
+		return bus;
26ccd9
+
26ccd9
+	ident = strdup(__ident);
26ccd9
+	if (!ident)
26ccd9
+		return NULL;
26ccd9
+
26ccd9
+	for (arg = strtok_r(ident, which_sep(__ident), &save); arg;
26ccd9
+	     arg = strtok_r(NULL, which_sep(__ident), &save)) {
26ccd9
+		if (strcmp(arg, "all") == 0)
26ccd9
+			break;
26ccd9
+
26ccd9
+		if ((sscanf(arg, "%d", &bus_id) == 1 ||
26ccd9
+		     sscanf(arg, "root%d", &bus_id) == 1) &&
26ccd9
+		    cxl_bus_get_id(bus) == bus_id)
26ccd9
+			break;
26ccd9
+
26ccd9
+		if (strcmp(arg, cxl_bus_get_devname(bus)) == 0)
26ccd9
+			break;
26ccd9
+
26ccd9
+		if (strcmp(arg, cxl_bus_get_provider(bus)) == 0)
26ccd9
+			break;
26ccd9
+	}
26ccd9
+
26ccd9
+	free(ident);
26ccd9
+	if (arg)
26ccd9
+		return bus;
26ccd9
+	return NULL;
26ccd9
+}
26ccd9
+
26ccd9
 static struct cxl_memdev *
26ccd9
 util_cxl_memdev_serial_filter(struct cxl_memdev *memdev, const char *__serials)
26ccd9
 {
26ccd9
@@ -98,21 +135,67 @@ static unsigned long params_to_flags(struct cxl_filter_params *param)
26ccd9
 	return flags;
26ccd9
 }
26ccd9
 
26ccd9
+static void splice_array(struct cxl_filter_params *p, struct json_object *jobjs,
26ccd9
+			 struct json_object *platform,
26ccd9
+			 const char *container_name, bool do_container)
26ccd9
+{
26ccd9
+	size_t count;
26ccd9
+
26ccd9
+	if (!json_object_array_length(jobjs)) {
26ccd9
+		json_object_put(jobjs);
26ccd9
+		return;
26ccd9
+	}
26ccd9
+
26ccd9
+	if (do_container) {
26ccd9
+		struct json_object *container = json_object_new_object();
26ccd9
+
26ccd9
+		if (!container) {
26ccd9
+			err(p, "failed to list: %s\n", container_name);
26ccd9
+			return;
26ccd9
+		}
26ccd9
+
26ccd9
+		json_object_object_add(container, container_name, jobjs);
26ccd9
+		json_object_array_add(platform, container);
26ccd9
+		return;
26ccd9
+	}
26ccd9
+
26ccd9
+	for (count = json_object_array_length(jobjs); count; count--) {
26ccd9
+		struct json_object *jobj = json_object_array_get_idx(jobjs, 0);
26ccd9
+
26ccd9
+		json_object_get(jobj);
26ccd9
+		json_object_array_del_idx(jobjs, 0, 1);
26ccd9
+		json_object_array_add(platform, jobj);
26ccd9
+	}
26ccd9
+	json_object_put(jobjs);
26ccd9
+}
26ccd9
+
26ccd9
 int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
26ccd9
 {
26ccd9
 	struct json_object *jplatform = json_object_new_array();
26ccd9
+	struct json_object *jdevs = NULL, *jbuses = NULL;
26ccd9
 	unsigned long flags = params_to_flags(p);
26ccd9
 	struct cxl_memdev *memdev;
26ccd9
+	int top_level_objs = 0;
26ccd9
+	struct cxl_bus *bus;
26ccd9
 
26ccd9
 	if (!jplatform) {
26ccd9
 		dbg(p, "platform object allocation failure\n");
26ccd9
 		return -ENOMEM;
26ccd9
 	}
26ccd9
 
26ccd9
+	jdevs = json_object_new_array();
26ccd9
+	if (!jdevs)
26ccd9
+		goto err;
26ccd9
+
26ccd9
+	jbuses = json_object_new_array();
26ccd9
+	if (!jbuses)
26ccd9
+		goto err;
26ccd9
+
26ccd9
 	cxl_memdev_foreach(ctx, memdev) {
26ccd9
 		struct json_object *jdev;
26ccd9
 
26ccd9
-		if (!util_cxl_memdev_filter(memdev, p->memdev_filter, p->serial_filter))
26ccd9
+		if (!util_cxl_memdev_filter(memdev, p->memdev_filter,
26ccd9
+					    p->serial_filter))
26ccd9
 			continue;
26ccd9
 		if (p->memdevs) {
26ccd9
 			jdev = util_cxl_memdev_to_json(memdev, flags);
26ccd9
@@ -120,11 +203,39 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
26ccd9
 				dbg(p, "memdev object allocation failure\n");
26ccd9
 				continue;
26ccd9
 			}
26ccd9
-			json_object_array_add(jplatform, jdev);
26ccd9
+			json_object_array_add(jdevs, jdev);
26ccd9
+		}
26ccd9
+	}
26ccd9
+
26ccd9
+	cxl_bus_foreach(ctx, bus) {
26ccd9
+		struct json_object *jbus;
26ccd9
+
26ccd9
+		if (!util_cxl_bus_filter(bus, p->bus_filter))
26ccd9
+			continue;
26ccd9
+		if (p->buses) {
26ccd9
+			jbus = util_cxl_bus_to_json(bus, flags);
26ccd9
+			if (!jbus) {
26ccd9
+				dbg(p, "bus object allocation failure\n");
26ccd9
+				continue;
26ccd9
+			}
26ccd9
+			json_object_array_add(jbuses, jbus);
26ccd9
 		}
26ccd9
 	}
26ccd9
 
26ccd9
+	if (json_object_array_length(jdevs))
26ccd9
+		top_level_objs++;
26ccd9
+	if (json_object_array_length(jbuses))
26ccd9
+		top_level_objs++;
26ccd9
+
26ccd9
+	splice_array(p, jdevs, jplatform, "anon memdevs", top_level_objs > 1);
26ccd9
+	splice_array(p, jbuses, jplatform, "buses", top_level_objs > 1);
26ccd9
+
26ccd9
 	util_display_json_array(stdout, jplatform, flags);
26ccd9
 
26ccd9
 	return 0;
26ccd9
+err:
26ccd9
+	json_object_put(jdevs);
26ccd9
+	json_object_put(jbuses);
26ccd9
+	json_object_put(jplatform);
26ccd9
+	return -ENOMEM;
26ccd9
 }
26ccd9
diff --git a/cxl/filter.h b/cxl/filter.h
26ccd9
index 12d9344..d41e757 100644
26ccd9
--- a/cxl/filter.h
26ccd9
+++ b/cxl/filter.h
26ccd9
@@ -9,7 +9,9 @@
26ccd9
 struct cxl_filter_params {
26ccd9
 	const char *memdev_filter;
26ccd9
 	const char *serial_filter;
26ccd9
+	const char *bus_filter;
26ccd9
 	bool memdevs;
26ccd9
+	bool buses;
26ccd9
 	bool idle;
26ccd9
 	bool human;
26ccd9
 	bool health;
26ccd9
diff --git a/cxl/json.c b/cxl/json.c
26ccd9
index d8e65df..a584594 100644
26ccd9
--- a/cxl/json.c
26ccd9
+++ b/cxl/json.c
26ccd9
@@ -221,3 +221,24 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev,
26ccd9
 	}
26ccd9
 	return jdev;
26ccd9
 }
26ccd9
+
26ccd9
+struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus,
26ccd9
+					 unsigned long flags)
26ccd9
+{
26ccd9
+	const char *devname = cxl_bus_get_devname(bus);
26ccd9
+	struct json_object *jbus, *jobj;
26ccd9
+
26ccd9
+	jbus = json_object_new_object();
26ccd9
+	if (!jbus)
26ccd9
+		return NULL;
26ccd9
+
26ccd9
+	jobj = json_object_new_string(devname);
26ccd9
+	if (jobj)
26ccd9
+		json_object_object_add(jbus, "bus", jobj);
26ccd9
+
26ccd9
+	jobj = json_object_new_string(cxl_bus_get_provider(bus));
26ccd9
+	if (jobj)
26ccd9
+		json_object_object_add(jbus, "provider", jobj);
26ccd9
+
26ccd9
+	return jbus;
26ccd9
+}
26ccd9
diff --git a/cxl/json.h b/cxl/json.h
26ccd9
index 3abcfe6..4abf6e5 100644
26ccd9
--- a/cxl/json.h
26ccd9
+++ b/cxl/json.h
26ccd9
@@ -1,8 +1,11 @@
26ccd9
 /* SPDX-License-Identifier: GPL-2.0 */
26ccd9
-/* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */
26ccd9
+/* Copyright (C) 2015-2022 Intel Corporation. All rights reserved. */
26ccd9
 #ifndef __CXL_UTIL_JSON_H__
26ccd9
 #define __CXL_UTIL_JSON_H__
26ccd9
 struct cxl_memdev;
26ccd9
 struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev,
26ccd9
 		unsigned long flags);
26ccd9
+struct cxl_bus;
26ccd9
+struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus,
26ccd9
+					 unsigned long flags);
26ccd9
 #endif /* __CXL_UTIL_JSON_H__ */
26ccd9
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
26ccd9
index 9839f26..8548a45 100644
26ccd9
--- a/cxl/lib/libcxl.c
26ccd9
+++ b/cxl/lib/libcxl.c
26ccd9
@@ -40,7 +40,9 @@ struct cxl_ctx {
26ccd9
 	int refcount;
26ccd9
 	void *userdata;
26ccd9
 	int memdevs_init;
26ccd9
+	int buses_init;
26ccd9
 	struct list_head memdevs;
26ccd9
+	struct list_head buses;
26ccd9
 	struct kmod_ctx *kmod_ctx;
26ccd9
 	void *private_data;
26ccd9
 };
26ccd9
@@ -64,6 +66,21 @@ static void free_memdev(struct cxl_memdev *memdev, struct list_head *head)
26ccd9
 	free(memdev);
26ccd9
 }
26ccd9
 
26ccd9
+static void __free_port(struct cxl_port *port, struct list_head *head)
26ccd9
+{
26ccd9
+	if (head)
26ccd9
+		list_del_from(head, &port->list);
26ccd9
+	free(port->dev_buf);
26ccd9
+	free(port->dev_path);
26ccd9
+	free(port->uport);
26ccd9
+}
26ccd9
+
26ccd9
+static void free_bus(struct cxl_bus *bus, struct list_head *head)
26ccd9
+{
26ccd9
+	__free_port(&bus->port, head);
26ccd9
+	free(bus);
26ccd9
+}
26ccd9
+
26ccd9
 /**
26ccd9
  * cxl_get_userdata - retrieve stored data pointer from library context
26ccd9
  * @ctx: cxl library context
26ccd9
@@ -130,6 +147,7 @@ CXL_EXPORT int cxl_new(struct cxl_ctx **ctx)
26ccd9
 	dbg(c, "log_priority=%d\n", c->ctx.log_priority);
26ccd9
 	*ctx = c;
26ccd9
 	list_head_init(&c->memdevs);
26ccd9
+	list_head_init(&c->buses);
26ccd9
 	c->kmod_ctx = kmod_ctx;
26ccd9
 
26ccd9
 	return 0;
26ccd9
@@ -160,6 +178,7 @@ CXL_EXPORT struct cxl_ctx *cxl_ref(struct cxl_ctx *ctx)
26ccd9
 CXL_EXPORT void cxl_unref(struct cxl_ctx *ctx)
26ccd9
 {
26ccd9
 	struct cxl_memdev *memdev, *_d;
26ccd9
+	struct cxl_bus *bus, *_b;
26ccd9
 
26ccd9
 	if (ctx == NULL)
26ccd9
 		return;
26ccd9
@@ -170,6 +189,9 @@ CXL_EXPORT void cxl_unref(struct cxl_ctx *ctx)
26ccd9
 	list_for_each_safe(&ctx->memdevs, memdev, _d, list)
26ccd9
 		free_memdev(memdev, &ctx->memdevs);
26ccd9
 
26ccd9
+	list_for_each_safe(&ctx->buses, bus, _b, port.list)
26ccd9
+		free_bus(bus, &ctx->buses);
26ccd9
+
26ccd9
 	kmod_unref(ctx->kmod_ctx);
26ccd9
 	info(ctx, "context %p released\n", ctx);
26ccd9
 	free(ctx);
26ccd9
@@ -449,6 +471,126 @@ CXL_EXPORT int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev)
26ccd9
 	return is_enabled(path);
26ccd9
 }
26ccd9
 
26ccd9
+static int cxl_port_init(struct cxl_port *port, struct cxl_ctx *ctx, int id,
26ccd9
+			 const char *cxlport_base)
26ccd9
+{
26ccd9
+	char *path = calloc(1, strlen(cxlport_base) + 100);
26ccd9
+	size_t rc;
26ccd9
+
26ccd9
+	if (!path)
26ccd9
+		return -ENOMEM;
26ccd9
+
26ccd9
+	port->id = id;
26ccd9
+	port->ctx = ctx;
26ccd9
+
26ccd9
+	port->dev_path = strdup(cxlport_base);
26ccd9
+	if (!port->dev_path)
26ccd9
+		goto err;
26ccd9
+
26ccd9
+	port->dev_buf = calloc(1, strlen(cxlport_base) + 50);
26ccd9
+	if (!port->dev_buf)
26ccd9
+		goto err;
26ccd9
+	port->buf_len = strlen(cxlport_base) + 50;
26ccd9
+
26ccd9
+	rc = snprintf(port->dev_buf, port->buf_len, "%s/uport", cxlport_base);
26ccd9
+	if (rc >= port->buf_len)
26ccd9
+		goto err;
26ccd9
+	port->uport = realpath(port->dev_buf, NULL);
26ccd9
+	if (!port->uport)
26ccd9
+		goto err;
26ccd9
+
26ccd9
+	return 0;
26ccd9
+err:
26ccd9
+	free(port->dev_path);
26ccd9
+	free(port->dev_buf);
26ccd9
+	free(path);
26ccd9
+	return -ENOMEM;
26ccd9
+}
26ccd9
+
26ccd9
+static void *add_cxl_bus(void *parent, int id, const char *cxlbus_base)
26ccd9
+{
26ccd9
+	const char *devname = devpath_to_devname(cxlbus_base);
26ccd9
+	struct cxl_bus *bus, *bus_dup;
26ccd9
+	struct cxl_ctx *ctx = parent;
26ccd9
+	struct cxl_port *port;
26ccd9
+	int rc;
26ccd9
+
26ccd9
+	dbg(ctx, "%s: base: \'%s\'\n", devname, cxlbus_base);
26ccd9
+
26ccd9
+	bus = calloc(1, sizeof(*bus));
26ccd9
+	if (!bus)
26ccd9
+		return NULL;
26ccd9
+
26ccd9
+	port = &bus->port;
26ccd9
+	rc = cxl_port_init(port, ctx, id, cxlbus_base);
26ccd9
+	if (rc)
26ccd9
+		goto err;
26ccd9
+
26ccd9
+	cxl_bus_foreach(ctx, bus_dup)
26ccd9
+		if (bus_dup->port.id == bus->port.id) {
26ccd9
+			free_bus(bus, NULL);
26ccd9
+			return bus_dup;
26ccd9
+		}
26ccd9
+
26ccd9
+	list_add(&ctx->buses, &port->list);
26ccd9
+	return bus;
26ccd9
+
26ccd9
+err:
26ccd9
+	free(bus);
26ccd9
+	return NULL;
26ccd9
+}
26ccd9
+
26ccd9
+static void cxl_buses_init(struct cxl_ctx *ctx)
26ccd9
+{
26ccd9
+	if (ctx->buses_init)
26ccd9
+		return;
26ccd9
+
26ccd9
+	ctx->buses_init = 1;
26ccd9
+
26ccd9
+	sysfs_device_parse(ctx, "/sys/bus/cxl/devices", "root", ctx,
26ccd9
+			   add_cxl_bus);
26ccd9
+}
26ccd9
+
26ccd9
+CXL_EXPORT struct cxl_bus *cxl_bus_get_first(struct cxl_ctx *ctx)
26ccd9
+{
26ccd9
+	cxl_buses_init(ctx);
26ccd9
+
26ccd9
+	return list_top(&ctx->buses, struct cxl_bus, port.list);
26ccd9
+}
26ccd9
+
26ccd9
+CXL_EXPORT struct cxl_bus *cxl_bus_get_next(struct cxl_bus *bus)
26ccd9
+{
26ccd9
+	struct cxl_ctx *ctx = bus->port.ctx;
26ccd9
+
26ccd9
+	return list_next(&ctx->buses, bus, port.list);
26ccd9
+}
26ccd9
+
26ccd9
+CXL_EXPORT const char *cxl_bus_get_devname(struct cxl_bus *bus)
26ccd9
+{
26ccd9
+	struct cxl_port *port = &bus->port;
26ccd9
+
26ccd9
+	return devpath_to_devname(port->dev_path);
26ccd9
+}
26ccd9
+
26ccd9
+CXL_EXPORT int cxl_bus_get_id(struct cxl_bus *bus)
26ccd9
+{
26ccd9
+	struct cxl_port *port = &bus->port;
26ccd9
+
26ccd9
+	return port->id;
26ccd9
+}
26ccd9
+
26ccd9
+CXL_EXPORT const char *cxl_bus_get_provider(struct cxl_bus *bus)
26ccd9
+{
26ccd9
+	struct cxl_port *port = &bus->port;
26ccd9
+	const char *devname = devpath_to_devname(port->uport);
26ccd9
+
26ccd9
+	if (strcmp(devname, "ACPI0017:00") == 0)
26ccd9
+		return "ACPI.CXL";
26ccd9
+	if (strcmp(devname, "cxl_acpi.0") == 0)
26ccd9
+		return "cxl_test";
26ccd9
+	return devname;
26ccd9
+}
26ccd9
+
26ccd9
 CXL_EXPORT void cxl_cmd_unref(struct cxl_cmd *cmd)
26ccd9
 {
26ccd9
 	if (!cmd)
26ccd9
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
26ccd9
index 4411035..781ff99 100644
26ccd9
--- a/cxl/lib/libcxl.sym
26ccd9
+++ b/cxl/lib/libcxl.sym
26ccd9
@@ -77,4 +77,9 @@ local:
26ccd9
 LIBCXL_2 {
26ccd9
 global:
26ccd9
 	cxl_memdev_get_serial;
26ccd9
+	cxl_bus_get_first;
26ccd9
+	cxl_bus_get_next;
26ccd9
+	cxl_bus_get_provider;
26ccd9
+	cxl_bus_get_devname;
26ccd9
+	cxl_bus_get_id;
26ccd9
 } LIBCXL_1;
26ccd9
diff --git a/cxl/lib/private.h b/cxl/lib/private.h
26ccd9
index 7c81e24..0758d05 100644
26ccd9
--- a/cxl/lib/private.h
26ccd9
+++ b/cxl/lib/private.h
26ccd9
@@ -34,6 +34,20 @@ struct cxl_memdev {
26ccd9
 	unsigned long long serial;
26ccd9
 };
26ccd9
 
26ccd9
+struct cxl_port {
26ccd9
+	int id;
26ccd9
+	void *dev_buf;
26ccd9
+	size_t buf_len;
26ccd9
+	char *dev_path;
26ccd9
+	char *uport;
26ccd9
+	struct cxl_ctx *ctx;
26ccd9
+	struct list_node list;
26ccd9
+};
26ccd9
+
26ccd9
+struct cxl_bus {
26ccd9
+	struct cxl_port port;
26ccd9
+};
26ccd9
+
26ccd9
 enum cxl_cmd_query_status {
26ccd9
 	CXL_CMD_QUERY_NOT_RUN = 0,
26ccd9
 	CXL_CMD_QUERY_OK,
26ccd9
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
26ccd9
index bcdede8..da66eb2 100644
26ccd9
--- a/cxl/libcxl.h
26ccd9
+++ b/cxl/libcxl.h
26ccd9
@@ -57,6 +57,17 @@ int cxl_memdev_write_label(struct cxl_memdev *memdev, void *buf, size_t length,
26ccd9
              memdev != NULL; \
26ccd9
              memdev = cxl_memdev_get_next(memdev))
26ccd9
 
26ccd9
+struct cxl_bus;
26ccd9
+struct cxl_bus *cxl_bus_get_first(struct cxl_ctx *ctx);
26ccd9
+struct cxl_bus *cxl_bus_get_next(struct cxl_bus *bus);
26ccd9
+const char *cxl_bus_get_provider(struct cxl_bus *bus);
26ccd9
+const char *cxl_bus_get_devname(struct cxl_bus *bus);
26ccd9
+int cxl_bus_get_id(struct cxl_bus *bus);
26ccd9
+
26ccd9
+#define cxl_bus_foreach(ctx, bus)                                              \
26ccd9
+	for (bus = cxl_bus_get_first(ctx); bus != NULL;                        \
26ccd9
+	     bus = cxl_bus_get_next(bus))
26ccd9
+
26ccd9
 struct cxl_cmd;
26ccd9
 const char *cxl_cmd_get_devname(struct cxl_cmd *cmd);
26ccd9
 struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, int opcode);
26ccd9
diff --git a/cxl/list.c b/cxl/list.c
26ccd9
index 7e2744d..9500e61 100644
26ccd9
--- a/cxl/list.c
26ccd9
+++ b/cxl/list.c
26ccd9
@@ -1,5 +1,5 @@
26ccd9
 // SPDX-License-Identifier: GPL-2.0
26ccd9
-/* Copyright (C) 2020-2021 Intel Corporation. All rights reserved. */
26ccd9
+/* Copyright (C) 2020-2022 Intel Corporation. All rights reserved. */
26ccd9
 #include <stdio.h>
26ccd9
 #include <errno.h>
26ccd9
 #include <stdlib.h>
26ccd9
@@ -14,11 +14,6 @@
26ccd9
 
26ccd9
 static struct cxl_filter_params param;
26ccd9
 
26ccd9
-static int num_list_flags(void)
26ccd9
-{
26ccd9
-	return param.memdevs;
26ccd9
-}
26ccd9
-
26ccd9
 static const struct option options[] = {
26ccd9
 	OPT_STRING('m', "memdev", &param.memdev_filter, "memory device name(s)",
26ccd9
 		   "filter by CXL memory device name(s)"),
26ccd9
@@ -27,6 +22,9 @@ static const struct option options[] = {
26ccd9
 		   "filter by CXL memory device serial number(s)"),
26ccd9
 	OPT_BOOLEAN('M', "memdevs", &param.memdevs,
26ccd9
 		    "include CXL memory device info"),
26ccd9
+	OPT_STRING('b', "bus", &param.bus_filter, "bus device name",
26ccd9
+		   "filter by CXL bus device name(s)"),
26ccd9
+	OPT_BOOLEAN('B', "buses", &param.buses, "include CXL bus info"),
26ccd9
 	OPT_BOOLEAN('i', "idle", &param.idle, "include disabled devices"),
26ccd9
 	OPT_BOOLEAN('u', "human", &param.human,
26ccd9
 		    "use human friendly number formats "),
26ccd9
@@ -35,6 +33,11 @@ static const struct option options[] = {
26ccd9
 	OPT_END(),
26ccd9
 };
26ccd9
 
26ccd9
+static int num_list_flags(void)
26ccd9
+{
26ccd9
+       return !!param.memdevs + !!param.buses;
26ccd9
+}
26ccd9
+
26ccd9
 int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx)
26ccd9
 {
26ccd9
 	const char * const u[] = {
26ccd9
@@ -53,7 +56,9 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx)
26ccd9
 	if (num_list_flags() == 0) {
26ccd9
 		if (param.memdev_filter || param.serial_filter)
26ccd9
 			param.memdevs = true;
26ccd9
-		else {
26ccd9
+		if (param.bus_filter)
26ccd9
+			param.buses = true;
26ccd9
+		if (num_list_flags() == 0) {
26ccd9
 			/*
26ccd9
 			 * TODO: We likely want to list regions by default if
26ccd9
 			 * nothing was explicitly asked for. But until we have
26ccd9
-- 
26ccd9
2.27.0
26ccd9