Skip to content

fix(gin): de-alias Anvil signals across logical contexts - #11569

Closed
dlamd1dai wants to merge 8 commits into
ROCm:developfrom
dlamd1dai:users/dondai/aicomrccl-2339-anvil-signal-alias
Closed

dlamd1dai wants to merge 8 commits into
ROCm:developfrom
dlamd1dai:users/dondai/aicomrccl-2339-anvil-signal-alias

Conversation

@dlamd1dai

@dlamd1dai dlamd1dai commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

Anvil SDMA currently allocates one GPU context per connection and ignores nContexts. When ncclGinAllContexts barriers run on a shared connection, every logical context writes and waits on the same signal cells, so a later barrier can complete on a stale count from an earlier one.

Technical Details

Give each logical context its own ncclGinAnvilSdmaGPUContext and LSA signal stripe while keeping SDMA queues, dirty bits, and counters shared at the connection. Device APIs now index ctx.handle by ctx.contextId. Resource-window bind walks nContexts slots instead of one slot per plugin context.

Signal peer addresses are indexed in GIN-team space (ctx->rank), matching resolveRemotePeerVa; regMrSym keeps devr->lsaSelf because that table is LSA-indexed. The signal span is registered in the IPC table once per bind rather than once per context, and the comm-wide collectives (the local-base allgather and the LSA conn-check) run at span scope so a comm with N logical contexts still costs one bootstrap round trip, not N.

Two fixes to the surrounding code are folded in, previously proposed standalone as #12450 and #12451 and closed in favour of this PR once the restructuring above made them inapplicable as separate patches:

  • The span base probe is sized with comm->nRanks and publishes the local VA at comm->rank. bootstrapAllGather writes one slot per world rank, so a GIN-team-sized buffer was overrun and the VA landed in the wrong slot under NCCL_GIN_CONNECTION_RAIL.
  • The bind path snapshots g_pendingByComm into a vector under pluginMutex and walks the copy. Its three reads (span geometry, slot binding, and fail: rollback) were unlocked while ginAnvilPendingAdd/Remove/Clear all hold that non-recursive mutex; find() also avoids default-inserting an empty list for a comm with nothing pending.

JIRA ID: AICOMRCCL-2339

Test Plan

  • Host plugin unit tests, including BindSignals_LogicalContextsUseDistinctSignalStripes and the ConnCheck_* suite
  • Device template tests, including SignalApis_SelectLogicalContext and PutValue_SelectLogicalContext
  • MPI regression Barrier_AllContextsConsecutiveSignalsDoNotAlias_SingleNode (Anvil, 1 node / 2 ranks, NCCL_GIN_TYPE=7, delayed second barrier)

Test Result

Anvil SDMA is NCCL_GIN_TYPE=7 (NCCL_NET_DEVICE_GIN_ANVIL_SDMA). Earlier runs in this section were recorded when the enum was 6.

Pre-rebase (commit 970a014315):

  • rccl-UnitTestsGinAnvilPlugin: 20/20 passed on MI355
  • GinAnvilSdmaTemplateTest.*: 13/13 passed on MI355 (before PutValue_SelectLogicalContext was added)
  • GinMPIDeviceTests.Barrier_AllContextsConsecutiveSignalsDoNotAlias_SingleNode on MI355: PASSED (7779 ms) on smci355-ccs-aus-m03-17, 1 node / 2 ranks, NCCL_GIN_ENABLE=1, NCCL_CUMEM_ENABLE=1. Ran rccl-UnitTestsMPI inside rccl-gin-pr11556-rs:latest with --ipc=host. Host mpirun on the same node segfaulted in HIP IPC during communicator bring-up (ncclP2pImportShareableBuffer / ncclDevrCommCreateInternal) while other GPU docker jobs held /dev/kfd and /dev/dri; that path never reached the kernel.
  • Same MPI filter on MI350: PASSED (8834 ms) on cv350-rck-g03-e23-08, 1 node / 2 ranks, same GIN env. Host mpirun -np 2; GPUs were idle. Used the gfx950 rccl-UnitTestsMPI/librccl.so.1 from the MI355 PR build (native CentOS 9 link of librccl.so failed on librocshmem_gin_host.a bitcode encoding; Docker was unavailable on that node).

