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

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