Blame SOURCES/0108-cxl-list-Move-enabled-memdevs-underneath-their-endpo.patch

e0018b
From 41d6769393f449008abf934e815f137360889633 Mon Sep 17 00:00:00 2001
e0018b
From: Dan Williams <dan.j.williams@intel.com>
e0018b
Date: Sun, 23 Jan 2022 16:53:45 -0800
e0018b
Subject: [PATCH 108/217] cxl/list: Move enabled memdevs underneath their
e0018b
 endpoint
e0018b
e0018b
When a memdev is enabled it means that the kernel was able to validate a
e0018b
CXL connection from the CXL root, through intervening switches, and to the
e0018b
endpoint. Reflect that state by listing memdevs as child objects of
e0018b
endpoints, or aggregated into an array if individual endpoints are not
e0018b
listed.
e0018b
e0018b
Link: https://lore.kernel.org/r/164298562531.3021641.10620937879296964476.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
 Documentation/cxl/cxl-list.txt   |  11 ++-
e0018b
 Documentation/cxl/lib/libcxl.txt |   2 +
e0018b
 cxl/filter.c                     | 130 ++++++++++++++++++++++++-------
e0018b
 cxl/json.c                       |   6 ++
e0018b
 cxl/lib/libcxl.c                 |  97 +++++++++++++++++++++++
e0018b
 cxl/lib/libcxl.sym               |   3 +
e0018b
 cxl/libcxl.h                     |   4 +
e0018b
 7 files changed, 223 insertions(+), 30 deletions(-)
e0018b
e0018b
diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt
e0018b
index 9c21ab7..1751868 100644
e0018b
--- a/Documentation/cxl/cxl-list.txt
e0018b
+++ b/Documentation/cxl/cxl-list.txt
e0018b
@@ -19,7 +19,16 @@ 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
+array named for the given object type. The top-level arrays are ellided
e0018b
+when the objects can nest under a higher object-type in the hierararchy.
e0018b
+The potential top-level array names and their nesting properties are:
e0018b
+
e0018b
+"anon memdevs":: (disabled memory devices) do not nest
e0018b
+"buses":: do not nest
e0018b
+"ports":: nest under buses
e0018b
+"endpoints":: nest under ports or buses (if ports are not emitted)
e0018b
+"memdevs":: nest under endpoints or ports (if endpoints are not
e0018b
+   emitted) or buses (if endpoints and ports are not emitted)
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
diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt
e0018b
index 91fd33e..73b0fb9 100644
e0018b
--- a/Documentation/cxl/lib/libcxl.txt
e0018b
+++ b/Documentation/cxl/lib/libcxl.txt
e0018b
@@ -41,6 +41,7 @@ struct cxl_memdev *cxl_memdev_get_first(struct cxl_ctx *ctx);
e0018b
 struct cxl_memdev *cxl_memdev_get_next(struct cxl_memdev *memdev);
e0018b
 struct cxl_ctx *cxl_memdev_get_ctx(struct cxl_memdev *memdev);
e0018b
 const char *cxl_memdev_get_host(struct cxl_memdev *memdev)
e0018b
+struct cxl_memdev *cxl_endpoint_get_memdev(struct cxl_endpoint *endpoint);
e0018b
 
e0018b
 #define cxl_memdev_foreach(ctx, memdev) \