Current head cf8cf9720f, rebased onto develop at 482e541856, retested from a clean worktree on MI355X (smci355-ccs-aus-m03-17), 1 node / 2 ranks, NCCL_GIN_TYPE=7, NCCL_GIN_ENABLE=1, NCCL_CUMEM_ENABLE=1, NCCL_GIN_NCONTEXTS=4:

  • rccl-UnitTestsGinAnvilPlugin: 37/37 passed (304 ms), including the full ConnCheck_* suite and the two span-scope cases added for the ARGUS finding. Both were confirmed to fail against 8eafd3d752 (3 allgathers at nContexts=3) and pass here (1).
  • Whole GinMPIDeviceTests.* suite on the Anvil backend: 44 passed, 20 skipped, 0 failed (40.1 s). Covers Barrier_AllContextsConsecutiveSignalsDoNotAlias_SingleNode (680 ms), all seven BarrierFence_*_SingleNode cases including AllContextsPut/AllContextsGet, MultiContext_AllFourRoute/NonPowerOf2/Exclusive, and Put_BasicAndOffsets_MultiContext/PutValue_Inline_MultiContext/WaitCounterAndSignal_MultiContext.
  • The 20 skips are topology or opt-in gated, not regressions: cross-node and 4-rank cases, RailConnection_Create, the VASignal_* cases (VA signals are unsupported on SDMA by design), and the pool tests that require NCCL_GIN_SIGNAL_POOL_SIZE>=0x40000000.

An earlier attempt at this run skipped all 64 cases because NCCL_CUMEM_ENABLE was unset, which trips the Symmetric memory required gate; the numbers above are from the corrected invocation.

Earlier post-rebase run on f32754f6c1, same node and env, before #12450/#12451 were folded in: plugin 37/37, GinMPIDeviceTests.* 39 passed, 23 skipped, 0 failed (34.5 s).

Environment note: the host ROCm on that node is 7.0.2.1 while its KFD driver is newer, and hipIpcOpenMemHandle segfaults during plain P2P transport setup (ncclP2pImportShareableBuffer) before any GIN code runs. The runs above use the node's ROCm 7.13 runtime in a container with --ipc=host, where the same binaries pass. Not related to this change; the same workaround was needed for the pre-rebase runs.

Submission Checklist

Made with Cursor

@dlamd1dai
dlamd1dai requested review from a team September 14, 2026 07:40
@therock-pr-bot

therock-pr-bot Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

✅ All Policy Checks Passed

Check Status Details
📝 PR Description ✅ Pass —
⛔ Forbidden Files ✅ Pass —
🧪 Unit Test ✅ Pass —
🚫 Draft PR 🔜 To Be Enabled —
🚩 Feature Flag 🔜 To Be Enabled —
📊 Code Coverage 🔜 To Be Enabled —

🎉 All policy checks passed!

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

🙋 Wish to Override Policy?

@therock-pr-bot

Copy link
Copy Markdown

🚫 Please fix the failed policies before requesting reviews.

The following policy checks failed:

  • ❌ PR Description

The Not ready to Review label has been added to this PR.
Once all policies pass, the label will be removed automatically.

@amd-argus amd-argus Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ARGUS

Bottom line: Worth settling before approval: each logical context now takes its own entry in the 16-slot Anvil IPC table, so a high ginContextCount fails ncclDevCommCreate.

Comment @argus review to re-run, or @argus re-review after pushing.

Comment thread projects/rccl/src/gin/gin_plugin_anvil_sdma.cc
Comment thread projects/rccl/test/transport/GinDeviceMPITests.cpp
Comment thread projects/rccl/src/gin/gin_plugin_anvil_sdma.cc Outdated
Comment thread projects/rccl/test/transport/GinDeviceMPITests.cpp Outdated
Comment thread projects/rccl/test/gin/GinAnvilPlugin_test.cpp Outdated
Comment thread projects/rccl/test/device/GinAnvilSdmaTemplate_test.cpp
Comment thread projects/rccl/src/gin/gin_plugin_anvil_sdma.cc Outdated
Comment thread projects/rccl/src/gin/gin_plugin_anvil_sdma.cc
@dlamd1dai

Copy link
Copy Markdown
Contributor Author

@argus re-review

Follow-up on the review of 99d2478 (now 757f9bab):

