Repository navigation
shim: make the activity flush outside the CUPTI guard, which deadlocked unloads (#214) - #217
Merged
Merged
Conversation
…ed unloads (#214) Under continuous PC sampling, a process that unloads modules from two threads hung on every run. The application thread inside cuModuleUnload holds a driver lock while CUPTI's MODULE_UNLOAD_STARTING callback drains PC data, and that drain waits for the CUPTI guard. The timer held the guard across cuptiActivityFlushAll, which waits on a CUPTI pool thread, and the pool thread was waiting for the driver lock. The flush is the one guarded call that waits on CUPTI's workers. cuptiPCSamplingGetData "never does device synchronization", and the timer's periodic PC drain under the guard never hung. So the flush wrapper no longer takes the guard. It is renamed ActivityFlushAll_NeverUnderGuard and counts any call made while the guard is held. The exit report prints the count as cupti_flush_under_guard, which must stay 0. Every other CUPTI call stays guarded, and the raw name still does not compile. The reproducer is now shim/nvidia/testdata/cuda_unload.cu, and `make -C shim check-nvidia-unload` runs it under both PC tiers on GPU hardware. It fails on a hang, on an undrained unload, or on a flush made under the guard. With the flush put back under the guard, it fails on the first continuous run.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #214.
The bug
Under continuous PC sampling, a process that unloads modules from more than one thread deadlocked on every run (15 of 15). There were three threads in the cycle:
cuModuleUnloadholds a driver lock. CUPTI'sMODULE_UNLOAD_STARTINGcallback then callspc_drain_all, which blocks on the CUPTI guard.cuptiActivityFlushAll, which waits on a CUPTI pool thread.The unload drain cannot be skipped or deferred: in continuous mode, CUPTI requires a PC-data flush after every load-unload-load, or two different instructions end up sharing a PC identity.
Which edge closes the cycle
I tested this with temporary switches on the reproducer (now
shim/nvidia/testdata/cuda_unload.cu):The flush is the only guarded call that waits on CUPTI's workers.
cupti_pcsampling.hsayscuptiPCSamplingGetData"never does device synchronization", and the timer's periodic PC drain under the guard never hung.cuptiActivityFlushPeriodis no substitute for the flush, because it only returns full buffers.The change
ActivityFlushAllbecomesActivityFlushAll_NeverUnderGuard, which doesn't take the guard. It counts any call made while the calling thread holds the guard, and the exit report prints that count ascupti_flush_under_guard, which must be 0. Every other CUPTI call stays guarded, and the rawcuptiActivityFlushAllname still doesn't compile.nvidia/cupti_guard.h. The adapter's comments on the unload drain and on the PC-context lock are updated to match.check-cupti-guard. Its compliant example now flushes after the guarded scope closes, not inside it.make -C shim check-nvidia-unloadruns the reproducer under both PC tiers, 3 times each. It fails on a hang (SIGTERM only, never SIGKILL), on an undrained unload, or on a flush made under the guard. It needs a GPU and CUPTI, so it is not part ofmake testor CI.The risk
The flush now runs at the same time as an application thread's guarded PC-sampling calls. That only happens on the unload, context-create and context-destroy callbacks, and on Tier A's start/stop at launch. CUPTI doesn't document whether that pairing is safe. The alternative is a measured permanent hang, and nothing went wrong in any run below.
Reading #99's trace again, it had the same shape: one of our locks held across a call that needed the driver lock, with the flush as the third party, not the cause. The counter means that a future change that puts the flush back under a
CallScopeshows up in the exit report, not as a hang.Verification
All runs are shim only, on an RTX 3090 with driver 610.57.04, CUDA 13.3 CUPTI and kernel 6.19.10.
make -C shim testandmake -C shim check-cupti-guardpass.check-nvidia-unload: 6 of 6 pass.CallScope, it fails on the first continuous run with a hang.module_unload_drains=1000, about 154k PCs,getdata_failed=0andcupti_flush_under_guard=0.start_failed=0andstop_failed=0.