e0018b
         for (memdev = cxl_memdev_get_first(ctx); \
e0018b
@@ -231,6 +232,7 @@ struct cxl_ctx *cxl_endpoint_get_ctx(struct cxl_endpoint *endpoint);
e0018b
 struct cxl_port *cxl_endpoint_get_parent(struct cxl_endpoint *endpoint);
e0018b
 struct cxl_port *cxl_endpoint_get_port(struct cxl_endpoint *endpoint);
e0018b
 const char *cxl_endpoint_get_host(struct cxl_endpoint *endpoint);
e0018b
+struct cxl_endpoint *cxl_memdev_get_endpoint(struct cxl_memdev *memdev);
e0018b
 
e0018b
 #define cxl_endpoint_foreach(port, endpoint)                                 \
e0018b
        for (endpoint = cxl_endpoint_get_first(port); endpoint != NULL;       \
e0018b
diff --git a/cxl/filter.c b/cxl/filter.c
e0018b
index 5d80d1b..2130816 100644
e0018b
--- a/cxl/filter.c
e0018b
+++ b/cxl/filter.c
e0018b
@@ -381,13 +381,16 @@ static struct json_object *pick_array(struct json_object *child,
e0018b
 }
e0018b
 
e0018b
 static void walk_endpoints(struct cxl_port *port, struct cxl_filter_params *p,
e0018b
-			   struct json_object *jeps, unsigned long flags)
e0018b
+			   struct json_object *jeps, struct json_object *jdevs,
e0018b
+			   unsigned long flags)
e0018b
 {
e0018b
 	struct cxl_endpoint *endpoint;
e0018b
 
e0018b
 	cxl_endpoint_foreach(port, endpoint) {
e0018b
 		struct cxl_port *ep_port = cxl_endpoint_get_port(endpoint);
e0018b
-		struct json_object *jendpoint;
e0018b
+		const char *devname = cxl_endpoint_get_devname(endpoint);
e0018b
+		struct json_object *jendpoint = NULL;
e0018b
+		struct cxl_memdev *memdev;
e0018b
 
e0018b
 		if (!util_cxl_endpoint_filter(endpoint, p->endpoint_filter))
e0018b
 			continue;
e0018b
@@ -398,24 +401,54 @@ static void walk_endpoints(struct cxl_port *port, struct cxl_filter_params *p,
e0018b
 			continue;
e0018b
 		if (!p->idle && !cxl_endpoint_is_enabled(endpoint))
e0018b
 			continue;
e0018b
-		jendpoint = util_cxl_endpoint_to_json(endpoint, flags);
e0018b
-		if (jendpoint)
e0018b
+		if (p->endpoints) {
e0018b
+			jendpoint = util_cxl_endpoint_to_json(endpoint, flags);
e0018b
+			if (!jendpoint) {
e0018b
+				err(p, "%s: failed to list\n", devname);
e0018b
+				continue;
e0018b
+			}
e0018b
 			json_object_array_add(jeps, jendpoint);
e0018b
+		}
e0018b
+		if (p->memdevs) {
e0018b
+			struct json_object *jobj;
e0018b
+
e0018b
+			memdev = cxl_endpoint_get_memdev(endpoint);
e0018b
+			if (!memdev)
e0018b
+				continue;
e0018b
+			if (!util_cxl_memdev_filter(memdev, p->memdev_filter,
e0018b
+						    p->serial_filter))
e0018b
+				continue;
e0018b
+			if (!p->idle && !cxl_memdev_is_enabled(memdev))
e0018b
+				continue;
e0018b
+			jobj = util_cxl_memdev_to_json(memdev, flags);
e0018b
+			if (!jobj) {
e0018b
+				err(p, "failed to json serialize %s\n",
e0018b
+				    cxl_memdev_get_devname(memdev));
e0018b
+				continue;
e0018b
+			}
e0018b
+			if (p->endpoints)
e0018b
+				json_object_object_add(jendpoint, "memdev",
e0018b
+						       jobj);
e0018b
+			else
e0018b
+				json_object_array_add(jdevs, jobj);
e0018b
+		}
e0018b
 	}
e0018b
 }
e0018b
 
e0018b
 static void walk_child_ports(struct cxl_port *parent_port,
e0018b
 			     struct cxl_filter_params *p,
e0018b
 			     struct json_object *jports,
e0018b
-			     struct json_object *jeps, unsigned long flags)
e0018b
+			     struct json_object *jeps,
e0018b
+			     struct json_object *jdevs, unsigned long flags)
e0018b
 {
e0018b
 	struct cxl_port *port;
e0018b
 
e0018b
 	cxl_port_foreach(parent_port, port) {
e0018b
 		const char *devname = cxl_port_get_devname(port);
e0018b
 		struct json_object *jport = NULL;
e0018b
+		struct json_object *jchilddevs = NULL;
e0018b
 		struct json_object *jchildports = NULL;
e0018b
-		struct json_object *jchildendpoints = NULL;
e0018b
+		struct json_object *jchildeps = NULL;
e0018b
 
e0018b
 		if (!util_cxl_port_filter(port, p->port_filter, pf_mode(p)))
e0018b
 			goto walk_children;
e0018b
@@ -436,28 +469,41 @@ static void walk_child_ports(struct cxl_port *parent_port,
e0018b
 				    devname);
e0018b
 				continue;
e0018b
 			}
e0018b
-		}
e0018b
 
e0018b
-		if (p->ports && p->endpoints) {
e0018b
-			jchildendpoints = json_object_new_array();
e0018b
-			if (!jchildendpoints) {
e0018b
-				err(p,
e0018b
-				    "%s: failed to enumerate child endpoints\n",
e0018b
-				    devname);
e0018b
-				continue;
e0018b
+			if (p->memdevs && !p->endpoints) {
e0018b
+				jchilddevs = json_object_new_array();
e0018b
+				if (!jchilddevs) {
e0018b
+					err(p,
e0018b
+					    "%s: failed to enumerate child memdevs\n",
e0018b
+					    devname);
e0018b
+					continue;
e0018b
+				}
e0018b
+			}
e0018b
+
e0018b
+			if (p->endpoints) {
e0018b
+				jchildeps = json_object_new_array();
e0018b
+				if (!jchildeps) {
e0018b
+					err(p,
e0018b
+					    "%s: failed to enumerate child endpoints\n",
e0018b
+					    devname);
e0018b
+					continue;
e0018b
+				}
e0018b
 			}
e0018b
 		}
e0018b
 
e0018b
 walk_children:
e0018b
-		if (p->endpoints)
e0018b
-			walk_endpoints(port, p, pick_array(jchildendpoints, jeps),
e0018b
-				       flags);
e0018b
+		if (p->endpoints || p->memdevs)
e0018b
+			walk_endpoints(port, p, pick_array(jchildeps, jeps),
e0018b
+				       pick_array(jchilddevs, jdevs), flags);
e0018b
 
e0018b
 		walk_child_ports(port, p, pick_array(jchildports, jports),
e0018b
-				 pick_array(jchildendpoints, jeps), flags);
e0018b
+				 pick_array(jchildeps, jeps),
e0018b
+				 pick_array(jchilddevs, jdevs), flags);
e0018b
 		cond_add_put_array_suffix(jport, "ports", devname, jchildports);
e0018b
 		cond_add_put_array_suffix(jport, "endpoints", devname,
e0018b
-					  jchildendpoints);
e0018b
+					  jchildeps);
e0018b
+		cond_add_put_array_suffix(jport, "memdevs", devname,
e0018b
+					  jchilddevs);
e0018b
 	}
