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

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