[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