e0018b
 }
e0018b
 
e0018b
@@ -466,6 +512,7 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
e0018b
 	struct json_object *jdevs = NULL, *jbuses = NULL, *jports = NULL;
e0018b
 	struct json_object *jplatform = json_object_new_array();
e0018b
 	unsigned long flags = params_to_flags(p);
e0018b
+	struct json_object *janondevs = NULL;
e0018b
 	struct json_object *jeps = NULL;
e0018b
 	struct cxl_memdev *memdev;
e0018b
 	int top_level_objs = 0;
e0018b
@@ -476,8 +523,8 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
e0018b
 		return -ENOMEM;
e0018b
 	}
e0018b
 
e0018b
-	jdevs = json_object_new_array();
e0018b
-	if (!jdevs)
e0018b
+	janondevs = json_object_new_array();
e0018b
+	if (!janondevs)
e0018b
 		goto err;
e0018b
 
e0018b
 	jbuses = json_object_new_array();
e0018b
@@ -492,20 +539,28 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
e0018b
 	if (!jeps)
e0018b
 		goto err;
e0018b
 
e0018b
+	jdevs = json_object_new_array();
e0018b
+	if (!jdevs)
e0018b
+		goto err;
e0018b
+
e0018b
 	dbg(p, "walk memdevs\n");