Thread Status
IPC table: one entry per logical context Fixed — span registered once via ginAnvilRegisterSignalSpan(); per-context bind only slices pointers
MPI regression not in gin_devmpi_sdma Fixed — case added next to Barrier_TwoRanks; deterministic signal-stripe assertions added
SignalApis_SelectLogicalContext coverage gaps Fixed — counter/signal APIs on contextId 0/1; new PutValue_SelectLogicalContext
Shared counters across contexts Fixed — per-context counter slice (countersDev + contextId * nCounters), matching proxy/gdaki
Cycle-count oracle flakiness Mitigated — post-barrier signal cell + pointer checks added
Plugin unit-test stride ambiguity Fixed — distinct nSignals / nSignalsPerContext / nContexts in BindSignals_LogicalContextsUseDistinctSignalStripes
Duplicated teardown / signals = nullptr on error Addressed in 91215b37

rocSHMEM scoped-atomics: split to #11591 so this PR stays scoped to AICOMRCCL-2339 signal de-aliasing. GIN CI on this PR may need that companion change (or an equivalent develop-side fix) to go green.

MPI on SUT: planning to run Barrier_AllContextsConsecutiveSignalsDoNotAlias_SingleNode on an Anvil 2-GPU node (NCCL_GIN_TYPE=6) and update the Test Result section once it completes.

@amd-argus amd-argus Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ARGUS

Bottom line: Worth settling before approval: PutValue_SelectLogicalContext gives both contexts the same signal_remote_addrs array, so it passes with the fix reverted.

Comment @argus review to re-run, or @argus re-review after pushing.

Comment thread projects/rccl/test/device/GinAnvilSdmaTemplate_test.cpp Outdated
Comment thread projects/rccl/test/device/GinAnvilSdmaTemplate_test.cpp Outdated
Comment thread projects/rccl/test/gin/GinAnvilPlugin_test.cpp Outdated
Comment thread projects/rccl/src/gin/gin_plugin_anvil_sdma.cc
Comment thread projects/rccl/test/transport/GinDeviceMPITests.cpp Outdated
Comment thread projects/rccl/src/gin/gin_plugin_anvil_sdma.cc Outdated
Comment thread projects/rccl/test/gin/GinAnvilPlugin_test.cpp
Comment thread projects/rccl/test/device/GinAnvilSdmaTemplate_test.cpp Outdated

@amd-argus amd-argus Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ARGUS

Bottom line: Worth settling before approval: the GIN backend gating at gin_plugin_anvil_sdma.cc:563 and the anvil-only build break in GinDeviceMPITests.cpp:1658.

Comment @argus review to re-run, or @argus re-review after pushing.

Comment thread projects/rccl/src/gin/gin_plugin_anvil_sdma.cc Outdated
Comment thread projects/rccl/test/transport/GinDeviceMPITests.cpp
@dlamd1dai
dlamd1dai force-pushed the users/dondai/aicomrccl-2339-anvil-signal-alias branch from 970a014 to 7a72254 Compare September 29, 2026 03:29
@dlamd1dai

Copy link
Copy Markdown
Contributor Author

@argus re-review

Rebased onto current develop (7a722544d5) and addressed the open ARGUS threads:

Thread Status
Empty pending list returns ncclInvalidArgument Fixed — returns ncclSuccess after ginAnvilPendingClear so non-anvil backends (e.g. NCCL_GIN_TYPE=5) keep creating communicators
MPI GetSignalPtr build break without Anvil Fixed — probe behind #if NCCL_GIN_ANVIL_SDMA_ENABLE; handle/contextId derived via ncclGin / ncclGinInitCommon
PutValue_SelectLogicalContext shared remote-addrs Fixed — distinct signal_remote_addrs arrays per logical context
ResetSignal only on contextId=0 Fixed — moved under contextId=1; context 0 sentinel stays 11
Counter stride ambiguity (nCounters==nSignals) Fixed — nCounters=5
Bind peer geometry on ctx->rank vs lsaSelf Fixed — bind loop uses devr->lsaSelf, matching ginAnvilRegisterSignalSpan / regMrSym
Bind nContexts parameter naming Fixed — renamed to nSignalSlots
Unused counters kernel arg Fixed — dropped

Also kept develop's LSA signal conn-check on the first bind, and indexed Get/FlushAsync through anvilGpuCtx for the APIs that landed after this PR branched.

