anitazha / rpms / ndctl

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