e0018b
 	cxl_memdev_foreach(ctx, memdev) {
e0018b
-		struct json_object *jdev;
e0018b
+		struct json_object *janondev;
e0018b
 
e0018b
 		if (!util_cxl_memdev_filter(memdev, p->memdev_filter,
e0018b
 					    p->serial_filter))
e0018b
 			continue;
e0018b
+		if (cxl_memdev_is_enabled(memdev))
e0018b
+			continue;
e0018b
+		if (!p->idle)
e0018b
+			continue;
e0018b
 		if (p->memdevs) {
e0018b
-			jdev = util_cxl_memdev_to_json(memdev, flags);
e0018b
-			if (!jdev) {
e0018b
+			janondev = util_cxl_memdev_to_json(memdev, flags);
e0018b
+			if (!janondev) {
e0018b
 				dbg(p, "memdev object allocation failure\n");
e0018b
 				continue;
e0018b
 			}
e0018b
-			json_object_array_add(jdevs, jdev);
e0018b
+			json_object_array_add(janondevs, janondev);
e0018b
 		}
e0018b
 	}
e0018b
 
e0018b
@@ -513,6 +568,7 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
e0018b
 	cxl_bus_foreach(ctx, bus) {
e0018b
 		struct json_object *jbus = NULL;
e0018b
 		struct json_object *jchildports = NULL;
e0018b
+		struct json_object *jchilddevs = NULL;
e0018b
 		struct json_object *jchildeps = NULL;
e0018b
 		struct cxl_port *port = cxl_bus_get_port(bus);
e0018b
 		const char *devname = cxl_bus_get_devname(bus);
e0018b
@@ -546,17 +602,29 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p)
e0018b
 					continue;
e0018b
 				}
e0018b
 			}
e0018b
+
e0018b
+			if (p->memdevs && !p->ports && !p->endpoints) {
e0018b
+				jchilddevs = json_object_new_array();
e0018b
+				if (!jchilddevs) {
e0018b
+					err(p,
e0018b
+					    "%s: failed to enumerate child memdevs\n",
e0018b
+					    devname);
e0018b
+					continue;
e0018b
+				}
e0018b
+			}
e0018b
 		}
e0018b
 walk_children:
e0018b
 		dbg(p, "walk ports\n");
e0018b
 		walk_child_ports(port, p, pick_array(jchildports, jports),
e0018b
-				 pick_array(jchildeps, jeps), flags);
e0018b
+				 pick_array(jchildeps, jeps),
e0018b
+				 pick_array(jchilddevs, jdevs), flags);
e0018b
 		cond_add_put_array_suffix(jbus, "ports", devname, jchildports);
e0018b
 		cond_add_put_array_suffix(jbus, "endpoints", devname,
e0018b
 					  jchildeps);
e0018b
+		cond_add_put_array_suffix(jbus, "memdevs", devname, jchilddevs);
e0018b
 	}
e0018b
 
e0018b
-	if (json_object_array_length(jdevs))
e0018b
+	if (json_object_array_length(janondevs))
e0018b
 		top_level_objs++;
e0018b
 	if (json_object_array_length(jbuses))
e0018b
 		top_level_objs++;
e0018b
@@ -564,20 +632,24 @@ walk_children:
e0018b
 		top_level_objs++;
e0018b
 	if (json_object_array_length(jeps))
e0018b
 		top_level_objs++;
e0018b
+	if (json_object_array_length(jdevs))
e0018b
+		top_level_objs++;
e0018b
 
e0018b
-	splice_array(p, jdevs, jplatform, "anon memdevs", top_level_objs > 1);
e0018b
+	splice_array(p, janondevs, jplatform, "anon memdevs", top_level_objs > 1);
e0018b
 	splice_array(p, jbuses, jplatform, "buses", top_level_objs > 1);
e0018b
 	splice_array(p, jports, jplatform, "ports", top_level_objs > 1);
e0018b
 	splice_array(p, jeps, jplatform, "endpoints", top_level_objs > 1);
e0018b
+	splice_array(p, jdevs, jplatform, "memdevs", 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(janondevs);
e0018b
 	json_object_put(jbuses);
e0018b
 	json_object_put(jports);
e0018b
 	json_object_put(jeps);
e0018b
+	json_object_put(jdevs);
e0018b
 	json_object_put(jplatform);
e0018b
 	return -ENOMEM;
e0018b
 }
e0018b
diff --git a/cxl/json.c b/cxl/json.c
e0018b
index 1868686..b809332 100644
e0018b
--- a/cxl/json.c
e0018b
+++ b/cxl/json.c
e0018b
@@ -224,6 +224,12 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev,
e0018b
 	if (jobj)