Follow-ups (separate PRs):

  • GDA (NCCL_GIN_TYPE=5) still aliases ctx.handle without [contextId] — opening separately under AICOMRCCL-2339
  • rccl-UnitTestsGinAnvilPlugin is already built by gin.sbatch but not executed — opening a CI wiring PR to run it

rocSHMEM: no longer in this diff (scoped-atomics landed via #11350). Removed the project: rocshmem label and rocshmem-reviewers request.

@dlamd1dai

Copy link
Copy Markdown
Contributor Author

Follow-up PRs opened from current develop:

@amd-argus amd-argus Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ARGUS

Bottom line: Worth settling before approval: hostAddrs at gin_plugin_anvil_sdma.cc:783 is sized in GIN-team ranks but anchored on devr->lsaSelf, so it reads past the allocation under RAIL.

Comment @argus review to re-run, or @argus re-review after pushing.

Comment thread projects/rccl/src/gin/gin_plugin_anvil_sdma.cc Outdated
Comment thread projects/rccl/src/gin/gin_plugin_anvil_sdma.cc Outdated
Comment thread projects/rccl/test/gin/gin_anvil_plugin_test_stubs.cc
Comment thread projects/rccl/test/gin/GinAnvilPlugin_test.cpp
Comment thread projects/rccl/src/gin/gin_plugin_anvil_sdma.cc Outdated
Comment thread projects/rccl/src/gin/gin_plugin_anvil_sdma.cc Outdated
Comment thread projects/rccl/src/gin/gin_plugin_anvil_sdma.cc Outdated
@dlamd1dai

dlamd1dai commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor Author

@argus re-review

Follow-up on 7a722544d5 (now 8eafd3d752):

Thread Status
hostAddrs anchored on lsaSelf but indexed in GIN-team space Fixed — span registration and per-context bind both use the GIN rank (ctx->rank). regMrSym still uses lsaSelf because that table is LSA-indexed.
lsaInputBase latched across SetLsaSelfAddr Fixed — latch cleared in SetLsaSelfAddr
Stripe test never separates slot from contextId Fixed — second pending context on the same comm; context 0 of the first context lands at slot 1

Follow-ups, not in this PR (based on develop; rebase onto this branch if it merges first):

Retest plan stays the single-node Anvil barrier (NCCL_GIN_TYPE=7, 1 node / 2 ranks).

@amd-argus amd-argus Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ARGUS

Bottom line: Worth settling before approval: the bind body's comm-wide allgather at gin_plugin_anvil_sdma.cc:798 now runs once per logical context instead of once per connection.

Comment @argus review to re-run, or @argus re-review after pushing.

Comment thread projects/rccl/src/gin/gin_plugin_anvil_sdma.cc
@dlamd1dai

Copy link
Copy Markdown
Contributor Author

CI status on 8eafd3d752 — two items need a maintainer

Everything that can run from a fork is green: mci/rocm-libraries/extra(rccl), mci/rocm-libraries/static-analysis(rccl), Host unit tests (CPU-only), Test rccl (shard 1/1) (gfx94X-dcgpu), the comm-libs and emulation build stages, and both TheRock summaries. The two red checks are infrastructure, not this change:

Check Why What unblocks it
RCCL PR gate IS_FORK=true, so coco smoke is never auto-dispatched (GATE_RESULT=skipped). The job's own error text says a maintainer must dispatch it. Please dispatch the RCCL coco PR workflow with this PR's pr_number and pr_sha, confirm it passes, then override the check.
therock-pr-bot policy_check.py hit its 900 s limit while the emulation and Windows compiler-runtime stages were still null. The policy table itself is all-pass. Re-run once the slower build stages finish, or override.

Separately, mci/rocm-libraries/precheckin(rccl) build 6 has been PENDING since 06:45 UTC with no completion — roughly 13 hours. That's the job that carries the GIN single-node result, so there is currently no GIN signal on the rebased code. Could someone with MathCI access check whether that build is stuck in queue and re-trigger it?

A new commit is coming shortly for the last open ARGUS thread, which will retrigger all of the above anyway — the coco dispatch is the one that will still need a manual hand.

@dlamd1dai

Copy link
Copy Markdown
Contributor Author

@argus re-review

Follow-up on 8eafd3d752 (now f32754f6c1):

Thread Status
Bind body's comm-wide allgather runs once per logical context Fixed — the local-base probe moved into ginAnvilRegisterSignalSpan (already per span) and the conn-check moved up to ncclGinAnvilBindResourceWindowSignals, called once after the context loop. The nRanks/lsaSelf warnings moved with them.
Low: dead spanRefs fallback Fixed — counter and ncclGinAnvilIpcTableUnregister fallback removed
Low: dead gpuCtxHost stores in ginAnvilFreeGinCtx Fixed — removed

Two regression tests pin the frequency, both verified to fail on 8eafd3d752 and pass on f32754f6c1:

Test 8eafd3d752 f32754f6c1
BindSignals_CommWideCollectivesRunOncePerSpan (nContexts=3, healthy gate) 3 bootstrapAllGather 1
BindSignals_SkippedConnCheckStillRunsOncePerSpan (nContexts=3, gate skips on lsaSize != nRanks) 3 intra-node allgathers 1

Single-node Anvil retest on MI355X (smci355-ccs-aus-m03-17), 1 node / 2 ranks, NCCL_GIN_TYPE=7:

  • rccl-UnitTestsGinAnvilPlugin 37/37
  • Barrier_AllContextsConsecutiveSignalsDoNotAlias_SingleNode PASSED (668 ms)
  • Full GinMPIDeviceTests.* on Anvil: 39 passed, 23 skipped, 0 failed — skips are the 4-rank, cross-node, RAIL, and VA-signal cases

The remaining Low (g_pendingByComm without pluginMutex) is still #12451 on develop, as is the world-size allgather sizing in #12450. Both will need a rebase onto this branch if it lands first.

@amd-argus amd-argus Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ARGUS

Bottom line: Nothing new worth raising; still open from earlier: gin_plugin_anvil_sdma.cc:768 and 1 more. Worth clearing before approval.

Comment @argus review to re-run, or @argus re-review after pushing.

@dlamd1dai
dlamd1dai force-pushed the users/dondai/aicomrccl-2339-anvil-signal-alias branch from 2d8eda4 to f32754f Compare October 1, 2026 03:56
dlamd1dai and others added 8 commits October 1, 2026 00:13
Co-authored-by: Cursor <cursoragent@cursor.com>
…ontexts

Register the full Anvil signal span once in the IPC table, slice counters per
logical context like proxy, replace rocSHmem __hip_atomic_* with scoped atomics,
and tighten regression coverage for de-aliased signal stripes.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add PutValue_SelectLogicalContext so non-zero contextId coverage matches
the other Anvil device APIs. Revert the scoped-atomic rocSHMEM hunk here;
it lands separately so this PR stays scoped to signal de-aliasing.

Co-authored-by: Cursor <cursoragent@cursor.com>
Move slot initialization before NCCLCHECKGOTO so the host plugin
builds, and set the Anvil backend enum explicitly in the regression
kernel instead of assigning a uint8_t ginNetDeviceTypes entry.

Co-authored-by: Cursor <cursoragent@cursor.com>
Make empty anvil pending lists succeed so non-anvil GIN backends keep
building communicators, align signal peer geometry on lsaSelf, rename the
bind slot count, and tighten logical-context tests so they fail if context
indexing regresses.

Co-authored-by: Cursor <cursoragent@cursor.com>
signal_remote_addrs is read with a GIN-team peer, so anchoring the table
on lsaSelf walks off the allocation under RAIL. Also reset the plugin
test LSA latch between binds, and cover a second pending context so
slot and contextId are not the same index.

Co-authored-by: Cursor <cursoragent@cursor.com>
The per-contextId loop pulled two comm-wide collectives into the bind body, so
ncclDevCommCreate paid one bootstrap round trip per logical context instead of
one per connection. The local-base probe moves into ginAnvilRegisterSignalSpan,
which is already per span, and the conn-check moves to the caller: its bypass
and skip paths return before the comm is marked, so the dedup flag never
suppressed the repeat.

Also drops the spanRefs fallback, which could only fire when the span was never
registered, and the dead gpuCtxHost stores after the context is untracked.

Co-authored-by: Cursor <cursoragent@cursor.com>
bootstrapAllGather writes one slot per world rank, so the span base probe is
sized and indexed with comm->nRanks / comm->rank. The GIN team is smaller under
NCCL_GIN_CONNECTION_RAIL, which overran the buffer and published the local VA in
the wrong slot.

The bind walk also read g_pendingByComm without pluginMutex, which add, remove,
and clear all hold. Snapshot the list under the lock once and walk the copy, so
registration and clear can still take the same non-recursive mutex.

Co-authored-by: Cursor <cursoragent@cursor.com>
@dlamd1dai
dlamd1dai force-pushed the users/dondai/aicomrccl-2339-anvil-signal-alias branch from f32754f to cf8cf97 Compare October 1, 2026 05:01
@dlamd1dai

Copy link
Copy Markdown
Contributor Author

Rebased onto develop at 482e541856 and force-pushed as cf8cf9720f. The PR is MERGEABLE again.

What changed since the last review pass

Retest (clean worktree at cf8cf9720f, MI355X smci355-ccs-aus-m03-17, 1 node / 2 ranks, NCCL_GIN_TYPE=7)

  • rccl-UnitTestsGinAnvilPlugin: 37/37 passed.
  • GinMPIDeviceTests.* on the Anvil backend: 44 passed, 20 skipped, 0 failed. The skips are topology or opt-in gated (cross-node, 4-rank, RailConnection_Create, the VASignal_* cases that SDMA does not implement, and the large signal-pool cases). Full breakdown in the Test Result section.

Asks

  1. RCCL PR gate is failing only because a fork PR cannot auto-dispatch the coco smoke run (IS_FORK=true, GATE_RESULT=skipped). Could a maintainer dispatch the "RCCL coco PR" workflow with pr_number=11569 and pr_sha=cf8cf9720f16391c76a19534b2af34eaea0c67cd and then override the gate? I cannot trigger it from the fork. MathCI was green on the previous head (precheckin(rccl), extra(rccl), static-analysis(rccl)) and is re-running on this one.
  2. This still has no human review. Would appreciate a look from @ROCm/RCCL-Reviewers.

@argus re-review

@amd-argus amd-argus Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ARGUS

Bottom line: LGTM.

Comment @argus review to re-run, or @argus re-review after pushing.

@dlamd1dai

Copy link
Copy Markdown
Contributor Author

Superseded by #12751 and closing in favour of it. Same commits, same content; identical head SHA.

Why. This PR was opened from a fork, and the required RCCL PR gate check can never pass that way. Fork pull requests get no secrets, so the RCCL coco PR workflow skips its Setup job, the coco smoke never runs, and the gate fails closed within seconds. Clearing it here would need a maintainer to dispatch the workflow by hand and then override the check, on every push. #12751 is branched in ROCm/rocm-systems, where the gate dispatches coco to both the Ruby and OCI clusters on its own.

Review history stays here: 27 threads, all resolved. No approval was lost -- this PR had no approval or changes-requested decision -- so the conclusions still apply to #12751.

Closing so this does not keep consuming CI or show up as a duplicate for reviewers; it stays readable and can be reopened.

@dlamd1dai dlamd1dai closed this Oct 3, 2026
dlamd1dai added a commit that referenced this pull request Oct 22, 2026
> **Replaces #12440.** Identical production commits; only the head
branch moved (plus follow-up unit-test coverage on this PR).
>
> #12440 was opened from a fork, where the required `RCCL PR gate` check
can never pass. Fork pull requests receive no secrets, so the `RCCL coco
PR` workflow skips its `Setup` job, the coco smoke never runs, and the
gate fails closed in a few seconds. Clearing it there needs a maintainer
to dispatch the workflow by hand and then override the check. This
branch lives in `ROCm/rocm-systems`, so the gate dispatches coco on its
own.
>
> Review threads from #12440 have been replied to and resolved (sentinel
assertion and device stripe coverage fixed here; host-plugin unit
target, cross-rank `nContexts` validation, and the rail allgather buffer
sizing left as follow-ups).

