Repository navigation
fix(gin): de-alias Anvil signals across logical contexts - #12751
Conversation
✅ All Policy Checks Passed
📖 Need help? See the Policy FAQ for details on every check and how to fix failures. |
There was a problem hiding this comment.
ARGUS
Bottom line: Looks approval ready. The per-context stripe layout, span refcount balance and slot accounting all check out against the caller in dev_runtime.cc; everything flagged inline is Low or Nit.
Comment @argus review to re-run, or @argus re-review after pushing.
cf8cf97 to
50e54f0
Compare
|
@argus re-review |
There was a problem hiding this comment.
ARGUS
Findings
2 finding(s) that could not be posted as inline comments
projects/rccl/test/transport/GinDeviceMPITests.cpp:2329(Low) The AllContexts aliasing regression test goes vacuous whenever a rank has more than one GIN connectionBarrier_AllContextsConsecutiveSignalsDoNotAlias_SingleNodegates only onginContextCount >= 2, but the aliasing it reproduces needs two logical contexts on one connection:ncclGinInitCommonderivescontextId = contextIndex / ginConnectionCount(gin__funcs.h:122-125), so atginCommCount == 2both contexts getcontextId0 on different handles and every assertion here passes withanvilGpuCtxreverted.MultiContext_NonPowerOf2in this same config list already runs withNCCL_GIN_NCONNECTIONS=3. Could the guard pin contexts-per-connection too?projects/rccl/src/dev_runtime.cc:2218(Low) The new fail-path teardown frees the GIN contexts but leaves ginContextCount and ginHandles[] populated The new fail-path teardown frees the GIN contexts but leavesoutDevComm->ginContextCountandginHandles[]populated, soncclDevCommDestroyon the failed struct re-entersncclGinDevCommFree(dev_runtime.cc:2699) and matches on adevHandles[0]->handlethis call already freed (gin_host.cc:471). The GIN setup path clears exactly these fields on its own error path (gin_host.cc:407-419). Could the fail path mirror that?
Bottom line: Looks approval ready. All six findings from the last round are fixed; the four inline notes are Low, mostly about the new regression test being able to go vacuous.
Comment @argus review to re-run, or @argus re-review after pushing.
There was a problem hiding this comment.
ARGUS
Findings
2 finding(s) that could not be posted as inline comments
projects/rccl/src/dev_runtime.cc:2081(Medium) GIN signal shadows are sized with the unrounded context count but indexed with the rounded one dev_runtime.cc:2081 sizes the GIN signal-shadow region with the unroundednGinContexts, while the device indexes it withcomm.ginContextCount, which gin_host.cc:268 sets toROUNDUP(reqs->ginContextCount, ginCommCount). ''' reserved dev_runtime.cc:2081 nGinContexts = 4 slots indexed gin__funcs.h:129 ROUNDUP(4, ginCommCount=3) = 6 slots overflow lands at ginAnvilNetSignalsOffset (dev_runtime.cc:2082) ''' The comment at dev_runtime.cc:2084 says the rounding exists so shadows stay in range, but it is applied to the Anvil span at 2091 instead. This predates the PR and line 2081 is untouched here, so it looks like a separate change.projects/rccl/CHANGELOG.md:47(Low) No CHANGELOG entry for a user-visible GIN device API fix projects/rccl/CHANGELOG.md has no entry for this fix. The unreleased 2.32.3 section's### Resolved issueslist ends at line 47 and already carries a comparable GIN bugfix at line 46, and .github/PULL_REQUEST_TEMPLATE.md:20 makes the CHANGELOG an approval-checklist item when changes impact library users. ConsecutivencclGinAllContextsbarriers returning early on a stale count is user-visible, so one bullet after line 47 would cover it.
Bottom line: Looks approval ready. All four findings from the last round are fixed; the new ones are Low apart from one Medium gap in test coverage.
Comment @argus review to re-run, or @argus re-review after pushing.
|
@argus re-review |
|
Thanks for the thorough re-review. Replies to the two findings that could not be posted inline:
|
There was a problem hiding this comment.
ARGUS
Findings
2 finding(s) that could not be posted as inline comments
projects/rccl/test/transport/GinDeviceMPITests.cpp:2317(Medium) The new MPI regression test omits SKIP_IF_GIN_UNSUPPORTED(), so a RAIL-only comm fails it instead of skippingBarrier_AllContextsConsecutiveSignalsDoNotAlias_SingleNodeis missingSKIP_IF_GIN_UNSUPPORTED()aftercreateTestCommunicator(). The four gates above it read only env vars and MPI topology, neverglobalGinSupport, so on a RAIL-only or GIN-less comm thedefaultGinReqs()FULL request makesncclDevCommCreatefail the test rather than skip it.Barrier_TwoRanksguards the same sequence at line 2204.projects/rccl/docs/userguide/source/api/device_gin.rst:272(Medium) The user guide still documents AICOMRCCL-2339 as an open follow-up, contradicting this PR's own CHANGELOG entry ThencclGinAllContextsdocs at docs/userguide/source/api/device_gin.rst:272 still say Anvil SDMA contexts share one signal array and call AICOMRCCL-2339 an open plugin follow-up, while this PR'sCHANGELOG.mdentry at line 48 says each logical context now has its own signals. Shipping both texts in one release leaves the user guide describing the bug this PR fixes. That file is outside the diff, so flagging rather than suggesting.
This PR changes 1,009 lines across 17 files. A change this size is hard to review
well, for a person and for this bot alike: reviewers lose the thread, defects hide in the
volume, and the feedback arrives too late to be cheap to act on. Splitting it into a
stacked pull request
gives each piece a reviewable diff and keeps the history. This review covers the whole change
regardless.
Bottom line: Worth settling before approval: the new MPI test hard-fails instead of skipping on a RAIL-only comm, and device_gin.rst still documents this bug as unfixed.
Comment @argus review to re-run, or @argus re-review after pushing.
|
Thanks for the re-review. Replies to the two findings that could not be posted inline:
On the size note: thanks for the suggestion. I'd like to keep this as one PR, since the plugin change and its tests only make sense together and most of the line count is tests. MI355 retest on e14907e: |
|
@argus re-review |
There was a problem hiding this comment.
ARGUS
This PR changes 1,079 lines across 18 files. A change this size is hard to review
well, for a person and for this bot alike: reviewers lose the thread, defects hide in the
volume, and the feedback arrives too late to be cheap to act on. Splitting it into a
stacked pull request
gives each piece a reviewable diff and keeps the history. This review covers the whole change
regardless.
Bottom line: LGTM.
Comment @argus review to re-run, or @argus re-review after pushing.
e14907e to
cdbcbe5
Compare
There was a problem hiding this comment.
ARGUS
This PR changes 1,079 lines across 18 files. A change this size is hard to review
well, for a person and for this bot alike: reviewers lose the thread, defects hide in the
volume, and the feedback arrives too late to be cheap to act on. Splitting it into a
stacked pull request
gives each piece a reviewable diff and keeps the history. This review covers the whole change
regardless.
Bottom line: LGTM.
Comment @argus review to re-run, or @argus re-review after pushing.
|
Pushed
HIP keeps freed blocks mapped, so pointer queries cannot detect a free. The plugin stubs therefore interpose Verified on MI355X (gfx950): |
There was a problem hiding this comment.
ARGUS
This PR changes 1,204 lines across 18 files. A change this size is hard to review
well, for a person and for this bot alike: reviewers lose the thread, defects hide in the
volume, and the feedback arrives too late to be cheap to act on. Splitting it into a
stacked pull request
gives each piece a reviewable diff and keeps the history. This review covers the whole change
regardless.
Bottom line: LGTM.
Comment @argus review to re-run, or @argus re-review after pushing.
There was a problem hiding this comment.
ARGUS
This PR changes 1,204 lines across 18 files. A change this size is hard to review
well, for a person and for this bot alike: reviewers lose the thread, defects hide in the
volume, and the feedback arrives too late to be cheap to act on. Splitting it into a
stacked pull request
gives each piece a reviewable diff and keeps the history. This review covers the whole change
regardless.
Bottom line: LGTM.
Comment @argus review to re-run, or @argus re-review after pushing.
There was a problem hiding this comment.
ARGUS
This PR changes 1,200 lines across 18 files. A change this size is hard to review
well, for a person and for this bot alike: reviewers lose the thread, defects hide in the
volume, and the feedback arrives too late to be cheap to act on. Splitting it into a
stacked pull request
gives each piece a reviewable diff and keeps the history. This review covers the whole change
regardless.
Bottom line: LGTM.
Comment @argus review to re-run, or @argus re-review after pushing.
33c7677 to
d08a9db
Compare
There was a problem hiding this comment.
ARGUS
This PR changes 1,201 lines across 18 files. A change this size is hard to review
well, for a person and for this bot alike: reviewers lose the thread, defects hide in the
volume, and the feedback arrives too late to be cheap to act on. Splitting it into a
stacked pull request
gives each piece a reviewable diff and keeps the history. This review covers the whole change
regardless.
Bottom line: LGTM.
Comment @argus review to re-run, or @argus re-review after pushing.
alex-breslow-amd
left a comment
There was a problem hiding this comment.
Already has gone through reviews
|
@alex-breslow-amd Thanks for the approval, and for flagging this. It's pre-existing on The only failing case in MathCI precheckin(rccl), on both gfx942 and gfx1100, is I built The likely cause is that the test |
00ba764 to
76a0c25
Compare
JIRA ID: AICOMRCCL-3122
## What
Fixes the math-ci precheckin(rccl) failure
`NcclDeviceRequirementsMicrotest.GinInboxA2ACreateRequirement_KnownBug_SingleRankTeamDividesByZero`
("failed to die"), from #13312. It's red on every RCCL PR, for example
#12751.
## How
Removes that death test and its now-unused core-dump helper.
The test relied on undefined behaviour: it needed x86_64 to trap on a
divide by zero and the compiler to keep a divide whose result was never
used. math-ci's optimized build drops the divide, so nothing trapped.
The production bug (a 1-rank team divides by zero in
`ncclGinInboxA2ACreateRequirement`) stays tracked in AICOMRCCL-3121. A
fix there should add a normal test that a 1-rank team is rejected.
## Coverage
Unchanged: the line is still covered by the other gin_scratch tests.
## Mutation Testing
N/A, test removal.
## Test Plan
`rccl-UnitTestsMicroNcclDevice` 13/13 in both Release and Debug
standalone builds, 20 shuffled repeats clean in each. The Release build
reproduced the math-ci failure before this change.
---------
Co-authored-by: Claude <noreply@anthropic.com>
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>
When ncclDevrCommCreateInternal failed after ncclGinDevCommSetup, the GIN contexts it created were never destroyed. Anvil kept them pending, and since each logical context now takes a signal slot, a retried create bound the stale contexts alongside its own and tripped the nSignalSlots bound. Free them with ncclGinDevCommFree on the failure path, as ncclDevCommDestroy does. Build the signal peer-address table in one helper for the span and per-context binds, dropping a self-address check that could not fire. Latch the test stub's LSA input base with a flag instead of a 0 sentinel, size the PutValue template test's IPC window to its 8-byte buffer, and add tests for the empty-pending bind and for destroyed unbound contexts releasing their slots. Co-authored-by: Cursor <cursoragent@cursor.com>
Address ARGUS re-review lows on 50e54f0: - Reset ginContextCount/ginHandles after the fail-path ncclGinDevCommFree so ncclDevCommDestroy cannot free the same handle twice. - Pin the AllContexts alias regression to two logical contexts on one connection, and time the delay with wall_clock64. - Assert the shared signal span IPC entry survives the first destroy and goes away with the last. Co-authored-by: Cursor <cursoragent@cursor.com>
Share ncclGinDevCommClearFields between the GIN setup error path and the devComm create fail path. Pin the stripe spacing by remote address, add a slot-out-of-range bind test, seed signals before the per-context reset check, and add a CHANGELOG entry. Co-authored-by: Cursor <cursoragent@cursor.com>
…t GIN Model bootstrapAllGather's world-sized write for non-int elements in the plugin stubs and pin that the span-base probe buffer holds one slot per world rank under a rail connection. Skip the MPI alias test when the comm lacks full GIN support, and drop the user-guide note describing the fixed signal aliasing as open. Co-authored-by: Cursor <cursoragent@cursor.com>
Interpose hipMalloc / hipExtMallocWithFlags / hipFree in the Anvil plugin stubs to keep a live-allocation ledger, since HIP keeps freed blocks mapped and pointer queries cannot detect a free. DestroyContext now has coverage for releasing the nContexts-slot GPU context array, the counter span, and every logical context's bound peer table while leaving the connection-owned SDMA queues and dirty word alive. A one-shot hipExtMallocWithFlags failure covers createContext's error path, and the mid-bind slot overflow test now asserts its partially bound peer tables are freed. Mirrors the host-side coverage requested on #12746. Co-authored-by: Cursor <cursoragent@cursor.com>
…dles The signal-span refcount is keyed by address and never iterated, so it does not need ordering. Clear ginHandles element-wise with nullptr instead of memset. Co-authored-by: Cursor <cursoragent@cursor.com>
76a0c25 to
c14f75b
Compare
|
Rebased onto The rebase picks up #13927, which drops @ROCm/rccl-ci, for the record on the previous head: Ruby smoke passed on the same run. Later OCI jobs on the same runner (from 15:30 UTC Oct 22) got past |
) ## Motivation Found while reviewing #12751 (AICOMRCCL-2339). The device indexes `ginSignalShadows` as `contextIndex * ginSignalCount` for every `contextIndex < devComm.ginContextCount`. `ncclGinDevCommSetup` rounds `ginContextCount` up to the chosen backend's `ginCommCount`. In `ncclDevrCommCreateInternal`, though, the shadow region was sized for the *requested* `reqs->ginContextCount`. When the connection count does not divide the requested count, the last contexts' shadows land past the region: in the Anvil signal span, or past the end of the resource window. For example, with `NCCL_GIN_NCONNECTIONS=3` and 8 requested contexts, the devComm uses 9 contexts but shadows are allocated for 8. The Anvil span next to it was rounded to the *widest* active backend, which is also not enough when backends differ: `ROUNDUP(4, 4) = 4 < ROUNDUP(4, 3) = 6`. ## Technical Details - Added a file-static `devrGinContextsTotal(ginState, nGinContexts)`. It returns the maximum of `ROUNDUP(nGinContexts, backends[b].ginCommCount)` over all active backends, so the size covers whichever backend `ncclGinDevCommSetup` picks, including a fallback. A `ginCommCount` of 0 is treated as 1. - The signal-shadow region and the Anvil signal span are both sized with it. When GIN is not enabled, the sizes are unchanged. ## Test Plan - New `DevrGinContextsTotal.*` cases in `test/host/dev-runtime-test.cc`: no backends, a single backend that rounds up, mixed backends where the widest is not the worst case, and a zero-connection backend. - `rccl-UnitTestsMicro` on MI355 (gfx950, ROCm 7.0.2, `ENABLE_ROCSHMEM_GIN=ON`). ## Test Result - `rccl-UnitTestsMicro`: 1667 of 1667 passed, including the 4 new cases. --------- Co-authored-by: Cursor <cursoragent@cursor.com>
TheRock Submodule Bump ActivityNewest first
|