e0018b
 		json_object_object_add(jdev, "host", jobj);
e0018b
 
e0018b
+	if (!cxl_memdev_is_enabled(memdev)) {
e0018b
+		jobj = json_object_new_string("disabled");
e0018b
+		if (jobj)
e0018b
+			json_object_object_add(jdev, "state", jobj);
e0018b
+	}
e0018b
+
e0018b
 	return jdev;
e0018b
 }
e0018b
 
e0018b
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
e0018b
index c4ddc7d..4523ca6 100644
e0018b
--- a/cxl/lib/libcxl.c
e0018b
+++ b/cxl/lib/libcxl.c
e0018b
@@ -480,6 +480,60 @@ CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev
e0018b
 	return memdev->firmware_version;
e0018b
 }
e0018b
 
e0018b
+static struct cxl_endpoint *cxl_port_find_endpoint(struct cxl_port *parent_port,
e0018b
+						   struct cxl_memdev *memdev)
e0018b
+{
e0018b
+	struct cxl_endpoint *endpoint;
e0018b
+	struct cxl_port *port;
e0018b
+
e0018b
+	cxl_port_foreach(parent_port, port) {
e0018b
+		cxl_endpoint_foreach(port, endpoint)
e0018b
+			if (strcmp(cxl_endpoint_get_host(endpoint),
e0018b
+				   cxl_memdev_get_devname(memdev)) == 0)
e0018b
+				return endpoint;
e0018b
+		endpoint = cxl_port_find_endpoint(port, memdev);
e0018b
+		if (endpoint)
e0018b
+			return endpoint;
e0018b
+	}
e0018b
+
e0018b
+	return NULL;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT struct cxl_endpoint *
e0018b
+cxl_memdev_get_endpoint(struct cxl_memdev *memdev)
e0018b
+{
e0018b
+	struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
e0018b
+	struct cxl_endpoint *endpoint = NULL;
e0018b
+	struct cxl_bus *bus;
e0018b
+
e0018b
+	if (memdev->endpoint)
e0018b
+		return memdev->endpoint;
e0018b
+
e0018b
+	if (!cxl_memdev_is_enabled(memdev))
e0018b
+		return NULL;
e0018b
+
e0018b
+	cxl_bus_foreach (ctx, bus) {
e0018b
+		struct cxl_port *port = cxl_bus_get_port(bus);
e0018b
+
e0018b
+		endpoint = cxl_port_find_endpoint(port, memdev);
e0018b
+		if (endpoint)
e0018b
+			break;
e0018b
+	}
e0018b
+
e0018b
+	if (!endpoint)
e0018b
+		return NULL;
e0018b
+
e0018b
+	if (endpoint->memdev && endpoint->memdev != memdev)
e0018b
+		err(ctx, "%s assigned to %s not %s\n",
e0018b
+		    cxl_endpoint_get_devname(endpoint),
e0018b
+		    cxl_memdev_get_devname(endpoint->memdev),
e0018b
+		    cxl_memdev_get_devname(memdev));
e0018b
+	memdev->endpoint = endpoint;
e0018b
+	endpoint->memdev = memdev;
e0018b
+
e0018b
+	return endpoint;
e0018b
+}
e0018b
+
e0018b
 CXL_EXPORT size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev)
e0018b
 {
e0018b
 	return memdev->lsa_size;
e0018b
@@ -495,6 +549,21 @@ static int is_enabled(const char *drvpath)
e0018b
 		return 1;
e0018b
 }
e0018b
 
e0018b
+CXL_EXPORT int cxl_memdev_is_enabled(struct cxl_memdev *memdev)
e0018b
+{
e0018b
+	struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
e0018b
+	char *path = memdev->dev_buf;
e0018b
+	int len = memdev->buf_len;
e0018b
+
e0018b
+	if (snprintf(path, len, "%s/driver", memdev->dev_path) >= len) {
e0018b
+		err(ctx, "%s: buffer too small!\n",
e0018b
+		    cxl_memdev_get_devname(memdev));
e0018b
+		return 0;
e0018b
+	}
e0018b
+
e0018b
+	return is_enabled(path);
e0018b
+}
e0018b
+
e0018b
 CXL_EXPORT int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev)