## Motivation

Follow-up to #11569 / AICOMRCCL-2339. Anvil SDMA now indexes
`ctx.handle` by `ctx.contextId`, but `NCCL_GIN_TYPE=5` (rocSHMEM-GDA)
still casts `ctx.handle` with no context index and never reads
`config->nContexts`, so AllContexts barriers on a shared connection can
still complete on a stale signal count.

## Technical Details

- Device APIs resolve `gdaGpuCtx(ctx)` as `handle[contextId]`
- `createContext` allocates one GPU-context slot per logical context,
with a shared QP array and contiguous signal/counter spans sliced per
context
- Adds `SignalApis_SelectLogicalContext` (sentinel-seeded reset) and
`Put_SelectLogicalContextSignalStripe` device coverage via a
multi-context `GdaEnv`

JIRA ID: AICOMRCCL-2339
AICOMRCCL-3072

## Test Plan

- `GinRocshmemGdaTemplateTest.SignalApis_SelectLogicalContext`
- `GinRocshmemGdaTemplateTest.Put_SelectLogicalContextSignalStripe`
- Existing GDA template suite still passes with `contextId=0` default

## Test Result

- Device template coverage updated in this PR; CI / coco smoke will
re-run on the new commit

## Submission Checklist

- [x] Look over the contributing guidelines at
https://github.com/ROCm/rocm-systems/blob/develop/CONTRIBUTING.md.


