[RFC PATCH 10/12] drm/fabric: make the synthetic provider writable

Konstantin Sinyuk <[email protected]>
Newsgroups org.kernel.vger.linux-doc,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-kselftest,org.kernel.vger.netdev
Message-ID <605d72ca904f63a854869c9f88426b767e61554c.1787552412.git.ksinyuk@kernel.org>
Add provisioning callbacks to fabricsim for endpoint attachment,
administrative-state changes and peer updates.

Reserve each endpoint's last port for userspace-managed peer provisioning;
the remaining ports stay provider-managed, so one topology covers both
peer authorities.

Add per-callback fault injection for error propagation and failure
atomicity. The new debugfs controls remain test-only.

Signed-off-by: Konstantin Sinyuk <[email protected]>
Assisted-by: GitHub-Copilot:claude-opus-4.8
---
 Documentation/gpu/drm-fabric.rst        |   4 +
 drivers/gpu/drm/fabric/drm_fabric_sim.c | 138 +++++++++++++++++-------
 2 files changed, 106 insertions(+), 36 deletions(-)

diff --git a/Documentation/gpu/drm-fabric.rst b/Documentation/gpu/drm-fabric.rst
index 4ee7cb7ef6ac..1fd48027eee3 100644
--- a/Documentation/gpu/drm-fabric.rst
+++ b/Documentation/gpu/drm-fabric.rst
@@ -454,6 +454,10 @@ ports. The switch shape links every endpoint to an opaque switch peer
 (``peer-type = switch``) whose id does not resolve to an endpoint, exercising the
 directed half-edge model without a first-class switch object.
 
+It implements the provisioning callbacks, so it is also the reference provider
+for the mutation commands. It reserves each endpoint's last port for userspace
+peer management and wires topology on the ports below it.
+
 Its debugfs knobs stimulate synthetic counter activity, operational-state changes
 and runtime endpoint add/remove. These files are unstable test controls and are
 not part of the uAPI; the stable, reviewed interface is the YAML-described
diff --git a/drivers/gpu/drm/fabric/drm_fabric_sim.c b/drivers/gpu/drm/fabric/drm_fabric_sim.c
index 7d489c6894bf..e441943c02c3 100644
--- a/drivers/gpu/drm/fabric/drm_fabric_sim.c
+++ b/drivers/gpu/drm/fabric/drm_fabric_sim.c
@@ -90,6 +90,7 @@ static struct dentry *fabricsim_debugfs_root;
 
 /* Test-only fault injection (debugfs). Sticky until cleared. */
 static bool fabricsim_fail_register;
+static bool fabricsim_fail_mutation;
 static u32 fabricsim_fail_errno = ENOMEM;
 
 /*
@@ -142,8 +143,45 @@ static int fabricsim_port_stats_get(struct drm_fabric_port *port,
 	return 0;
 }
 
+/* The mutation hooks only fail on request; the core owns the model. */
+static int fabricsim_endpoint_set(struct drm_fabric_endpoint *ep,
+				  const struct drm_fabric_endpoint_change *change,
+				  struct drm_fabric *fabric)
+{
+	if (fabricsim_fail_mutation)
+		return fabricsim_injected_errno();
+	return 0;
+}
+
+static int fabricsim_port_set(struct drm_fabric_port *port,
+			      enum drm_fabric_admin_state admin)
+{
+	if (fabricsim_fail_mutation)
+		return fabricsim_injected_errno();
+	return 0;
+}
+
+static int fabricsim_port_peer_new(struct drm_fabric_port *port,
+				   const struct drm_fabric_peer *peer)
+{
+	if (fabricsim_fail_mutation)
+		return fabricsim_injected_errno();
+	return 0;
+}
+
+static int fabricsim_port_peer_del(struct drm_fabric_port *port)
+{
+	if (fabricsim_fail_mutation)
+		return fabricsim_injected_errno();
+	return 0;
+}
+
 static const struct drm_fabric_ops fabricsim_ops = {
 	.port_stats_get		= fabricsim_port_stats_get,
+	.endpoint_set		= fabricsim_endpoint_set,
+	.port_set		= fabricsim_port_set,
+	.port_peer_new		= fabricsim_port_peer_new,
+	.port_peer_del		= fabricsim_port_peer_del,
 };
 
 #define FABRICSIM_TICK_MS 100
@@ -343,12 +381,9 @@ static void fabricsim_link_linear(void)
 		struct drm_fabric_endpoint *ep_b = fabricsim_slots[i + 1]->ep;
 		struct drm_fabric_port *pa, *pb;
 
-		/*
-		 * Interior nodes consume two ports; stop rather than walk off
-		 * an endpoint's port array if it was sized too small.
-		 */
-		if (pa_idx >= fabricsim_slots[i]->num_ports ||
-		    pb_idx >= fabricsim_slots[i + 1]->num_ports)
+		/* -1 skips the reserved last port. */
+		if (pa_idx >= fabricsim_slots[i]->num_ports - 1 ||
+		    pb_idx >= fabricsim_slots[i + 1]->num_ports - 1)
 			break;
 
 		pa = fabricsim_slots[i]->ports[pa_idx].port;
