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