1 /**
2 * Copyright (C) Mellanox Technologies Ltd. 2019.  ALL RIGHTS RESERVED.
3 *
4 * See file LICENSE for terms.
5 */
6 
7 #ifdef HAVE_CONFIG_H
8 #  include "config.h"
9 #endif
10 
11 #include <uct/ib/mlx5/ib_mlx5.h>
12 #include "ib_mlx5_ifc.h"
13 
14 #include <ucs/arch/bitops.h>
15 #include <ucs/profile/profile.h>
16 
17 typedef struct {
18     struct mlx5dv_devx_obj     *dvmr;
19     int                        mr_num;
20     size_t                     length;
21     struct ibv_mr              *mrs[];
22 } uct_ib_mlx5_ksm_data_t;
23 
24 typedef union uct_ib_mlx5_mr {
25     uct_ib_mr_t                super;
26     uct_ib_mlx5_ksm_data_t     *ksm_data;
27 } uct_ib_mlx5_mr_t;
28 
29 typedef struct uct_ib_mlx5_mem {
30     uct_ib_mem_t               super;
31 #if HAVE_DEVX
32     struct mlx5dv_devx_obj     *atomic_dvmr;
33 #endif
34     uct_ib_mlx5_mr_t           mrs[];
35 } uct_ib_mlx5_mem_t;
36 
37 
uct_ib_mlx5_reg_key(uct_ib_md_t * md,void * address,size_t length,uint64_t access_flags,uct_ib_mem_t * ib_memh,uct_ib_mr_type_t mr_type)38 static ucs_status_t uct_ib_mlx5_reg_key(uct_ib_md_t *md, void *address,
39                                         size_t length, uint64_t access_flags,
40                                         uct_ib_mem_t *ib_memh, uct_ib_mr_type_t mr_type)
41 {
42     uct_ib_mlx5_mem_t *memh = ucs_derived_of(ib_memh, uct_ib_mlx5_mem_t);
43 
44     return uct_ib_reg_key_impl(md, address, length, access_flags, ib_memh,
45                                &memh->mrs[mr_type].super, mr_type);
46 }
47 
uct_ib_mlx5_dereg_key(uct_ib_md_t * md,uct_ib_mem_t * ib_memh,uct_ib_mr_type_t mr_type)48 static ucs_status_t uct_ib_mlx5_dereg_key(uct_ib_md_t *md,
49                                           uct_ib_mem_t *ib_memh,
50                                           uct_ib_mr_type_t mr_type)
51 {
52     uct_ib_mlx5_mem_t *memh = ucs_derived_of(ib_memh, uct_ib_mlx5_mem_t);
53 
54     return uct_ib_dereg_mr(memh->mrs[mr_type].super.ib);
55 }
56 
uct_ib_mlx5_reg_atomic_key(uct_ib_md_t * ibmd,uct_ib_mem_t * ib_memh)57 static ucs_status_t uct_ib_mlx5_reg_atomic_key(uct_ib_md_t *ibmd,
58                                                uct_ib_mem_t *ib_memh)
59 {
60     uct_ib_mr_type_t mr_type = uct_ib_memh_get_atomic_base_mr_type(ib_memh);
61     uct_ib_mlx5_mem_t *memh = ucs_derived_of(ib_memh, uct_ib_mlx5_mem_t);
62 
63     if (mr_type != UCT_IB_MR_STRICT_ORDER) {
64         return UCS_ERR_UNSUPPORTED;
65     }
66 
67     memh->super.atomic_rkey = memh->mrs[mr_type].super.ib->rkey;
68     return UCS_OK;
69 }
70 
71 static ucs_status_t
uct_ib_mlx5_mem_prefetch(uct_ib_md_t * md,uct_ib_mem_t * ib_memh,void * addr,size_t length)72 uct_ib_mlx5_mem_prefetch(uct_ib_md_t *md, uct_ib_mem_t *ib_memh, void *addr,
73                          size_t length)
74 {
75 #if HAVE_DECL_IBV_ADVISE_MR
76     struct ibv_sge sg_list;
77     int ret;
78 
79     if (!(ib_memh->flags & UCT_IB_MEM_FLAG_ODP)) {
80         return UCS_OK;
81     }
82 
83     ucs_debug("memh %p prefetch %p length %zu", ib_memh, addr, length);
84 
85     sg_list.lkey   = ib_memh->lkey;
86     sg_list.addr   = (uintptr_t)addr;
87     sg_list.length = length;
88 
89     ret = UCS_PROFILE_CALL(ibv_advise_mr, md->pd,
90                            IBV_ADVISE_MR_ADVICE_PREFETCH_WRITE,
91                            IB_UVERBS_ADVISE_MR_FLAG_FLUSH, &sg_list, 1);
92     if (ret) {
93         ucs_error("ibv_advise_mr(addr=%p length=%zu) returned %d: %m",
94                   addr, length, ret);
95         return UCS_ERR_IO_ERROR;
96     }
97 #endif
98     return UCS_OK;
99 }
100 
uct_ib_mlx5_has_roce_port(uct_ib_device_t * dev)101 static int uct_ib_mlx5_has_roce_port(uct_ib_device_t *dev)
102 {
103     int port_num;
104 
105     for (port_num = dev->first_port;
106          port_num < dev->first_port + dev->num_ports;
107          port_num++)
108     {
109         if (uct_ib_device_is_port_roce(dev, port_num)) {
110             return 1;
111         }
112     }
113 
114     return 0;
115 }
116 
uct_ib_mlx5_parse_relaxed_order(uct_ib_mlx5_md_t * md,const uct_ib_md_config_t * md_config)117 static void uct_ib_mlx5_parse_relaxed_order(uct_ib_mlx5_md_t *md,
118                                             const uct_ib_md_config_t *md_config)
119 {
120     int num_mrs = 1;  /* UCT_IB_MR_DEFAULT */
121 
122     uct_ib_md_parse_relaxed_order(&md->super, md_config);
123 
124     if (md->super.relaxed_order) {
125         ++num_mrs;    /* UCT_IB_MR_STRICT_ORDER */
126     }
127 
128     md->super.memh_struct_size = sizeof(uct_ib_mlx5_mem_t) +
129                                 (sizeof(uct_ib_mlx5_mr_t) * num_mrs);
130 }
131 
132 #if HAVE_DEVX
133 
134 typedef struct uct_ib_mlx5_dbrec_page {
135     uct_ib_mlx5_devx_umem_t    mem;
136 } uct_ib_mlx5_dbrec_page_t;
137 
138 
uct_ib_mlx5_calc_mkey_inlen(int list_size)139 static size_t uct_ib_mlx5_calc_mkey_inlen(int list_size)
140 {
141     return UCT_IB_MLX5DV_ST_SZ_BYTES(create_mkey_in) +
142            UCT_IB_MLX5DV_ST_SZ_BYTES(klm) * list_size;
143 }
144 
uct_ib_mlx5_alloc_mkey_inbox(int list_size,char ** in_p)145 static ucs_status_t uct_ib_mlx5_alloc_mkey_inbox(int list_size, char **in_p)
146 {
147     size_t inlen;
148     char *in;
149 
150     inlen = uct_ib_mlx5_calc_mkey_inlen(list_size);
151     in    = ucs_calloc(1, inlen, "mkey mailbox");
152     if (in == NULL) {
153         return UCS_ERR_NO_MEMORY;
154     }
155 
156     *in_p = in;
157     return UCS_OK;
158 }
159 
uct_ib_mlx5_devx_reg_ksm(uct_ib_mlx5_md_t * md,intptr_t addr,size_t length,int list_size,size_t entity_size,char * in,struct mlx5dv_devx_obj ** mr_p,uint32_t * mkey)160 static ucs_status_t uct_ib_mlx5_devx_reg_ksm(uct_ib_mlx5_md_t *md,
161                                              intptr_t addr, size_t length,
162                                              int list_size, size_t entity_size,
163                                              char *in,
164                                              struct mlx5dv_devx_obj **mr_p,
165                                              uint32_t *mkey)
166 {
167     char out[UCT_IB_MLX5DV_ST_SZ_BYTES(create_mkey_out)] = {};
168     struct mlx5dv_pd dvpd                                = {};
169     struct mlx5dv_obj dv                                 = {};
170     struct mlx5dv_devx_obj *mr;
171     void *mkc;
172 
173     dv.pd.in   = md->super.pd;
174     dv.pd.out  = &dvpd;
175     mlx5dv_init_obj(&dv, MLX5DV_OBJ_PD);
176 
177     UCT_IB_MLX5DV_SET(create_mkey_in, in, opcode, UCT_IB_MLX5_CMD_OP_CREATE_MKEY);
178     mkc = UCT_IB_MLX5DV_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
179     UCT_IB_MLX5DV_SET(mkc, mkc, access_mode_1_0, UCT_IB_MLX5_MKC_ACCESS_MODE_KSM);
180     UCT_IB_MLX5DV_SET(mkc, mkc, a, 1);
181     UCT_IB_MLX5DV_SET(mkc, mkc, rw, 1);
182     UCT_IB_MLX5DV_SET(mkc, mkc, rr, 1);
183     UCT_IB_MLX5DV_SET(mkc, mkc, lw, 1);
184     UCT_IB_MLX5DV_SET(mkc, mkc, lr, 1);
185     UCT_IB_MLX5DV_SET(mkc, mkc, pd, dvpd.pdn);
186     UCT_IB_MLX5DV_SET(mkc, mkc, translations_octword_size, list_size);
187     UCT_IB_MLX5DV_SET(mkc, mkc, log_entity_size, ucs_ilog2(entity_size));
188     UCT_IB_MLX5DV_SET(mkc, mkc, qpn, 0xffffff);
189     UCT_IB_MLX5DV_SET(mkc, mkc, mkey_7_0, addr & 0xff);
190     UCT_IB_MLX5DV_SET64(mkc, mkc, start_addr, addr);
191     UCT_IB_MLX5DV_SET64(mkc, mkc, len, length);
192     UCT_IB_MLX5DV_SET(create_mkey_in, in, translations_octword_actual_size, list_size);
193 
194     mr = mlx5dv_devx_obj_create(md->super.dev.ibv_context, in,
195                                 uct_ib_mlx5_calc_mkey_inlen(list_size),
196                                 out, sizeof(out));
197     if (mr == NULL) {
198         ucs_debug("mlx5dv_devx_obj_create(CREATE_MKEY, mode=KSM) failed, syndrome %x: %m",
199                   UCT_IB_MLX5DV_GET(create_mkey_out, out, syndrome));
200         return UCS_ERR_UNSUPPORTED;
201     }
202 
203     *mr_p = mr;
204     *mkey = (UCT_IB_MLX5DV_GET(create_mkey_out, out, mkey_index) << 8) |
205             (addr & 0xff);
206 
207     return UCS_OK;
208 }
209 
210 static ucs_status_t
uct_ib_mlx5_devx_reg_ksm_data(uct_ib_mlx5_md_t * md,uct_ib_mlx5_ksm_data_t * ksm_data,size_t length,off_t off,struct mlx5dv_devx_obj ** mr_p,uint32_t * mkey)211 uct_ib_mlx5_devx_reg_ksm_data(uct_ib_mlx5_md_t *md,
212                               uct_ib_mlx5_ksm_data_t *ksm_data,
213                               size_t length, off_t off,
214                               struct mlx5dv_devx_obj **mr_p,
215                               uint32_t *mkey)
216 {
217     ucs_status_t status;
218     char *in;
219     void *klm;
220     int i;
221 
222     status = uct_ib_mlx5_alloc_mkey_inbox(ksm_data->mr_num, &in);
223     if (status != UCS_OK) {
224         return UCS_ERR_NO_MEMORY;
225     }
226 
227     klm = UCT_IB_MLX5DV_ADDR_OF(create_mkey_in, in, klm_pas_mtt);
228     for (i = 0; i < ksm_data->mr_num; i++) {
229         UCT_IB_MLX5DV_SET64(klm, klm, address, (intptr_t)ksm_data->mrs[i]->addr);
230         UCT_IB_MLX5DV_SET(klm, klm, byte_count, ksm_data->mrs[i]->length);
231         UCT_IB_MLX5DV_SET(klm, klm, mkey, ksm_data->mrs[i]->lkey);
232         klm = UCS_PTR_BYTE_OFFSET(klm, UCT_IB_MLX5DV_ST_SZ_BYTES(klm));
233     }
234 
235     status = uct_ib_mlx5_devx_reg_ksm(md, (intptr_t)ksm_data->mrs[0]->addr + off,
236                                       length, ksm_data->mr_num,
237                                       ksm_data->mrs[0]->length, in, mr_p, mkey);
238     ucs_free(in);
239     return status;
240 }
241 
uct_ib_mlx5_devx_reg_atomic_key(uct_ib_md_t * ibmd,uct_ib_mem_t * ib_memh)242 static ucs_status_t uct_ib_mlx5_devx_reg_atomic_key(uct_ib_md_t *ibmd,
243                                                     uct_ib_mem_t *ib_memh)
244 {
245     uct_ib_mr_type_t mr_type = uct_ib_memh_get_atomic_base_mr_type(ib_memh);
246     uct_ib_mlx5_mem_t *memh  = ucs_derived_of(ib_memh, uct_ib_mlx5_mem_t);
247     uct_ib_mlx5_md_t *md     = ucs_derived_of(ibmd, uct_ib_mlx5_md_t);
248     uct_ib_mlx5_mr_t *mr     = &memh->mrs[mr_type];
249     size_t reg_length, length;
250     ucs_status_t status;
251     int list_size, i;
252     void *klm;
253     char *in;
254     intptr_t addr;
255     uint8_t mr_id;
256 
257     if (!(md->flags & UCT_IB_MLX5_MD_FLAG_KSM)) {
258         return uct_ib_mlx5_reg_atomic_key(ibmd, ib_memh);
259     }
260 
261     status = uct_ib_mlx5_md_get_atomic_mr_id(ibmd, &mr_id);
262     if (status != UCS_OK) {
263         return status;
264     }
265 
266     if (memh->super.flags & UCT_IB_MEM_MULTITHREADED) {
267         return uct_ib_mlx5_devx_reg_ksm_data(md, mr->ksm_data, mr->ksm_data->length,
268                                              uct_ib_md_atomic_offset(mr_id),
269                                              &memh->atomic_dvmr,
270                                              &memh->super.atomic_rkey);
271     }
272 
273     reg_length = UCT_IB_MD_MAX_MR_SIZE;
274     addr       = (intptr_t)mr->super.ib->addr & ~(reg_length - 1);
275     /* FW requires indirect atomic MR addr and length to be aligned
276      * to max supported atomic argument size */
277     length     = ucs_align_up(mr->super.ib->length +
278                               (intptr_t)mr->super.ib->addr - addr,
279                               md->super.dev.atomic_align);
280     list_size  = ucs_div_round_up(length, reg_length);
281 
282     status = uct_ib_mlx5_alloc_mkey_inbox(list_size, &in);
283     if (status != UCS_OK) {
284         return status;
285     }
286 
287     klm = UCT_IB_MLX5DV_ADDR_OF(create_mkey_in, in, klm_pas_mtt);
288     for (i = 0; i < list_size; i++) {
289         if (i == list_size - 1) {
290             UCT_IB_MLX5DV_SET(klm, klm, byte_count, length % reg_length);
291         } else {
292             UCT_IB_MLX5DV_SET(klm, klm, byte_count, reg_length);
293         }
294         UCT_IB_MLX5DV_SET(klm, klm, mkey, mr->super.ib->lkey);
295         UCT_IB_MLX5DV_SET64(klm, klm, address, addr + (i * reg_length));
296         klm = UCS_PTR_BYTE_OFFSET(klm, UCT_IB_MLX5DV_ST_SZ_BYTES(klm));
297     }
298 
299     status = uct_ib_mlx5_devx_reg_ksm(md, addr + uct_ib_md_atomic_offset(mr_id),
300                                       length, list_size, reg_length, in,
301                                       &memh->atomic_dvmr,
302                                       &memh->super.atomic_rkey);
303     if (status != UCS_OK) {
304         if (status == UCS_ERR_UNSUPPORTED) {
305             md->flags &= ~UCT_IB_MLX5_MD_FLAG_KSM;
306         }
307         goto out;
308     }
309 
310     ucs_debug("KSM registered memory %p..%p offset 0x%x on %s rkey 0x%x",
311               mr->super.ib->addr, UCS_PTR_BYTE_OFFSET(mr->super.ib->addr,
312               mr->super.ib->length), uct_ib_md_atomic_offset(mr_id),
313               uct_ib_device_name(&md->super.dev), memh->super.atomic_rkey);
314 out:
315     ucs_free(in);
316     return status;
317 }
318 
uct_ib_mlx5_devx_dereg_atomic_key(uct_ib_md_t * ibmd,uct_ib_mem_t * ib_memh)319 static ucs_status_t uct_ib_mlx5_devx_dereg_atomic_key(uct_ib_md_t *ibmd,
320                                                       uct_ib_mem_t *ib_memh)
321 {
322     uct_ib_mlx5_mem_t *memh = ucs_derived_of(ib_memh, uct_ib_mlx5_mem_t);
323     uct_ib_mlx5_md_t *md    = ucs_derived_of(ibmd, uct_ib_mlx5_md_t);
324     int ret;
325 
326     if (!(md->flags & UCT_IB_MLX5_MD_FLAG_KSM)) {
327         return UCS_OK;
328     }
329 
330     ret = mlx5dv_devx_obj_destroy(memh->atomic_dvmr);
331     if (ret != 0) {
332         ucs_error("mlx5dv_devx_obj_destroy(MKEY, ATOMIC) failed: %m");
333         return UCS_ERR_IO_ERROR;
334     }
335 
336     return UCS_OK;
337 }
338 
uct_ib_mlx5_devx_reg_multithreaded(uct_ib_md_t * ibmd,void * address,size_t length,uint64_t access_flags,uct_ib_mem_t * ib_memh,uct_ib_mr_type_t mr_type)339 static ucs_status_t uct_ib_mlx5_devx_reg_multithreaded(uct_ib_md_t *ibmd,
340                                                        void *address, size_t length,
341                                                        uint64_t access_flags,
342                                                        uct_ib_mem_t *ib_memh,
343                                                        uct_ib_mr_type_t mr_type)
344 {
345     uct_ib_mlx5_md_t *md    = ucs_derived_of(ibmd, uct_ib_mlx5_md_t);
346     uct_ib_mlx5_mem_t *memh = ucs_derived_of(ib_memh, uct_ib_mlx5_mem_t);
347     uct_ib_mlx5_mr_t *mr    = &memh->mrs[mr_type];
348     size_t chunk            = md->super.config.mt_reg_chunk;
349     uct_ib_mlx5_ksm_data_t *ksm_data;
350     size_t ksm_data_size;
351     ucs_status_t status;
352     uint32_t mkey;
353     int mr_num;
354 
355     if (!(md->flags & UCT_IB_MLX5_MD_FLAG_KSM) ||
356         !(md->flags & UCT_IB_MLX5_MD_FLAG_INDIRECT_ATOMICS)) {
357         return UCS_ERR_UNSUPPORTED;
358     }
359 
360     mr_num        = ucs_div_round_up(length, chunk);
361     ksm_data_size = (mr_num * sizeof(*ksm_data->mrs)) + sizeof(*ksm_data);
362     ksm_data      = ucs_calloc(1, ksm_data_size, "ksm_data");
363     if (!ksm_data) {
364         status = UCS_ERR_NO_MEMORY;
365         goto err;
366     }
367 
368     ucs_trace("multithreaded register memory %p..%p chunks %d",
369               address, UCS_PTR_BYTE_OFFSET(address, length), mr_num);
370 
371     ksm_data->mr_num = mr_num;
372     status = uct_ib_md_handle_mr_list_multithreaded(ibmd, address, length,
373                                                     access_flags, chunk,
374                                                     ksm_data->mrs);
375     if (status != UCS_OK) {
376         goto err;
377     }
378 
379     status = uct_ib_mlx5_devx_reg_ksm_data(md, ksm_data, length, 0,
380                                            &ksm_data->dvmr, &mkey);
381     if (status != UCS_OK) {
382         goto err_dereg;
383     }
384 
385     ksm_data->length = length;
386     mr->ksm_data     = ksm_data;
387 
388     if (mr_type == UCT_IB_MR_DEFAULT) {
389         uct_ib_memh_init_keys(ib_memh, mkey, mkey);
390     }
391     return UCS_OK;
392 
393 err_dereg:
394     uct_ib_md_handle_mr_list_multithreaded(ibmd, address, length, UCT_IB_MEM_DEREG,
395                                            chunk, ksm_data->mrs);
396 err:
397     ucs_free(ksm_data);
398     return status;
399 }
400 
uct_ib_mlx5_devx_dereg_multithreaded(uct_ib_md_t * ibmd,uct_ib_mem_t * ib_memh,uct_ib_mr_type_t mr_type)401 static ucs_status_t uct_ib_mlx5_devx_dereg_multithreaded(uct_ib_md_t *ibmd,
402                                                          uct_ib_mem_t *ib_memh,
403                                                          uct_ib_mr_type_t mr_type)
404 {
405     uct_ib_mlx5_mem_t *memh = ucs_derived_of(ib_memh, uct_ib_mlx5_mem_t);
406     uct_ib_mlx5_mr_t *mr    = &memh->mrs[mr_type];
407     size_t chunk            = ibmd->config.mt_reg_chunk;
408     ucs_status_t s, status  = UCS_OK;
409     int ret;
410 
411     s = uct_ib_md_handle_mr_list_multithreaded(ibmd, 0, mr->ksm_data->length,
412                                                UCT_IB_MEM_DEREG, chunk,
413                                                mr->ksm_data->mrs);
414     if (s == UCS_ERR_UNSUPPORTED) {
415         s = uct_ib_dereg_mrs(mr->ksm_data->mrs, mr->ksm_data->mr_num);
416         if (s != UCS_OK) {
417             status = s;
418         }
419     } else if (s != UCS_OK) {
420         status = s;
421     }
422 
423     ret = mlx5dv_devx_obj_destroy(mr->ksm_data->dvmr);
424     if (ret != 0) {
425         ucs_error("mlx5dv_devx_obj_destroy(MKEY, KSM) failed: %m");
426         status = UCS_ERR_IO_ERROR;
427     }
428 
429     ucs_free(mr->ksm_data);
430 
431     return status;
432 }
433 
uct_ib_mlx5_add_page(ucs_mpool_t * mp,size_t * size_p,void ** page_p)434 static ucs_status_t uct_ib_mlx5_add_page(ucs_mpool_t *mp, size_t *size_p, void **page_p)
435 {
436     uct_ib_mlx5_md_t *md = ucs_container_of(mp, uct_ib_mlx5_md_t, dbrec_pool);
437     uct_ib_mlx5_dbrec_page_t *page;
438     size_t size = ucs_align_up(*size_p + sizeof(*page), ucs_get_page_size());
439     uct_ib_mlx5_devx_umem_t mem;
440     ucs_status_t status;
441 
442     status = uct_ib_mlx5_md_buf_alloc(md, size, 1, (void **)&page, &mem, "devx dbrec");
443     if (status != UCS_OK) {
444         return status;
445     }
446 
447     page->mem = mem;
448     *size_p   = size - sizeof(*page);
449     *page_p   = page + 1;
450     return UCS_OK;
451 }
452 
uct_ib_mlx5_init_dbrec(ucs_mpool_t * mp,void * obj,void * chunk)453 static void uct_ib_mlx5_init_dbrec(ucs_mpool_t *mp, void *obj, void *chunk)
454 {
455     uct_ib_mlx5_dbrec_page_t *page = (uct_ib_mlx5_dbrec_page_t*)chunk - 1;
456     uct_ib_mlx5_dbrec_t *dbrec     = obj;
457 
458     dbrec->mem_id = page->mem.mem->umem_id;
459     dbrec->offset = UCS_PTR_BYTE_DIFF(chunk, obj) + sizeof(*page);
460 }
461 
uct_ib_mlx5_free_page(ucs_mpool_t * mp,void * chunk)462 static void uct_ib_mlx5_free_page(ucs_mpool_t *mp, void *chunk)
463 {
464     uct_ib_mlx5_md_t *md = ucs_container_of(mp, uct_ib_mlx5_md_t, dbrec_pool);
465     uct_ib_mlx5_dbrec_page_t *page = (uct_ib_mlx5_dbrec_page_t*)chunk - 1;
466     uct_ib_mlx5_md_buf_free(md, page, &page->mem);
467 }
468 
469 static ucs_mpool_ops_t uct_ib_mlx5_dbrec_ops = {
470     .chunk_alloc   = uct_ib_mlx5_add_page,
471     .chunk_release = uct_ib_mlx5_free_page,
472     .obj_init      = uct_ib_mlx5_init_dbrec,
473     .obj_cleanup   = NULL
474 };
475 
476 static ucs_status_t
uct_ib_mlx5_devx_check_odp(uct_ib_mlx5_md_t * md,const uct_ib_md_config_t * md_config,void * cap)477 uct_ib_mlx5_devx_check_odp(uct_ib_mlx5_md_t *md,
478                            const uct_ib_md_config_t *md_config, void *cap)
479 {
480     char out[UCT_IB_MLX5DV_ST_SZ_BYTES(query_hca_cap_out)] = {};
481     char in[UCT_IB_MLX5DV_ST_SZ_BYTES(query_hca_cap_in)]   = {};
482     void *odp;
483     int ret;
484 
485     if (md_config->devx_objs & UCS_BIT(UCT_IB_DEVX_OBJ_RCQP)) {
486         ucs_debug("%s: disable ODP because it's not supported for DevX QP",
487                   uct_ib_device_name(&md->super.dev));
488         goto no_odp;
489     }
490 
491     if (uct_ib_mlx5_has_roce_port(&md->super.dev)) {
492         ucs_debug("%s: disable ODP on RoCE", uct_ib_device_name(&md->super.dev));
493         goto no_odp;
494     }
495 
496     if (!UCT_IB_MLX5DV_GET(cmd_hca_cap, cap, pg)) {
497         goto no_odp;
498     }
499 
500     odp = UCT_IB_MLX5DV_ADDR_OF(query_hca_cap_out, out, capability);
501     UCT_IB_MLX5DV_SET(query_hca_cap_in, in, opcode, UCT_IB_MLX5_CMD_OP_QUERY_HCA_CAP);
502     UCT_IB_MLX5DV_SET(query_hca_cap_in, in, op_mod, UCT_IB_MLX5_HCA_CAP_OPMOD_GET_CUR |
503                                                    (UCT_IB_MLX5_CAP_ODP << 1));
504     ret = mlx5dv_devx_general_cmd(md->super.dev.ibv_context, in, sizeof(in),
505                                   out, sizeof(out));
506     if (ret != 0) {
507         ucs_error("mlx5dv_devx_general_cmd(QUERY_HCA_CAP, ODP) failed: %m");
508         return UCS_ERR_IO_ERROR;
509     }
510 
511     if (!UCT_IB_MLX5DV_GET(odp_cap, odp, ud_odp_caps.send) ||
512         !UCT_IB_MLX5DV_GET(odp_cap, odp, rc_odp_caps.send) ||
513         !UCT_IB_MLX5DV_GET(odp_cap, odp, rc_odp_caps.write) ||
514         !UCT_IB_MLX5DV_GET(odp_cap, odp, rc_odp_caps.read)) {
515         goto no_odp;
516     }
517 
518     if ((md->super.dev.flags & UCT_IB_DEVICE_FLAG_DC) &&
519         (!UCT_IB_MLX5DV_GET(odp_cap, odp, dc_odp_caps.send) ||
520          !UCT_IB_MLX5DV_GET(odp_cap, odp, dc_odp_caps.write) ||
521          !UCT_IB_MLX5DV_GET(odp_cap, odp, dc_odp_caps.read))) {
522         goto no_odp;
523     }
524 
525     if (md->super.config.odp.max_size == UCS_MEMUNITS_AUTO) {
526         if (UCT_IB_MLX5DV_GET(cmd_hca_cap, cap, umr_extended_translation_offset)) {
527             md->super.config.odp.max_size = 1ul << 55;
528         } else {
529             md->super.config.odp.max_size = 1ul << 28;
530         }
531     }
532 
533     if (UCT_IB_MLX5DV_GET(cmd_hca_cap, cap, fixed_buffer_size) &&
534         UCT_IB_MLX5DV_GET(cmd_hca_cap, cap, null_mkey) &&
535         UCT_IB_MLX5DV_GET(cmd_hca_cap, cap, umr_extended_translation_offset)) {
536         md->super.dev.flags |= UCT_IB_DEVICE_FLAG_ODP_IMPLICIT;
537     }
538 
539     return UCS_OK;
540 
541 no_odp:
542     md->super.config.odp.max_size = 0;
543     return UCS_OK;
544 }
545 
546 static struct ibv_context *
uct_ib_mlx5_devx_open_device(struct ibv_device * ibv_device,struct mlx5dv_context_attr * dv_attr)547 uct_ib_mlx5_devx_open_device(struct ibv_device *ibv_device,
548                              struct mlx5dv_context_attr *dv_attr)
549 {
550     struct ibv_context *ctx;
551     struct ibv_cq *cq;
552 
553     ctx = mlx5dv_open_device(ibv_device, dv_attr);
554     if (ctx == NULL) {
555         return NULL;
556     }
557 
558     cq = ibv_create_cq(ctx, 1, NULL, NULL, 0);
559     if (cq == NULL) {
560         ibv_close_device(ctx);
561         return NULL;
562     }
563 
564     ibv_destroy_cq(cq);
565     return ctx;
566 }
567 
568 static uct_ib_md_ops_t uct_ib_mlx5_devx_md_ops;
569 
uct_ib_mlx5_devx_md_open(struct ibv_device * ibv_device,const uct_ib_md_config_t * md_config,uct_ib_md_t ** p_md)570 static ucs_status_t uct_ib_mlx5_devx_md_open(struct ibv_device *ibv_device,
571                                              const uct_ib_md_config_t *md_config,
572                                              uct_ib_md_t **p_md)
573 {
574     char out[UCT_IB_MLX5DV_ST_SZ_BYTES(query_hca_cap_out)] = {};
575     char in[UCT_IB_MLX5DV_ST_SZ_BYTES(query_hca_cap_in)]   = {};
576     struct mlx5dv_context_attr dv_attr = {};
577     ucs_status_t status = UCS_OK;
578     struct ibv_context *ctx;
579     uct_ib_device_t *dev;
580     uct_ib_mlx5_md_t *md;
581     void *cap;
582     int ret;
583 
584 #if HAVE_DECL_MLX5DV_IS_SUPPORTED
585     if (!mlx5dv_is_supported(ibv_device)) {
586         return UCS_ERR_UNSUPPORTED;
587     }
588 #endif
589 
590     if (md_config->devx == UCS_NO) {
591         return UCS_ERR_UNSUPPORTED;
592     }
593 
594     dv_attr.flags |= MLX5DV_CONTEXT_FLAGS_DEVX;
595     ctx = uct_ib_mlx5_devx_open_device(ibv_device, &dv_attr);
596     if (ctx == NULL) {
597         if (md_config->devx == UCS_YES) {
598             status = UCS_ERR_IO_ERROR;
599             ucs_error("DEVX requested but not supported by %s",
600                       ibv_get_device_name(ibv_device));
601         } else {
602             status = UCS_ERR_UNSUPPORTED;
603             ucs_debug("mlx5dv_open_device(%s) failed: %m",
604                       ibv_get_device_name(ibv_device));
605         }
606         goto err;
607     }
608 
609     md = ucs_calloc(1, sizeof(*md), "ib_mlx5_md");
610     if (md == NULL) {
611         status = UCS_ERR_NO_MEMORY;
612         goto err_free_context;
613     }
614 
615     dev              = &md->super.dev;
616     dev->ibv_context = ctx;
617     md->super.config = md_config->ext;
618 
619     status = uct_ib_device_query(dev, ibv_device);
620     if (status != UCS_OK) {
621         goto err_free;
622     }
623 
624     cap = UCT_IB_MLX5DV_ADDR_OF(query_hca_cap_out, out, capability);
625     UCT_IB_MLX5DV_SET(query_hca_cap_in, in, opcode, UCT_IB_MLX5_CMD_OP_QUERY_HCA_CAP);
626     UCT_IB_MLX5DV_SET(query_hca_cap_in, in, op_mod, UCT_IB_MLX5_HCA_CAP_OPMOD_GET_CUR |
627                                                    (UCT_IB_MLX5_CAP_GENERAL << 1));
628     ret = mlx5dv_devx_general_cmd(ctx, in, sizeof(in), out, sizeof(out));
629     if (ret != 0) {
630         if ((errno == EPERM) || (errno == EPROTONOSUPPORT) ||
631             (errno == EOPNOTSUPP)) {
632             status = UCS_ERR_UNSUPPORTED;
633             ucs_debug("mlx5dv_devx_general_cmd(QUERY_HCA_CAP) failed: %m");
634         } else {
635             ucs_error("mlx5dv_devx_general_cmd(QUERY_HCA_CAP) failed: %m");
636             status = UCS_ERR_IO_ERROR;
637         }
638         goto err_free;
639     }
640 
641     if (UCT_IB_MLX5DV_GET(cmd_hca_cap, cap, log_max_msg) !=
642         UCT_IB_MLX5_LOG_MAX_MSG_SIZE) {
643         status = UCS_ERR_UNSUPPORTED;
644         ucs_debug("Unexpected QUERY_HCA_CAP.log_max_msg %d\n",
645                   UCT_IB_MLX5DV_GET(cmd_hca_cap, cap, log_max_msg));
646         goto err_free;
647     }
648 
649     if (UCT_IB_MLX5DV_GET(cmd_hca_cap, cap, dct)) {
650         dev->flags |= UCT_IB_DEVICE_FLAG_DC;
651     }
652 
653     if (UCT_IB_MLX5DV_GET(cmd_hca_cap, cap, rndv_offload_dc)) {
654         md->flags |= UCT_IB_MLX5_MD_FLAG_DC_TM;
655     }
656 
657     if (UCT_IB_MLX5DV_GET(cmd_hca_cap, cap, compact_address_vector)) {
658         dev->flags |= UCT_IB_DEVICE_FLAG_AV;
659     }
660 
661     if (UCT_IB_MLX5DV_GET(cmd_hca_cap, cap, fixed_buffer_size)) {
662         md->flags |= UCT_IB_MLX5_MD_FLAG_KSM;
663     }
664 
665     if (UCT_IB_MLX5DV_GET(cmd_hca_cap, cap, ext_stride_num_range)) {
666         md->flags |= UCT_IB_MLX5_MD_FLAG_MP_RQ;
667     }
668 
669     if (!UCT_IB_MLX5DV_GET(cmd_hca_cap, cap, umr_modify_atomic_disabled)) {
670         md->flags |= UCT_IB_MLX5_MD_FLAG_INDIRECT_ATOMICS;
671     }
672 
673     if (UCT_IB_MLX5DV_GET(cmd_hca_cap, cap, log_max_rmp) > 0) {
674         md->flags |= UCT_IB_MLX5_MD_FLAG_RMP;
675     }
676 
677     status = uct_ib_mlx5_devx_check_odp(md, md_config, cap);
678     if (status != UCS_OK) {
679         goto err_free;
680     }
681 
682     if (UCT_IB_MLX5DV_GET(cmd_hca_cap, cap, atomic)) {
683         int ops = UCT_IB_MLX5_ATOMIC_OPS_CMP_SWAP |
684                   UCT_IB_MLX5_ATOMIC_OPS_FETCH_ADD;
685         uint8_t arg_size;
686         int cap_ops, mode8b;
687 
688         UCT_IB_MLX5DV_SET(query_hca_cap_in, in, op_mod, UCT_IB_MLX5_HCA_CAP_OPMOD_GET_CUR |
689                                                        (UCT_IB_MLX5_CAP_ATOMIC << 1));
690         ret = mlx5dv_devx_general_cmd(ctx, in, sizeof(in), out, sizeof(out));
691         if (ret != 0) {
692             ucs_error("mlx5dv_devx_general_cmd(QUERY_HCA_CAP, ATOMIC) failed: %m");
693             status = UCS_ERR_IO_ERROR;
694             goto err_free;
695         }
696 
697         arg_size = UCT_IB_MLX5DV_GET(atomic_caps, cap, atomic_size_qp);
698         cap_ops  = UCT_IB_MLX5DV_GET(atomic_caps, cap, atomic_operations);
699         mode8b   = UCT_IB_MLX5DV_GET(atomic_caps, cap, atomic_req_8B_endianness_mode);
700 
701         if ((cap_ops & ops) == ops) {
702             dev->atomic_arg_sizes = sizeof(uint64_t);
703             if (!mode8b) {
704                 dev->atomic_arg_sizes_be = sizeof(uint64_t);
705             }
706         }
707 
708         dev->atomic_align = ucs_rounddown_pow2(arg_size);
709 
710         ops |= UCT_IB_MLX5_ATOMIC_OPS_MASKED_CMP_SWAP |
711                UCT_IB_MLX5_ATOMIC_OPS_MASKED_FETCH_ADD;
712 
713         arg_size &= UCT_IB_MLX5DV_GET(query_hca_cap_out, out,
714                                       capability.atomic_caps.atomic_size_dc);
715 
716         if ((cap_ops & ops) == ops) {
717             dev->ext_atomic_arg_sizes = arg_size;
718             if (mode8b) {
719                 arg_size &= ~(sizeof(uint64_t));
720             }
721             dev->ext_atomic_arg_sizes_be = arg_size;
722         }
723 
724         dev->pci_fadd_arg_sizes  = UCT_IB_MLX5DV_GET(atomic_caps, cap, fetch_add_pci_atomic) << 2;
725         dev->pci_cswap_arg_sizes = UCT_IB_MLX5DV_GET(atomic_caps, cap, compare_swap_pci_atomic) << 2;
726     }
727 
728     md->super.ops = &uct_ib_mlx5_devx_md_ops;
729 
730     uct_ib_mlx5_parse_relaxed_order(md, md_config);
731     status = uct_ib_md_open_common(&md->super, ibv_device, md_config);
732     if (status != UCS_OK) {
733         goto err_free;
734     }
735 
736     ucs_recursive_spinlock_init(&md->dbrec_lock, 0);
737     status = ucs_mpool_init(&md->dbrec_pool, 0,
738                             sizeof(uct_ib_mlx5_dbrec_t), 0,
739                             UCS_SYS_CACHE_LINE_SIZE,
740                             ucs_get_page_size() / UCS_SYS_CACHE_LINE_SIZE - 1,
741                             UINT_MAX, &uct_ib_mlx5_dbrec_ops, "devx dbrec");
742     if (status != UCS_OK) {
743         goto err_free;
744     }
745 
746     status = uct_ib_mlx5_md_buf_alloc(md, ucs_get_page_size(), 0, &md->zero_buf,
747                                       &md->zero_mem, "zero umem");
748     if (status != UCS_OK) {
749         goto err_release_dbrec;
750     }
751 
752     dev->flags |= UCT_IB_DEVICE_FLAG_MLX5_PRM;
753     md->flags  |= UCT_IB_MLX5_MD_FLAG_DEVX;
754     md->flags  |= UCT_IB_MLX5_MD_FLAGS_DEVX_OBJS(md_config->devx_objs);
755     *p_md       = &md->super;
756     return status;
757 
758 err_release_dbrec:
759     ucs_mpool_cleanup(&md->dbrec_pool, 1);
760 err_free:
761     ucs_free(md);
762 err_free_context:
763     ibv_close_device(ctx);
764 err:
765     return status;
766 }
767 
uct_ib_mlx5_devx_md_cleanup(uct_ib_md_t * ibmd)768 void uct_ib_mlx5_devx_md_cleanup(uct_ib_md_t *ibmd)
769 {
770     uct_ib_mlx5_md_t *md = ucs_derived_of(ibmd, uct_ib_mlx5_md_t);
771     ucs_status_t status;
772 
773     uct_ib_mlx5_md_buf_free(md, md->zero_buf, &md->zero_mem);
774     ucs_mpool_cleanup(&md->dbrec_pool, 1);
775     status = ucs_recursive_spinlock_destroy(&md->dbrec_lock);
776     if (status != UCS_OK) {
777         ucs_warn("ucs_recursive_spinlock_destroy() failed (%d)", status);
778     }
779 }
780 
781 static uct_ib_md_ops_t uct_ib_mlx5_devx_md_ops = {
782     .open                = uct_ib_mlx5_devx_md_open,
783     .cleanup             = uct_ib_mlx5_devx_md_cleanup,
784     .reg_key             = uct_ib_mlx5_reg_key,
785     .dereg_key           = uct_ib_mlx5_dereg_key,
786     .reg_atomic_key      = uct_ib_mlx5_devx_reg_atomic_key,
787     .dereg_atomic_key    = uct_ib_mlx5_devx_dereg_atomic_key,
788     .reg_multithreaded   = uct_ib_mlx5_devx_reg_multithreaded,
789     .dereg_multithreaded = uct_ib_mlx5_devx_dereg_multithreaded,
790     .mem_prefetch        = uct_ib_mlx5_mem_prefetch,
791     .get_atomic_mr_id    = uct_ib_mlx5_md_get_atomic_mr_id,
792 };
793 
794 UCT_IB_MD_OPS(uct_ib_mlx5_devx_md_ops, 2);
795 
796 #endif
797 
uct_ib_mlx5dv_check_dc(uct_ib_device_t * dev)798 static ucs_status_t uct_ib_mlx5dv_check_dc(uct_ib_device_t *dev)
799 {
800     ucs_status_t status = UCS_OK;
801 #if HAVE_DC_DV
802     struct ibv_srq_init_attr srq_attr = {};
803     struct ibv_context *ctx = dev->ibv_context;
804     struct ibv_qp_init_attr_ex qp_attr = {};
805     struct mlx5dv_qp_init_attr dv_attr = {};
806     struct ibv_qp_attr attr = {};
807     struct ibv_srq *srq;
808     struct ibv_pd *pd;
809     struct ibv_cq *cq;
810     struct ibv_qp *qp;
811     int ret;
812 
813     ucs_debug("checking for DC support on %s", uct_ib_device_name(dev));
814 
815     pd = ibv_alloc_pd(ctx);
816     if (pd == NULL) {
817         ucs_error("ibv_alloc_pd() failed: %m");
818         return UCS_ERR_IO_ERROR;
819     }
820 
821     cq = ibv_create_cq(ctx, 1, NULL, NULL, 0);
822     if (cq == NULL) {
823         ucs_error("ibv_create_cq() failed: %m");
824         status = UCS_ERR_IO_ERROR;
825         goto err_cq;
826     }
827 
828     srq_attr.attr.max_sge   = 1;
829     srq_attr.attr.max_wr    = 1;
830     srq = ibv_create_srq(pd, &srq_attr);
831     if (srq == NULL) {
832         ucs_error("ibv_create_srq() failed: %m");
833         status = UCS_ERR_IO_ERROR;
834         goto err_srq;
835     }
836 
837     qp_attr.send_cq              = cq;
838     qp_attr.recv_cq              = cq;
839     qp_attr.qp_type              = IBV_QPT_DRIVER;
840     qp_attr.comp_mask            = IBV_QP_INIT_ATTR_PD;
841     qp_attr.pd                   = pd;
842     qp_attr.srq                  = srq;
843 
844     dv_attr.comp_mask            = MLX5DV_QP_INIT_ATTR_MASK_DC;
845     dv_attr.dc_init_attr.dc_type = MLX5DV_DCTYPE_DCT;
846     dv_attr.dc_init_attr.dct_access_key = UCT_IB_KEY;
847 
848     /* create DCT qp successful means DC is supported */
849     qp = mlx5dv_create_qp(ctx, &qp_attr, &dv_attr);
850     if (qp == NULL) {
851         ucs_debug("failed to create DCT on %s: %m", uct_ib_device_name(dev));
852         goto err_qp;
853     }
854 
855     attr.qp_state        = IBV_QPS_INIT;
856     attr.port_num        = 1;
857     attr.qp_access_flags = IBV_ACCESS_REMOTE_WRITE |
858                            IBV_ACCESS_REMOTE_READ  |
859                            IBV_ACCESS_REMOTE_ATOMIC;
860     ret = ibv_modify_qp(qp, &attr, IBV_QP_STATE |
861                                    IBV_QP_PKEY_INDEX |
862                                    IBV_QP_PORT |
863                                    IBV_QP_ACCESS_FLAGS);
864     if (ret != 0) {
865         ucs_debug("failed to ibv_modify_qp(DCT, INIT) on %s: %m",
866                   uct_ib_device_name(dev));
867         goto err;
868     }
869 
870     /* always set global address parameters, in case the port is RoCE or SRIOV */
871     attr.qp_state                  = IBV_QPS_RTR;
872     attr.min_rnr_timer             = 1;
873     attr.path_mtu                  = IBV_MTU_256;
874     attr.ah_attr.port_num          = 1;
875     attr.ah_attr.sl                = 0;
876     attr.ah_attr.is_global         = 1;
877     attr.ah_attr.grh.hop_limit     = 1;
878     attr.ah_attr.grh.traffic_class = 0;
879     attr.ah_attr.grh.sgid_index    = 0;
880 
881     ret = ibv_modify_qp(qp, &attr, IBV_QP_STATE |
882                                    IBV_QP_MIN_RNR_TIMER |
883                                    IBV_QP_AV |
884                                    IBV_QP_PATH_MTU);
885 
886     if (ret == 0) {
887         ucs_debug("DC is supported on %s", uct_ib_device_name(dev));
888         dev->flags |= UCT_IB_DEVICE_FLAG_DC;
889     } else {
890         ucs_debug("failed to ibv_modify_qp(DCT, RTR) on %s: %m",
891                   uct_ib_device_name(dev));
892     }
893 
894 err:
895     uct_ib_destroy_qp(qp);
896 err_qp:
897     uct_ib_destroy_srq(srq);
898 err_srq:
899     ibv_destroy_cq(cq);
900 err_cq:
901     ibv_dealloc_pd(pd);
902 #endif
903     return status;
904 }
905 
906 static uct_ib_md_ops_t uct_ib_mlx5_md_ops;
907 
uct_ib_mlx5dv_md_open(struct ibv_device * ibv_device,const uct_ib_md_config_t * md_config,uct_ib_md_t ** p_md)908 static ucs_status_t uct_ib_mlx5dv_md_open(struct ibv_device *ibv_device,
909                                           const uct_ib_md_config_t *md_config,
910                                           uct_ib_md_t **p_md)
911 {
912     ucs_status_t status = UCS_OK;
913     struct ibv_context *ctx;
914     uct_ib_device_t *dev;
915     uct_ib_mlx5_md_t *md;
916 
917 #if HAVE_DECL_MLX5DV_IS_SUPPORTED
918     if (!mlx5dv_is_supported(ibv_device)) {
919         return UCS_ERR_UNSUPPORTED;
920     }
921 #endif
922 
923     ctx = ibv_open_device(ibv_device);
924     if (ctx == NULL) {
925         ucs_debug("ibv_open_device(%s) failed: %m", ibv_get_device_name(ibv_device));
926         status = UCS_ERR_UNSUPPORTED;
927         goto err;
928     }
929 
930     md = ucs_calloc(1, sizeof(*md), "ib_mlx5_md");
931     if (md == NULL) {
932         status = UCS_ERR_NO_MEMORY;
933         goto err_free_context;
934     }
935 
936     dev              = &md->super.dev;
937     dev->ibv_context = ctx;
938     md->super.config = md_config->ext;
939 
940     status = uct_ib_device_query(dev, ibv_device);
941     if (status != UCS_OK) {
942         goto err_free;
943     }
944 
945     if (!(uct_ib_device_spec(dev)->flags & UCT_IB_DEVICE_FLAG_MLX5_PRM)) {
946         status = UCS_ERR_UNSUPPORTED;
947         goto err_free;
948     }
949 
950     if (UCT_IB_HAVE_ODP_IMPLICIT(&dev->dev_attr) &&
951         !uct_ib_mlx5_has_roce_port(dev)) {
952         dev->flags |= UCT_IB_DEVICE_FLAG_ODP_IMPLICIT;
953     }
954 
955     if (IBV_EXP_HAVE_ATOMIC_HCA(&dev->dev_attr)) {
956         dev->atomic_arg_sizes = sizeof(uint64_t);
957 
958 #if HAVE_STRUCT_IBV_DEVICE_ATTR_EX_PCI_ATOMIC_CAPS
959         dev->pci_fadd_arg_sizes  = dev->dev_attr.pci_atomic_caps.fetch_add << 2;
960         dev->pci_cswap_arg_sizes = dev->dev_attr.pci_atomic_caps.compare_swap << 2;
961 #endif
962     }
963 
964     status = uct_ib_mlx5dv_check_dc(dev);
965     if (status != UCS_OK) {
966         goto err_free;
967     }
968 
969     md->super.ops = &uct_ib_mlx5_md_ops;
970 
971     uct_ib_mlx5_parse_relaxed_order(md, md_config);
972     status = uct_ib_md_open_common(&md->super, ibv_device, md_config);
973     if (status != UCS_OK) {
974         goto err_free;
975     }
976 
977     dev->flags |= UCT_IB_DEVICE_FLAG_MLX5_PRM;
978     /* cppcheck-suppress autoVariables */
979     *p_md = &md->super;
980     return UCS_OK;
981 
982 err_free:
983     ucs_free(md);
984 err_free_context:
985     ibv_close_device(ctx);
986 err:
987     return status;
988 }
989 
990 static uct_ib_md_ops_t uct_ib_mlx5_md_ops = {
991     .open                = uct_ib_mlx5dv_md_open,
992     .cleanup             = (uct_ib_md_cleanup_func_t)ucs_empty_function,
993     .reg_key             = uct_ib_mlx5_reg_key,
994     .dereg_key           = uct_ib_mlx5_dereg_key,
995     .reg_atomic_key      = uct_ib_mlx5_reg_atomic_key,
996     .dereg_atomic_key    = (uct_ib_md_dereg_atomic_key_func_t)ucs_empty_function_return_success,
997     .reg_multithreaded   = (uct_ib_md_reg_multithreaded_func_t)ucs_empty_function_return_unsupported,
998     .dereg_multithreaded = (uct_ib_md_dereg_multithreaded_func_t)ucs_empty_function_return_unsupported,
999     .mem_prefetch        = uct_ib_mlx5_mem_prefetch,
1000     .get_atomic_mr_id    = (uct_ib_md_get_atomic_mr_id_func_t)ucs_empty_function_return_unsupported,
1001 };
1002 
1003 UCT_IB_MD_OPS(uct_ib_mlx5_md_ops, 1);
1004 
1005