Motivation
Anvil SDMA currently allocates one GPU context per connection and ignores
nContexts. WhenncclGinAllContextsbarriers 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
ncclGinAnvilSdmaGPUContextand LSA signal stripe while keeping SDMA queues, dirty bits, and counters shared at the connection. Device APIs now indexctx.handlebyctx.contextId. Resource-window bind walksnContextsslots instead of one slot per plugin context.Signal peer addresses are indexed in GIN-team space (
ctx->rank), matchingresolveRemotePeerVa;regMrSymkeepsdevr->lsaSelfbecause 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:
comm->nRanksand publishes the local VA atcomm->rank.bootstrapAllGatherwrites one slot per world rank, so a GIN-team-sized buffer was overrun and the VA landed in the wrong slot underNCCL_GIN_CONNECTION_RAIL.g_pendingByComminto a vector underpluginMutexand walks the copy. Its three reads (span geometry, slot binding, andfail:rollback) were unlocked whileginAnvilPendingAdd/Remove/Clearall 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
BindSignals_LogicalContextsUseDistinctSignalStripesand theConnCheck_*suiteSignalApis_SelectLogicalContextandPutValue_SelectLogicalContextBarrier_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 MI355GinAnvilSdmaTemplateTest.*: 13/13 passed on MI355 (beforePutValue_SelectLogicalContextwas added)GinMPIDeviceTests.Barrier_AllContextsConsecutiveSignalsDoNotAlias_SingleNodeon MI355: PASSED (7779 ms) onsmci355-ccs-aus-m03-17, 1 node / 2 ranks,NCCL_GIN_ENABLE=1,NCCL_CUMEM_ENABLE=1. Ranrccl-UnitTestsMPIinsiderccl-gin-pr11556-rs:latestwith--ipc=host. Hostmpirunon the same node segfaulted in HIP IPC during communicator bring-up (ncclP2pImportShareableBuffer/ncclDevrCommCreateInternal) while other GPU docker jobs held/dev/kfdand/dev/dri; that path never reached the kernel.cv350-rck-g03-e23-08, 1 node / 2 ranks, same GIN env. Hostmpirun -np 2; GPUs were idle. Used the gfx950rccl-UnitTestsMPI/librccl.so.1from the MI355 PR build (native CentOS 9 link oflibrccl.sofailed onlibrocshmem_gin_host.abitcode encoding; Docker was unavailable on that node).Current head
cf8cf9720f, rebased ontodevelopat482e541856, 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 fullConnCheck_*suite and the two span-scope cases added for the ARGUS finding. Both were confirmed to fail against8eafd3d752(3 allgathers atnContexts=3) and pass here (1).GinMPIDeviceTests.*suite on the Anvil backend: 44 passed, 20 skipped, 0 failed (40.1 s). CoversBarrier_AllContextsConsecutiveSignalsDoNotAlias_SingleNode(680 ms), all sevenBarrierFence_*_SingleNodecases includingAllContextsPut/AllContextsGet,MultiContext_AllFourRoute/NonPowerOf2/Exclusive, andPut_BasicAndOffsets_MultiContext/PutValue_Inline_MultiContext/WaitCounterAndSignal_MultiContext.RailConnection_Create, theVASignal_*cases (VA signals are unsupported on SDMA by design), and the pool tests that requireNCCL_GIN_SIGNAL_POOL_SIZE>=0x40000000.An earlier attempt at this run skipped all 64 cases because
NCCL_CUMEM_ENABLEwas unset, which trips theSymmetric memory requiredgate; 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
hipIpcOpenMemHandlesegfaults 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