11/* *
2- * Copyright (c) NVIDIA CORPORATION & AFFILIATES, 2025. ALL RIGHTS RESERVED.
2+ * Copyright (c) NVIDIA CORPORATION & AFFILIATES, 2025-2026 . ALL RIGHTS RESERVED.
33 *
44 * See file LICENSE for terms.
55 */
@@ -61,7 +61,8 @@ class cuda_vmm_mem_buffer {
6161 void *ptr () const ;
6262
6363protected:
64- void init (size_t size, unsigned handle_type);
64+ void init (size_t size, unsigned handle_type,
65+ CUmemLocationType location_type = CU_MEM_LOCATION_TYPE_DEVICE );
6566
6667private:
6768 size_t m_size = 0 ;
@@ -87,20 +88,23 @@ void *cuda_vmm_mem_buffer::ptr() const
8788 return (void *)m_ptr;
8889}
8990
90- void cuda_vmm_mem_buffer::init (size_t size, unsigned handle_type)
91+ void cuda_vmm_mem_buffer::init (size_t size, unsigned handle_type,
92+ CUmemLocationType location_type)
9193{
92- size_t granularity = 0 ;
93- CUmemAllocationProp prop = {};
94- CUmemAccessDesc access_desc = {};
94+ size_t granularity = 0 ;
95+ CUmemAllocationProp prop = {};
96+ CUmemAccessDesc access_desc[2 ] = {};
97+ unsigned num_access = 1 ;
98+ bool host_located = (location_type != CU_MEM_LOCATION_TYPE_DEVICE );
9599 CUdevice device;
96100 if (cuCtxGetDevice (&device) != CUDA_SUCCESS ) {
97101 UCS_TEST_ABORT (" failed to get the device handle for the current "
98102 " context" );
99103 }
100104
101- prop.type = CU_MEM_ALLOCATION_TYPE_PINNED ;
102- prop.location .type = CU_MEM_LOCATION_TYPE_DEVICE ;
103- prop.location .id = device;
105+ prop.type = CU_MEM_ALLOCATION_TYPE_PINNED ;
106+ prop.location .type = location_type ;
107+ prop.location .id = host_located ? 0 : device;
104108 if (handle_type != 0 ) {
105109 prop.requestedHandleTypes = (CUmemAllocationHandleType)handle_type;
106110 }
@@ -123,10 +127,16 @@ void cuda_vmm_mem_buffer::init(size_t size, unsigned handle_type)
123127 goto err_address_free;
124128 }
125129
126- access_desc.location .type = CU_MEM_LOCATION_TYPE_DEVICE ;
127- access_desc.location .id = device;
128- access_desc.flags = CU_MEM_ACCESS_FLAGS_PROT_READWRITE ;
129- if (cuMemSetAccess (m_ptr, m_size, &access_desc, 1 ) != CUDA_SUCCESS ) {
130+ access_desc[0 ].location .type = CU_MEM_LOCATION_TYPE_DEVICE ;
131+ access_desc[0 ].location .id = device;
132+ access_desc[0 ].flags = CU_MEM_ACCESS_FLAGS_PROT_READWRITE ;
133+ if (host_located) {
134+ access_desc[1 ].location .type = location_type;
135+ access_desc[1 ].location .id = 0 ;
136+ access_desc[1 ].flags = CU_MEM_ACCESS_FLAGS_PROT_READWRITE ;
137+ num_access = 2 ;
138+ }
139+ if (cuMemSetAccess (m_ptr, m_size, access_desc, num_access) != CUDA_SUCCESS ) {
130140 goto err_mem_unmap;
131141 }
132142
@@ -139,7 +149,7 @@ void cuda_vmm_mem_buffer::init(size_t size, unsigned handle_type)
139149err_mem_release:
140150 cuMemRelease (m_alloc_handle);
141151err:
142- UCS_TEST_SKIP_R (" failed to allocate CUDA fabric memory" );
152+ UCS_TEST_SKIP_R (" failed to allocate CUDA VMM memory" );
143153}
144154
145155#if HAVE_CUDA_FABRIC
@@ -155,6 +165,16 @@ cuda_fabric_mem_buffer::cuda_fabric_mem_buffer(size_t size,
155165}
156166#endif
157167
168+ #if CUDA_VERSION >= 12020
169+ class cuda_host_vmm_mem_buffer : public cuda_vmm_mem_buffer {
170+ public:
171+ cuda_host_vmm_mem_buffer (size_t size, ucs_memory_type_t mem_type)
172+ {
173+ init (size, 0 , CU_MEM_LOCATION_TYPE_HOST_NUMA );
174+ }
175+ };
176+ #endif
177+
158178UCS_TEST_P (test_switch_cuda_device, detect_mem_type_cuda)
159179{
160180 detect_mem_type<mem_buffer>(UCS_MEMORY_TYPE_CUDA );
@@ -432,6 +452,22 @@ class test_mem_alloc_device : public test_switch_cuda_device {
432452#endif
433453 }
434454
455+ void query_registrable_no_current_context (void *address, size_t size)
456+ {
457+ uct_md_mem_attr_v2_t mem_attr = {};
458+ ucs_status_t query_status = UCS_ERR_NO_ELEM ;
459+
460+ std::thread ([&]() {
461+ mem_attr.field_mask = UCT_MD_MEM_ATTR_V2_FIELD_MEM_TYPE |
462+ UCT_MD_MEM_ATTR_V2_FIELD_MEM_FLAGS ;
463+ query_status = uct_md_mem_query_v2 (md (), address, size, &mem_attr);
464+ }).join ();
465+
466+ ASSERT_UCS_OK (query_status);
467+ EXPECT_EQ (UCS_MEMORY_TYPE_CUDA , mem_attr.mem_type );
468+ EXPECT_TRUE (mem_attr.mem_flags & UCS_MEM_FLAG_REGISTRABLE );
469+ }
470+
435471private:
436472 std::vector<ucs_sys_device_t > m_sys_dev;
437473
@@ -518,6 +554,44 @@ UCS_TEST_P(test_mem_alloc_device, no_current_context_cuda_registrable,
518554 EXPECT_UCS_OK (free_status);
519555}
520556
557+ UCS_TEST_P (test_mem_alloc_device, no_current_context_user_mem_registrable,
558+ " CUDA_COPY_ASYNC_MEM_TYPE=cuda" )
559+ {
560+ constexpr size_t size = 4 * UCS_MBYTE ;
561+ CUdeviceptr dptr = 0 ;
562+
563+ ASSERT_EQ (CUDA_SUCCESS , cuMemAlloc (&dptr, size));
564+
565+ query_registrable_no_current_context (reinterpret_cast <void *>(dptr), size);
566+
567+ EXPECT_EQ (CUDA_SUCCESS , cuMemFree (dptr));
568+ }
569+
570+ UCS_TEST_P (test_mem_alloc_device, no_current_context_vmm_mem_registrable,
571+ " CUDA_COPY_ASYNC_MEM_TYPE=cuda" )
572+ {
573+ constexpr size_t size = 4 * UCS_MBYTE ;
574+ cuda_vmm_mem_buffer buffer (size, UCS_MEMORY_TYPE_CUDA );
575+
576+ query_registrable_no_current_context (buffer.ptr (), size);
577+ }
578+
579+ #if CUDA_VERSION >= 12020
580+ /* Host-located VMM is not dmabuf-exportable but is registrable by IB.
581+ *
582+ * TODO: REG_WHOLE_ALLOC=off is needed because of an existing issue with
583+ * base_address being set to 0, causing cache pollution with this type of memory
584+ */
585+ UCS_TEST_P (test_mem_alloc_device, host_vmm_mem_registrable,
586+ " CUDA_COPY_REG_WHOLE_ALLOC=off" )
587+ {
588+ constexpr size_t size = 4 * UCS_MBYTE ;
589+ cuda_host_vmm_mem_buffer buffer (size, UCS_MEMORY_TYPE_CUDA );
590+
591+ query_registrable_no_current_context (buffer.ptr (), size);
592+ }
593+ #endif
594+
521595_UCT_MD_INSTANTIATE_TEST_CASE (test_mem_alloc_device, cuda_cpy);
522596
523597class test_p2p_no_current_cuda_ctx : public uct_p2p_rma_test {
0 commit comments