LCOV - code coverage report
Current view: top level - lib/vfu_tgt - tgt_endpoint.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 0 434 0.0 %
Date: 2024-12-14 21:01:04 Functions: 0 33 0.0 %

          Line data    Source code
       1             : /*   SPDX-License-Identifier: BSD-3-Clause
       2             :  *   Copyright (C) 2022 Intel Corporation.
       3             :  *   All rights reserved.
       4             :  */
       5             : 
       6             : #include "spdk/stdinc.h"
       7             : #include "spdk/env.h"
       8             : #include "spdk/thread.h"
       9             : #include "spdk/log.h"
      10             : #include "spdk/util.h"
      11             : #include "spdk/memory.h"
      12             : #include "spdk/cpuset.h"
      13             : #include "spdk/likely.h"
      14             : #include "spdk/vfu_target.h"
      15             : 
      16             : #include "tgt_internal.h"
      17             : 
      18             : struct tgt_pci_device_ops {
      19             :         struct spdk_vfu_endpoint_ops ops;
      20             :         TAILQ_ENTRY(tgt_pci_device_ops) link;
      21             : };
      22             : 
      23             : static struct spdk_cpuset g_tgt_core_mask;
      24             : static pthread_mutex_t g_endpoint_lock = PTHREAD_MUTEX_INITIALIZER;
      25             : static TAILQ_HEAD(, spdk_vfu_endpoint) g_endpoint = TAILQ_HEAD_INITIALIZER(g_endpoint);
      26             : static TAILQ_HEAD(, tgt_pci_device_ops) g_pci_device_ops = TAILQ_HEAD_INITIALIZER(g_pci_device_ops);
      27             : static char g_endpoint_path_dirname[PATH_MAX] = "";
      28             : static uint32_t g_fini_endpoint_cnt = 0;
      29             : static spdk_vfu_fini_cb g_fini_cb = NULL;
      30             : 
      31             : static struct spdk_vfu_endpoint_ops *
      32           0 : tgt_get_pci_device_ops(const char *device_type_name)
      33             : {
      34             :         struct tgt_pci_device_ops *pci_ops, *tmp;
      35           0 :         bool exist = false;
      36             : 
      37           0 :         pthread_mutex_lock(&g_endpoint_lock);
      38           0 :         TAILQ_FOREACH_SAFE(pci_ops, &g_pci_device_ops, link, tmp) {
      39           0 :                 if (!strncmp(device_type_name, pci_ops->ops.name, SPDK_VFU_MAX_NAME_LEN)) {
      40           0 :                         exist = true;
      41           0 :                         break;
      42             :                 }
      43             :         }
      44           0 :         pthread_mutex_unlock(&g_endpoint_lock);
      45             : 
      46           0 :         if (exist) {
      47           0 :                 return &pci_ops->ops;
      48             :         }
      49           0 :         return NULL;
      50             : }
      51             : 
      52             : int
      53           0 : spdk_vfu_register_endpoint_ops(struct spdk_vfu_endpoint_ops *ops)
      54             : {
      55             :         struct tgt_pci_device_ops *pci_ops;
      56             :         struct spdk_vfu_endpoint_ops *tmp;
      57             : 
      58           0 :         tmp = tgt_get_pci_device_ops(ops->name);
      59           0 :         if (tmp) {
      60           0 :                 return -EEXIST;
      61             :         }
      62             : 
      63           0 :         pci_ops = calloc(1, sizeof(*pci_ops));
      64           0 :         if (!pci_ops) {
      65           0 :                 return -ENOMEM;
      66             :         }
      67           0 :         pci_ops->ops = *ops;
      68             : 
      69           0 :         pthread_mutex_lock(&g_endpoint_lock);
      70           0 :         TAILQ_INSERT_TAIL(&g_pci_device_ops, pci_ops, link);
      71           0 :         pthread_mutex_unlock(&g_endpoint_lock);
      72             : 
      73           0 :         return 0;
      74             : }
      75             : 
      76             : static char *
      77           0 : tgt_get_base_path(void)
      78             : {
      79           0 :         return g_endpoint_path_dirname;
      80             : }
      81             : 
      82             : int
      83           0 : spdk_vfu_set_socket_path(const char *basename)
      84             : {
      85             :         int ret;
      86             : 
      87           0 :         if (basename && strlen(basename) > 0) {
      88           0 :                 ret = snprintf(g_endpoint_path_dirname, sizeof(g_endpoint_path_dirname) - 2, "%s", basename);
      89           0 :                 if (ret <= 0) {
      90           0 :                         return -EINVAL;
      91             :                 }
      92           0 :                 if ((size_t)ret >= sizeof(g_endpoint_path_dirname) - 2) {
      93           0 :                         SPDK_ERRLOG("Char dev dir path length %d is too long\n", ret);
      94           0 :                         return -EINVAL;
      95             :                 }
      96             : 
      97           0 :                 if (g_endpoint_path_dirname[ret - 1] != '/') {
      98           0 :                         g_endpoint_path_dirname[ret] = '/';
      99           0 :                         g_endpoint_path_dirname[ret + 1]  = '\0';
     100             :                 }
     101             :         }
     102             : 
     103           0 :         return 0;
     104             : }
     105             : 
     106             : struct spdk_vfu_endpoint *
     107           0 : spdk_vfu_get_endpoint_by_name(const char *name)
     108             : {
     109             :         struct spdk_vfu_endpoint *endpoint, *tmp;
     110           0 :         bool exist = false;
     111             : 
     112           0 :         pthread_mutex_lock(&g_endpoint_lock);
     113           0 :         TAILQ_FOREACH_SAFE(endpoint, &g_endpoint, link, tmp) {
     114           0 :                 if (!strncmp(name, endpoint->name, SPDK_VFU_MAX_NAME_LEN)) {
     115           0 :                         exist = true;
     116           0 :                         break;
     117             :                 }
     118             :         }
     119           0 :         pthread_mutex_unlock(&g_endpoint_lock);
     120             : 
     121           0 :         if (exist) {
     122           0 :                 return endpoint;
     123             :         }
     124           0 :         return NULL;
     125             : }
     126             : 
     127             : static int
     128           0 : tgt_vfu_ctx_poller(void *ctx)
     129             : {
     130           0 :         struct spdk_vfu_endpoint *endpoint = ctx;
     131           0 :         vfu_ctx_t *vfu_ctx = endpoint->vfu_ctx;
     132             :         int ret;
     133             : 
     134           0 :         ret = vfu_run_ctx(vfu_ctx);
     135           0 :         if (spdk_unlikely(ret == -1)) {
     136           0 :                 if (errno == EBUSY) {
     137           0 :                         return SPDK_POLLER_IDLE;
     138             :                 }
     139             : 
     140           0 :                 if (errno == ENOTCONN) {
     141           0 :                         spdk_poller_unregister(&endpoint->vfu_ctx_poller);
     142           0 :                         if (endpoint->ops.detach_device) {
     143           0 :                                 endpoint->ops.detach_device(endpoint);
     144             :                         }
     145           0 :                         endpoint->is_attached = false;
     146           0 :                         return SPDK_POLLER_BUSY;
     147             :                 }
     148             :         }
     149             : 
     150           0 :         return ret != 0 ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE;
     151             : }
     152             : 
     153             : static int
     154           0 : tgt_accept_poller(void *ctx)
     155             : {
     156           0 :         struct spdk_vfu_endpoint *endpoint = ctx;
     157             :         int ret;
     158             : 
     159           0 :         if (endpoint->is_attached) {
     160           0 :                 return SPDK_POLLER_IDLE;
     161             :         }
     162             : 
     163           0 :         ret = vfu_attach_ctx(endpoint->vfu_ctx);
     164           0 :         if (ret == 0) {
     165           0 :                 ret = endpoint->ops.attach_device(endpoint);
     166           0 :                 if (!ret) {
     167           0 :                         SPDK_NOTICELOG("%s: attached successfully\n", spdk_vfu_get_endpoint_id(endpoint));
     168             :                         /* Polling socket too frequently will cause performance issue */
     169           0 :                         endpoint->vfu_ctx_poller = SPDK_POLLER_REGISTER(tgt_vfu_ctx_poller, endpoint, 1000);
     170           0 :                         endpoint->is_attached = true;
     171             :                 }
     172           0 :                 return SPDK_POLLER_BUSY;
     173             :         }
     174             : 
     175           0 :         if (errno == EAGAIN || errno == EWOULDBLOCK) {
     176           0 :                 return SPDK_POLLER_IDLE;
     177             :         }
     178             : 
     179           0 :         return SPDK_POLLER_BUSY;
     180             : }
     181             : 
     182             : static void
     183           0 : tgt_log_cb(vfu_ctx_t *vfu_ctx, int level, char const *msg)
     184             : {
     185           0 :         struct spdk_vfu_endpoint *endpoint = vfu_get_private(vfu_ctx);
     186             : 
     187           0 :         if (level >= LOG_DEBUG) {
     188           0 :                 SPDK_DEBUGLOG(vfu, "%s: %s\n", spdk_vfu_get_endpoint_id(endpoint), msg);
     189           0 :         } else if (level >= LOG_INFO) {
     190           0 :                 SPDK_INFOLOG(vfu, "%s: %s\n", spdk_vfu_get_endpoint_id(endpoint), msg);
     191           0 :         } else if (level >= LOG_NOTICE) {
     192           0 :                 SPDK_NOTICELOG("%s: %s\n", spdk_vfu_get_endpoint_id(endpoint), msg);
     193           0 :         } else if (level >= LOG_WARNING) {
     194           0 :                 SPDK_WARNLOG("%s: %s\n", spdk_vfu_get_endpoint_id(endpoint), msg);
     195             :         } else {
     196           0 :                 SPDK_ERRLOG("%s: %s\n", spdk_vfu_get_endpoint_id(endpoint), msg);
     197             :         }
     198           0 : }
     199             : 
     200             : static int
     201           0 : tgt_get_log_level(void)
     202             : {
     203             :         int level;
     204             : 
     205           0 :         if (SPDK_DEBUGLOG_FLAG_ENABLED("vfu")) {
     206           0 :                 return LOG_DEBUG;
     207             :         }
     208             : 
     209           0 :         level = spdk_log_to_syslog_level(spdk_log_get_level());
     210           0 :         if (level < 0) {
     211           0 :                 return LOG_ERR;
     212             :         }
     213             : 
     214           0 :         return level;
     215             : }
     216             : 
     217             : static void
     218           0 : init_pci_config_space(vfu_pci_config_space_t *p, uint16_t ipin)
     219             : {
     220             :         /* MLBAR */
     221           0 :         p->hdr.bars[0].raw = 0x0;
     222             :         /* MUBAR */
     223           0 :         p->hdr.bars[1].raw = 0x0;
     224             : 
     225             :         /* vendor specific, let's set them to zero for now */
     226           0 :         p->hdr.bars[3].raw = 0x0;
     227           0 :         p->hdr.bars[4].raw = 0x0;
     228           0 :         p->hdr.bars[5].raw = 0x0;
     229             : 
     230             :         /* enable INTx */
     231           0 :         p->hdr.intr.ipin = ipin;
     232           0 : }
     233             : 
     234             : static void
     235           0 : tgt_memory_region_add_cb(vfu_ctx_t *vfu_ctx, vfu_dma_info_t *info)
     236             : {
     237           0 :         struct spdk_vfu_endpoint *endpoint = vfu_get_private(vfu_ctx);
     238             :         void *map_start, *map_end;
     239             :         int ret;
     240             : 
     241           0 :         if (!info->vaddr) {
     242           0 :                 return;
     243             :         }
     244             : 
     245           0 :         map_start = info->mapping.iov_base;
     246           0 :         map_end = info->mapping.iov_base + info->mapping.iov_len;
     247             : 
     248           0 :         if (((uintptr_t)info->mapping.iov_base & MASK_2MB) ||
     249           0 :             (info->mapping.iov_len & MASK_2MB)) {
     250           0 :                 SPDK_DEBUGLOG(vfu, "Invalid memory region vaddr %p, IOVA %p-%p\n",
     251             :                               info->vaddr, map_start, map_end);
     252           0 :                 return;
     253             :         }
     254             : 
     255           0 :         if (info->prot == (PROT_WRITE | PROT_READ)) {
     256           0 :                 ret = spdk_mem_register(info->mapping.iov_base, info->mapping.iov_len);
     257           0 :                 if (ret) {
     258           0 :                         SPDK_ERRLOG("Memory region register %p-%p failed, ret=%d\n",
     259             :                                     map_start, map_end, ret);
     260             :                 }
     261             :         }
     262             : 
     263           0 :         if (endpoint->ops.post_memory_add) {
     264           0 :                 endpoint->ops.post_memory_add(endpoint, map_start, map_end);
     265             :         }
     266             : }
     267             : 
     268             : static void
     269           0 : tgt_memory_region_remove_cb(vfu_ctx_t *vfu_ctx, vfu_dma_info_t *info)
     270             : {
     271           0 :         struct spdk_vfu_endpoint *endpoint = vfu_get_private(vfu_ctx);
     272             :         void *map_start, *map_end;
     273           0 :         int ret = 0;
     274             : 
     275           0 :         if (!info->vaddr) {
     276           0 :                 return;
     277             :         }
     278             : 
     279           0 :         map_start = info->mapping.iov_base;
     280           0 :         map_end = info->mapping.iov_base + info->mapping.iov_len;
     281             : 
     282           0 :         if (((uintptr_t)info->mapping.iov_base & MASK_2MB) ||
     283           0 :             (info->mapping.iov_len & MASK_2MB)) {
     284           0 :                 SPDK_DEBUGLOG(vfu, "Invalid memory region vaddr %p, IOVA %p-%p\n",
     285             :                               info->vaddr, map_start, map_end);
     286           0 :                 return;
     287             :         }
     288             : 
     289           0 :         if (endpoint->ops.pre_memory_remove) {
     290           0 :                 endpoint->ops.pre_memory_remove(endpoint, map_start, map_end);
     291             :         }
     292             : 
     293           0 :         if (info->prot == (PROT_WRITE | PROT_READ)) {
     294           0 :                 ret = spdk_mem_unregister(info->mapping.iov_base, info->mapping.iov_len);
     295           0 :                 if (ret) {
     296           0 :                         SPDK_ERRLOG("Memory region unregister %p-%p failed, ret=%d\n",
     297             :                                     map_start, map_end, ret);
     298             :                 }
     299             :         }
     300             : }
     301             : 
     302             : static int
     303           0 : tgt_device_quiesce_cb(vfu_ctx_t *vfu_ctx)
     304             : {
     305           0 :         struct spdk_vfu_endpoint *endpoint = vfu_get_private(vfu_ctx);
     306             :         int ret;
     307             : 
     308           0 :         assert(endpoint->ops.quiesce_device);
     309           0 :         ret = endpoint->ops.quiesce_device(endpoint);
     310           0 :         if (ret) {
     311           0 :                 errno = EBUSY;
     312           0 :                 ret = -1;
     313             :         }
     314             : 
     315           0 :         return ret;
     316             : }
     317             : 
     318             : static int
     319           0 : tgt_device_reset_cb(vfu_ctx_t *vfu_ctx, vfu_reset_type_t type)
     320             : {
     321           0 :         struct spdk_vfu_endpoint *endpoint = vfu_get_private(vfu_ctx);
     322             : 
     323           0 :         SPDK_DEBUGLOG(vfu, "Device reset type %u\n", type);
     324             : 
     325           0 :         assert(endpoint->ops.reset_device);
     326           0 :         return endpoint->ops.reset_device(endpoint);
     327             : }
     328             : 
     329             : static int
     330           0 : tgt_endpoint_realize(struct spdk_vfu_endpoint *endpoint)
     331             : {
     332             :         int ret;
     333             :         uint8_t buf[512];
     334             :         struct vsc *vendor_cap;
     335             :         ssize_t cap_offset;
     336             :         uint16_t vendor_cap_idx, cap_size, sparse_mmap_idx;
     337             :         struct spdk_vfu_pci_device pci_dev;
     338             :         uint8_t region_idx;
     339             : 
     340           0 :         assert(endpoint->ops.get_device_info);
     341           0 :         ret = endpoint->ops.get_device_info(endpoint, &pci_dev);
     342           0 :         if (ret) {
     343           0 :                 SPDK_ERRLOG("%s: failed to get pci device info\n", spdk_vfu_get_endpoint_id(endpoint));
     344           0 :                 return ret;
     345             :         }
     346             : 
     347           0 :         endpoint->vfu_ctx = vfu_create_ctx(VFU_TRANS_SOCK, endpoint->uuid, LIBVFIO_USER_FLAG_ATTACH_NB,
     348             :                                            endpoint, VFU_DEV_TYPE_PCI);
     349           0 :         if (endpoint->vfu_ctx == NULL) {
     350           0 :                 SPDK_ERRLOG("%s: error creating libvfio-user context\n", spdk_vfu_get_endpoint_id(endpoint));
     351           0 :                 return -EFAULT;
     352             :         }
     353           0 :         vfu_setup_log(endpoint->vfu_ctx, tgt_log_cb, tgt_get_log_level());
     354             : 
     355           0 :         ret = vfu_pci_init(endpoint->vfu_ctx, VFU_PCI_TYPE_EXPRESS, PCI_HEADER_TYPE_NORMAL, 0);
     356           0 :         if (ret < 0) {
     357           0 :                 SPDK_ERRLOG("vfu_ctx %p failed to initialize PCI\n", endpoint->vfu_ctx);
     358           0 :                 goto error;
     359             :         }
     360             : 
     361           0 :         vfu_pci_set_id(endpoint->vfu_ctx, pci_dev.id.vid, pci_dev.id.did, pci_dev.id.ssvid,
     362           0 :                        pci_dev.id.ssid);
     363           0 :         vfu_pci_set_class(endpoint->vfu_ctx, pci_dev.class.bcc, pci_dev.class.scc, pci_dev.class.pi);
     364             : 
     365             :         /* Add Vendor Capabilities */
     366           0 :         for (vendor_cap_idx = 0; vendor_cap_idx < pci_dev.nr_vendor_caps; vendor_cap_idx++) {
     367           0 :                 memset(buf, 0, sizeof(buf));
     368           0 :                 cap_size = endpoint->ops.get_vendor_capability(endpoint, buf, 256, vendor_cap_idx);
     369           0 :                 if (cap_size) {
     370           0 :                         vendor_cap = (struct vsc *)buf;
     371           0 :                         assert(vendor_cap->hdr.id == PCI_CAP_ID_VNDR);
     372           0 :                         assert(vendor_cap->size == cap_size);
     373             : 
     374           0 :                         cap_offset = vfu_pci_add_capability(endpoint->vfu_ctx, 0, 0, vendor_cap);
     375           0 :                         if (cap_offset < 0) {
     376           0 :                                 SPDK_ERRLOG("vfu_ctx %p failed add vendor capability\n", endpoint->vfu_ctx);
     377           0 :                                 ret = -EFAULT;
     378           0 :                                 goto error;
     379             :                         }
     380             :                 }
     381             :         }
     382             : 
     383             :         /* Add Standard PCI Capabilities */
     384           0 :         cap_offset = vfu_pci_add_capability(endpoint->vfu_ctx, 0, 0, &pci_dev.pmcap);
     385           0 :         if (cap_offset < 0) {
     386           0 :                 SPDK_ERRLOG("vfu_ctx %p failed add pmcap\n", endpoint->vfu_ctx);
     387           0 :                 ret = -EFAULT;
     388           0 :                 goto error;
     389             :         }
     390           0 :         SPDK_DEBUGLOG(vfu, "%s PM cap_offset %ld\n", spdk_vfu_get_endpoint_id(endpoint), cap_offset);
     391             : 
     392           0 :         cap_offset = vfu_pci_add_capability(endpoint->vfu_ctx, 0, 0, &pci_dev.pxcap);
     393           0 :         if (cap_offset < 0) {
     394           0 :                 SPDK_ERRLOG("vfu_ctx %p failed add pxcap\n", endpoint->vfu_ctx);
     395           0 :                 ret = -EFAULT;
     396           0 :                 goto error;
     397             :         }
     398           0 :         SPDK_DEBUGLOG(vfu, "%s PX cap_offset %ld\n", spdk_vfu_get_endpoint_id(endpoint), cap_offset);
     399             : 
     400           0 :         cap_offset = vfu_pci_add_capability(endpoint->vfu_ctx, 0, 0, &pci_dev.msixcap);
     401           0 :         if (cap_offset < 0) {
     402           0 :                 SPDK_ERRLOG("vfu_ctx %p failed add msixcap\n", endpoint->vfu_ctx);
     403           0 :                 ret = -EFAULT;
     404           0 :                 goto error;
     405             :         }
     406           0 :         SPDK_DEBUGLOG(vfu, "%s MSIX cap_offset %ld\n", spdk_vfu_get_endpoint_id(endpoint), cap_offset);
     407             : 
     408             :         /* Setup PCI Regions */
     409           0 :         for (region_idx = 0; region_idx < VFU_PCI_DEV_NUM_REGIONS; region_idx++) {
     410           0 :                 struct spdk_vfu_pci_region *region = &pci_dev.regions[region_idx];
     411             :                 struct iovec sparse_mmap[SPDK_VFU_MAXIMUM_SPARSE_MMAP_REGIONS];
     412           0 :                 if (!region->len) {
     413           0 :                         continue;
     414             :                 }
     415             : 
     416           0 :                 if (region->nr_sparse_mmaps) {
     417           0 :                         assert(region->nr_sparse_mmaps <= SPDK_VFU_MAXIMUM_SPARSE_MMAP_REGIONS);
     418           0 :                         for (sparse_mmap_idx = 0; sparse_mmap_idx < region->nr_sparse_mmaps; sparse_mmap_idx++) {
     419           0 :                                 sparse_mmap[sparse_mmap_idx].iov_base = (void *)region->mmaps[sparse_mmap_idx].offset;
     420           0 :                                 sparse_mmap[sparse_mmap_idx].iov_len = region->mmaps[sparse_mmap_idx].len;
     421             :                         }
     422             :                 }
     423             : 
     424           0 :                 ret = vfu_setup_region(endpoint->vfu_ctx, region_idx, region->len, region->access_cb, region->flags,
     425           0 :                                        region->nr_sparse_mmaps ? sparse_mmap : NULL, region->nr_sparse_mmaps,
     426             :                                        region->fd, region->offset);
     427           0 :                 if (ret) {
     428           0 :                         SPDK_ERRLOG("vfu_ctx %p failed to setup region %u\n", endpoint->vfu_ctx, region_idx);
     429           0 :                         goto error;
     430             :                 }
     431           0 :                 SPDK_DEBUGLOG(vfu, "%s: region %u, len 0x%"PRIx64", callback %p, nr sparse mmaps %u, fd %d\n",
     432             :                               spdk_vfu_get_endpoint_id(endpoint), region_idx, region->len, region->access_cb,
     433             :                               region->nr_sparse_mmaps, region->fd);
     434             :         }
     435             : 
     436           0 :         ret = vfu_setup_device_dma(endpoint->vfu_ctx, tgt_memory_region_add_cb,
     437             :                                    tgt_memory_region_remove_cb);
     438           0 :         if (ret < 0) {
     439           0 :                 SPDK_ERRLOG("vfu_ctx %p failed to setup dma callback\n", endpoint->vfu_ctx);
     440           0 :                 goto error;
     441             :         }
     442             : 
     443           0 :         if (endpoint->ops.reset_device) {
     444           0 :                 ret = vfu_setup_device_reset_cb(endpoint->vfu_ctx, tgt_device_reset_cb);
     445           0 :                 if (ret < 0) {
     446           0 :                         SPDK_ERRLOG("vfu_ctx %p failed to setup reset callback\n", endpoint->vfu_ctx);
     447           0 :                         goto error;
     448             :                 }
     449             :         }
     450             : 
     451           0 :         if (endpoint->ops.quiesce_device) {
     452           0 :                 vfu_setup_device_quiesce_cb(endpoint->vfu_ctx, tgt_device_quiesce_cb);
     453             :         }
     454             : 
     455           0 :         ret = vfu_setup_device_nr_irqs(endpoint->vfu_ctx, VFU_DEV_INTX_IRQ, pci_dev.nr_int_irqs);
     456           0 :         if (ret < 0) {
     457           0 :                 SPDK_ERRLOG("vfu_ctx %p failed to setup INTX\n", endpoint->vfu_ctx);
     458           0 :                 goto error;
     459             :         }
     460             : 
     461           0 :         ret = vfu_setup_device_nr_irqs(endpoint->vfu_ctx, VFU_DEV_MSIX_IRQ, pci_dev.nr_msix_irqs);
     462           0 :         if (ret < 0) {
     463           0 :                 SPDK_ERRLOG("vfu_ctx %p failed to setup MSIX\n", endpoint->vfu_ctx);
     464           0 :                 goto error;
     465             :         }
     466             : 
     467           0 :         ret = vfu_realize_ctx(endpoint->vfu_ctx);
     468           0 :         if (ret < 0) {
     469           0 :                 SPDK_ERRLOG("vfu_ctx %p failed to realize\n", endpoint->vfu_ctx);
     470           0 :                 goto error;
     471             :         }
     472             : 
     473           0 :         endpoint->pci_config_space = vfu_pci_get_config_space(endpoint->vfu_ctx);
     474           0 :         assert(endpoint->pci_config_space != NULL);
     475           0 :         init_pci_config_space(endpoint->pci_config_space, pci_dev.intr_ipin);
     476             : 
     477           0 :         assert(cap_offset != 0);
     478           0 :         endpoint->msix = (struct msixcap *)((uint8_t *)endpoint->pci_config_space + cap_offset);
     479             : 
     480           0 :         return 0;
     481             : 
     482           0 : error:
     483           0 :         if (endpoint->vfu_ctx) {
     484           0 :                 vfu_destroy_ctx(endpoint->vfu_ctx);
     485             :         }
     486           0 :         return ret;
     487             : }
     488             : 
     489             : static int
     490           0 : vfu_parse_core_mask(const char *mask, struct spdk_cpuset *cpumask)
     491             : {
     492             :         int rc;
     493             :         struct spdk_cpuset negative_vfu_mask;
     494             : 
     495           0 :         if (cpumask == NULL) {
     496           0 :                 return -1;
     497             :         }
     498             : 
     499           0 :         if (mask == NULL) {
     500           0 :                 spdk_cpuset_copy(cpumask, &g_tgt_core_mask);
     501           0 :                 return 0;
     502             :         }
     503             : 
     504           0 :         rc = spdk_cpuset_parse(cpumask, mask);
     505           0 :         if (rc < 0) {
     506           0 :                 SPDK_ERRLOG("invalid cpumask %s\n", mask);
     507           0 :                 return -1;
     508             :         }
     509             : 
     510           0 :         spdk_cpuset_copy(&negative_vfu_mask, &g_tgt_core_mask);
     511           0 :         spdk_cpuset_negate(&negative_vfu_mask);
     512           0 :         spdk_cpuset_and(&negative_vfu_mask, cpumask);
     513             : 
     514           0 :         if (spdk_cpuset_count(&negative_vfu_mask) != 0) {
     515           0 :                 SPDK_ERRLOG("one of selected cpu is outside of core mask(=%s)\n",
     516             :                             spdk_cpuset_fmt(&g_tgt_core_mask));
     517           0 :                 return -1;
     518             :         }
     519             : 
     520           0 :         spdk_cpuset_and(cpumask, &g_tgt_core_mask);
     521             : 
     522           0 :         if (spdk_cpuset_count(cpumask) == 0) {
     523           0 :                 SPDK_ERRLOG("no cpu is selected among core mask(=%s)\n",
     524             :                             spdk_cpuset_fmt(&g_tgt_core_mask));
     525           0 :                 return -1;
     526             :         }
     527             : 
     528           0 :         return 0;
     529             : }
     530             : 
     531             : static void
     532           0 : tgt_endpoint_start_thread(void *arg1)
     533             : {
     534           0 :         struct spdk_vfu_endpoint *endpoint = arg1;
     535             : 
     536           0 :         endpoint->accept_poller = SPDK_POLLER_REGISTER(tgt_accept_poller, endpoint, 1000);
     537           0 :         assert(endpoint->accept_poller != NULL);
     538           0 : }
     539             : 
     540             : static void
     541           0 : tgt_endpoint_thread_try_exit(void *arg1)
     542             : {
     543           0 :         struct spdk_vfu_endpoint *endpoint = arg1;
     544             :         static spdk_vfu_fini_cb fini_cb = NULL;
     545             :         int res;
     546             : 
     547           0 :         res = endpoint->ops.destruct(endpoint);
     548           0 :         if (res == -EAGAIN) {
     549             :                 /* Let's retry */
     550           0 :                 spdk_thread_send_msg(endpoint->thread, tgt_endpoint_thread_try_exit, endpoint);
     551           0 :                 return;
     552           0 :         } else if (res) {
     553             :                 /* We're ignoring this error for now as we have nothing to do with it */
     554           0 :                 SPDK_ERRLOG("Endpoint destruct failed with %d\n", res);
     555             :         }
     556             : 
     557           0 :         free(endpoint);
     558             : 
     559           0 :         pthread_mutex_lock(&g_endpoint_lock);
     560           0 :         if (g_fini_cb) { /* called due to spdk_vfu_fini() */
     561           0 :                 g_fini_endpoint_cnt--;
     562             : 
     563           0 :                 if (!g_fini_endpoint_cnt) {
     564           0 :                         fini_cb = g_fini_cb;
     565           0 :                         g_fini_cb = NULL;
     566             :                 }
     567             :         }
     568           0 :         pthread_mutex_unlock(&g_endpoint_lock);
     569             : 
     570           0 :         if (fini_cb) {
     571           0 :                 fini_cb();
     572             :         }
     573             : 
     574           0 :         spdk_thread_exit(spdk_get_thread());
     575             : }
     576             : 
     577             : static void
     578           0 : tgt_endpoint_thread_exit(void *arg1)
     579             : {
     580           0 :         struct spdk_vfu_endpoint *endpoint = arg1;
     581             : 
     582           0 :         spdk_poller_unregister(&endpoint->accept_poller);
     583           0 :         spdk_poller_unregister(&endpoint->vfu_ctx_poller);
     584             : 
     585             :         /* Ensure the attached device is stopped before destroying the vfu context */
     586           0 :         if (endpoint->ops.detach_device) {
     587           0 :                 endpoint->ops.detach_device(endpoint);
     588             :         }
     589             : 
     590           0 :         if (endpoint->vfu_ctx) {
     591           0 :                 vfu_destroy_ctx(endpoint->vfu_ctx);
     592             :         }
     593             : 
     594           0 :         tgt_endpoint_thread_try_exit(endpoint);
     595           0 : }
     596             : 
     597             : int
     598           0 : spdk_vfu_create_endpoint(const char *endpoint_name, const char *cpumask_str,
     599             :                          const char *dev_type_name)
     600             : {
     601             :         char *basename;
     602           0 :         char uuid[PATH_MAX] = "";
     603           0 :         struct spdk_cpuset cpumask = {};
     604             :         struct spdk_vfu_endpoint *endpoint;
     605             :         struct spdk_vfu_endpoint_ops *ops;
     606           0 :         int ret = 0;
     607             : 
     608           0 :         ret = vfu_parse_core_mask(cpumask_str, &cpumask);
     609           0 :         if (ret) {
     610           0 :                 return ret;
     611             :         }
     612             : 
     613           0 :         if (strlen(endpoint_name) >= SPDK_VFU_MAX_NAME_LEN - 1) {
     614           0 :                 return -ENAMETOOLONG;
     615             :         }
     616             : 
     617           0 :         if (spdk_vfu_get_endpoint_by_name(endpoint_name)) {
     618           0 :                 SPDK_ERRLOG("%s already exist\n", endpoint_name);
     619           0 :                 return -EEXIST;
     620             :         }
     621             : 
     622             :         /* Find supported PCI device type */
     623           0 :         ops = tgt_get_pci_device_ops(dev_type_name);
     624           0 :         if (!ops) {
     625           0 :                 SPDK_ERRLOG("Request %s device type isn't registered\n", dev_type_name);
     626           0 :                 return -ENOTSUP;
     627             :         }
     628             : 
     629           0 :         basename = tgt_get_base_path();
     630           0 :         if (snprintf(uuid, sizeof(uuid), "%s%s", basename, endpoint_name) >= (int)sizeof(uuid)) {
     631           0 :                 SPDK_ERRLOG("Resulting socket path for endpoint %s is too long: %s%s\n",
     632             :                             endpoint_name, basename, endpoint_name);
     633           0 :                 return -EINVAL;
     634             :         }
     635             : 
     636           0 :         endpoint = calloc(1, sizeof(*endpoint));
     637           0 :         if (!endpoint) {
     638           0 :                 return -ENOMEM;
     639             :         }
     640             : 
     641           0 :         endpoint->endpoint_ctx = ops->init(endpoint, basename, endpoint_name);
     642           0 :         if (!endpoint->endpoint_ctx) {
     643           0 :                 free(endpoint);
     644           0 :                 return -EINVAL;
     645             :         }
     646           0 :         endpoint->ops = *ops;
     647           0 :         snprintf(endpoint->name, SPDK_VFU_MAX_NAME_LEN, "%s", endpoint_name);
     648           0 :         snprintf(endpoint->uuid, sizeof(uuid), "%s", uuid);
     649             : 
     650           0 :         SPDK_DEBUGLOG(vfu, "Construct endpoint %s\n", endpoint_name);
     651             :         /* Endpoint realize */
     652           0 :         ret = tgt_endpoint_realize(endpoint);
     653           0 :         if (ret) {
     654           0 :                 endpoint->ops.destruct(endpoint);
     655           0 :                 free(endpoint);
     656           0 :                 return ret;
     657             :         }
     658             : 
     659           0 :         endpoint->thread = spdk_thread_create(endpoint_name, &cpumask);
     660           0 :         if (!endpoint->thread) {
     661           0 :                 endpoint->ops.destruct(endpoint);
     662           0 :                 vfu_destroy_ctx(endpoint->vfu_ctx);
     663           0 :                 free(endpoint);
     664           0 :                 return -EFAULT;
     665             :         }
     666             : 
     667           0 :         ret = 0;
     668           0 :         pthread_mutex_lock(&g_endpoint_lock);
     669           0 :         if (!g_fini_cb) {
     670           0 :                 TAILQ_INSERT_TAIL(&g_endpoint, endpoint, link);
     671             :         } else { /* spdk_vfu_fini has been called */
     672           0 :                 ret = -EPERM;
     673             :         }
     674           0 :         pthread_mutex_unlock(&g_endpoint_lock);
     675             : 
     676           0 :         if (ret) {
     677             :                 /* we're in the process of destruction, no new endpoint creation is allowed */
     678           0 :                 spdk_thread_destroy(endpoint->thread);
     679           0 :                 endpoint->ops.destruct(endpoint);
     680           0 :                 vfu_destroy_ctx(endpoint->vfu_ctx);
     681           0 :                 free(endpoint);
     682           0 :                 return -EFAULT;
     683             :         }
     684             : 
     685           0 :         spdk_thread_send_msg(endpoint->thread, tgt_endpoint_start_thread, endpoint);
     686             : 
     687           0 :         return 0;
     688             : }
     689             : 
     690             : int
     691           0 : spdk_vfu_delete_endpoint(const char *endpoint_name)
     692             : {
     693             :         struct spdk_vfu_endpoint *endpoint;
     694             : 
     695           0 :         endpoint = spdk_vfu_get_endpoint_by_name(endpoint_name);
     696           0 :         if (!endpoint) {
     697           0 :                 SPDK_ERRLOG("%s doesn't exist\n", endpoint_name);
     698           0 :                 return -ENOENT;
     699             :         }
     700             : 
     701           0 :         SPDK_NOTICELOG("Destruct endpoint %s\n", endpoint_name);
     702             : 
     703           0 :         pthread_mutex_lock(&g_endpoint_lock);
     704           0 :         TAILQ_REMOVE(&g_endpoint, endpoint, link);
     705           0 :         pthread_mutex_unlock(&g_endpoint_lock);
     706           0 :         spdk_thread_send_msg(endpoint->thread, tgt_endpoint_thread_exit, endpoint);
     707             : 
     708           0 :         return 0;
     709             : }
     710             : 
     711             : const char *
     712           0 : spdk_vfu_get_endpoint_id(struct spdk_vfu_endpoint *endpoint)
     713             : {
     714           0 :         return endpoint->uuid;
     715             : }
     716             : 
     717             : const char *
     718           0 : spdk_vfu_get_endpoint_name(struct spdk_vfu_endpoint *endpoint)
     719             : {
     720           0 :         return endpoint->name;
     721             : }
     722             : 
     723             : vfu_ctx_t *
     724           0 : spdk_vfu_get_vfu_ctx(struct spdk_vfu_endpoint *endpoint)
     725             : {
     726           0 :         return endpoint->vfu_ctx;
     727             : }
     728             : 
     729             : void *
     730           0 : spdk_vfu_get_endpoint_private(struct spdk_vfu_endpoint *endpoint)
     731             : {
     732           0 :         return endpoint->endpoint_ctx;
     733             : }
     734             : 
     735             : bool
     736           0 : spdk_vfu_endpoint_msix_enabled(struct spdk_vfu_endpoint *endpoint)
     737             : {
     738           0 :         return endpoint->msix->mxc.mxe;
     739             : }
     740             : 
     741             : bool
     742           0 : spdk_vfu_endpoint_intx_enabled(struct spdk_vfu_endpoint *endpoint)
     743             : {
     744           0 :         return !endpoint->pci_config_space->hdr.cmd.id;
     745             : }
     746             : 
     747             : void *
     748           0 : spdk_vfu_endpoint_get_pci_config(struct spdk_vfu_endpoint *endpoint)
     749             : {
     750           0 :         return (void *)endpoint->pci_config_space;
     751             : }
     752             : 
     753             : void
     754           0 : spdk_vfu_init(spdk_vfu_init_cb init_cb)
     755             : {
     756             :         uint32_t i;
     757             :         size_t len;
     758             : 
     759           0 :         if (g_endpoint_path_dirname[0] == '\0') {
     760           0 :                 if (getcwd(g_endpoint_path_dirname, sizeof(g_endpoint_path_dirname) - 2) == NULL) {
     761           0 :                         SPDK_ERRLOG("getcwd failed\n");
     762           0 :                         return;
     763             :                 }
     764             : 
     765           0 :                 len = strlen(g_endpoint_path_dirname);
     766           0 :                 if (g_endpoint_path_dirname[len - 1] != '/') {
     767           0 :                         g_endpoint_path_dirname[len] = '/';
     768           0 :                         g_endpoint_path_dirname[len + 1] = '\0';
     769             :                 }
     770             :         }
     771             : 
     772           0 :         spdk_cpuset_zero(&g_tgt_core_mask);
     773           0 :         SPDK_ENV_FOREACH_CORE(i) {
     774           0 :                 spdk_cpuset_set_cpu(&g_tgt_core_mask, i, true);
     775             :         }
     776             : 
     777           0 :         init_cb(0);
     778             : }
     779             : 
     780             : void *
     781           0 : spdk_vfu_map_one(struct spdk_vfu_endpoint *endpoint, uint64_t addr, uint64_t len, dma_sg_t *sg,
     782             :                  struct iovec *iov,
     783             :                  int prot)
     784             : {
     785             :         int ret;
     786             : 
     787           0 :         assert(endpoint != NULL);
     788           0 :         assert(endpoint->vfu_ctx != NULL);
     789           0 :         assert(sg != NULL);
     790           0 :         assert(iov != NULL);
     791             : 
     792           0 :         ret = vfu_addr_to_sgl(endpoint->vfu_ctx, (void *)(uintptr_t)addr, len, sg, 1, prot);
     793           0 :         if (ret < 0) {
     794           0 :                 return NULL;
     795             :         }
     796             : 
     797           0 :         ret = vfu_sgl_get(endpoint->vfu_ctx, sg, iov, 1, 0);
     798           0 :         if (ret != 0) {
     799           0 :                 return NULL;
     800             :         }
     801             : 
     802           0 :         assert(iov->iov_base != NULL);
     803           0 :         return iov->iov_base;
     804             : }
     805             : 
     806             : void
     807           0 : spdk_vfu_unmap_sg(struct spdk_vfu_endpoint *endpoint, dma_sg_t *sg, struct iovec *iov, int iovcnt)
     808             : {
     809           0 :         assert(endpoint != NULL);
     810           0 :         assert(endpoint->vfu_ctx != NULL);
     811           0 :         assert(sg != NULL);
     812           0 :         assert(iov != NULL);
     813             : 
     814           0 :         vfu_sgl_put(endpoint->vfu_ctx, sg, iov, iovcnt);
     815           0 : }
     816             : 
     817             : void
     818           0 : spdk_vfu_fini(spdk_vfu_fini_cb fini_cb)
     819             : {
     820             :         struct spdk_vfu_endpoint *endpoint, *tmp;
     821             :         struct tgt_pci_device_ops *ops, *ops_tmp;
     822           0 :         uint32_t endpoint_cnt = 0;
     823             : 
     824           0 :         pthread_mutex_lock(&g_endpoint_lock);
     825           0 :         assert(!g_fini_cb);
     826           0 :         TAILQ_FOREACH_SAFE(ops, &g_pci_device_ops, link, ops_tmp) {
     827           0 :                 TAILQ_REMOVE(&g_pci_device_ops, ops, link);
     828           0 :                 free(ops);
     829             :         }
     830             : 
     831           0 :         TAILQ_FOREACH_SAFE(endpoint, &g_endpoint, link, tmp) {
     832           0 :                 TAILQ_REMOVE(&g_endpoint, endpoint, link);
     833           0 :                 endpoint_cnt++;
     834           0 :                 spdk_thread_send_msg(endpoint->thread, tgt_endpoint_thread_exit, endpoint);
     835             :         }
     836             : 
     837             :         /* NOTE: g_fini_cb and g_fini_endpoint_cnt are accessed under the same mutex so it's safe to assign them here */
     838           0 :         if (endpoint_cnt) {
     839           0 :                 g_fini_endpoint_cnt = endpoint_cnt;
     840           0 :                 g_fini_cb = fini_cb;
     841             :         }
     842           0 :         pthread_mutex_unlock(&g_endpoint_lock);
     843             : 
     844           0 :         if (!endpoint_cnt) {
     845           0 :                 fini_cb();
     846             :         }
     847           0 : }
     848           0 : SPDK_LOG_REGISTER_COMPONENT(vfu)

Generated by: LCOV version 1.15