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