@@ -379,7 +414,8 @@ static void fabricsim_link_mesh(void)
 			if (i == j)
 				continue;
 
-			if (port_idx >= fabricsim_slots[i]->num_ports)
+			/* -1 skips the reserved last port. */
+			if (port_idx >= fabricsim_slots[i]->num_ports - 1)
 				break;
 
 			/*
@@ -421,7 +457,8 @@ static void fabricsim_link_switch(void)
 		struct drm_fabric_port *leaf_port =
 			fabricsim_slots[i]->ports[0].port;
 
-		if (!leaf_port)
+		/* Port 0 is the uplink; skip an endpoint with only the reserved port. */
+		if (!leaf_port || fabricsim_slots[i]->num_ports < 2)
 			continue;
 
 		/* One directed half-edge from the leaf to an opaque switch. */
@@ -467,11 +504,12 @@ static void fabricsim_ep_debugfs_create(struct fabricsim_ep_priv *ep_priv)
 }
 
 /*
- * Create one endpoint at @slot with @nports ports, registered as a member of
- * the synthetic fabric.  Returns the new ep_priv or an ERR_PTR.  Caller holds
+ * Create an endpoint at @slot with @nports ports. @orphan registers it
+ * without a fabric for a later ENDPOINT_SET attach. Caller holds
  * fabricsim_lock.
  */
-static struct fabricsim_ep_priv *fabricsim_make_ep(int slot, int nports)
+static struct fabricsim_ep_priv *fabricsim_make_ep(int slot, int nports,
+						   bool orphan)
 {
 	struct drm_fabric_endpoint_desc edesc = {};
 	struct drm_fabric_port_desc pdescs[16];
@@ -509,6 +547,13 @@ static struct fabricsim_ep_priv *fabricsim_make_ep(int slot, int nports)
 		pdescs[j].index = j;
 		pdescs[j].max_lane_count = 4;
 		pdescs[j].max_lane_signaling_rate_mbps = 200000; /* 200 Gbps/lane */
+		/*
+		 * Reserve the last port for userspace peer tests; a single-port
+		 * endpoint therefore has no provider-managed port.
+		 */
+		pdescs[j].peer_mode = (j == nports - 1) ?
+			DRM_FABRIC_PEER_MODE_USERSPACE :
+			DRM_FABRIC_PEER_MODE_PROVIDER;
 	}
 
 	snprintf(ep_name, sizeof(ep_name), "sim-ep%d", slot);
@@ -540,7 +585,8 @@ static struct fabricsim_ep_priv *fabricsim_make_ep(int slot, int nports)
 		timer_setup(&pp->activity_timer, fabricsim_activity_tick, 0);
 	}
 
-	ep_priv->ep = drm_fabric_endpoint_register(fabricsim_fabric, &edesc);
+	ep_priv->ep = drm_fabric_endpoint_register(orphan ? NULL : fabricsim_fabric,
+						   &edesc);
 	if (IS_ERR(ep_priv->ep)) {
 		ret = PTR_ERR(ep_priv->ep);
 		goto err_ports;
@@ -597,7 +643,7 @@ static void fabricsim_destroy_ep(struct fabricsim_ep_priv *ep_priv)
 	kfree(ep_priv);
 }
 
-static int fabricsim_add_endpoint(int nports)
+static int fabricsim_add_endpoint(int nports, bool orphan)
 {
 	struct fabricsim_ep_priv *ep_priv;
 	int slot, ret;
@@ -616,7 +662,7 @@ static int fabricsim_add_endpoint(int nports)
 		return -ENOSPC;
 	}
 
