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

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