1 /**
2  * Copyright (c) UT-Battelle, LLC. 2014-2015. ALL RIGHTS RESERVED.
3  * Copyright (C) Mellanox Technologies Ltd. 2001-2015.  ALL RIGHTS RESERVED.
4  * Copyright (c) Los Alamos National Security, LLC. 2016.  ALL RIGHTS RESERVED.
5  * See file LICENSE for terms.
6  */
7 
8 #ifdef HAVE_CONFIG_H
9 #  include "config.h"
10 #endif
11 
12 #include "xpmem.h"
13 
14 #include <uct/sm/mm/base/mm_md.h>
15 #include <uct/sm/mm/base/mm_iface.h>
16 #include <ucs/datastruct/khash.h>
17 #include <ucs/debug/memtrack.h>
18 #include <ucs/type/init_once.h>
19 #include <ucs/type/spinlock.h>
20 #include <ucs/memory/rcache.h>
21 #include <ucs/debug/log.h>
22 
23 
24 /* XPMEM memory domain configuration */
25 typedef struct uct_xpmem_md_config {
26     uct_mm_md_config_t      super;
27 } uct_xpmem_md_config_t;
28 
29 /* Remote process memory */
30 typedef struct uct_xpmem_remote_mem {
31     xpmem_apid_t            apid;
32     xpmem_segid_t           xsegid;
33     ucs_rcache_t            *rcache;
34     int                     refcount;
35 } uct_xpmem_remote_mem_t;
36 
37 /* Cache entry for remote memory region */
38 typedef struct uct_xpmem_remote_region {
39     ucs_rcache_region_t     super;
40     void                    *attach_address;
41     uct_xpmem_remote_mem_t  *rmem;
42 } uct_xpmem_remote_region_t;
43 
44 typedef struct uct_xpmem_iface_addr {
45     xpmem_segid_t           xsegid;
46 } UCS_S_PACKED uct_xpmem_iface_addr_t;
47 
48 typedef struct uct_xpmem_packed_rkey {
49     xpmem_segid_t           xsegid;
50     uintptr_t               address;
51     size_t                  length;
52 } UCS_S_PACKED uct_xpmem_packed_rkey_t;
53 
54 KHASH_INIT(xpmem_remote_mem, xpmem_segid_t, uct_xpmem_remote_mem_t*, 1,
55            kh_int64_hash_func, kh_int64_hash_equal)
56 
57 /* Global XPMEM segment which maps the entire process virtual address space */
58 static ucs_init_once_t uct_xpmem_global_seg_init_once = UCS_INIT_ONCE_INITIALIZER;
59 static xpmem_segid_t   uct_xpmem_global_xsegid        = -1;
60 
61 /* Hash of remote regions */
62 static khash_t(xpmem_remote_mem) uct_xpmem_remote_mem_hash;
63 static ucs_recursive_spinlock_t  uct_xpmem_remote_mem_lock;
64 
65 static ucs_config_field_t uct_xpmem_md_config_table[] = {
66   {"MM_", "", NULL,
67    ucs_offsetof(uct_xpmem_md_config_t, super),
68    UCS_CONFIG_TYPE_TABLE(uct_mm_md_config_table)},
69 
70   {NULL}
71 };
72 
73 UCS_STATIC_INIT {
74     ucs_recursive_spinlock_init(&uct_xpmem_remote_mem_lock, 0);
75     kh_init_inplace(xpmem_remote_mem, &uct_xpmem_remote_mem_hash);
76 }
77 
78 UCS_STATIC_CLEANUP {
79     uct_xpmem_remote_mem_t *rmem;
80     ucs_status_t status;
81 
82     kh_foreach_value(&uct_xpmem_remote_mem_hash, rmem, {
83         ucs_warn("remote segment id %lx apid %lx is not released, refcount %d",
84                  (unsigned long)rmem->xsegid, (unsigned long)rmem->apid,
85                  rmem->refcount);
86     })
87     kh_destroy_inplace(xpmem_remote_mem, &uct_xpmem_remote_mem_hash);
88 
89     status = ucs_recursive_spinlock_destroy(&uct_xpmem_remote_mem_lock);
90     if (status != UCS_OK) {
91         ucs_warn("ucs_recursive_spinlock_destroy() failed: %s",
92                  ucs_status_string(status));
93     }
94 }
95 
uct_xpmem_query()96 static ucs_status_t uct_xpmem_query()
97 {
98     int version;
99 
100     version = xpmem_version();
101     if (version < 0) {
102         ucs_debug("xpmem_version() returned %d (%m), xpmem is unavailable",
103                   version);
104         return UCS_ERR_UNSUPPORTED;
105     }
106 
107     ucs_debug("xpmem version: %d", version);
108     return UCS_OK;
109 }
110 
uct_xpmem_md_query(uct_md_h md,uct_md_attr_t * md_attr)111 static ucs_status_t uct_xpmem_md_query(uct_md_h md, uct_md_attr_t *md_attr)
112 {
113     uct_mm_md_query(md, md_attr, 0);
114 
115     md_attr->cap.flags        |= UCT_MD_FLAG_REG;
116     md_attr->reg_cost          = ucs_linear_func_make(60.0e-9, 0);
117     md_attr->cap.max_reg       = ULONG_MAX;
118     md_attr->cap.reg_mem_types = UCS_MEMORY_TYPES_CPU_ACCESSIBLE;
119     md_attr->rkey_packed_size  = sizeof(uct_xpmem_packed_rkey_t);
120 
121     return UCS_OK;
122 }
123 
124 static UCS_F_ALWAYS_INLINE size_t
uct_xpmem_rcache_region_length(uct_xpmem_remote_region_t * xpmem_region)125 uct_xpmem_rcache_region_length(uct_xpmem_remote_region_t *xpmem_region)
126 {
127     return xpmem_region->super.super.end - xpmem_region->super.super.start;
128 }
129 
130 static ucs_status_t
uct_xpmem_rcache_mem_reg(void * context,ucs_rcache_t * rcache,void * arg,ucs_rcache_region_t * region,uint16_t flags)131 uct_xpmem_rcache_mem_reg(void *context, ucs_rcache_t *rcache, void *arg,
132                          ucs_rcache_region_t *region, uint16_t flags)
133 {
134     uct_xpmem_remote_mem_t    *rmem         = context;
135     uct_xpmem_remote_region_t *xpmem_region =
136                     ucs_derived_of(region, uct_xpmem_remote_region_t);
137     struct xpmem_addr addr;
138     size_t length;
139 
140     addr.apid   = rmem->apid;
141     addr.offset = xpmem_region->super.super.start;
142     length      = uct_xpmem_rcache_region_length(xpmem_region);
143 
144     xpmem_region->attach_address = xpmem_attach(addr, length, NULL);
145     VALGRIND_MAKE_MEM_DEFINED(&xpmem_region->attach_address,
146                               sizeof(xpmem_region->attach_address));
147     if (xpmem_region->attach_address == MAP_FAILED) {
148         ucs_error("failed to attach xpmem apid 0x%lx offset 0x%lx length %zu: %m",
149                   (unsigned long)addr.apid, addr.offset, length);
150         return UCS_ERR_IO_ERROR;
151     }
152 
153     xpmem_region->rmem = rmem;
154 
155     ucs_trace("xpmem attached apid 0x%lx offset 0x%lx length %zu at %p",
156               (unsigned long)addr.apid, addr.offset, length,
157               xpmem_region->attach_address);
158 
159     VALGRIND_MAKE_MEM_DEFINED(xpmem_region->attach_address, length);
160     return UCS_OK;
161 }
162 
uct_xpmem_rcache_mem_dereg(void * context,ucs_rcache_t * rcache,ucs_rcache_region_t * region)163 static void uct_xpmem_rcache_mem_dereg(void *context, ucs_rcache_t *rcache,
164                                        ucs_rcache_region_t *region)
165 {
166     uct_xpmem_remote_region_t *xpmem_region =
167                     ucs_derived_of(region, uct_xpmem_remote_region_t);
168     int ret;
169 
170     ucs_trace("xpmem detaching address %p", xpmem_region->attach_address);
171 
172     ret = xpmem_detach(xpmem_region->attach_address);
173     if (ret < 0) {
174         ucs_warn("Failed to xpmem_detach: %m");
175     }
176 
177     xpmem_region->attach_address = NULL;
178     xpmem_region->rmem           = NULL;
179 }
180 
uct_xpmem_rcache_dump_region(void * context,ucs_rcache_t * rcache,ucs_rcache_region_t * region,char * buf,size_t max)181 static void uct_xpmem_rcache_dump_region(void *context, ucs_rcache_t *rcache,
182                                          ucs_rcache_region_t *region, char *buf,
183                                          size_t max)
184 {
185     uct_xpmem_remote_mem_t    *rmem         = context;
186     uct_xpmem_remote_region_t *xpmem_region =
187                     ucs_derived_of(region, uct_xpmem_remote_region_t);
188 
189     snprintf(buf, max, "apid 0x%lx attach_addr %p rmem %p",
190              (unsigned long)rmem->apid, xpmem_region->attach_address, rmem);
191 }
192 
193 static ucs_rcache_ops_t uct_xpmem_rcache_ops = {
194     .mem_reg     = uct_xpmem_rcache_mem_reg,
195     .mem_dereg   = uct_xpmem_rcache_mem_dereg,
196     .dump_region = uct_xpmem_rcache_dump_region
197 };
198 
199 static UCS_F_NOINLINE ucs_status_t
uct_xpmem_make_global_xsegid(xpmem_segid_t * xsegid_p)200 uct_xpmem_make_global_xsegid(xpmem_segid_t *xsegid_p)
201 {
202     /* double-checked locking */
203     UCS_INIT_ONCE(&uct_xpmem_global_seg_init_once) {
204         if (uct_xpmem_global_xsegid < 0) {
205             uct_xpmem_global_xsegid = xpmem_make(0, XPMEM_MAXADDR_SIZE,
206                                                  XPMEM_PERMIT_MODE, (void*)0600);
207             VALGRIND_MAKE_MEM_DEFINED(&uct_xpmem_global_xsegid,
208                                       sizeof(uct_xpmem_global_xsegid));
209         }
210     }
211 
212     if (uct_xpmem_global_xsegid < 0) {
213         ucs_error("xpmem failed to register process address space: %m");
214         return UCS_ERR_IO_ERROR;
215     }
216 
217     ucs_debug("xpmem registered global segment id 0x%lx",
218               (unsigned long)uct_xpmem_global_xsegid);
219     *xsegid_p = uct_xpmem_global_xsegid;
220     return UCS_OK;
221 }
222 
223 static UCS_F_ALWAYS_INLINE ucs_status_t
uct_xpmem_get_global_xsegid(xpmem_segid_t * xsegid_p)224 uct_xpmem_get_global_xsegid(xpmem_segid_t *xsegid_p)
225 {
226     if (ucs_unlikely(uct_xpmem_global_xsegid < 0)) {
227         return uct_xpmem_make_global_xsegid(xsegid_p);
228     }
229 
230     *xsegid_p = uct_xpmem_global_xsegid;
231     return UCS_OK;
232 }
233 
234 /* lock must be held */
235 static UCS_F_NOINLINE ucs_status_t
uct_xpmem_rmem_add(xpmem_segid_t xsegid,uct_xpmem_remote_mem_t ** rmem_p)236 uct_xpmem_rmem_add(xpmem_segid_t xsegid, uct_xpmem_remote_mem_t **rmem_p)
237 {
238     ucs_rcache_params_t rcache_params;
239     uct_xpmem_remote_mem_t *rmem;
240     ucs_status_t status;
241     khiter_t khiter;
242     int khret;
243 
244     rmem = ucs_malloc(sizeof(*rmem), "xpmem_rmem");
245     if (rmem == NULL) {
246         ucs_error("failed to allocate xpmem rmem");
247         status = UCS_ERR_NO_MEMORY;
248         goto err;
249     }
250 
251     rmem->refcount = 0;
252     rmem->xsegid   = xsegid;
253 
254     rmem->apid = xpmem_get(xsegid, XPMEM_RDWR, XPMEM_PERMIT_MODE, NULL);
255     VALGRIND_MAKE_MEM_DEFINED(&rmem->apid, sizeof(rmem->apid));
256     if (rmem->apid < 0) {
257         ucs_error("xpmem_get(segid=0x%lx) failed: %m", (unsigned long)xsegid);
258         status = UCS_ERR_SHMEM_SEGMENT;
259         goto err_free;
260     }
261 
262     rcache_params.region_struct_size = sizeof(uct_xpmem_remote_region_t);
263     rcache_params.alignment          = ucs_get_page_size();
264     rcache_params.max_alignment      = ucs_get_page_size();
265     rcache_params.ucm_events         = 0;
266     rcache_params.ucm_event_priority = 0;
267     rcache_params.ops                = &uct_xpmem_rcache_ops;
268     rcache_params.context            = rmem;
269     rcache_params.flags              = UCS_RCACHE_FLAG_NO_PFN_CHECK;
270 
271     status = ucs_rcache_create(&rcache_params, "xpmem_remote_mem",
272                                ucs_stats_get_root(), &rmem->rcache);
273     if (status != UCS_OK) {
274         ucs_error("failed to create xpmem remote cache: %s",
275                   ucs_status_string(status));
276         goto err_release_seg;
277     }
278 
279     khiter = kh_put(xpmem_remote_mem, &uct_xpmem_remote_mem_hash, xsegid,
280                     &khret);
281     ucs_assertv_always((khret == 1) || (khret == 2), "khret=%d", khret);
282     ucs_assert_always (khiter != kh_end(&uct_xpmem_remote_mem_hash));
283     kh_val(&uct_xpmem_remote_mem_hash, khiter) = rmem;
284 
285     ucs_trace("xpmem attached to remote segment id 0x%lx apid 0x%lx rcache %p",
286               (unsigned long)xsegid, (unsigned long)rmem->apid, rmem->rcache);
287 
288     *rmem_p = rmem;
289     return UCS_OK;
290 
291 err_release_seg:
292     xpmem_release(rmem->apid);
293 err_free:
294     ucs_free(rmem);
295 err:
296     return status;
297 }
298 
299 /* lock must be held */
300 static UCS_F_NOINLINE void
uct_xpmem_rmem_del(uct_xpmem_remote_mem_t * rmem)301 uct_xpmem_rmem_del(uct_xpmem_remote_mem_t *rmem)
302 {
303     khiter_t khiter;
304     int ret;
305 
306     ucs_assert(rmem->refcount == 0);
307 
308     ucs_trace("detaching remote segment rmem %p apid %lx", rmem,
309               (unsigned long)rmem->apid);
310 
311     khiter = kh_get(xpmem_remote_mem, &uct_xpmem_remote_mem_hash, rmem->xsegid);
312     ucs_assert(kh_val(&uct_xpmem_remote_mem_hash, khiter) == rmem);
313     kh_del(xpmem_remote_mem, &uct_xpmem_remote_mem_hash, khiter);
314 
315     ucs_rcache_destroy(rmem->rcache);
316 
317     ret = xpmem_release(rmem->apid);
318     if (ret) {
319         ucs_warn("xpmem_release(apid=0x%lx) failed: %m",
320                  (unsigned long)rmem->apid);
321     }
322 
323     ucs_free(rmem);
324 }
325 
326 static ucs_status_t
uct_xpmem_rmem_get(xpmem_segid_t xsegid,uct_xpmem_remote_mem_t ** rmem_p)327 uct_xpmem_rmem_get(xpmem_segid_t xsegid, uct_xpmem_remote_mem_t **rmem_p)
328 {
329     uct_xpmem_remote_mem_t *rmem;
330     ucs_status_t status;
331     khiter_t khiter;
332 
333     ucs_recursive_spin_lock(&uct_xpmem_remote_mem_lock);
334 
335     khiter = kh_get(xpmem_remote_mem, &uct_xpmem_remote_mem_hash, xsegid);
336     if (ucs_likely(khiter != kh_end(&uct_xpmem_remote_mem_hash))) {
337         rmem = kh_val(&uct_xpmem_remote_mem_hash, khiter);
338     } else {
339         status = uct_xpmem_rmem_add(xsegid, &rmem);
340         if (status != UCS_OK) {
341             *rmem_p = NULL;
342             goto out_unlock;
343         }
344     }
345 
346     ++rmem->refcount;
347     *rmem_p = rmem;
348     status  = UCS_OK;
349 
350 out_unlock:
351     ucs_recursive_spin_unlock(&uct_xpmem_remote_mem_lock);
352     return status;
353 }
354 
uct_xpmem_rmem_put(uct_xpmem_remote_mem_t * rmem)355 static void uct_xpmem_rmem_put(uct_xpmem_remote_mem_t *rmem)
356 {
357     ucs_recursive_spin_lock(&uct_xpmem_remote_mem_lock);
358     if (--rmem->refcount == 0) {
359         uct_xpmem_rmem_del(rmem);
360     }
361     ucs_recursive_spin_unlock(&uct_xpmem_remote_mem_lock);
362 }
363 
364 static ucs_status_t
uct_xpmem_mem_attach_common(xpmem_segid_t xsegid,uintptr_t remote_address,size_t length,uct_xpmem_remote_region_t ** region_p)365 uct_xpmem_mem_attach_common(xpmem_segid_t xsegid, uintptr_t remote_address,
366                             size_t length, uct_xpmem_remote_region_t **region_p)
367 {
368     ucs_rcache_region_t *rcache_region;
369     uct_xpmem_remote_mem_t *rmem;
370     uintptr_t start, end;
371     ucs_status_t status;
372 
373     status = uct_xpmem_rmem_get(xsegid, &rmem);
374     if (status != UCS_OK) {
375         goto err;
376     }
377 
378     start = ucs_align_down_pow2(remote_address,          ucs_get_page_size());
379     end   = ucs_align_up_pow2  (remote_address + length, ucs_get_page_size());
380 
381     status = ucs_rcache_get(rmem->rcache, (void*)start, end - start,
382                             PROT_READ|PROT_WRITE, NULL, &rcache_region);
383     if (status != UCS_OK) {
384         goto err_rmem_put;
385     }
386 
387     *region_p = ucs_derived_of(rcache_region, uct_xpmem_remote_region_t);
388     return UCS_OK;
389 
390 err_rmem_put:
391     uct_xpmem_rmem_put(rmem);
392 err:
393     return status;
394 }
395 
uct_xpmem_mem_detach_common(uct_xpmem_remote_region_t * xpmem_region)396 static void uct_xpmem_mem_detach_common(uct_xpmem_remote_region_t *xpmem_region)
397 {
398     uct_xpmem_remote_mem_t *rmem = xpmem_region->rmem;
399 
400     ucs_rcache_region_put(rmem->rcache, &xpmem_region->super);
401     uct_xpmem_rmem_put(rmem);
402 }
403 
uct_xmpem_mem_reg(uct_md_h md,void * address,size_t length,unsigned flags,uct_mem_h * memh_p)404 static ucs_status_t uct_xmpem_mem_reg(uct_md_h md, void *address, size_t length,
405                                       unsigned flags, uct_mem_h *memh_p)
406 {
407     ucs_status_t status;
408     uct_mm_seg_t *seg;
409 
410     status = uct_mm_seg_new(address, length, &seg);
411     if (status != UCS_OK) {
412         return status;
413     }
414 
415     seg->seg_id  = (uintptr_t)address; /* to be used by mem_attach */
416     *memh_p      = seg;
417     return UCS_OK;
418 }
419 
uct_xmpem_mem_dereg(uct_md_h md,uct_mem_h memh)420 static ucs_status_t uct_xmpem_mem_dereg(uct_md_h md, uct_mem_h memh)
421 {
422     uct_mm_seg_t *seg = memh;
423     ucs_free(seg);
424     return UCS_OK;
425 }
426 
427 static ucs_status_t
uct_xpmem_mkey_pack(uct_md_h md,uct_mem_h memh,void * rkey_buffer)428 uct_xpmem_mkey_pack(uct_md_h md, uct_mem_h memh, void *rkey_buffer)
429 {
430     uct_mm_seg_t                    *seg = memh;
431     uct_xpmem_packed_rkey_t *packed_rkey = rkey_buffer;
432     xpmem_segid_t xsegid;
433     ucs_status_t status;
434 
435     ucs_assert((uintptr_t)seg->address == seg->seg_id); /* sanity */
436 
437     status = uct_xpmem_get_global_xsegid(&xsegid);
438     if (status != UCS_OK) {
439         return status;
440     }
441 
442     packed_rkey->xsegid  = xsegid;
443     packed_rkey->address = (uintptr_t)seg->address;
444     packed_rkey->length  = seg->length;
445     return UCS_OK;
446 }
447 
uct_xpmem_iface_addr_length(uct_mm_md_t * md)448 static size_t uct_xpmem_iface_addr_length(uct_mm_md_t *md)
449 {
450     return sizeof(uct_xpmem_iface_addr_t);
451 }
452 
uct_xpmem_iface_addr_pack(uct_mm_md_t * md,void * buffer)453 static ucs_status_t uct_xpmem_iface_addr_pack(uct_mm_md_t *md, void *buffer)
454 {
455     uct_xpmem_iface_addr_t *xpmem_iface_addr = buffer;
456     xpmem_segid_t xsegid;
457     ucs_status_t status;
458 
459     status = uct_xpmem_get_global_xsegid(&xsegid);
460     if (status != UCS_OK) {
461         return status;
462     }
463 
464     xpmem_iface_addr->xsegid = xsegid;
465     return UCS_OK;
466 }
467 
uct_xpmem_mem_attach(uct_mm_md_t * md,uct_mm_seg_id_t seg_id,size_t length,const void * iface_addr,uct_mm_remote_seg_t * rseg)468 static ucs_status_t uct_xpmem_mem_attach(uct_mm_md_t *md, uct_mm_seg_id_t seg_id,
469                                          size_t length, const void *iface_addr,
470                                          uct_mm_remote_seg_t *rseg)
471 {
472     const uct_xpmem_iface_addr_t *xpmem_iface_addr = iface_addr;
473     uintptr_t                       remote_address = seg_id;
474     uct_xpmem_remote_region_t *xpmem_region;
475     ucs_status_t status;
476     ptrdiff_t offset;
477 
478     ucs_assert(xpmem_iface_addr != NULL);
479     status = uct_xpmem_mem_attach_common(xpmem_iface_addr->xsegid,
480                                          remote_address, length, &xpmem_region);
481     if (status != UCS_OK) {
482         return status;
483     }
484 
485     /* In order to obtain the local access address of the remote segment
486      * (rseg->address), we need to calculate its offset from the beginning of the
487      * region on remote side (offset), and then add it to the local base address
488      * of the attached region (xpmem_region->attach_address).
489      */
490     offset        = remote_address - xpmem_region->super.super.start;
491     rseg->address = UCS_PTR_BYTE_OFFSET(xpmem_region->attach_address, offset);
492     rseg->cookie  = xpmem_region;
493 
494     return UCS_OK;
495 }
496 
uct_xpmem_mem_detach(uct_mm_md_t * md,const uct_mm_remote_seg_t * rseg)497 static void uct_xpmem_mem_detach(uct_mm_md_t *md,
498                                  const uct_mm_remote_seg_t *rseg)
499 {
500     uct_xpmem_mem_detach_common(rseg->cookie);
501 }
502 
503 static ucs_status_t
uct_xpmem_rkey_unpack(uct_component_t * component,const void * rkey_buffer,uct_rkey_t * rkey_p,void ** handle_p)504 uct_xpmem_rkey_unpack(uct_component_t *component, const void *rkey_buffer,
505                       uct_rkey_t *rkey_p, void **handle_p)
506 {
507     const uct_xpmem_packed_rkey_t *packed_rkey = rkey_buffer;
508     uct_xpmem_remote_region_t *xpmem_region;
509     ucs_status_t status;
510 
511     status = uct_xpmem_mem_attach_common(packed_rkey->xsegid,
512                                          packed_rkey->address,
513                                          packed_rkey->length,
514                                          &xpmem_region);
515     if (status != UCS_OK) {
516         return status;
517     }
518 
519     uct_mm_md_make_rkey(xpmem_region->attach_address,
520                         xpmem_region->super.super.start, rkey_p);
521     *handle_p = xpmem_region;
522 
523     return UCS_OK;
524 }
525 
526 static ucs_status_t
uct_xpmem_rkey_release(uct_component_t * component,uct_rkey_t rkey,void * handle)527 uct_xpmem_rkey_release(uct_component_t *component, uct_rkey_t rkey, void *handle)
528 {
529     uct_xpmem_mem_detach_common(handle);
530     return UCS_OK;
531 }
532 
533 static uct_mm_md_mapper_ops_t uct_xpmem_md_ops = {
534     .super = {
535         .close                  = uct_mm_md_close,
536         .query                  = uct_xpmem_md_query,
537         .mem_alloc              = (uct_md_mem_alloc_func_t)ucs_empty_function_return_unsupported,
538         .mem_free               = (uct_md_mem_free_func_t)ucs_empty_function_return_unsupported,
539         .mem_advise             = (uct_md_mem_advise_func_t)ucs_empty_function_return_unsupported,
540         .mem_reg                = uct_xmpem_mem_reg,
541         .mem_dereg              = uct_xmpem_mem_dereg,
542         .mkey_pack              = uct_xpmem_mkey_pack,
543         .is_sockaddr_accessible = (uct_md_is_sockaddr_accessible_func_t)ucs_empty_function_return_zero,
544         .detect_memory_type     = (uct_md_detect_memory_type_func_t)ucs_empty_function_return_unsupported
545     },
546    .query                       = uct_xpmem_query,
547    .iface_addr_length           = uct_xpmem_iface_addr_length,
548    .iface_addr_pack             = uct_xpmem_iface_addr_pack,
549    .mem_attach                  = uct_xpmem_mem_attach,
550    .mem_detach                  = uct_xpmem_mem_detach,
551    .is_reachable                = (uct_mm_mapper_is_reachable_func_t)ucs_empty_function_return_one
552 };
553 
554 UCT_MM_TL_DEFINE(xpmem, &uct_xpmem_md_ops, uct_xpmem_rkey_unpack,
555                  uct_xpmem_rkey_release, "XPMEM_")
556