-	ep_priv = fabricsim_make_ep(slot, nports);
+	ep_priv = fabricsim_make_ep(slot, nports, orphan);
 	if (IS_ERR(ep_priv)) {
 		ret = PTR_ERR(ep_priv);
 		mutex_unlock(&fabricsim_lock);
@@ -667,7 +713,7 @@ static int fabricsim_bulk_add(int n)
 		return -EINVAL;
 
 	while (added < n) {
-		ret = fabricsim_add_endpoint(1);
+		ret = fabricsim_add_endpoint(1, false);
 		if (ret < 0)
 			return added ? added : ret;
 		added++;
@@ -713,15 +759,28 @@ static int fabricsim_parse_int(const char __user *buf, size_t count, int dflt)
 	return val;
 }
 
-static ssize_t fabricsim_add_ep_write(struct file *file, const char __user *buf,
-				      size_t count, loff_t *ppos)
+static ssize_t fabricsim_add_ep_common(const char __user *buf, size_t count,
+				       bool orphan)
 {
 	int nports = fabricsim_parse_int(buf, count, ports_per_ep);
-	int ret = fabricsim_add_endpoint(nports);
+	int ret = fabricsim_add_endpoint(nports, orphan);
 
 	return ret < 0 ? ret : count;
 }
 
+static ssize_t fabricsim_add_ep_write(struct file *file, const char __user *buf,
+				      size_t count, loff_t *ppos)
+{
+	return fabricsim_add_ep_common(buf, count, false);
+}
+
+static ssize_t fabricsim_add_orphan_write(struct file *file,
+					  const char __user *buf,
+					  size_t count, loff_t *ppos)
+{
+	return fabricsim_add_ep_common(buf, count, true);
+}
+
 static ssize_t fabricsim_del_ep_write(struct file *file, const char __user *buf,
 				      size_t count, loff_t *ppos)
 {
@@ -736,6 +795,11 @@ static const struct file_operations fabricsim_add_ep_fops = {
 	.write	= fabricsim_add_ep_write,
 };
 
+static const struct file_operations fabricsim_add_orphan_fops = {
+	.owner	= THIS_MODULE,
+	.write	= fabricsim_add_orphan_write,
+};
+
 static const struct file_operations fabricsim_del_ep_fops = {
 	.owner	= THIS_MODULE,
 	.write	= fabricsim_del_ep_write,
@@ -810,6 +874,8 @@ static const struct file_operations fabricsim_fail_errno_fops = {
  */
 static int __init fabricsim_setup_params(void)
 {
+	int wired;
+
 	/*
 	 * Reject an unrecognised topology rather than falling back to mesh, so
 	 * a typo cannot fake a shape.
@@ -830,22 +896,17 @@ static int __init fabricsim_setup_params(void)
 	if (ports_per_ep > 16)
 		ports_per_ep = 16;
 
-	/*
-	 * A mesh gives every endpoint (N-1) peers, so the busiest endpoint needs
-	 * at least (N-1) ports. The switch shape only needs one port per leaf
-	 * (a single half-edge to the opaque switch), so it is not bumped here.
-	 */
-	if (strcmp(topology, "mesh") == 0 && ports_per_ep < num_endpoints - 1)
-		ports_per_ep = num_endpoints - 1;
+	/* Peers wired per endpoint: mesh N-1, linear interior 2, switch 1. */
+	if (strcmp(topology, "mesh") == 0)
+		wired = num_endpoints - 1;
+	else if (strcmp(topology, "linear") == 0 && num_endpoints > 2)
+		wired = 2;
+	else
+		wired = 1;
 
-	/*
-	 * A linear chain gives every interior node two neighbours, so it needs
-	 * at least two ports; bump a too-small request rather than index past
-	 * the endpoint's port array.
-	 */
-	if (strcmp(topology, "linear") == 0 && num_endpoints > 2 &&
-	    ports_per_ep < 2)
-		ports_per_ep = 2;
+	/* make_ep() reserves the last port, so @wired alone drops an edge. */
+	if (ports_per_ep < wired + 1)
+		ports_per_ep = wired + 1;
 
 	fabricsim_init_eps = num_endpoints;
 
@@ -877,7 +938,7 @@ static int __init fabricsim_init(void)
 	mutex_lock(&fabricsim_lock);
 	for (i = 0; i < fabricsim_init_eps; i++) {
 		struct fabricsim_ep_priv *ep_priv =
-			fabricsim_make_ep(i, ports_per_ep);
+			fabricsim_make_ep(i, ports_per_ep, false);
 
 		if (IS_ERR(ep_priv)) {
 			ret = PTR_ERR(ep_priv);
@@ -910,6 +971,8 @@ static int __init fabricsim_init(void)
 	if (fabricsim_debugfs_root) {
 		debugfs_create_file("add_endpoint", 0200, fabricsim_debugfs_root,
 				    NULL, &fabricsim_add_ep_fops);
+		debugfs_create_file("add_orphan", 0200, fabricsim_debugfs_root,
+				    NULL, &fabricsim_add_orphan_fops);
 		debugfs_create_file("del_endpoint", 0200, fabricsim_debugfs_root,
 				    NULL, &fabricsim_del_ep_fops);
 
@@ -921,9 +984,12 @@ static int __init fabricsim_init(void)
 		debugfs_create_bool("fail_register", 0644,
 				    fabricsim_debugfs_root,
 				    &fabricsim_fail_register);
-		debugfs_create_file("fail_errno", 0644,
+		debugfs_create_bool("fail_mutation", 0644,
 				    fabricsim_debugfs_root,
-				    NULL, &fabricsim_fail_errno_fops);
+				    &fabricsim_fail_mutation);
+		debugfs_create_file("fail_errno", 0644,
+				    fabricsim_debugfs_root, NULL,
+				    &fabricsim_fail_errno_fops);
 	}
 
 	pr_info("fabricsim: registered %s topology with %d endpoints, %d ports/ep\n",
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.