Repository navigation
fix(rccl): roll back a GIN activation that fails after connect #14231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bb5968d
1da36b4
5527192
ad48d9d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -827,6 +827,42 @@ static void symMemoryUnregister(struct ncclComm* comm, struct ncclDevrMemory* me | |
| } | ||
| } | ||
|
|
||
| // Undo an incomplete GIN activation in ncclDevrCommCreateInternal. Memories and | ||
| // windows that existed before it carried no GIN state, so everything GIN holds | ||
| // now came from this activation. | ||
| static void symGinActivationRollbackWindows(struct ncclDevrState* devr, cudaStream_t stream) { | ||
| for (int i = 0; i < devr->winSortedCount; i++) { | ||
| struct ncclDevrWindow* win = devr->winSorted[i].win; | ||
| struct ncclWindow_vidmem* winHost; | ||
| if (ncclShadowPoolToHost(&devr->shadows, win->vidmem, &winHost) != ncclSuccess) continue; | ||
| if (winHost->ginMultiSegmentWins == nullptr) continue; | ||
| (void)ncclShadowPoolFree(&devr->shadows, winHost->ginMultiSegmentWins, stream); | ||
| winHost->ginMultiSegmentWins = nullptr; | ||
| winHost->numSegments = 0; | ||
| for (int c = 0; c < NCCL_GIN_MAX_CONNECTIONS; c++) winHost->ginWinsDefaultBackend[c] = nullptr; | ||
| CUDACHECKIGNORE( | ||
| cudaMemcpyAsync(win->vidmem, winHost, sizeof(struct ncclWindow_vidmem), cudaMemcpyHostToDevice, stream)); | ||
| } | ||
| } | ||
|
|
||
| static void symGinActivationRollbackMemories(struct ncclComm* comm) { | ||
| struct ncclDevrState* devr = &comm->devrState; | ||
| for (struct ncclDevrMemory* mem = devr->memHead; mem != nullptr; mem = mem->next) { | ||
| if (mem->ginSegmentInfos == nullptr) continue; | ||
| for (int segment = 0; segment < mem->numGinSegments; segment++) { | ||
| (void)ncclGinDeregister(comm, mem->ginSegmentInfos[segment].ginHostWins); | ||
| } | ||
| free(mem->ginSegmentInfos); | ||
| mem->ginSegmentInfos = nullptr; | ||
| mem->numGinSegments = 1; | ||
| for (int i = 0; i < NCCL_GIN_MAX_CONNECTIONS * NCCL_GIN_MAX_ACTIVE_BACKENDS; i++) { | ||
| mem->ginHostWins[i] = nullptr; | ||
| mem->ginDevWins[i] = nullptr; | ||
| } | ||
| } | ||
| devr->ginEnabled = false; | ||
| } | ||
|
|
||
| static void symMemoryUnmapLsaRank(struct ncclDevrState* devr, struct ncclDevrMemory* mem, int rank) { | ||
| uintptr_t base = reinterpret_cast<uintptr_t>(devr->lsaFlatBase); | ||
| uintptr_t addr = base + rank * devr->bigSize + mem->bigOffset; | ||
|
|
@@ -1847,6 +1883,7 @@ ncclResult_t ncclDevrCommCreateInternal(struct ncclComm* comm, struct ncclDevCom | |
| struct ncclTeam world = ncclTeamWorld(comm); | ||
| struct ncclTeam lsa = ncclTeamInnerFactor(world, devr->lsaSize); | ||
| bool ginActivated = false; | ||
| bool ginActivationDone = false; | ||
| bool cftUcActivated = false; | ||
| bool hasCountedCftMemory = symHasCountedCftMemory(devr); | ||
| struct ncclDevrTeam* tmLsa; | ||
|
|
@@ -1910,7 +1947,6 @@ ncclResult_t ncclDevrCommCreateInternal(struct ncclComm* comm, struct ncclDevCom | |
| } | ||
|
|
||
| ginActivated = !devr->ginEnabled; | ||
| devr->ginEnabled = true; | ||
| } | ||
|
|
||
| if (reqs->worldGinBarrierCount > 0 && requestedConnectionType == NCCL_GIN_CONNECTION_RAIL) { | ||
|
|
@@ -1928,6 +1964,9 @@ ncclResult_t ncclDevrCommCreateInternal(struct ncclComm* comm, struct ncclDevCom | |
|
|
||
| if (ginActivated) { | ||
| NCCLCHECKGOTO(ncclGinConnectOnce(comm), ret, fail); | ||
| // Latch only on success: a failed connect leaves ginCommCount stale or 0, and | ||
| // a retry must reconnect rather than reach ncclGinDevCommSetup. | ||
| devr->ginEnabled = true; | ||
| // Register all preexisting memories with GIN. Update the windows later when | ||
| // we have a stream. | ||
| for (struct ncclDevrMemory* mem = devr->memHead; mem != nullptr; mem = mem->next) { | ||
|
|
@@ -2131,6 +2170,7 @@ ncclResult_t ncclDevrCommCreateInternal(struct ncclComm* comm, struct ncclDevCom | |
| stream), | ||
| ret, fail_stream); | ||
| } | ||
| ginActivationDone = true; | ||
| } | ||
|
|
||
| NCCLCHECKGOTO(symWindowTableInitOnce(comm, stream), ret, fail_stream); // ensure devr->windowTable exists | ||
|
|
@@ -2207,8 +2247,12 @@ ncclResult_t ncclDevrCommCreateInternal(struct ncclComm* comm, struct ncclDevCom | |
| } | ||
| symMemoryDropRef(comm, mem); | ||
| fail_stream: | ||
| if (ginActivated && !ginActivationDone) symGinActivationRollbackWindows(devr, stream); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Medium
|
||
| CUDACHECKIGNORE(cudaStreamDestroy(stream)); | ||
| fail: | ||
| // A retry sees ginEnabled and skips activation, so leaving GIN latched here | ||
| // would build GIN devcomms over unregistered memories and stale windows. | ||
| if (ginActivated && !ginActivationDone) symGinActivationRollbackMemories(comm); | ||
| CUDACHECKIGNORE(cudaThreadExchangeStreamCaptureMode(&captureMode)); | ||
| return ret; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Low
Could this header fit the one-line budget? The safety argument still fits: