From 779550b277aeffce29147425f13ba8ccca8c038e Mon Sep 17 00:00:00 2001 From: Yang Xu Date: Sat, 15 Aug 2026 22:48:36 -0700 Subject: [PATCH] frost: fix oversized-SMEM query crashing all frost GEMM on CUDA<13.4 cuda-python oversized_shared_memory_per_block() passed a bare attribute ordinal (150, CU_DEVICE_ATTRIBUTE_MAX_OVERSIZED_SHARED_MEMORY_PER_BLOCK, added in CUDA 13.4) to cuda-python's cuDeviceGetAttribute. That binding is strongly typed on the attribute -- it reads attrib.value -- so a bare int (for an enum member the installed cuda-python does not carry; 13.0.2 tops out at 148) raises "'int' object has no attribute 'value'". The query is on the tile-selection hot path (_sm_smem_budget_bytes_of), so this one call took down every frost GEMM kernel: on develop tip the frost gemm suite is 5641 failed / 163 passed, all with that single signature; the query was introduced in #593. Gate on the driver's CUDA version instead: the attribute arrived in 13.4, so a driver older than that has no such mode -> 0 by design (not an error), and the enum member -- which an older cuda-python lacks -- is never touched. From 13.4 the attribute is real, so query it via the proper enum and let a genuine failure raise rather than masking it as 0. This keeps "expected absence" (below 13.4) distinct from an unexpected driver error, and needs no ctypes / bare-ordinal workaround. Validated: frost gemm suite 5804 passed / 0 failed after the fix (was 5641 failed / 163 passed); test_public_execute_flavors.py 30 passed on py3.12 (fe-jax, driver 13.2 -> returns 0). Build-time + lru_cached: 0.38us first call, 50ns cached, never on the execute path. Co-Authored-By: Claude Opus 4.8 --- python/cudnn/frost/device.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/python/cudnn/frost/device.py b/python/cudnn/frost/device.py index c66f60177..de5db84ef 100644 --- a/python/cudnn/frost/device.py +++ b/python/cudnn/frost/device.py @@ -119,20 +119,22 @@ def shared_memory_per_block_optin(device: int) -> int: return int(_ck(*drv.cuDeviceGetAttribute(drv.CUdevice_attribute.CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_BLOCK_OPTIN, handle))) -# CU_DEVICE_ATTRIBUTE_MAX_OVERSIZED_SHARED_MEMORY_PER_BLOCK. Named in CUDA 13.4's -# cuda.h; cuda-python's CUdevice_attribute does not carry it yet, so ask by ordinal. -_ATTR_MAX_OVERSIZED_SHARED_MEMORY_PER_BLOCK = 150 - - @functools.lru_cache(maxsize=None) def oversized_shared_memory_per_block(device: int) -> int: """Per-CTA SMEM ceiling in the *oversized* carveout (327 KiB vs the 227 KiB opt-in limit on SM 10.7), which the part gives by shrinking L1 to 8 kB — free for a TMA-fed GEMM. 0 when the driver has no such mode.""" drv = _driver() + # CU_DEVICE_ATTRIBUTE_MAX_OVERSIZED_SHARED_MEMORY_PER_BLOCK arrived in CUDA 13.4. + # A driver older than that has no such mode -> 0 by design (not an error), and we + # do not touch the enum member (which an older cuda-python's CUdevice_attribute + # does not carry -- passing a bare ordinal would raise, since the binding reads + # attrib.value). From 13.4 the attribute is real: query it and let a genuine + # failure raise rather than masking it as 0. + if int(_ck(*drv.cuDriverGetVersion())) < 13040: + return 0 handle = _device_handle(device) - err, value = drv.cuDeviceGetAttribute(_ATTR_MAX_OVERSIZED_SHARED_MEMORY_PER_BLOCK, handle) - return int(value) if int(err) == 0 else 0 + return int(_ck(*drv.cuDeviceGetAttribute(drv.CUdevice_attribute.CU_DEVICE_ATTRIBUTE_MAX_OVERSIZED_SHARED_MEMORY_PER_BLOCK, handle))) @functools.lru_cache(maxsize=None)