From e7a3851e3b502778a4cdebf5c4ad5a365afbd3f1 Mon Sep 17 00:00:00 2001 From: Martin Durant Date: Tue, 3 Nov 2026 13:40:58 -0500 Subject: [PATCH 1/2] [fix] VM resource options --- .../core/_memory/_virtual_memory_resource.pyi | 13 ++++++----- .../core/_memory/_virtual_memory_resource.pyx | 22 ++++++++++--------- cuda_core/cuda/core/typing.py | 2 +- cuda_core/docs/source/release/1.3.0-notes.rst | 9 ++++++++ cuda_core/tests/test_memory.py | 13 +++++++++++ 5 files changed, 42 insertions(+), 17 deletions(-) diff --git a/cuda_core/cuda/core/_memory/_virtual_memory_resource.pyi b/cuda_core/cuda/core/_memory/_virtual_memory_resource.pyi index 536699eed2e..73c42646e7c 100644 --- a/cuda_core/cuda/core/_memory/_virtual_memory_resource.pyi +++ b/cuda_core/cuda/core/_memory/_virtual_memory_resource.pyi @@ -28,18 +28,19 @@ class VirtualMemoryResourceOptions: Attributes ---------- allocation_type: :obj:`~_memory.VirtualMemoryAllocationType` | str - Controls the type of allocation. + Controls the type of allocation. Only ``"pinned"`` is supported; + use :class:`ManagedMemoryResource` for managed memory. location_type: :obj:`~_memory.VirtualMemoryLocationType` | str Controls the location of the allocation. ``"host_numa"`` requires ``numa_id``. ``"host_numa_current"`` is rejected because the driver does not accept it for physical allocations. - handle_type: :obj:`~_memory.VirtualMemoryHandleType` | str + handle_type: :obj:`~_memory.VirtualMemoryHandleType` | str | None Export handle type for the physical allocation, and the switch for sharing buffers with other processes (see :attr:`VirtualMemoryResource.is_ipc_enabled`). Use ``"posix_fd"`` on - Linux if you plan to import/export the allocation. Use `None` if you - don't need an exportable handle. ``location_type="host"`` requires - `None`. + if you plan to import/export the allocation. Use `None` if you + don't need an exportable handle. The default on Windows is `None`, + "posix_fd" otherwise. ``location_type="host"`` requires `None`. gpu_direct_rdma: bool Hint that the allocation should be GDR-capable (if supported). granularity: :obj:`~_memory.VirtualMemoryGranularityType` | str @@ -64,7 +65,7 @@ class VirtualMemoryResourceOptions: """ allocation_type: VirtualMemoryAllocationType = VirtualMemoryAllocationType.PINNED location_type: VirtualMemoryLocationType = VirtualMemoryLocationType.DEVICE - handle_type: VirtualMemoryHandleType = VirtualMemoryHandleType.POSIX_FD + handle_type: VirtualMemoryHandleType | None = field(default_factory=lambda: None if platform.system() == 'Windows' else VirtualMemoryHandleType.POSIX_FD) granularity: VirtualMemoryGranularityType = VirtualMemoryGranularityType.RECOMMENDED gpu_direct_rdma: bool = False addr_hint: int | None = 0 diff --git a/cuda_core/cuda/core/_memory/_virtual_memory_resource.pyx b/cuda_core/cuda/core/_memory/_virtual_memory_resource.pyx index 90ac28b1d54..241abca092d 100644 --- a/cuda_core/cuda/core/_memory/_virtual_memory_resource.pyx +++ b/cuda_core/cuda/core/_memory/_virtual_memory_resource.pyx @@ -58,7 +58,6 @@ from typing import TYPE_CHECKING, Iterable from cuda.core._device import Device, _device_from_uuid from cuda.core._utils.cuda_utils import driver -from cuda.core._utils.version import BUILD_CUDA_MAJOR from cuda.core.typing import ( DevicePointerType, VirtualMemoryAccessType, @@ -97,18 +96,19 @@ class VirtualMemoryResourceOptions: Attributes ---------- allocation_type: :obj:`~_memory.VirtualMemoryAllocationType` | str - Controls the type of allocation. + Controls the type of allocation. Only ``"pinned"`` is supported; + use :class:`ManagedMemoryResource` for managed memory. location_type: :obj:`~_memory.VirtualMemoryLocationType` | str Controls the location of the allocation. ``"host_numa"`` requires ``numa_id``. ``"host_numa_current"`` is rejected because the driver does not accept it for physical allocations. - handle_type: :obj:`~_memory.VirtualMemoryHandleType` | str + handle_type: :obj:`~_memory.VirtualMemoryHandleType` | str | None Export handle type for the physical allocation, and the switch for sharing buffers with other processes (see :attr:`VirtualMemoryResource.is_ipc_enabled`). Use ``"posix_fd"`` on - Linux if you plan to import/export the allocation. Use `None` if you - don't need an exportable handle. ``location_type="host"`` requires - `None`. + if you plan to import/export the allocation. Use `None` if you + don't need an exportable handle. The default on Windows is `None`, + "posix_fd" otherwise. ``location_type="host"`` requires `None`. gpu_direct_rdma: bool Hint that the allocation should be GDR-capable (if supported). granularity: :obj:`~_memory.VirtualMemoryGranularityType` | str @@ -134,7 +134,9 @@ class VirtualMemoryResourceOptions: allocation_type: VirtualMemoryAllocationType = VirtualMemoryAllocationType.PINNED location_type: VirtualMemoryLocationType = VirtualMemoryLocationType.DEVICE - handle_type: VirtualMemoryHandleType = VirtualMemoryHandleType.POSIX_FD + handle_type: VirtualMemoryHandleType | None = field( + default_factory=lambda: None if platform.system() == "Windows" else VirtualMemoryHandleType.POSIX_FD + ) granularity: VirtualMemoryGranularityType = VirtualMemoryGranularityType.RECOMMENDED gpu_direct_rdma: bool = False addr_hint: int | None = 0 @@ -170,12 +172,11 @@ class VirtualMemoryResourceOptions: VirtualMemoryLocationType.HOST_NUMA_CURRENT: _l.CU_MEM_LOCATION_TYPE_HOST_NUMA_CURRENT, } _t = driver.CUmemAllocationType - # CUDA 13 added MANAGED to CUmemAllocationType. The CUDA 12 build has no such member. + # cuMemCreate only supports PINNED, even on CUDA 13 builds with a MANAGED enum member. _allocation_type = {VirtualMemoryAllocationType.PINNED: _t.CU_MEM_ALLOCATION_TYPE_PINNED} # noqa: RUF012 - if BUILD_CUDA_MAJOR >= 13: - _allocation_type[VirtualMemoryAllocationType.MANAGED] = _t.CU_MEM_ALLOCATION_TYPE_MANAGED def __post_init__(self) -> None: + self._allocation_type_to_driver(self.allocation_type) # Allocation and pickling each read the peers, so a one-shot iterable # such as a generator must not reach them. self.peers = tuple(int(peer) for peer in self.peers) @@ -490,6 +491,7 @@ cdef class VirtualMemoryResource(MemoryResource): Shared by ``__init__`` and ``modify_allocation``, so a per-call configuration is held to the same rules as the resource's own. """ + VirtualMemoryResourceOptions._allocation_type_to_driver(cfg.allocation_type) if cfg.location_type != self.config.location_type: raise ValueError( f"config.location_type {str(cfg.location_type)!r} does not match the resource's " diff --git a/cuda_core/cuda/core/typing.py b/cuda_core/cuda/core/typing.py index 90ccab61a23..f4089674504 100644 --- a/cuda_core/cuda/core/typing.py +++ b/cuda_core/cuda/core/typing.py @@ -198,7 +198,7 @@ class VirtualMemoryAllocationType(StrEnum): """Physical memory type for a virtual memory backing allocation. * ``PINNED`` — pinned/non-migratable physical allocation (placement via :class:`VirtualMemoryLocationType`). - * ``MANAGED`` — CUDA managed (unified) memory (CUDA 13+ only). + * ``MANAGED`` — reserved; not supported by :class:`~cuda.core.VirtualMemoryResource`. """ PINNED = "pinned" diff --git a/cuda_core/docs/source/release/1.3.0-notes.rst b/cuda_core/docs/source/release/1.3.0-notes.rst index 7a0d6cb0e01..ed51a055d09 100644 --- a/cuda_core/docs/source/release/1.3.0-notes.rst +++ b/cuda_core/docs/source/release/1.3.0-notes.rst @@ -367,6 +367,15 @@ Fixes and enhancements ``repr`` now shows ``U`` for unified operands. (`#2649 `__) +- :class:`VirtualMemoryResourceOptions` rejects ``allocation_type="managed"`` + with ``ValueError`` as ``cuMemCreate`` only supports + pinned allocations. Use :class:`ManagedMemoryResource` for managed memory. + (`#2910 `__) + +- The default :class:`VirtualMemoryResourceOptions` ``handle_type`` on Windows + is now ``None``, avoiding the unsupported POSIX descriptor; Linux keeps its ``"posix_fd"`` default. + (`#2910 `__) + - :class:`VirtualMemoryResource` can now allocate with ``location_type="host_numa"``. Every such allocation failed with ``CUDA_ERROR_INVALID_VALUE``, because the driver received ``-1`` as the NUMA diff --git a/cuda_core/tests/test_memory.py b/cuda_core/tests/test_memory.py index a2afff99469..90b6a5ad6be 100644 --- a/cuda_core/tests/test_memory.py +++ b/cuda_core/tests/test_memory.py @@ -3348,6 +3348,19 @@ def test_vmm_options_spec_validators_raise(method, spec, match): fn(spec) +@pytest.mark.parametrize("system", ["Linux", "Windows"]) +@pytest.mark.thread_unsafe(reason="patches platform.system") +def test_vmm_options_default_handle_type(monkeypatch, system): + monkeypatch.setattr(platform, "system", lambda: system) + options = VirtualMemoryResourceOptions() + expected = None if system == "Windows" else VirtualMemoryHandleType.POSIX_FD + assert options.handle_type == expected + assert VirtualMemoryResourceOptions(handle_type="win32_kmt").handle_type == "win32_kmt" + assert VirtualMemoryResourceOptions(handle_type=None).handle_type is None + with pytest.raises(ValueError): + VirtualMemoryResourceOptions(allocation_type=VirtualMemoryAllocationType.MANAGED) + + def test_vmm_options_handle_type_win32_raises(): """_handle_type_to_driver raises NotImplementedError for 'win32'.""" with pytest.raises(NotImplementedError, match="win32 is currently not supported"): From 55c156e2bc1c114e6441cba5ef26c3d58a0508a8 Mon Sep 17 00:00:00 2001 From: Martin Durant Date: Wed, 4 Nov 2026 16:55:09 -0500 Subject: [PATCH 2/2] Apply suggestions --- .../core/_memory/_virtual_memory_resource.pyi | 4 ++-- .../core/_memory/_virtual_memory_resource.pyx | 4 ++-- cuda_core/tests/test_memory.py | 18 ++++++++++++------ 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/cuda_core/cuda/core/_memory/_virtual_memory_resource.pyi b/cuda_core/cuda/core/_memory/_virtual_memory_resource.pyi index 73c42646e7c..32562a6c8c2 100644 --- a/cuda_core/cuda/core/_memory/_virtual_memory_resource.pyi +++ b/cuda_core/cuda/core/_memory/_virtual_memory_resource.pyi @@ -65,7 +65,7 @@ class VirtualMemoryResourceOptions: """ allocation_type: VirtualMemoryAllocationType = VirtualMemoryAllocationType.PINNED location_type: VirtualMemoryLocationType = VirtualMemoryLocationType.DEVICE - handle_type: VirtualMemoryHandleType | None = field(default_factory=lambda: None if platform.system() == 'Windows' else VirtualMemoryHandleType.POSIX_FD) + handle_type: VirtualMemoryHandleType | str | None = field(default_factory=lambda: None if platform.system() == 'Windows' else VirtualMemoryHandleType.POSIX_FD) granularity: VirtualMemoryGranularityType = VirtualMemoryGranularityType.RECOMMENDED gpu_direct_rdma: bool = False addr_hint: int | None = 0 @@ -93,7 +93,7 @@ class VirtualMemoryResourceOptions: @staticmethod def _location_type_to_driver(spec: VirtualMemoryLocationType) -> int: ... @staticmethod - def _handle_type_to_driver(spec: VirtualMemoryHandleType | None) -> int: ... + def _handle_type_to_driver(spec: VirtualMemoryHandleType | str | None) -> int: ... @staticmethod def _granularity_to_driver(spec: VirtualMemoryGranularityType) -> int: ... diff --git a/cuda_core/cuda/core/_memory/_virtual_memory_resource.pyx b/cuda_core/cuda/core/_memory/_virtual_memory_resource.pyx index 241abca092d..9af21db3ff0 100644 --- a/cuda_core/cuda/core/_memory/_virtual_memory_resource.pyx +++ b/cuda_core/cuda/core/_memory/_virtual_memory_resource.pyx @@ -134,7 +134,7 @@ class VirtualMemoryResourceOptions: allocation_type: VirtualMemoryAllocationType = VirtualMemoryAllocationType.PINNED location_type: VirtualMemoryLocationType = VirtualMemoryLocationType.DEVICE - handle_type: VirtualMemoryHandleType | None = field( + handle_type: VirtualMemoryHandleType | str | None = field( default_factory=lambda: None if platform.system() == "Windows" else VirtualMemoryHandleType.POSIX_FD ) granularity: VirtualMemoryGranularityType = VirtualMemoryGranularityType.RECOMMENDED @@ -203,7 +203,7 @@ class VirtualMemoryResourceOptions: return loc_type # type: ignore[no-any-return] @staticmethod - def _handle_type_to_driver(spec: VirtualMemoryHandleType | None) -> int: + def _handle_type_to_driver(spec: VirtualMemoryHandleType | str | None) -> int: if spec == "win32": raise NotImplementedError("win32 is currently not supported, please reach out to the CUDA Python team") handle_type = VirtualMemoryResourceOptions._handle_types.get(spec) diff --git a/cuda_core/tests/test_memory.py b/cuda_core/tests/test_memory.py index f1984ee28bc..953ded9f04a 100644 --- a/cuda_core/tests/test_memory.py +++ b/cuda_core/tests/test_memory.py @@ -1768,7 +1768,11 @@ def test_vmm_host_modify_allocation_rejects_exportable_handle_type(init_cuda): buf = mr.allocate(4096) try: with pytest.raises(ValueError, match="handle_type=None"): - mr.modify_allocation(buf, 2 * buf.size, config=VirtualMemoryResourceOptions(location_type="host")) + mr.modify_allocation( + buf, + 2 * buf.size, + config=VirtualMemoryResourceOptions(location_type="host", handle_type=VirtualMemoryHandleType.POSIX_FD), + ) finally: buf.close() @@ -3384,17 +3388,19 @@ def test_vmm_host_location_types_report_host_accessible(init_cuda, location_type deciding the resource is not bound to a device, so is_host_accessible must agree; otherwise a NUMA-located resource claims to be neither host- nor device-accessible. The driver rejects an exportable handle type for HOST - memory only, so the default handle type is rejected at construction for - "host" and accepted for "host_numa". + memory only, so an exportable default handle type is rejected at + construction for "host" and accepted for "host_numa". The Windows + default, None, is accepted for both location types. """ device = Device() device.set_current() numa = {"numa_id": 0} if location_type == "host_numa" else {} - if location_type == "host": + config = VirtualMemoryResourceOptions(location_type=location_type, **numa) + if location_type == "host" and config.handle_type is not None: with pytest.raises(ValueError, match="handle_type=None"): - VirtualMemoryResource(device, config=VirtualMemoryResourceOptions(location_type=location_type)) + VirtualMemoryResource(device, config=config) else: - VirtualMemoryResource(device, config=VirtualMemoryResourceOptions(location_type=location_type, **numa)) + VirtualMemoryResource(device, config=config) mr = VirtualMemoryResource( device, config=VirtualMemoryResourceOptions(location_type=location_type, handle_type=None, **numa) )