Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (c) Intel Corporation. All rights reserved.
3 : : * Copyright (c) 2023-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4 : : */
5 : :
6 : : #include "spdk_internal/rdma_utils.h"
7 : :
8 : : #include "spdk/log.h"
9 : : #include "spdk/string.h"
10 : : #include "spdk/likely.h"
11 : :
12 : : #include "spdk_internal/assert.h"
13 : :
14 : : #include <rdma/rdma_cma.h>
15 : : #include <rdma/rdma_verbs.h>
16 : :
17 : : struct rdma_utils_device {
18 : : struct ibv_pd *pd;
19 : : struct ibv_context *context;
20 : : int ref;
21 : : bool removed;
22 : : TAILQ_ENTRY(rdma_utils_device) tailq;
23 : : };
24 : :
25 : : struct spdk_rdma_utils_mem_map {
26 : : struct spdk_mem_map *map;
27 : : struct ibv_pd *pd;
28 : : struct spdk_nvme_rdma_hooks *hooks;
29 : : uint32_t ref_count;
30 : : uint32_t access_flags;
31 : : LIST_ENTRY(spdk_rdma_utils_mem_map) link;
32 : : };
33 : :
34 : : struct rdma_utils_memory_domain {
35 : : TAILQ_ENTRY(rdma_utils_memory_domain) link;
36 : : uint32_t ref;
37 : : enum spdk_dma_device_type type;
38 : : struct ibv_pd *pd;
39 : : struct spdk_memory_domain *domain;
40 : : struct spdk_memory_domain_rdma_ctx rdma_ctx;
41 : : };
42 : :
43 : : static pthread_mutex_t g_dev_mutex = PTHREAD_MUTEX_INITIALIZER;
44 : : static struct ibv_context **g_ctx_list = NULL;
45 : : static TAILQ_HEAD(, rdma_utils_device) g_dev_list = TAILQ_HEAD_INITIALIZER(g_dev_list);
46 : :
47 : : static LIST_HEAD(, spdk_rdma_utils_mem_map) g_rdma_utils_mr_maps = LIST_HEAD_INITIALIZER(
48 : : &g_rdma_utils_mr_maps);
49 : : static pthread_mutex_t g_rdma_mr_maps_mutex = PTHREAD_MUTEX_INITIALIZER;
50 : :
51 : : static TAILQ_HEAD(, rdma_utils_memory_domain) g_memory_domains = TAILQ_HEAD_INITIALIZER(
52 : : g_memory_domains);
53 : : static pthread_mutex_t g_memory_domains_lock = PTHREAD_MUTEX_INITIALIZER;
54 : :
55 : : static int
56 : 9698 : rdma_utils_mem_notify(void *cb_ctx, struct spdk_mem_map *map,
57 : : enum spdk_mem_map_notify_action action,
58 : : void *vaddr, size_t size)
59 : : {
60 : 9698 : struct spdk_rdma_utils_mem_map *rmap = cb_ctx;
61 : 9698 : struct ibv_pd *pd = rmap->pd;
62 : : struct ibv_mr *mr;
63 : : uint32_t access_flags;
64 : : int rc;
65 : :
66 [ + + - ]: 9698 : switch (action) {
67 : 4849 : case SPDK_MEM_MAP_NOTIFY_REGISTER:
68 [ + - - + ]: 4849 : if (rmap->hooks && rmap->hooks->get_rkey) {
69 : 0 : rc = spdk_mem_map_set_translation(map, (uint64_t)vaddr, size,
70 : 0 : rmap->hooks->get_rkey(pd, vaddr, size));
71 : : } else {
72 : 4849 : access_flags = rmap->access_flags;
73 : : #ifdef IBV_ACCESS_OPTIONAL_FIRST
74 [ - + ]: 4849 : access_flags |= IBV_ACCESS_RELAXED_ORDERING;
75 : : #endif
76 : 4849 : mr = ibv_reg_mr(pd, vaddr, size, access_flags);
77 [ - + ]: 4849 : if (mr == NULL) {
78 : 0 : SPDK_ERRLOG("ibv_reg_mr() failed\n");
79 : 0 : return -1;
80 : : } else {
81 : 4849 : rc = spdk_mem_map_set_translation(map, (uint64_t)vaddr, size, (uint64_t)mr);
82 : : }
83 : : }
84 : 4849 : break;
85 : 4849 : case SPDK_MEM_MAP_NOTIFY_UNREGISTER:
86 [ + - + - ]: 4849 : if (rmap->hooks == NULL || rmap->hooks->get_rkey == NULL) {
87 : 4849 : mr = (struct ibv_mr *)spdk_mem_map_translate(map, (uint64_t)vaddr, NULL);
88 [ + - ]: 4849 : if (mr) {
89 : 4849 : ibv_dereg_mr(mr);
90 : : }
91 : : }
92 : 4849 : rc = spdk_mem_map_clear_translation(map, (uint64_t)vaddr, size);
93 : 4849 : break;
94 : 0 : default:
95 : 0 : SPDK_UNREACHABLE();
96 : : }
97 : :
98 : 9698 : return rc;
99 : : }
100 : :
101 : : static int
102 : 376 : rdma_check_contiguous_entries(uint64_t addr_1, uint64_t addr_2)
103 : : {
104 : : /* Two contiguous mappings will point to the same address which is the start of the RDMA MR. */
105 : 376 : return addr_1 == addr_2;
106 : : }
107 : :
108 : : const struct spdk_mem_map_ops g_rdma_map_ops = {
109 : : .notify_cb = rdma_utils_mem_notify,
110 : : .are_contiguous = rdma_check_contiguous_entries
111 : : };
112 : :
113 : : static void
114 : 381 : _rdma_free_mem_map(struct spdk_rdma_utils_mem_map *map)
115 : : {
116 [ - + ]: 381 : assert(map);
117 : :
118 [ + - ]: 381 : if (map->hooks) {
119 : 381 : spdk_free(map);
120 : : } else {
121 : 0 : free(map);
122 : : }
123 : 381 : }
124 : :
125 : : struct spdk_rdma_utils_mem_map *
126 : 980 : spdk_rdma_utils_create_mem_map(struct ibv_pd *pd, struct spdk_nvme_rdma_hooks *hooks,
127 : : uint32_t access_flags)
128 : : {
129 : : struct spdk_rdma_utils_mem_map *map;
130 : :
131 [ - + ]: 980 : if (pd->context->device->transport_type == IBV_TRANSPORT_IWARP) {
132 : : /* IWARP requires REMOTE_WRITE permission for RDMA_READ operation */
133 : 0 : access_flags |= IBV_ACCESS_REMOTE_WRITE;
134 : : }
135 : :
136 [ - + ]: 980 : pthread_mutex_lock(&g_rdma_mr_maps_mutex);
137 : : /* Look up existing mem map registration for this pd */
138 [ + + ]: 1028 : LIST_FOREACH(map, &g_rdma_utils_mr_maps, link) {
139 [ + + + - ]: 647 : if (map->pd == pd && map->access_flags == access_flags) {
140 : 599 : map->ref_count++;
141 [ - + ]: 599 : pthread_mutex_unlock(&g_rdma_mr_maps_mutex);
142 : 599 : return map;
143 : : }
144 : : }
145 : :
146 [ + - ]: 381 : if (hooks) {
147 : 381 : map = spdk_zmalloc(sizeof(*map), 0, NULL, SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA);
148 : : } else {
149 : 0 : map = calloc(1, sizeof(*map));
150 : : }
151 [ - + ]: 381 : if (!map) {
152 [ # # ]: 0 : pthread_mutex_unlock(&g_rdma_mr_maps_mutex);
153 : 0 : SPDK_ERRLOG("Memory allocation failed\n");
154 : 0 : return NULL;
155 : : }
156 : 381 : map->pd = pd;
157 : 381 : map->ref_count = 1;
158 : 381 : map->hooks = hooks;
159 : 381 : map->access_flags = access_flags;
160 : 381 : map->map = spdk_mem_map_alloc(0, &g_rdma_map_ops, map);
161 [ - + ]: 381 : if (!map->map) {
162 : 0 : SPDK_ERRLOG("Unable to create memory map\n");
163 : 0 : _rdma_free_mem_map(map);
164 [ # # ]: 0 : pthread_mutex_unlock(&g_rdma_mr_maps_mutex);
165 : 0 : return NULL;
166 : : }
167 [ + + ]: 381 : LIST_INSERT_HEAD(&g_rdma_utils_mr_maps, map, link);
168 : :
169 [ - + ]: 381 : pthread_mutex_unlock(&g_rdma_mr_maps_mutex);
170 : :
171 : 381 : return map;
172 : : }
173 : :
174 : : void
175 : 995 : spdk_rdma_utils_free_mem_map(struct spdk_rdma_utils_mem_map **_map)
176 : : {
177 : : struct spdk_rdma_utils_mem_map *map;
178 : :
179 [ - + ]: 995 : if (!_map) {
180 : 0 : return;
181 : : }
182 : :
183 : 995 : map = *_map;
184 [ + + ]: 995 : if (!map) {
185 : 15 : return;
186 : : }
187 : 980 : *_map = NULL;
188 : :
189 [ - + ]: 980 : pthread_mutex_lock(&g_rdma_mr_maps_mutex);
190 [ - + ]: 980 : assert(map->ref_count > 0);
191 : 980 : map->ref_count--;
192 [ + + ]: 980 : if (map->ref_count != 0) {
193 [ - + ]: 599 : pthread_mutex_unlock(&g_rdma_mr_maps_mutex);
194 : 599 : return;
195 : : }
196 : :
197 [ + + ]: 381 : LIST_REMOVE(map, link);
198 [ - + ]: 381 : pthread_mutex_unlock(&g_rdma_mr_maps_mutex);
199 [ + - ]: 381 : if (map->map) {
200 : 381 : spdk_mem_map_free(&map->map);
201 : : }
202 : 381 : _rdma_free_mem_map(map);
203 : : }
204 : :
205 : : int
206 : 17943934 : spdk_rdma_utils_get_translation(struct spdk_rdma_utils_mem_map *map, void *address,
207 : : size_t length, struct spdk_rdma_utils_memory_translation *translation)
208 : : {
209 : 17943934 : uint64_t real_length = length;
210 : :
211 [ - + ]: 17943934 : assert(map);
212 [ - + ]: 17943934 : assert(address);
213 [ - + ]: 17943934 : assert(translation);
214 : :
215 [ + - - + ]: 17943934 : if (map->hooks && map->hooks->get_rkey) {
216 : 0 : translation->translation_type = SPDK_RDMA_UTILS_TRANSLATION_KEY;
217 : 0 : translation->mr_or_key.key = spdk_mem_map_translate(map->map, (uint64_t)address, &real_length);
218 : : } else {
219 : 17943934 : translation->translation_type = SPDK_RDMA_UTILS_TRANSLATION_MR;
220 : 17943934 : translation->mr_or_key.mr = (struct ibv_mr *)spdk_mem_map_translate(map->map, (uint64_t)address,
221 : : &real_length);
222 [ - + ]: 17943934 : if (spdk_unlikely(!translation->mr_or_key.mr)) {
223 : 0 : SPDK_ERRLOG("No translation for ptr %p, size %zu\n", address, length);
224 : 0 : return -EINVAL;
225 : : }
226 : : }
227 : :
228 [ - + ]: 17943934 : assert(real_length >= length);
229 : :
230 : 17943934 : return 0;
231 : : }
232 : :
233 : :
234 : : static struct rdma_utils_device *
235 : 103 : rdma_add_dev(struct ibv_context *context)
236 : : {
237 : : struct rdma_utils_device *dev;
238 : :
239 : 103 : dev = calloc(1, sizeof(*dev));
240 [ - + ]: 103 : if (dev == NULL) {
241 : 0 : SPDK_ERRLOG("Failed to allocate RDMA device object.\n");
242 : 0 : return NULL;
243 : : }
244 : :
245 : 103 : dev->pd = ibv_alloc_pd(context);
246 [ - + ]: 103 : if (dev->pd == NULL) {
247 : 0 : SPDK_ERRLOG("ibv_alloc_pd() failed: %s (%d)\n", spdk_strerror(errno), errno);
248 : 0 : free(dev);
249 : 0 : return NULL;
250 : : }
251 : :
252 : 103 : dev->context = context;
253 : 103 : TAILQ_INSERT_TAIL(&g_dev_list, dev, tailq);
254 : :
255 : 103 : return dev;
256 : : }
257 : :
258 : : static void
259 : 1022 : rdma_remove_dev(struct rdma_utils_device *dev)
260 : : {
261 [ + + + + : 1022 : if (!dev->removed || dev->ref > 0) {
+ + ]
262 : 919 : return;
263 : : }
264 : :
265 : : /* Deallocate protection domain only if the device is already removed and
266 : : * there is no reference.
267 : : */
268 [ + + ]: 103 : TAILQ_REMOVE(&g_dev_list, dev, tailq);
269 : 103 : ibv_dealloc_pd(dev->pd);
270 : 103 : free(dev);
271 : : }
272 : :
273 : : static int
274 : 1836 : ctx_cmp(const void *_c1, const void *_c2)
275 : : {
276 : 1836 : struct ibv_context *c1 = *(struct ibv_context **)_c1;
277 : 1836 : struct ibv_context *c2 = *(struct ibv_context **)_c2;
278 : :
279 [ + + ]: 1836 : return c1 < c2 ? -1 : c1 > c2;
280 : : }
281 : :
282 : : static int
283 : 1844 : rdma_sync_dev_list(void)
284 : : {
285 : : struct ibv_context **new_ctx_list;
286 : : int i, j;
287 : 1844 : int num_devs = 0;
288 : :
289 : : /*
290 : : * rdma_get_devices() returns a NULL terminated array of opened RDMA devices,
291 : : * and sets num_devs to the number of the returned devices.
292 : : */
293 : 1844 : new_ctx_list = rdma_get_devices(&num_devs);
294 [ - + ]: 1844 : if (new_ctx_list == NULL) {
295 : 0 : SPDK_ERRLOG("rdma_get_devices() failed: %s (%d)\n", spdk_strerror(errno), errno);
296 : 0 : return -ENODEV;
297 : : }
298 : :
299 [ - + ]: 1844 : if (num_devs == 0) {
300 : 0 : rdma_free_devices(new_ctx_list);
301 : 0 : SPDK_ERRLOG("Returned RDMA device array was empty\n");
302 : 0 : return -ENODEV;
303 : : }
304 : :
305 : : /*
306 : : * Sort new_ctx_list by addresses to update devices easily.
307 : : */
308 [ - + ]: 1844 : qsort(new_ctx_list, num_devs, sizeof(struct ibv_context *), ctx_cmp);
309 : :
310 [ + + ]: 1844 : if (g_ctx_list == NULL) {
311 : : /* If no old array, this is the first call. Add all devices. */
312 [ + + ]: 150 : for (i = 0; new_ctx_list[i] != NULL; i++) {
313 : 100 : rdma_add_dev(new_ctx_list[i]);
314 : : }
315 : :
316 : 50 : goto exit;
317 : : }
318 : :
319 [ + + + + ]: 5376 : for (i = j = 0; new_ctx_list[i] != NULL || g_ctx_list[j] != NULL;) {
320 : 3582 : struct ibv_context *new_ctx = new_ctx_list[i];
321 : 3582 : struct ibv_context *old_ctx = g_ctx_list[j];
322 : 3582 : bool add = false, remove = false;
323 : :
324 : : /*
325 : : * If a context exists only in the new array, create a device for it,
326 : : * or if a context exists only in the old array, try removing the
327 : : * corresponding device.
328 : : */
329 : :
330 [ - + ]: 3582 : if (old_ctx == NULL) {
331 : 0 : add = true;
332 [ + + ]: 3582 : } else if (new_ctx == NULL) {
333 : 3 : remove = true;
334 [ + + ]: 3579 : } else if (new_ctx < old_ctx) {
335 : 3 : add = true;
336 [ + + ]: 3576 : } else if (old_ctx < new_ctx) {
337 : 3 : remove = true;
338 : : }
339 : :
340 [ + + ]: 3582 : if (add) {
341 : 3 : rdma_add_dev(new_ctx_list[i]);
342 : 3 : i++;
343 [ + + ]: 3579 : } else if (remove) {
344 : : struct rdma_utils_device *dev, *tmp;
345 : :
346 [ + + ]: 21 : TAILQ_FOREACH_SAFE(dev, &g_dev_list, tailq, tmp) {
347 [ + + ]: 15 : if (dev->context == g_ctx_list[j]) {
348 : 6 : dev->removed = true;
349 : 6 : rdma_remove_dev(dev);
350 : : }
351 : : }
352 : 6 : j++;
353 : : } else {
354 : 3573 : i++;
355 : 3573 : j++;
356 : : }
357 : : }
358 : :
359 : : /* Free the old array. */
360 : 1794 : rdma_free_devices(g_ctx_list);
361 : :
362 : 1844 : exit:
363 : : /*
364 : : * Keep the newly returned array so that allocated protection domains
365 : : * are not freed unexpectedly.
366 : : */
367 : 1844 : g_ctx_list = new_ctx_list;
368 : 1844 : return 0;
369 : : }
370 : :
371 : : struct ibv_pd *
372 : 925 : spdk_rdma_utils_get_pd(struct ibv_context *context)
373 : : {
374 : : struct rdma_utils_device *dev;
375 : : int rc;
376 : :
377 [ - + ]: 925 : pthread_mutex_lock(&g_dev_mutex);
378 : :
379 : 925 : rc = rdma_sync_dev_list();
380 [ - + ]: 925 : if (rc != 0) {
381 [ # # ]: 0 : pthread_mutex_unlock(&g_dev_mutex);
382 : :
383 : 0 : SPDK_ERRLOG("Failed to sync RDMA device list\n");
384 : 0 : return NULL;
385 : : }
386 : :
387 [ + + ]: 939 : TAILQ_FOREACH(dev, &g_dev_list, tailq) {
388 [ + + + + : 933 : if (dev->context == context && !dev->removed) {
+ + ]
389 : 919 : dev->ref++;
390 [ - + ]: 919 : pthread_mutex_unlock(&g_dev_mutex);
391 : :
392 : 919 : return dev->pd;
393 : : }
394 : : }
395 : :
396 [ - + ]: 6 : pthread_mutex_unlock(&g_dev_mutex);
397 : :
398 : 6 : SPDK_ERRLOG("Failed to get PD\n");
399 : 6 : return NULL;
400 : : }
401 : :
402 : : void
403 : 919 : spdk_rdma_utils_put_pd(struct ibv_pd *pd)
404 : : {
405 : : struct rdma_utils_device *dev, *tmp;
406 : :
407 [ - + ]: 919 : pthread_mutex_lock(&g_dev_mutex);
408 : :
409 [ + + ]: 2754 : TAILQ_FOREACH_SAFE(dev, &g_dev_list, tailq, tmp) {
410 [ + + ]: 1835 : if (dev->pd == pd) {
411 [ - + ]: 919 : assert(dev->ref > 0);
412 : 919 : dev->ref--;
413 : :
414 : 919 : rdma_remove_dev(dev);
415 : : }
416 : : }
417 : :
418 : 919 : rdma_sync_dev_list();
419 : :
420 [ - + ]: 919 : pthread_mutex_unlock(&g_dev_mutex);
421 : 919 : }
422 : :
423 : : __attribute__((destructor)) static void
424 : 2516 : _rdma_utils_fini(void)
425 : : {
426 : : struct rdma_utils_device *dev, *tmp;
427 : :
428 [ + + ]: 2613 : TAILQ_FOREACH_SAFE(dev, &g_dev_list, tailq, tmp) {
429 : 97 : dev->removed = true;
430 : 97 : dev->ref = 0;
431 : 97 : rdma_remove_dev(dev);
432 : : }
433 : :
434 [ + + ]: 2516 : if (g_ctx_list != NULL) {
435 : 50 : rdma_free_devices(g_ctx_list);
436 : 50 : g_ctx_list = NULL;
437 : : }
438 : 2516 : }
439 : :
440 : : struct spdk_memory_domain *
441 : 913 : spdk_rdma_utils_get_memory_domain(struct ibv_pd *pd)
442 : : {
443 : 913 : struct rdma_utils_memory_domain *domain = NULL;
444 : 0 : struct spdk_memory_domain_ctx ctx;
445 : : int rc;
446 : :
447 [ - + ]: 913 : pthread_mutex_lock(&g_memory_domains_lock);
448 : :
449 [ + + ]: 915 : TAILQ_FOREACH(domain, &g_memory_domains, link) {
450 [ + + ]: 624 : if (domain->pd == pd) {
451 : 622 : domain->ref++;
452 [ - + ]: 622 : pthread_mutex_unlock(&g_memory_domains_lock);
453 : 622 : return domain->domain;
454 : : }
455 : : }
456 : :
457 : 291 : domain = calloc(1, sizeof(*domain));
458 [ - + ]: 291 : if (!domain) {
459 : 0 : SPDK_ERRLOG("Memory allocation failed\n");
460 [ # # ]: 0 : pthread_mutex_unlock(&g_memory_domains_lock);
461 : 0 : return NULL;
462 : : }
463 : :
464 : 291 : domain->rdma_ctx.size = sizeof(domain->rdma_ctx);
465 : 291 : domain->rdma_ctx.ibv_pd = pd;
466 : 291 : ctx.size = sizeof(ctx);
467 : 291 : ctx.user_ctx = &domain->rdma_ctx;
468 : :
469 : 291 : rc = spdk_memory_domain_create(&domain->domain, SPDK_DMA_DEVICE_TYPE_RDMA, &ctx,
470 : : SPDK_RDMA_DMA_DEVICE);
471 [ - + ]: 291 : if (rc) {
472 : 0 : SPDK_ERRLOG("Failed to create memory domain\n");
473 : 0 : free(domain);
474 [ # # ]: 0 : pthread_mutex_unlock(&g_memory_domains_lock);
475 : 0 : return NULL;
476 : : }
477 : :
478 : 291 : domain->pd = pd;
479 : 291 : domain->ref = 1;
480 : 291 : TAILQ_INSERT_TAIL(&g_memory_domains, domain, link);
481 : :
482 [ - + ]: 291 : pthread_mutex_unlock(&g_memory_domains_lock);
483 : :
484 : 291 : return domain->domain;
485 : : }
486 : :
487 : : int
488 : 885 : spdk_rdma_utils_put_memory_domain(struct spdk_memory_domain *_domain)
489 : : {
490 : 885 : struct rdma_utils_memory_domain *domain = NULL;
491 : :
492 [ - + ]: 885 : if (!_domain) {
493 : 0 : return 0;
494 : : }
495 : :
496 [ - + ]: 885 : pthread_mutex_lock(&g_memory_domains_lock);
497 : :
498 [ + - ]: 887 : TAILQ_FOREACH(domain, &g_memory_domains, link) {
499 [ + + ]: 887 : if (domain->domain == _domain) {
500 : 885 : break;
501 : : }
502 : : }
503 : :
504 [ - + ]: 885 : if (!domain) {
505 [ # # ]: 0 : pthread_mutex_unlock(&g_memory_domains_lock);
506 : 0 : return -ENODEV;
507 : : }
508 [ - + ]: 885 : assert(domain->ref > 0);
509 : :
510 : 885 : domain->ref--;
511 : :
512 [ + + ]: 885 : if (domain->ref == 0) {
513 : 284 : spdk_memory_domain_destroy(domain->domain);
514 [ - + ]: 284 : TAILQ_REMOVE(&g_memory_domains, domain, link);
515 : 284 : free(domain);
516 : : }
517 : :
518 [ - + ]: 885 : pthread_mutex_unlock(&g_memory_domains_lock);
519 : :
520 : 885 : return 0;
521 : : }
|