LCOV - code coverage report
Current view: top level - lib/rdma_utils - rdma_utils.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 85 261 32.6 %
Date: 2024-11-05 10:06:02 Functions: 7 16 43.8 %

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

Generated by: LCOV version 1.15