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

Reply via email to