Repository navigation
HIP gfx1030, 2x RX 6900 XT: verify hangs after a long prompt when MMQ prompt path, adaptive expert swaps and SDMA meet (any one off avoids it) #884
Description
Summary
On 2x RX 6900 XT (gfx1030, HIP, Linux), engine 0.1.39 hangs on the first verify window after a ~31K-token prompt, once the engine has served about 7 earlier requests: verify: timed out at layer 1; its GPU waits were released but the GPU did not finish within 5 s (#267). Stock 6f32ec0 at default settings hung in 5 of 5 runs of the sequence below (and in 11 of 12 more runs of it with one diagnostic setting that did not avoid it, listed further down).
It needs three things at once; taking away any one of them stops it in my runs: the MMQ prompt path (STRATA_PREFILL_MMQ=0: 0/4), the primary card's adaptive expert swaps (--adapt-every 100000: 0/7), and SDMA copies (HSA_ENABLE_SDMA=0: 0/4). At the hang the primary card is 98-99% busy with no waves resident: its command processor is stuck at a wait, not a kernel. The root cause is not known yet; what is measured and what is not is listed below.
This is not #649, although the message is the same; see the last section.
Environment
- Strata
6f32ec0(engine 0.1.39),build-hipwith-DSTRATA_PREFILL_MMQ=ON(as setup.py builds for AMD), gfx1030 - 2x RX 6900 XT 16 GB, PCIe 4.0 x8 each; Ryzen 5 5600X; 128 GB DDR4 (about 69 GB still free at the hang)
- Linux 7.0, ROCm 10.0
- Model: Qwen3.8-Flash-Next GSQ-RCO IQ3_S, native pack,
--ple-gguf,--mtpdraft head - Arguments:
--expert-cache auto --prefill auto --spec 4 --spec-min-p 0.5 --max-context 131072 --kv int8 --kv-resident 32768 --expert-cache-device1 auto --remote-expert-opt --pcie-frac 0,HIP_VISIBLE_DEVICES=1,0(the expert arena, 46.84 GiB, is loaded whole andcudaHostRegistered)
Reproduction
A fresh server, then 8 chat requests, one after the other (greedy, thinking off, max_tokens 256): 7 short ones of 1.0K-4.9K tokens, then one of 31,219 tokens. The texts are slices of the repository's own docs and sources; the content does not matter (other slices hang the same way).
- Stock, default settings, this sequence: 5 of 5 runs hung on the 31K request. With
--prompt-cache 12instead of the default 6: 1 of 2. - With 5 or fewer earlier requests before the 31K one: 0 of 5 (one of those with
--prompt-cache 2). - Every stock hang was the first verify window after the batched prompt (6 tokens at position 31212). The one hang in the batched prompt read itself -
no progress for 60 s ... (reading the prompt (batched): waiting for the GPU (attention, router) at layer 1 of the prompt chunk from token 0)- was in theSTRATA_SH_STREAM=0arm below.
STRATA_VERIFY_TRACE=1
The host serves layer 0 within about 3.3 ms (flag 1 A 1 B 1). The GPU's breadcrumbs stop after layer 0's waitCPU; combine is never stamped:
layer 0 group 0: pre(0)=0.0 hc-read0(1)=101.6 ... waitB(21)=661.2 PCIe grp(22)=672.4 waitCPU(23)=3849.1
TIMEOUT window 410 step 1 layer 1 aux 600 | seq 1 flag 1 A 1 B 1
RELEASE window 410 ... | seq 1 flag 1 A 1 B 1
RELEASE-NOT-DRAINED window 410 ... aux 5000 | seq 1 flag 4294967295 A 4294967295 B 4294967295
The same in every hang I traced (5 runs). The CPU pool is not starved: it serves the layer in milliseconds.
The A/B settings suggested for this message in #649
Each was confirmed in the engine's own startup line or log, one per run, stopping at the first hang:
| setting | result |
|---|---|
STRATA_VERIFY_COHERENT=1 ("coherent words explicit") |
hung on run 1 |
STRATA_DOORBELL_STORE=1 ("doorbell stored") |
hung on run 1 |
STRATA_RESIDENT_PIN=0 |
does not apply here (only with --resident-experts / --resident-budget-gib); instead STRATA_ARENA_PIN_GIB=1 ("0 slices pinned (0 GiB)", the whole arena pageable): hung on run 1 |
What the GPU is doing at the hang
- Busy:
gpu_busy_percentof the primary card (0000:0f:00.0) is 98-99% for the whole hang; the helper card is at 0%. - Kernel log: no amdgpu / kfd / ttm messages at all.
- rocgdb: I attached with rocgdb (this needed an opt-in
prctl(PR_SET_PTRACER)in a diagnostic build). One HSA queue of the primary card has 2,781 packets pending. The packet at its read index is a dispatch ofgr_norm_split_kernel, grid [1536,4,1], which is the hyper-connection read for this window's 6 tokens. Another HSA queue has 376 packets pending, and its head packet is not a dispatch. The process has no AMDGPU waves at all. Together with the 99% busy reading, the command processor is waiting on something; no shader is spinning. - Attach changes the outcome: in the two runs where rocgdb attached, the GPU finished about 2.9 s after the release (
RELEASE-DRAINED). In the four runs without it, it did not finish within 5 s. Attaching makes KFD unmap and remap the process's queues, so I could not read the head packets' signals with rocgdb before the state changed.
Bisection
One setting per arm, up to 4 runs per arm, stopping at the first hang:
| arm | hangs |
|---|---|
| stock, default settings | 5 of 5 |
--adapt-every 100000 |
0 of 7 |
primary card's adaptive swaps only (helper's RemoteExpertOpt::adapt switched off in a diagnostic build) |
hung on run 1 |
helper's RemoteExpertOpt::adapt only (primary's swaps switched off) |
0 of 4 |
HSA_ENABLE_SDMA=0 |
0 of 4 |
STRATA_SH_STREAM=0 |
hung on run 2 |
STRATA_PREFILL_MMQ=0 (same -DSTRATA_PREFILL_MMQ=ON binary; checked in the engine's /proc/<pid>/environ) |
0 of 4 |
During the hangs, VRAM and GTT use were flat (sampled every ~1.6 s), and amd-evicted-vram in the engine's DRM fdinfo stayed 0.
Reading (not proven)
Three things are each necessary: the primary card's adaptive swaps (cudaMemcpyAsync H2D on adapt_stream, run on an SDMA engine), SDMA itself, and the MMQ prompt path. Both prompt paths stream the expert blobs over the copy stream, but the MMQ path adds per-group cross-stream waits and events (#372). The sequence is: swaps land in cache slots during the previous request's decode; the next prompt borrows cache slots and MMQ runs its groups there; right after the slots are refilled, the first verify window stalls. At the hang, the primary card's command processor sits on a cross-queue wait that queue remapping releases.
What I have not shown is whether the signal it waits on has already completed (a stale read or lost wakeup on the ROCm side) or genuinely has not (the copy, or an event dependency). Next I plan to read the head packets and their signal values from inside the process through an HSA_TOOLS_LIB shim, because rocgdb disturbs the state. Also not explained: why it needs about 7 earlier requests.
Cost of the three settings that avoid it
Same requests, means of 3-4 runs:
--adapt-every 100000: the prompt read is unchanged; decode is 3-11% slower (54-62 vs 60-69 t/s).HSA_ENABLE_SDMA=0: decode is unchanged; the prompt read is 10-24% slower (short prompts 219-235 vs 287-308 t/s, 4K 385-405 vs 450-459, 31K 426 vs 471; the 31K baseline is the--adapt-every 100000arm's, since the stock runs hung there, and that switch does not touch the prompt read).STRATA_PREFILL_MMQ=0: decode is unchanged; the prompt read is 28-42% slower (short prompts 175-179 vs 285-307 t/s, 4K 297-314 vs 449-458, 31K 339 vs 471).
Not #649
#649 prints the same message, but there it hangs in decode at varying layers, on --resident-budget-gib with 31 GB of RAM, and STRATA_RESIDENT_PIN=0 avoided it. Here it hangs right after a long prompt, always between layers 0 and 1, with 128 GB of RAM, and an unpinned arena does not avoid it.
I can run an instrumented build here, or share the full logs and the request script.
Edited 2026-10-05: the stock run count was corrected from "8 of 10" to 5 of 5 at default settings (the earlier figure mixed in runs with other settings); the prompt-read hang is attributed to the STRATA_SH_STREAM=0 arm, and the 31K baseline of the cost section is marked as the --adapt-every arm's.
Activity
Vop3p commentedon Oct 5, 2026
Update: at the hang, the barrier packet that holds everything up depends on a signal that has already completed, and the command processor does not move past it.
How it was read
An
HSA_TOOLS_LIBshim (157 lines of C++) that wrapshsa_queue_createandhsa_amd_queue_createand records every AQL queue the process creates. When a trigger file appears, it writes, for each queue with pending packets, the read and write indices, the packets around the read index, and the current value of each packet's completion signal and of each barrier packet's dependency signals. It reads the same signals and queues again 2 s later. It only reads memory from inside the process; there is no debugger and no queue remap.Checked on a small program first: stream B waits on an event that stream A records after a kernel spinning on a host flag. The shim showed B's queue head as
BARRIER_ANDwith its dependency signal at 1, and that signal was the completion signal of A's event-record packet. After the host flag was set, the same signal read 0, both queues were empty, and the program's result was correct.At the hang
Stock
6f32ec0, the same reproduction andSTRATA_VERIFY_TRACE=1as above, plusHSA_TOOLS_LIBset to the shim (the only change). It hung on run 1 (RELEASE-NOT-DRAINED). Both reads, 2 s apart, were identical:BARRIER_ANDBARRIER_ANDThe signal the second queue waits on is not the completion signal of any packet the shim printed (from two packets before to eight after each queue's read index); it did not print the rest of the pending packets.
Reading
The stall starts at a barrier-AND packet whose only dependency has completed and which the command processor does not release; the verify stream then waits behind it, legitimately, on that queue's later dispatch. This fits the earlier observations: the primary card at 98-99% busy with no waves resident, and the queues moving again after a remap (a rocgdb attach). In this snapshot the dependency chain Strata set up is satisfied, so I read this as a ROCm-side problem: the command processor does not see a signal that has completed. I have not shown why.
Not shown yet: where the completed signal comes from. It is not among the completion signals in the printed windows. Since
HSA_ENABLE_SDMA=0avoids the hang, I suspect an SDMA copy's completion signal, but I have no direct evidence of that. Next, the shim will also record the completion signals passed tohsa_amd_memory_async_copy*, so the next hang can be matched against them. If it is an SDMA copy, I will try to reproduce this outside Strata and report it to ROCm.I can share the shim's source if it is useful.
Vop3p commentedon Oct 5, 2026
Update 2: the barrier that never passes is inserted by
hipGraphLaunchinto the queue that runs the verify graph's shared-expert branch, not by anyhipStreamWaitEventin Strata.What was added
The queue shim now samples every queue's read and write index every 20 ms. At a hang it reports how long each read index has not moved and when the head packet first appeared. Checked on a small program, where a kernel holds a queue for about 1 s before the dump: it reported 1,080-1,180 ms.
An
LD_PRELOADwrapper recordshipStreamWaitEvent,hipEventRecordandhipGraphLaunch: the time, stream, event and return address of each call. The return addresses are resolved to functions withaddr2line. Checked on a small program, where it found both waits and both records, at the right lines.A first version also called
hipEventQueryinside the wait wrapper. That invalidated the engine's global-mode graph capture ("operation failed due to a previous error during capture"), so that run is discarded. The version used below makes no extra HIP calls excepthipStreamIsCapturing, and all five verify windows were captured without error.Run
Stock
6f32ec0, the same reproduction withSTRATA_VERIFY_TRACE=1, plus both tools loaded. Run 1 finished all 8 requests without a hang. Run 2 hung (RELEASE-NOT-DRAINED). Both tools dumped on the same trigger; their dump times can differ by up to about 0.2 s.Queue shim:
BARRIER_AND(dependency signal value 0) had not moved its read index for 86.7 s. But its head packet first appeared only 20.3 s before the dump; before that the queue was empty. It had been idle through the whole 31K prompt read.hsa_signal_silent_storeto 1, in a batch of adjacent pool signals) 89.0 s before the dump, at the end of the previous request's decode.Wrapper:
hipStreamWaitEventoutside stream capture was 20.66 s before the dump, inPrefill::run_impl(the prompt's compute stream waiting on an event the copy thread had recorded 0.26 s earlier).hipGraphLaunchin the last 30 s was 20.20 s before the dump, fromVerifier::run: the hung verify window.Which branch: the first dispatch behind the stuck barrier has grid (15360,1,1), which is 6 tokens x 2560: the shared-expert branch's f32-to-bf16 conversion (
STRATA_SH_STREAM,sh_cs_in the captured graph). So the stuck queue is the one HIP runs that graph branch on.Reading
When the verify graph is launched after its shared-expert branch's queue has been idle for a long time, the
BARRIER_ANDthat HIP puts at the head of that branch depends on a signal last armed 89 s earlier, during the previous request's decode. That signal completed long ago, and the command processor does not release the barrier. The main stream then waits for the branch's result, and the window stalls. I infer, but have not shown, that this dependency is HIP's marker for the branch's previous launch.Strata issues no wait here that could be removed, so the barrier itself looks like a HIP/ROCm-side problem. Still not explained: why the MMQ prompt path, the adaptive swaps and SDMA are each needed for it to happen.
One more data point: the
STRATA_SH_STREAM=0arm in the bisection table above (two runs) did hang once, but in the batched prompt read at layer 1, not in a verify window. So the shared-expert fork may be only one of the places where this happens, andSTRATA_SH_STREAM=0is not a workaround I can confirm. That is a single run, though.Next, I will try a standalone HIP reproduction: a captured graph with a forked branch, launched repeatedly; then the branch's queue left idle while other streams run SDMA copies; then launched again. If it reproduces, I will report it to ROCm with the program.
xyzzing commentedon Oct 5, 2026
I'm trying to work out the HIP optimizations, please kindly share notes. My are at https://github.com/xyzzing/Strata
hip: STRATA_HIP_ADAPT_KERNEL_COPY=1 copies adaptive swaps with a kern…
Niko1221 commentedon Oct 6, 2026
Great analysis, thank you. The barrier that never passes looks like a ROCm problem, so please report it to ROCm. Meanwhile 0.1.40 has an opt-in workaround: STRATA_HIP_ADAPT_KERNEL_COPY=1 does the adaptive swaps' host-to-device copies with a kernel instead of SDMA (HSA_ENABLE_SDMA=0 and --adapt-every 100000 also avoid it). We could not test it on 2x gfx1030. Please tell us if it helps.
hip: STRATA_HIP_ADAPT_KERNEL_COPY=1 copies adaptive swaps with a kern…
hip: STRATA_HIP_ADAPT_KERNEL_COPY=1 copies adaptive swaps with a kern…
Vop3p commentedon Oct 6, 2026
Yes, it helps here.
STRATA_HIP_ADAPT_KERNEL_COPY=1on 0.1.40.1 (82f46a8, stock), 2x RX 6900 XT layer split, adaptive swaps on, GFXOFF at the kernel's default, the repository'sbenchmark.py(4,096 / 32,768 / 128,000 tokens x 3), one cold server per row, run alternately this evening:STRATA_HIP_ADAPT_KERNEL_COPY=1The engine logs
STRATA_HIP_ADAPT_KERNEL_COPY=1: adaptive swaps copy with a kernel, not SDMA (#884)in every run with it, and it costs nothing measurable: prompt 444-448 / 721-724 / 820-821 tok/s and decode 62.0 / 66.9 / 58.9 against 446 / 723 / 821 and 62.1 / 67.0 / 59.0 without it. Three against three is a small sample, but the control stalled in the same hour.Two more things found since my last update, both on this machine:
/sys/kernel/debug/dri/N/amdgpu_gfxoff, 4 bytes, 0), the 0.1.39 stack that hung in 7 of 8 runs of the sequence in this issue hung in 0 of 30, and 0.1.40.1's layer split ran 54 requests at 4K-128K without a stall (the 0.1.40.1 report in bench: community report, 2x RX 6900 XT (gfx1030) + Ryzen 5 5600X, IQ3_S at 131K on 0.1.40.1 - one card, layer split and expert helper; stock, the two gfx103x switches, and #1150 #1151 #1167 #1270 was measured that way for the two-card rows). It does not change speed. One trap if anyone repeats it: the knob is a reference count, not a level - a 0 written is only undone by a later 1, so a wrapper that writes 0 per engine must write exactly one 1 per 0. amdgpu already disables GFXOFF during compute on GC 11 (older MES firmware), GC 12 and GC 9 APUs (amdgpu_amdkfd_set_compute_idle, "temporary workaround ... compute applications"); gfx10.3 is not on that list, which fits what we see.Reported to ROCm as asked: rocm-systems #12669 and #12638 have our reproduction on 10.0 / 10.1 / 10.2 and the queue state at the hang; I will add the GFXOFF result there. For daily use on this machine either the kernel copy or GFXOFF off around the engine is enough; we run the GFXOFF wrapper in production today.
Developed with an AI coding assistant; every number above was measured on 2x RX 6900 XT (gfx1030, PCIe 4.0 x8 each) / Ryzen 5 5600X, ROCm 10.0.
Niko1221 commentedon Oct 7, 2026
The queue stuck on a completed barrier signal inside hipGraphLaunch looks like a ROCm problem, so please report it to ROCm with your trace. In 0.1.40 there is an opt-in switch for the path, STRATA_HIP_ADAPT_KERNEL_COPY=1; try it with and without, and tell us. We keep this open meanwhile.
hip: STRATA_HIP_ADAPT_KERNEL_COPY=1 copies adaptive swaps with a kern…
Vop3p commentedon Oct 9, 2026
Notes are public now: https://github.com/xjc10/dual-6900xt-flash-next-tuning (an interactive timeline of every production state on 2x RX 6900 XT, README in English and Chinese, and the A/B harness).
What helped on gfx1030, in order of size:
STRATA_HIP_PROMPT_F16=1) — 32K prompt 723 → 1,616 tok/s together with the attention kernel of HIP gfx103x: PR #540's attention kernel with 8 cells per step and DPP lane exchanges (bit-exact, prompt +6-12% on RX 6900 XT) #849.STRATA_SH_STREAM=1: +5–9% on this box (the opposite of 0.1.39: decode on an RX 6800 (gfx1030, Windows) is 12-22% slower than 0.1.38; STRATA_SH_STREAM=0 removes it #816's RX 6800 on Windows).STRATA_HIP_ADAPT_KERNEL_COPY=1avoids it at no cost.v_dot4_i32_i8encoding instead ofv_mov 0 + v_dot4c: iq3_s kernel +2.6%, q6_K +4.7% (but −10% on q4_K/q5_K, so it is per-type).What did not help here:
--spec 3 --spec-min-p 0.70from your fork (flat to −2–3% on 6900 XT), tensor parallel (+6%), a smaller--kv-resident,--pipeline-windows 2(+1–6% but the output changes between starts), the 16-point spec/min-p grid (4/0.5 is the peak), and batch slots (resident experts 41% < the 50% gate).Your card is gfx1100 with WMMA, so the rocBLAS table and the SIMT scorer are gfx103x-specific; the FP16 path, SH_STREAM, kernel-copy and the encoder placement should carry over.
Developed with an AI coding assistant; every number above was measured on 2x RX 6900 XT (gfx1030, PCIe 4.0 x8 each) / Ryzen 5 5600X, ROCm 10.0.
gfx906: document why the deployment runs HSA_ENABLE_SDMA=0
Niko1221 commentedon Oct 13, 2026
Thanks for the report. To keep the tracker current we are closing issues older than three days: 0.1.44 is out with a long list of fixes, and many older reports are covered by it or by 0.1.43.1.
If this still happens on 0.1.44, please leave a comment here with your engine log (and the version line it prints), and we will reopen it. A new issue that links this one works too.
Vop3p commentedon Oct 13, 2026
Closing is fine from my side - for the record, a re-test on 0.1.43.1 (stock tag,
a0116b27) in the same conditions as the 0.1.40.1 run: 2x RX 6900 XT layer split, setup's arguments for an SSD, the adaptive tier at its defaults, GFXOFF at the kernel's default,benchmark.py4,096 / 32,768 / 128,000 tokens x 3, one cold server per row:STRATA_HIP_ADAPT_KERNEL_COPYSTRATA_HIP_ADAPT_KERNEL_COPY=1On 0.1.40.1 every server without the switch stalled at the first 128K request. The switch's line is printed from
adapt_copy_h2d(), so the swaps did run; speed is the same with and without it. I will re-run it on 0.1.44 and come back here only if it returns.One line in
docs/AMD_HIP.mdis out of date: "(untested on the reporter's machine)" could read "(on the reporter's 2x RX 6900 XT it prevented the hang on 0.1.40.1; on 0.1.43.1 the hang no longer reproduces there)".Developed with an AI coding assistant; every number above was measured on 2x RX 6900 XT (gfx1030, PCIe 4.0 x8 each) / Ryzen 5 5600X, ROCm 10.0.
zspitzer commentedon Oct 17, 2026
@Vop3p, this looks like a close relative of a hang I reported to ROCm from Windows: ROCm/rocm-systems#12535. RX 7900 XT (gfx1100), so RDNA3 and the PAL backend rather than RDNA2 and ROCr, but the same shape: small pinned H2D copies go to SDMA, and the next compute submission never retires although everything before it has. 0% busy, no waves, no TDR. Keeping the copies off SDMA cures it there too.
Since it shows up on two OSes, two driver stacks and two GPU generations, I'm wondering whether it lives below the runtime, and your GFXOFF result points the same way. Would you be up for running the standalone repro on your box? It's ~90 lines of plain PyTorch, no Strata or other app involved, and takes a few minutes:
https://github.com/zspitzer/rocm-windows-sdma-hang-gfx1100
loop.pystarts a fresh process per run, since it's a first-use hang, and printshung N of 20. A single run usually passes. On my box the default hangs in roughly 1 run in 3 (14 of 48), and with SDMA off in none. If it hangs with your GFXOFF wrapper on as well, a third arm would be very telling. The script printsSA PAL_DISABLE_SDMA=unseton its first line; that variable only means something on Windows, so ignore it.A hang on gfx1030 / Linux would mean this isn't Windows PAL at all, and I'd add it to the ROCm issue (and yours). A clean 0 of 20 is useful too. Thanks either way.