Made with [Cursor](https://cursor.com)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
dlamd1dai added a commit that referenced this pull request Oct 24, 2026
> **Replaces #11569.** Identical commits and content; head is
`cf8cf9720f16391c76a19534b2af34eaea0c67cd` on both.
>
> #11569 was opened from a fork, where the required `RCCL PR gate` check
can never pass. Fork pull requests receive no secrets, so the `RCCL coco
PR` workflow skips its `Setup` job, the coco smoke never runs, and the
gate fails closed within seconds. Clearing it there needs a maintainer
to dispatch the workflow by hand and override the check, on every push.
This branch lives in `ROCm/rocm-systems`, so the gate dispatches coco to
the Ruby and OCI clusters on its own.
>
> Review history stays on #11569: 27 threads, all resolved. No approval
was lost; it had no approval or changes-requested decision.

## Motivation

Anvil SDMA currently allocates one GPU context per connection and
ignores `nContexts`. When `ncclGinAllContexts` barriers run on a shared
connection, every logical context writes and waits on the same signal
cells, so a later barrier can complete on a stale count from an earlier
one.

## Technical Details

Give each logical context its own `ncclGinAnvilSdmaGPUContext` and LSA
signal stripe while keeping SDMA queues, dirty bits, and counters shared
at the connection. Device APIs now index `ctx.handle` by
`ctx.contextId`. Resource-window bind walks `nContexts` slots instead of
one slot per plugin context.

Signal peer addresses are indexed in GIN-team space (`ctx->rank`),
matching `resolveRemotePeerVa`; `regMrSym` keeps `devr->lsaSelf` because
that table is LSA-indexed. The signal span is registered in the IPC
table once per bind rather than once per context, and the comm-wide
collectives (the local-base allgather and the LSA conn-check) run at
span scope so a comm with N logical contexts still costs one bootstrap
round trip, not N.

Two fixes to the surrounding code are folded in, previously proposed
standalone as #12450 and #12451 and closed in favour of this PR once the
restructuring above made them inapplicable as separate patches:

- The span base probe is sized with `comm->nRanks` and publishes the
local VA at `comm->rank`. `bootstrapAllGather` writes one slot per world
rank, so a GIN-team-sized buffer was overrun and the VA landed in the
wrong slot under `NCCL_GIN_CONNECTION_RAIL`.
- The bind path snapshots `g_pendingByComm` into a vector under
`pluginMutex` and walks the copy. Its three reads (span geometry, slot
binding, and `fail:` rollback) were unlocked while
`ginAnvilPendingAdd`/`Remove`/`Clear` all hold that non-recursive mutex;
`find()` also avoids default-inserting an empty list for a comm with
nothing pending.

JIRA ID: AICOMRCCL-2339
AICOMRCCL-3072

## Test Plan

- Host plugin unit tests, including
`BindSignals_LogicalContextsUseDistinctSignalStripes` and the
`ConnCheck_*` suite
- Device template tests, including `SignalApis_SelectLogicalContext` and
`PutValue_SelectLogicalContext`
- MPI regression
`Barrier_AllContextsConsecutiveSignalsDoNotAlias_SingleNode` (Anvil, 1
node / 2 ranks, `NCCL_GIN_TYPE=7`, delayed second barrier)

## Test Result

Anvil SDMA is `NCCL_GIN_TYPE=7` (`NCCL_NET_DEVICE_GIN_ANVIL_SDMA`).
Earlier runs in this section were recorded when the enum was 6.

Pre-rebase (commit `970a014315`):

- `rccl-UnitTestsGinAnvilPlugin`: 20/20 passed on MI355
- `GinAnvilSdmaTemplateTest.*`: 13/13 passed on MI355 (before
`PutValue_SelectLogicalContext` was added)
-
`GinMPIDeviceTests.Barrier_AllContextsConsecutiveSignalsDoNotAlias_SingleNode`
on MI355: **PASSED** (7779 ms) on `smci355-ccs-aus-m03-17`, 1 node / 2
ranks, `NCCL_GIN_ENABLE=1`, `NCCL_CUMEM_ENABLE=1`. Ran
`rccl-UnitTestsMPI` inside `rccl-gin-pr11556-rs:latest` with
`--ipc=host`. Host `mpirun` on the same node segfaulted in HIP IPC
during communicator bring-up (`ncclP2pImportShareableBuffer` /
`ncclDevrCommCreateInternal`) while other GPU docker jobs held
`/dev/kfd` and `/dev/dri`; that path never reached the kernel.
- Same MPI filter on MI350: **PASSED** (8834 ms) on
`cv350-rck-g03-e23-08`, 1 node / 2 ranks, same GIN env. Host `mpirun -np
2`; GPUs were idle. Used the gfx950 `rccl-UnitTestsMPI`/`librccl.so.1`
from the MI355 PR build (native CentOS 9 link of `librccl.so` failed on
`librocshmem_gin_host.a` bitcode encoding; Docker was unavailable on
that node).

Current head `cf8cf9720f`, rebased onto `develop` at `482e541856`,
retested from a clean worktree on MI355X (`smci355-ccs-aus-m03-17`), 1
node / 2 ranks, `NCCL_GIN_TYPE=7`, `NCCL_GIN_ENABLE=1`,
`NCCL_CUMEM_ENABLE=1`, `NCCL_GIN_NCONTEXTS=4`:

- `rccl-UnitTestsGinAnvilPlugin`: **37/37 passed** (304 ms), including
the full `ConnCheck_*` suite and the two span-scope cases added for the
ARGUS finding. Both were confirmed to fail against `8eafd3d752` (3
allgathers at `nContexts=3`) and pass here (1).
- Whole `GinMPIDeviceTests.*` suite on the Anvil backend: **44 passed,
20 skipped, 0 failed** (40.1 s). Covers
`Barrier_AllContextsConsecutiveSignalsDoNotAlias_SingleNode` (680 ms),
all seven `BarrierFence_*_SingleNode` cases including
`AllContextsPut`/`AllContextsGet`,
`MultiContext_AllFourRoute`/`NonPowerOf2`/`Exclusive`, and
`Put_BasicAndOffsets_MultiContext`/`PutValue_Inline_MultiContext`/`WaitCounterAndSignal_MultiContext`.
- The 20 skips are topology or opt-in gated, not regressions: cross-node
and 4-rank cases, `RailConnection_Create`, the `VASignal_*` cases (VA
signals are unsupported on SDMA by design), and the pool tests that
require `NCCL_GIN_SIGNAL_POOL_SIZE>=0x40000000`.

An earlier attempt at this run skipped all 64 cases because
`NCCL_CUMEM_ENABLE` was unset, which trips the `Symmetric memory
required` gate; the numbers above are from the corrected invocation.

Earlier post-rebase run on `f32754f6c1`, same node and env, before
#12450/#12451 were folded in: plugin **37/37**, `GinMPIDeviceTests.*`
**39 passed, 23 skipped, 0 failed** (34.5 s).

Environment note: the host ROCm on that node is 7.0.2.1 while its KFD
driver is newer, and `hipIpcOpenMemHandle` segfaults during plain P2P
transport setup (`ncclP2pImportShareableBuffer`) before any GIN code
runs. The runs above use the node's ROCm 7.13 runtime in a container
with `--ipc=host`, where the same binaries pass. Not related to this
change; the same workaround was needed for the pre-rebase runs.

## Submission Checklist

- [x] Look over the contributing guidelines at
https://github.com/ROCm/rocm-systems/blob/develop/CONTRIBUTING.md.

Made with [Cursor](https://cursor.com)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant