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 */
88#include < uct/test_p2p_rma.h>
99
1010extern " C" {
11+ #include < ucs/memory/memtype_cache.h>
1112#include < ucs/sys/ptr_arith.h>
1213#include < uct/base/uct_md.h>
1314}
@@ -60,7 +61,8 @@ class cuda_vmm_mem_buffer {
6061 void *ptr () const ;
6162
6263protected:
63- 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 );
6466
6567private:
6668 size_t m_size = 0 ;
@@ -86,20 +88,23 @@ void *cuda_vmm_mem_buffer::ptr() const
8688 return (void *)m_ptr;
8789}
8890
89- 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)
9093{
91- size_t granularity = 0 ;
92- CUmemAllocationProp prop = {};
93- 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 );
9499 CUdevice device;
95100 if (cuCtxGetDevice (&device) != CUDA_SUCCESS ) {
96101 UCS_TEST_ABORT (" failed to get the device handle for the current "
97102 " context" );
98103 }
99104
100- prop.type = CU_MEM_ALLOCATION_TYPE_PINNED ;
101- prop.location .type = CU_MEM_LOCATION_TYPE_DEVICE ;
102- 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;
103108 if (handle_type != 0 ) {
104109 prop.requestedHandleTypes = (CUmemAllocationHandleType)handle_type;
105110 }
@@ -122,10 +127,16 @@ void cuda_vmm_mem_buffer::init(size_t size, unsigned handle_type)
122127 goto err_address_free;
123128 }
124129
125- access_desc.location .type = CU_MEM_LOCATION_TYPE_DEVICE ;
126- access_desc.location .id = device;
127- access_desc.flags = CU_MEM_ACCESS_FLAGS_PROT_READWRITE ;
128- 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 ) {
129140 goto err_mem_unmap;
130141 }
131142
@@ -138,7 +149,7 @@ void cuda_vmm_mem_buffer::init(size_t size, unsigned handle_type)
138149err_mem_release:
139150 cuMemRelease (m_alloc_handle);
140151err:
141- UCS_TEST_SKIP_R (" failed to allocate CUDA fabric memory" );
152+ UCS_TEST_SKIP_R (" failed to allocate CUDA VMM memory" );
142153}
143154
144155#if HAVE_CUDA_FABRIC
@@ -154,6 +165,16 @@ cuda_fabric_mem_buffer::cuda_fabric_mem_buffer(size_t size,
154165}
155166#endif
156167
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+
157178UCS_TEST_P (test_switch_cuda_device, detect_mem_type_cuda)
158179{
159180 detect_mem_type<mem_buffer>(UCS_MEMORY_TYPE_CUDA );
@@ -431,6 +452,22 @@ class test_mem_alloc_device : public test_switch_cuda_device {
431452#endif
432453 }
433454
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+
434471private:
435472 std::vector<ucs_sys_device_t > m_sys_dev;
436473
@@ -474,6 +511,87 @@ UCS_TEST_P(test_mem_alloc_device, same_device_cuda_fabric_implicit,
474511 test_same_device_alloc (UCS_MEMORY_TYPE_CUDA , false );
475512}
476513
514+ UCS_TEST_P (test_mem_alloc_device, no_current_context_cuda_registrable,
515+ " CUDA_COPY_ASYNC_MEM_TYPE=cuda" )
516+ {
517+ ucs_memory_info_t mem_info = {};
518+ ucs_status_t lookup_status = UCS_ERR_NO_ELEM ;
519+ ucs_status_t alloc_status = UCS_ERR_NO_MEMORY ;
520+ CUresult ctx_status = CUDA_ERROR_UNKNOWN ;
521+ CUcontext cuda_ctx = nullptr ;
522+
523+ /* UCP initializes the cache before allocating rendezvous fragments. */
524+ ucs_memory_info_t init_mem_info = {};
525+ (void )ucs_memtype_cache_lookup (&init_mem_info, sizeof (init_mem_info),
526+ &init_mem_info);
527+
528+ std::thread ([&]() {
529+ ctx_status = cuCtxGetCurrent (&cuda_ctx);
530+ if ((ctx_status != CUDA_SUCCESS ) || (cuda_ctx != nullptr )) {
531+ return ;
532+ }
533+
534+ alloc_status = allocate (UCS_MEMORY_TYPE_CUDA );
535+ if (alloc_status != UCS_OK ) {
536+ return ;
537+ }
538+
539+ lookup_status = ucs_memtype_cache_lookup (mem.address , mem.length ,
540+ &mem_info);
541+ }).join ();
542+
543+ EXPECT_EQ (CUDA_SUCCESS , ctx_status);
544+ EXPECT_EQ (nullptr , cuda_ctx);
545+ ASSERT_UCS_OK (alloc_status);
546+
547+ ucs_status_t free_status = uct_mem_free (&mem);
548+
549+ EXPECT_UCS_OK (lookup_status);
550+ if (lookup_status == UCS_OK ) {
551+ EXPECT_EQ (UCS_MEMORY_TYPE_CUDA , mem_info.type );
552+ EXPECT_TRUE (mem_info.mem_flags & UCS_MEM_FLAG_REGISTRABLE );
553+ }
554+ EXPECT_UCS_OK (free_status);
555+ }
556+
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+
477595_UCT_MD_INSTANTIATE_TEST_CASE (test_mem_alloc_device, cuda_cpy);
478596
479597class test_p2p_no_current_cuda_ctx : public uct_p2p_rma_test {
0 commit comments