e0018b
 {
e0018b
 	struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
e0018b
@@ -660,6 +729,34 @@ CXL_EXPORT int cxl_endpoint_is_enabled(struct cxl_endpoint *endpoint)
e0018b
 	return cxl_port_is_enabled(&endpoint->port);
e0018b
 }
e0018b
 
e0018b
+CXL_EXPORT struct cxl_memdev *
e0018b
+cxl_endpoint_get_memdev(struct cxl_endpoint *endpoint)
e0018b
+{
e0018b
+	struct cxl_ctx *ctx = cxl_endpoint_get_ctx(endpoint);
e0018b
+	struct cxl_memdev *memdev;
e0018b
+
e0018b
+	if (endpoint->memdev)
e0018b
+		return endpoint->memdev;
e0018b
+
e0018b
+	if (!cxl_endpoint_is_enabled(endpoint))
e0018b
+		return NULL;
e0018b
+
e0018b
+	cxl_memdev_foreach(ctx, memdev)
e0018b
+		if (strcmp(cxl_memdev_get_devname(memdev),
e0018b
+			   cxl_endpoint_get_host(endpoint)) == 0) {
e0018b
+			if (memdev->endpoint && memdev->endpoint != endpoint)
e0018b
+				err(ctx, "%s assigned to %s not %s\n",
e0018b
+				    cxl_memdev_get_devname(memdev),
e0018b
+				    cxl_endpoint_get_devname(memdev->endpoint),
e0018b
+				    cxl_endpoint_get_devname(endpoint));
e0018b
+			endpoint->memdev = memdev;
e0018b
+			memdev->endpoint = endpoint;
e0018b
+			return memdev;
e0018b
+		}
e0018b
+
e0018b
+	return NULL;
e0018b
+}
e0018b
+
e0018b
 static void *add_cxl_port(void *parent, int id, const char *cxlport_base)
e0018b
 {
e0018b
 	const char *devname = devpath_to_devname(cxlport_base);
e0018b
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
e0018b
index 8f0688a..321acac 100644
e0018b
--- a/cxl/lib/libcxl.sym
e0018b
+++ b/cxl/lib/libcxl.sym
e0018b
@@ -106,4 +106,7 @@ global:
e0018b
 	cxl_endpoint_get_parent;
e0018b
 	cxl_endpoint_get_port;
e0018b
 	cxl_endpoint_get_host;
e0018b
+	cxl_endpoint_get_memdev;
e0018b
+	cxl_memdev_get_endpoint;
e0018b
+	cxl_memdev_is_enabled;
e0018b
 } LIBCXL_1;
e0018b
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
e0018b
index 5487b55..790ece8 100644
e0018b
--- a/cxl/libcxl.h
e0018b
+++ b/cxl/libcxl.h
e0018b
@@ -46,6 +46,8 @@ unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev);
e0018b
 unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev);
e0018b
 const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev);
e0018b
 size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev);
e0018b
+struct cxl_endpoint;
e0018b
+struct cxl_endpoint *cxl_memdev_get_endpoint(struct cxl_memdev *memdev);
e0018b
 int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev);
e0018b
 int cxl_memdev_zero_label(struct cxl_memdev *memdev, size_t length,
e0018b
 		size_t offset);
e0018b
@@ -100,6 +102,8 @@ int cxl_endpoint_is_enabled(struct cxl_endpoint *endpoint);
e0018b
 struct cxl_port *cxl_endpoint_get_parent(struct cxl_endpoint *endpoint);
e0018b
 struct cxl_port *cxl_endpoint_get_port(struct cxl_endpoint *endpoint);
e0018b
 const char *cxl_endpoint_get_host(struct cxl_endpoint *endpoint);
e0018b
+struct cxl_memdev *cxl_endpoint_get_memdev(struct cxl_endpoint *endpoint);
e0018b
+int cxl_memdev_is_enabled(struct cxl_memdev *memdev);
e0018b
 
e0018b
 #define cxl_endpoint_foreach(port, endpoint)                                   \
e0018b
 	for (endpoint = cxl_endpoint_get_first(port); endpoint != NULL;        \
e0018b
-- 
e0018b
2.27.0
e0018b