LCOV - code coverage report
Current view: top level - lib/rdma - common.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 85 273 31.1 %
Date: 2024-07-11 03:15:54 Functions: 7 20 35.0 %

          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 : }

Generated by: LCOV version 1.15