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