From 198cbb1a6bbb94609b2b3a4d9578161763c1f417 Mon Sep 17 00:00:00 2001 From: Uday Arora Date: Sun, 19 Jul 2026 03:46:00 -0700 Subject: [PATCH 1/2] cuda.core: validate pinned pool support Reject unsupported host memory pools during allocation instead of allowing a later copy to fail with CUDA_ERROR_INVALID_VALUE. Signed-off-by: Uday Arora --- .../core/_memory/_pinned_memory_resource.pyi | 14 +++++++ .../core/_memory/_pinned_memory_resource.pyx | 42 ++++++++++++++++++- cuda_core/tests/test_memory.py | 18 ++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/cuda_core/cuda/core/_memory/_pinned_memory_resource.pyi b/cuda_core/cuda/core/_memory/_pinned_memory_resource.pyi index a83cd8ea581..9cad97a1d0d 100644 --- a/cuda_core/cuda/core/_memory/_pinned_memory_resource.pyi +++ b/cuda_core/cuda/core/_memory/_pinned_memory_resource.pyi @@ -5,8 +5,11 @@ from __future__ import annotations import uuid from dataclasses import dataclass +from cuda.core._memory._buffer import Buffer from cuda.core._memory._ipc import IPCAllocationHandle from cuda.core._memory._memory_pool import _MemPool +from cuda.core._stream import Stream +from cuda.core.graph import GraphBuilder @dataclass @@ -63,6 +66,14 @@ class PinnedMemoryResource(_MemPool): Notes ----- + The device associated with ``stream`` must support host memory pools. If + ``numa_id`` is set or derived for IPC, it must support host NUMA memory pools. + You can query these capabilities through + ``Device.properties.host_memory_pools_supported`` and + ``Device.properties.host_numa_memory_pools_supported``. If the required pool + is unsupported and stream-ordered allocation is not needed, use + :class:`LegacyPinnedMemoryResource`. + To create an IPC-Enabled memory resource (MR) that is capable of sharing allocations between processes, specify ``ipc_enabled=True`` in the initializer option. When IPC is enabled and ``numa_id`` is not specified, the NUMA node @@ -76,6 +87,9 @@ class PinnedMemoryResource(_MemPool): def __init__(self, options: PinnedMemoryResourceOptions | dict[str, object] | None=None) -> None: ... + def allocate(self, size: int, *, stream: Stream | GraphBuilder) -> Buffer: + """Allocate a host-pinned buffer asynchronously on the supplied stream.""" + def __reduce__(self) -> tuple[object, ...]: ... diff --git a/cuda_core/cuda/core/_memory/_pinned_memory_resource.pyx b/cuda_core/cuda/core/_memory/_pinned_memory_resource.pyx index 4335fbb41c2..2caeaf24de0 100644 --- a/cuda_core/cuda/core/_memory/_pinned_memory_resource.pyx +++ b/cuda_core/cuda/core/_memory/_pinned_memory_resource.pyx @@ -5,9 +5,11 @@ from __future__ import annotations from cuda.bindings cimport cydriver -from cuda.core._memory._memory_pool cimport _MemPool, MP_init_create_pool, MP_init_current_pool +from cuda.core._memory._buffer cimport Buffer +from cuda.core._memory._memory_pool cimport _MemPool, _MP_allocate, MP_init_create_pool, MP_init_current_pool from cuda.core._memory cimport _ipc from cuda.core._memory._ipc cimport IPCAllocationHandle +from cuda.core._stream cimport Stream, Stream_accept from cuda.core._utils.cuda_utils cimport ( check_or_create_options, HANDLE_RETURN, @@ -19,6 +21,12 @@ import platform # no-cython-lint import uuid from cuda.core._utils.cuda_utils import check_multiprocessing_start_method +from cuda.core._utils.cuda_utils import CUDAError # no-cython-lint + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from cuda.core.graph import GraphBuilder __all__ = ['PinnedMemoryResource', 'PinnedMemoryResourceOptions'] @@ -78,6 +86,14 @@ cdef class PinnedMemoryResource(_MemPool): Notes ----- + The device associated with ``stream`` must support host memory pools. If + ``numa_id`` is set or derived for IPC, it must support host NUMA memory pools. + You can query these capabilities through + ``Device.properties.host_memory_pools_supported`` and + ``Device.properties.host_numa_memory_pools_supported``. If the required pool + is unsupported and stream-ordered allocation is not needed, use + :class:`LegacyPinnedMemoryResource`. + To create an IPC-Enabled memory resource (MR) that is capable of sharing allocations between processes, specify ``ipc_enabled=True`` in the initializer option. When IPC is enabled and ``numa_id`` is not specified, the NUMA node @@ -91,6 +107,30 @@ cdef class PinnedMemoryResource(_MemPool): def __init__(self, options: PinnedMemoryResourceOptions | dict[str, object] | None = None) -> None: _PMR_init(self, options) + def allocate(self, size_t size, *, stream: Stream | GraphBuilder) -> Buffer: + """Allocate a host-pinned buffer asynchronously on the supplied stream.""" + if self.is_mapped: + raise TypeError("Cannot allocate from a mapped IPC-enabled memory resource") + cdef Stream s = Stream_accept(stream) + device = s.device + + cdef bint supported + if self._numa_id >= 0: + supported = device.properties.host_numa_memory_pools_supported + else: + IF CUDA_CORE_BUILD_MAJOR >= 13: + supported = device.properties.host_memory_pools_supported + ELSE: + supported = True + + if not supported: + raise CUDAError( + f"CUDA_ERROR_NOT_SUPPORTED: CUDA device {device.device_id} does not " + "support the requested host memory pool for PinnedMemoryResource. " + "Use LegacyPinnedMemoryResource if memory-pool features are not required." + ) + return _MP_allocate(self, size, s) + def __reduce__(self) -> tuple[object, ...]: return PinnedMemoryResource.from_registry, (self.uuid,) diff --git a/cuda_core/tests/test_memory.py b/cuda_core/tests/test_memory.py index 6af4d025b03..8b4889c21f7 100644 --- a/cuda_core/tests/test_memory.py +++ b/cuda_core/tests/test_memory.py @@ -751,6 +751,24 @@ def test_pinned_memory_resource_initialization(init_cuda): buffer.close() +@pytest.mark.agent_authored(model="gpt-5.6") +def test_pinned_memory_resource_rejects_unsupported_host_pool(init_cuda): + device = init_cuda + try: + supported = device.properties.host_memory_pools_supported + except AttributeError: + pytest.skip("Host memory pool capability query requires CUDA 13.0 or later") + if supported: + pytest.skip("Device supports host memory pools") + + mr = PinnedMemoryResource() + try: + with pytest.raises(CUDAError, match="CUDA_ERROR_NOT_SUPPORTED.*LegacyPinnedMemoryResource"): + mr.allocate(1024, stream=device.default_stream) + finally: + mr.close() + + def test_managed_memory_resource_initialization(init_cuda): device = Device() skip_if_managed_memory_unsupported(device) From 5b7c9b1a1781a7ad266557f215a8b2f7655f3d6a Mon Sep 17 00:00:00 2001 From: Andy Jost Date: Thu, 6 Aug 2026 10:32:56 -0700 Subject: [PATCH 2/2] cuda.core: tighten pinned host pool capability check Drop the unnecessary CUDA 12 fence around host_memory_pools_supported, raise RuntimeError instead of a synthetic CUDAError, and keep the regression test hardware-gated for devices without host memory pools. --- .../core/_memory/_pinned_memory_resource.pyx | 23 ++++++++----------- cuda_core/tests/test_memory.py | 13 ++++------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/cuda_core/cuda/core/_memory/_pinned_memory_resource.pyx b/cuda_core/cuda/core/_memory/_pinned_memory_resource.pyx index 2caeaf24de0..e5f89606330 100644 --- a/cuda_core/cuda/core/_memory/_pinned_memory_resource.pyx +++ b/cuda_core/cuda/core/_memory/_pinned_memory_resource.pyx @@ -21,7 +21,6 @@ import platform # no-cython-lint import uuid from cuda.core._utils.cuda_utils import check_multiprocessing_start_method -from cuda.core._utils.cuda_utils import CUDAError # no-cython-lint from typing import TYPE_CHECKING @@ -113,21 +112,17 @@ cdef class PinnedMemoryResource(_MemPool): raise TypeError("Cannot allocate from a mapped IPC-enabled memory resource") cdef Stream s = Stream_accept(stream) device = s.device - - cdef bint supported - if self._numa_id >= 0: - supported = device.properties.host_numa_memory_pools_supported - else: - IF CUDA_CORE_BUILD_MAJOR >= 13: - supported = device.properties.host_memory_pools_supported - ELSE: - supported = True + cdef bint supported = ( + device.properties.host_numa_memory_pools_supported + if self._numa_id >= 0 + else device.properties.host_memory_pools_supported + ) if not supported: - raise CUDAError( - f"CUDA_ERROR_NOT_SUPPORTED: CUDA device {device.device_id} does not " - "support the requested host memory pool for PinnedMemoryResource. " - "Use LegacyPinnedMemoryResource if memory-pool features are not required." + raise RuntimeError( + f"CUDA device {device.device_id} does not support the requested " + "host memory pool for PinnedMemoryResource. Use " + "LegacyPinnedMemoryResource if memory-pool features are not required." ) return _MP_allocate(self, size, s) diff --git a/cuda_core/tests/test_memory.py b/cuda_core/tests/test_memory.py index 8b4889c21f7..5c98ec668ae 100644 --- a/cuda_core/tests/test_memory.py +++ b/cuda_core/tests/test_memory.py @@ -751,19 +751,16 @@ def test_pinned_memory_resource_initialization(init_cuda): buffer.close() -@pytest.mark.agent_authored(model="gpt-5.6") +@pytest.mark.agent_authored(model="cursor-grok-4.5") def test_pinned_memory_resource_rejects_unsupported_host_pool(init_cuda): + """allocate() must fail on devices without host memory pool support (see #2486).""" device = init_cuda - try: - supported = device.properties.host_memory_pools_supported - except AttributeError: - pytest.skip("Host memory pool capability query requires CUDA 13.0 or later") - if supported: + if device.properties.host_memory_pools_supported: pytest.skip("Device supports host memory pools") - mr = PinnedMemoryResource() + mr = PinnedMemoryResource(PinnedMemoryResourceOptions(max_size=POOL_SIZE)) try: - with pytest.raises(CUDAError, match="CUDA_ERROR_NOT_SUPPORTED.*LegacyPinnedMemoryResource"): + with pytest.raises(RuntimeError, match="does not support.*LegacyPinnedMemoryResource"): mr.allocate(1024, stream=device.default_stream) finally: mr.close()