You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Support for Recover method on bridged endpoints #149
Currently, a Recover invocation will attempt a Get Endpoint ID (to check endpoint connectivity), but will do so using a physical addressing:
/* * Test if we still have connectivity to the endpoint. If we do, we will get a * response reporting the current EID. This is the test recommended by 8.17.6 * of DSP0236 v1.3.1. */rc=query_get_endpoint_id(ctx, &peer->phys, &peer->recovery.eid,
&peer->recovery.endpoint_type,
&peer->recovery.medium_spec, /*peer=*/NULL);
The NULL peer causes the physical addressing there.
Since we cannot target bridged endpoints with a phys address, we will need to use vanilla EID addressing for those.
I'd suggest working out a plan here before writing the implementation though, there are some design points for the recovery flow that we would need to sort out too. Mainly:
we'll probably want to check connectivity to the bridge too
we'll need to handle cases where the EID has been reassigned by the downstream bus owner (ie, has a new UUID), by generating a remove & re-add.
method_endpoint_recover (D-Bus entry point) — unchanged for both peer types
flowchart TD
A([method_endpoint_recover called]) --> B{peer->degraded?}
B -- yes --> Z([no-op, return OK])
B -- no --> C["set recovery.npolls, recovery.delay\nschedule peer_endpoint_recover timer\npeer->degraded = true\nemit Connectivity changed"]
C --> D([return — common for direct and downstream peers])
Loading
peer_endpoint_recover (timer callback) — downstream branch inserted after physical probe
flowchart TD
TIMER([timer fires: peer_endpoint_recover]) --> DEC[Decrement recovery.npolls]
DEC --> PHYS["[EXISTING — unchanged]\nPhysical probe\nquery_get_endpoint_id\n dest=peer->phys, peer=NULL\n → for direct: tests the device\n → for downstream: tests the bridge\nstores result in recovery.eid"]
PHYS --> PHYSOK{responded?}
PHYSOK -- no --> RESCHED
PHYSOK -- yes --> BRIDGE["[NEW]\nfind_bridge_for_peer:\nscan peers for p where\n p.pool_size > 0\n && match_phys(p.phys, peer.phys)\n && p.pool_start <= peer.eid\n < p.pool_start + p.pool_size"]
BRIDGE --> ISBRIDGE{downstream\npeer?}
%% ── DOWNSTREAM BRANCH (new) ──────────────────────────────
ISBRIDGE -- "yes\n(bridge found)" --> EIDRP["[NEW — downstream only]\nEID-routed probe\nquery_get_endpoint_id\n dest=peer->phys, peer=peer\n → routed through bridge to device\noverwrites recovery.eid with\ndownstream device's EID"]
EIDRP --> EIDOK{responded?}
EIDOK -- no --> RESCHED
EIDOK -- yes --> UUIDQ["[NEW — downstream only]\nUUID via EID routing\nquery_get_peer_uuid_by_eid\n → gets downstream device UUID\n not the bridge UUID"]
UUIDQ --> UUIDOK{UUID query\nsucceeded?}
UUIDOK -- no --> RECLAIM
UUIDOK -- yes --> UUIDMATCH{UUID matches\npeer->uuid?}
UUIDMATCH -- "yes — same device" --> EIDMATCH{recovery.eid\n== peer->eid?}
EIDMATCH -- yes --> SUCCESS
EIDMATCH -- "no — EID drifted\nbridge is bus owner,\ncannot send Set Endpoint ID" --> RECLAIM
UUIDMATCH -- "no or nil UUID\nnew device at this EID" --> READD["[NEW — downstream only]\nBridge re-assigned EID to new device\n\nsave phys / eid / net\nremove_peer old → emits EndpointRemoved\nadd_peer eid, net, allow_bridged=true\nsetup_added_peer new → emits EndpointAdded"]
READD --> DONE([return])
%% ── DIRECT ENDPOINT BRANCH (existing, unchanged) ─────────
ISBRIDGE -- "no\n(direct endpoint)" --> DIRECT["[EXISTING — unchanged]\nif recovery.eid != peer->eid:\n query_get_peer_uuid_by_phys\n UUID mismatch → remove_peer\n + endpoint_assign_eid\n UUID match → endpoint_send_set_endpoint_id\n + change_peer_eid"]
DIRECT --> SUCCESS
%% ── SHARED SUCCESS PATH (existing, unchanged) ────────────
SUCCESS(["[EXISTING — unchanged]\npeer->degraded = false\nemit Connectivity changed\nunref recovery.source\nzero recovery.delay / npolls"])
%% ── SHARED RESCHEDULE / RECLAIM (existing, unchanged) ────
RESCHED{recovery.npolls > 0?}
RESCHED -- yes --> RETRY[reschedule timer\nat recovery.delay]
RESCHED -- no --> RECLAIM
RECLAIM([reclaim: remove_peer])
Activity
faizana-nvidia commentedon Jul 27, 2026
Hello @jk-ozlabs
I too was looking for this, if you still haven't picked it up as your current working item, I'll be happy to push a change.
jk-ozlabs commentedon Jul 28, 2026
@faizana-nvidia sure, if you want to look at this!
I'd suggest working out a plan here before writing the implementation though, there are some design points for the recovery flow that we would need to sort out too. Mainly:
faizana-nvidia commentedon Jul 28, 2026
great! let me update here with the flow
faizana-nvidia commentedon Aug 13, 2026
Downstream Endpoint Recovery Flow
method_endpoint_recover (D-Bus entry point) — unchanged for both peer types
flowchart TD A([method_endpoint_recover called]) --> B{peer->degraded?} B -- yes --> Z([no-op, return OK]) B -- no --> C["set recovery.npolls, recovery.delay\nschedule peer_endpoint_recover timer\npeer->degraded = true\nemit Connectivity changed"] C --> D([return — common for direct and downstream peers])peer_endpoint_recover (timer callback) — downstream branch inserted after physical probe
flowchart TD TIMER([timer fires: peer_endpoint_recover]) --> DEC[Decrement recovery.npolls] DEC --> PHYS["[EXISTING — unchanged]\nPhysical probe\nquery_get_endpoint_id\n dest=peer->phys, peer=NULL\n → for direct: tests the device\n → for downstream: tests the bridge\nstores result in recovery.eid"] PHYS --> PHYSOK{responded?} PHYSOK -- no --> RESCHED PHYSOK -- yes --> BRIDGE["[NEW]\nfind_bridge_for_peer:\nscan peers for p where\n p.pool_size > 0\n && match_phys(p.phys, peer.phys)\n && p.pool_start <= peer.eid\n < p.pool_start + p.pool_size"] BRIDGE --> ISBRIDGE{downstream\npeer?} %% ── DOWNSTREAM BRANCH (new) ────────────────────────────── ISBRIDGE -- "yes\n(bridge found)" --> EIDRP["[NEW — downstream only]\nEID-routed probe\nquery_get_endpoint_id\n dest=peer->phys, peer=peer\n → routed through bridge to device\noverwrites recovery.eid with\ndownstream device's EID"] EIDRP --> EIDOK{responded?} EIDOK -- no --> RESCHED EIDOK -- yes --> UUIDQ["[NEW — downstream only]\nUUID via EID routing\nquery_get_peer_uuid_by_eid\n → gets downstream device UUID\n not the bridge UUID"] UUIDQ --> UUIDOK{UUID query\nsucceeded?} UUIDOK -- no --> RECLAIM UUIDOK -- yes --> UUIDMATCH{UUID matches\npeer->uuid?} UUIDMATCH -- "yes — same device" --> EIDMATCH{recovery.eid\n== peer->eid?} EIDMATCH -- yes --> SUCCESS EIDMATCH -- "no — EID drifted\nbridge is bus owner,\ncannot send Set Endpoint ID" --> RECLAIM UUIDMATCH -- "no or nil UUID\nnew device at this EID" --> READD["[NEW — downstream only]\nBridge re-assigned EID to new device\n\nsave phys / eid / net\nremove_peer old → emits EndpointRemoved\nadd_peer eid, net, allow_bridged=true\nsetup_added_peer new → emits EndpointAdded"] READD --> DONE([return]) %% ── DIRECT ENDPOINT BRANCH (existing, unchanged) ───────── ISBRIDGE -- "no\n(direct endpoint)" --> DIRECT["[EXISTING — unchanged]\nif recovery.eid != peer->eid:\n query_get_peer_uuid_by_phys\n UUID mismatch → remove_peer\n + endpoint_assign_eid\n UUID match → endpoint_send_set_endpoint_id\n + change_peer_eid"] DIRECT --> SUCCESS %% ── SHARED SUCCESS PATH (existing, unchanged) ──────────── SUCCESS(["[EXISTING — unchanged]\npeer->degraded = false\nemit Connectivity changed\nunref recovery.source\nzero recovery.delay / npolls"]) %% ── SHARED RESCHEDULE / RECLAIM (existing, unchanged) ──── RESCHED{recovery.npolls > 0?} RESCHED -- yes --> RETRY[reschedule timer\nat recovery.delay] RESCHED -- no --> RECLAIM RECLAIM([reclaim: remove_peer])faizana-nvidia commentedon Aug 13, 2026
Updating the flow here, will create PR for the same if this flow looks okay.
amboar commentedon Oct 1, 2026
I find that mermaid diagram tricky to comprehend. A text description may help.
faizana-nvidia commentedon Oct 1, 2026
yeah its outdated now, flow has changed a bit based on review discussion on #175