Skip to content

[ecmp] Deduplicate redundant multicast next hops via per-target ECMP selection#1012

Open
zeeshanlakhani wants to merge 3 commits into
masterfrom
zl/mcast-tx-ecmp-select-one
Open

[ecmp] Deduplicate redundant multicast next hops via per-target ECMP selection#1012
zeeshanlakhani wants to merge 3 commits into
masterfrom
zl/mcast-tx-ecmp-select-one

Conversation

@zeeshanlakhani

@zeeshanlakhani zeeshanlakhani commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

The multicast forwarding loop emitted a copy to every programmed next hop. A next hop is a switch endpoint that fans out the packet to every sled (the underlay) or out the front panel (the external) behind its port groups. Redundant next hops sharing a replication target therefore reach the same destinations and deliver the flow twice, and a receiver cannot tell the duplicates apart, so we handle deduplication at Tx.

This PR adds a select_nexthops function, which resolves one next hop per replication target in a single scan over the forwarding table, partitioning eligible hops by replication mode and indexing each partition with the flow's L4 hash. Selection prefers a Both replication hop that satisfies the pair with one packet, splitting the flow into per-target copies only when no single hop covers both. The emit loop builds its emissions (at most two) directly from that selection, so the emitted replication reflects which targets chose the hop. All next hops stay programmed for (warm) failover.

Also included:

Tests: added an Rx-path isolation test that drives handle_mcast_rx directly by injecting a raw Geneve-over-IPv6 multicast frame over DLPI, with no guest Tx, so a delivered packet can only have arrived via the underlay receive path.

(Local) builds: we thread in TGT_BASE through the buildomat jobs so artifacts can be written to a user-owned directory, and guard the cleanup chown against re-rooting the tree when run elevated locally.

On dtrace, we added per-key mcast aggregations for E2E delivery verification. The delivery script tracked multicast events only in coarse rollups (by event, vni, port, group), which confirm that traffic moved but cannot attribute a specific group's fanout or loss to a port, next hop, or underlay address. End-to-end emulation testing provides this resolution to assert which sleds and switch ports a flow reached.

The delivery script tracked multicast events only in coarse rollups (by
event, vni, port, group), which confirm that traffic moved but cannot
attribute a specific group's fanout or loss to a port, next hop, or
underlay address. End-to-end emulation testing profers this resolution to
assert which sleds and switch ports a flow reached.
…selection

The multicast forwarding loop emitted a copy to every programmed next hop.
A next hop is a switch endpoint that fans out the packet to every sled (the
underlay) or out the front panel (the external) behind its port groups.
Redundant next hops sharing a replication target therefore reach the same
destinations and deliver the flow twice, and a receiver cannot tell the
duplicates apart, so we handle deduplication at Tx.

This PR adds `select_nexthops`, which resolves one next hop per replication
target in two passes over the forwarding table. The loop composes the two
selections into each hop's effective replication because a `Both` replication
hop is narrowed when chosen for one target but not the other, and skipped when
chosen for neither. All next hops stay programmed for failover.

Also included:

Tests: add an Rx-path isolation test that drives handle_mcast_rx directly by
injecting a raw Geneve-over-IPv6 multicast frame over DLPI, with no guest Tx, so
a delivered packet can only have arrived via the underlay receive path.

(Local) builds: we thread in TGT_BASE through the buildomat jobs so artifacts
can be written to a user-owned directory, and guard the cleanup chown against
re-rooting the tree when run elevated locally.
@zeeshanlakhani zeeshanlakhani changed the title Zl/mcast tx ecmp select one [ecmp] Deduplicate redundant multicast next hops via per-target ECMP selection Jun 29, 2026
@zeeshanlakhani zeeshanlakhani self-assigned this Jul 3, 2026

@FelixMcFelix FelixMcFelix left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Zeeshan, I think this looks correct for Underlay replication. I've not looked at the added tests yet, I wanted to make sure I'm on the right page with how the external/underlay split is handled in the datapath first.

Comment thread xde/src/xde.rs Outdated
Comment thread xde/src/xde.rs Outdated
Comment thread xde/src/xde.rs Outdated
Comment thread xde/src/xde.rs Outdated
Comment thread xde/src/xde.rs Outdated
Comment thread xde/src/xde.rs Outdated
…ting

Here's the list of review follow-ups:

*Selection reworking*:
  - Rewrite `select_nexthops` as a single scan that partitions eligible
    hops by replication mode onto stack-held heapless vecs.
  - Coalesce first approach: when any eligible hop replicates to `Both`,
    hash-select among those and pin both targets to that hop, so one packet
    covers the pair and the switch performs the replication. The flow splits
    only when no single hop covers both targets, with each copy carrying its
    target's disjoint replication instruction(s).
  - Evaluate source filters once per hop during the scan, firing the
    `mcast_tx_fwd_source_filtered` stat and probe at the point of
    exclusion.
  - Build the emit path's emissions (at most two) directly from the
    selection, replacing the forwarding-map rescan and membership match.

*Reserved replication*:
  - Remove the emit path's `Reserved` pass-through, which cloned the
    packet only to discard it without a stat or probe.
  - Reject `Reserved` at the set ioctl with EINVAL instead, as an accepted
    entry serves no replication target.

*Upper Bound*:
  - We add a `MAX_MULTICAST_NEXT_HOPS` (@ 4: two switches plus reconciler
    headroom) and enforce it at the set ioctl via the cardinality of the
    merged key set, so no per-mode partition can overextend its capacity.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants