[CUDA] Fix XQA GroupQueryAttention cudaErrorInvalidValue on Blackwell (sm_120)#29706
Open
tianleiwu wants to merge 2 commits into
Open
[CUDA] Fix XQA GroupQueryAttention cudaErrorInvalidValue on Blackwell (sm_120)#29706tianleiwu wants to merge 2 commits into
tianleiwu wants to merge 2 commits into
Conversation
Wayne-Ch
approved these changes
Jul 14, 2026
Wayne-Ch
left a comment
There was a problem hiding this comment.
Approving from the Foundry Local customer validation perspective. This directly matches the HY-MT1.5/HY-MT2 RTX 5090 failure we reproduced: the reduced/no-cuDNN CUDA EP path fails in GroupQueryAttention with cudaErrorInvalidValue, while the full/cuDNN EP path passes on the same machine. Assuming CI completes successfully, this is needed to unblock the next validation package.
baijumeswani
approved these changes
Jul 14, 2026
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.
Summary
GroupQueryAttention's XQA decode kernel failed on consumer Blackwell GPUs (RTX 50-series, sm_120) with
cudaErrorInvalidValue, while working fine on A100 (sm_80) and H200 (sm_90). This adds a runtime shared-memory capability check so XQA is only selected when the device can actually satisfy the kernel's dynamic shared-memory request, and otherwise falls back to cuDNN SDPA / Flash. A100/H200 behavior is unchanged.Root cause
XQA bakes its shared-memory layout at compile time from
__CUDA_ARCH__:preferedKHeadPartBytes=128,cacheVTileSeqLen=64) → up to ~140 KB of dynamic shared memory for head_size 128/256.Release/packaging binaries are built with a maximum arch of
90-virtual(compute_90 PTX only, no native sm_120 SASS). On sm_120 the driver JIT-compiles that sm_90 PTX, so the kernel'ssmemSizecarries the Hopper value (~140 KB).launchMHAthen callscudaFuncSetAttribute(..., cudaFuncAttributeMaxDynamicSharedMemorySize, size), which exceeds sm_120's ~99 KB per-block opt-in limit (sharedMemPerBlockOptin) and returnscudaErrorInvalidValue. A100 (163 KB) and H200 (227 KB) have enough room, so they were unaffected.Key changes
xqa/xqa_impl_gen.cuhGetSmemSize()host helper that reads the per-kernelsmemSizedevice symbol (accurate even for a PTX kernel JIT-compiled for the running SM).xqa/xqa_loader_fp16_impl.cuhGetXQAKernelSmemBytes(group_size)head-dim dispatcher. The non-quantized fp16 footprint is an upper bound for the int8/fp8/bf16 variants (smaller cache element), so one query covers all XQA paths.xqa/xqa_loader_fp16.cu,xqa/xqa_loader.hGetXQARequiredSharedMemoryBytes(device_prop, head_size, num_heads, kv_num_heads); a single non-templated entry point used by both the fp16 and bf16 GQA kernels.group_query_attention.cc,group_query_attention.hrequired_smem <= device_prop.sharedMemPerBlockOptin; fall back to cuDNN SDPA / Flash when it does not fit. Result is cached per node (xqa_shared_memory_ok_) since head_size/group are constant.xqa/mha_impl.cuhlaunchMHA: if the requested shared memory still exceeds the device limit, throw an actionable message (which SM to build for / how to disable XQA) instead of the opaquecudaErrorInvalidValue.CUDA graph safety
GetXQARequiredSharedMemoryBytesusescudaMemcpyFromSymbol, which synchronizes and is illegal during CUDA graph capture. The query is:onnxruntime::llm::common::isCapturing(Stream(context))so the synchronizing call is only issued when the compute stream is not capturing;If the value is somehow still unresolved while capturing, XQA is conservatively skipped for that run (safe fallback) without caching, so a later non-capturing run can resolve it. Warm-up and capture therefore make the same XQA/fallback decision, keeping the captured graph consistent with replay.
Testing notes
CMAKE_CUDA_ARCHITECTURES="80;90"(the configuration that reproduces the failure); all compile cleanly.cudaErrorInvalidValue. SetORT_ENABLE_ATTENTION_KERNEL_DEBUG_INFO=1to confirm the selected backend.120inCMAKE_CUDA_ARCHITECTURES(and100for datacenter Blackwell). With a native sm_120 cubin the layout is ~80 KB and fits, so XQA is selected.