get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/34923/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 34923,
    "url": "http://patches.dpdk.org/api/patches/34923/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20180204145542.38345-3-xiao.w.wang@intel.com/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20180204145542.38345-3-xiao.w.wang@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20180204145542.38345-3-xiao.w.wang@intel.com",
    "date": "2018-02-04T14:55:41",
    "name": "[dpdk-dev,2/3] net/vdpa_virtio_pci: introduce vdpa sample driver",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "481ab7fb8aaa9595b89511093f8e98029301787a",
    "submitter": {
        "id": 281,
        "url": "http://patches.dpdk.org/api/people/281/?format=api",
        "name": "Xiao Wang",
        "email": "xiao.w.wang@intel.com"
    },
    "delegate": {
        "id": 2642,
        "url": "http://patches.dpdk.org/api/users/2642/?format=api",
        "username": "mcoquelin",
        "first_name": "Maxime",
        "last_name": "Coquelin",
        "email": "maxime.coquelin@redhat.com"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20180204145542.38345-3-xiao.w.wang@intel.com/mbox/",
    "series": [],
    "comments": "http://patches.dpdk.org/api/patches/34923/comments/",
    "check": "fail",
    "checks": "http://patches.dpdk.org/api/patches/34923/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@dpdk.org",
        "Delivered-To": "patchwork@dpdk.org",
        "Received": [
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id 56EAB1B1D5;\n\tSun,  4 Feb 2018 15:56:52 +0100 (CET)",
            "from mga02.intel.com (mga02.intel.com [134.134.136.20])\n\tby dpdk.org (Postfix) with ESMTP id C0BFD1B1C9\n\tfor <dev@dpdk.org>; Sun,  4 Feb 2018 15:56:48 +0100 (CET)",
            "from orsmga001.jf.intel.com ([10.7.209.18])\n\tby orsmga101.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384;\n\t04 Feb 2018 06:56:48 -0800",
            "from dpdk-xiao-1.sh.intel.com ([10.67.110.153])\n\tby orsmga001.jf.intel.com with ESMTP; 04 Feb 2018 06:56:46 -0800"
        ],
        "X-Amp-Result": "SKIPPED(no attachment in message)",
        "X-Amp-File-Uploaded": "False",
        "X-ExtLoop1": "1",
        "X-IronPort-AV": "E=Sophos;i=\"5.46,458,1511856000\"; d=\"scan'208\";a=\"28692309\"",
        "From": "Xiao Wang <xiao.w.wang@intel.com>",
        "To": "dev@dpdk.org",
        "Cc": "jianfeng.tan@intel.com, tiwei.bie@intel.com, maxime.coquelin@redhat.com, \n\tyliu@fridaylinux.org, cunming.liang@intel.com, dan.daly@intel.com,\n\tzhihong.wang@intel.com, Xiao Wang <xiao.w.wang@intel.com>",
        "Date": "Sun,  4 Feb 2018 22:55:41 +0800",
        "Message-Id": "<20180204145542.38345-3-xiao.w.wang@intel.com>",
        "X-Mailer": "git-send-email 2.15.1",
        "In-Reply-To": "<20180204145542.38345-1-xiao.w.wang@intel.com>",
        "References": "<20180204145542.38345-1-xiao.w.wang@intel.com>",
        "Subject": "[dpdk-dev] [PATCH 2/3] net/vdpa_virtio_pci: introduce vdpa sample\n\tdriver",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://dpdk.org/ml/options/dev>,\n\t<mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://dpdk.org/ml/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://dpdk.org/ml/listinfo/dev>,\n\t<mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "This driver is a reference sample of making vDPA device driver based\non vhost lib, this driver uses a standard virtio-net PCI device as\nvDPA device, it can serve as a backend for a virtio-net pci device\nin nested VM.\n\nThe key driver ops implemented are:\n\n* vdpa_virtio_eng_init\nMapping virtio pci device with VFIO into userspace, and read device\ncapability and intialize internal data.\n\n* vdpa_virtio_eng_uninit\nRelease the mapped device.\n\n* vdpa_virtio_info_query\nDevice capability reporting, e.g. queue number, features.\n\n* vdpa_virtio_dev_config\nWith the guest virtio information provideed by vhost lib, this\nfunction configures device and IOMMU to set up vhost datapath,\nwhich includes: Rx/Tx vring, VFIO interrupt, kick relay.\n\n* vdpa_virtio_dev_close\nUnset the stuff that are configured previously by dev_conf.\n\nThis driver requires the virtio device supports VIRTIO_F_IOMMU_PLATFORM\n, because the buffer address written in desc is IOVA.\n\nBecause vDPA driver needs to set up MSI-X vector to interrupt the guest,\nonly vfio-pci is supported currently.\n\nSigned-off-by: Xiao Wang <xiao.w.wang@intel.com>\n---\n config/common_base                                 |    6 +\n config/common_linuxapp                             |    1 +\n drivers/net/Makefile                               |    1 +\n drivers/net/vdpa_virtio_pci/Makefile               |   31 +\n .../net/vdpa_virtio_pci/rte_eth_vdpa_virtio_pci.c  | 1527 ++++++++++++++++++++\n .../rte_vdpa_virtio_pci_version.map                |    4 +\n mk/rte.app.mk                                      |    1 +\n 7 files changed, 1571 insertions(+)\n create mode 100644 drivers/net/vdpa_virtio_pci/Makefile\n create mode 100644 drivers/net/vdpa_virtio_pci/rte_eth_vdpa_virtio_pci.c\n create mode 100644 drivers/net/vdpa_virtio_pci/rte_vdpa_virtio_pci_version.map",
    "diff": "diff --git a/config/common_base b/config/common_base\nindex ad03cf433..aaa775129 100644\n--- a/config/common_base\n+++ b/config/common_base\n@@ -791,6 +791,12 @@ CONFIG_RTE_LIBRTE_VHOST_DEBUG=n\n #\n CONFIG_RTE_LIBRTE_PMD_VHOST=n\n \n+#\n+# Compile VDPA VIRTIO PCI driver\n+# To compile, CONFIG_RTE_LIBRTE_VHOST should be enabled.\n+#\n+CONFIG_RTE_LIBRTE_VDPA_VIRTIO_PCI=n\n+\n #\n # Compile the test application\n #\ndiff --git a/config/common_linuxapp b/config/common_linuxapp\nindex ff98f2355..83446090c 100644\n--- a/config/common_linuxapp\n+++ b/config/common_linuxapp\n@@ -15,6 +15,7 @@ CONFIG_RTE_LIBRTE_PMD_KNI=y\n CONFIG_RTE_LIBRTE_VHOST=y\n CONFIG_RTE_LIBRTE_VHOST_NUMA=y\n CONFIG_RTE_LIBRTE_PMD_VHOST=y\n+CONFIG_RTE_LIBRTE_VDPA_VIRTIO_PCI=y\n CONFIG_RTE_LIBRTE_PMD_AF_PACKET=y\n CONFIG_RTE_LIBRTE_PMD_TAP=y\n CONFIG_RTE_LIBRTE_AVP_PMD=y\ndiff --git a/drivers/net/Makefile b/drivers/net/Makefile\nindex e1127326b..0a45ef603 100644\n--- a/drivers/net/Makefile\n+++ b/drivers/net/Makefile\n@@ -53,6 +53,7 @@ endif # $(CONFIG_RTE_LIBRTE_SCHED)\n \n ifeq ($(CONFIG_RTE_LIBRTE_VHOST),y)\n DIRS-$(CONFIG_RTE_LIBRTE_PMD_VHOST) += vhost\n+DIRS-$(CONFIG_RTE_LIBRTE_VDPA_VIRTIO_PCI) += vdpa_virtio_pci\n endif # $(CONFIG_RTE_LIBRTE_VHOST)\n \n ifeq ($(CONFIG_RTE_LIBRTE_MRVL_PMD),y)\ndiff --git a/drivers/net/vdpa_virtio_pci/Makefile b/drivers/net/vdpa_virtio_pci/Makefile\nnew file mode 100644\nindex 000000000..147d7a7a3\n--- /dev/null\n+++ b/drivers/net/vdpa_virtio_pci/Makefile\n@@ -0,0 +1,31 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(c) 2018 Intel Corporation\n+\n+include $(RTE_SDK)/mk/rte.vars.mk\n+\n+#\n+# library name\n+#\n+LIB = librte_vdpa_virtio_pci.a\n+\n+LDLIBS += -lpthread\n+LDLIBS += -lrte_eal -lrte_mempool -lrte_pci\n+LDLIBS += -lrte_ethdev -lrte_net -lrte_kvargs -lrte_vhost\n+LDLIBS += -lrte_bus_vdev -lrte_bus_pci\n+\n+CFLAGS += -O3\n+CFLAGS += $(WERROR_FLAGS)\n+CFLAGS += -I$(RTE_SDK)/lib/librte_eal/linuxapp/eal\n+CFLAGS += -I$(RTE_SDK)/drivers/bus/pci/linux\n+CFLAGS += -DALLOW_EXPERIMENTAL_API\n+\n+EXPORT_MAP := rte_vdpa_virtio_pci_version.map\n+\n+LIBABIVER := 1\n+\n+#\n+# all source are stored in SRCS-y\n+#\n+SRCS-$(CONFIG_RTE_LIBRTE_PMD_VHOST) += rte_eth_vdpa_virtio_pci.c\n+\n+include $(RTE_SDK)/mk/rte.lib.mk\ndiff --git a/drivers/net/vdpa_virtio_pci/rte_eth_vdpa_virtio_pci.c b/drivers/net/vdpa_virtio_pci/rte_eth_vdpa_virtio_pci.c\nnew file mode 100644\nindex 000000000..5e63b15e6\n--- /dev/null\n+++ b/drivers/net/vdpa_virtio_pci/rte_eth_vdpa_virtio_pci.c\n@@ -0,0 +1,1527 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2018 Intel Corporation\n+ */\n+\n+#include <unistd.h>\n+#include <pthread.h>\n+#include <fcntl.h>\n+#include <linux/pci_regs.h>\n+#include <linux/virtio_net.h>\n+#include <linux/virtio_config.h>\n+#include <linux/virtio_pci.h>\n+#include <sys/ioctl.h>\n+#include <sys/epoll.h>\n+#include <sys/mman.h>\n+\n+#include <rte_vfio.h>\n+#include <rte_mbuf.h>\n+#include <rte_ethdev.h>\n+#include <rte_ethdev_vdev.h>\n+#include <rte_malloc.h>\n+#include <rte_memcpy.h>\n+#include <rte_bus_pci.h>\n+#include <rte_bus_vdev.h>\n+#include <rte_kvargs.h>\n+#include <rte_vhost.h>\n+#include <rte_vdpa.h>\n+#include <rte_io.h>\n+#include <rte_cycles.h>\n+#include <rte_spinlock.h>\n+#include <eal_vfio.h>\n+#include <pci_init.h>\n+\n+#define MAX_QUEUES\t\t1\n+#define VIRTIO_F_IOMMU_PLATFORM\t33\n+#define MSIX_IRQ_SET_BUF_LEN (sizeof(struct vfio_irq_set) + \\\n+\t\tsizeof(int) * (MAX_QUEUES * 2 + 1))\n+\n+#define ETH_VDPA_VIRTIO_PCI_BDF_ARG\t\"bdf\"\n+\n+static const char *const valid_arguments[] = {\n+\tETH_VDPA_VIRTIO_PCI_BDF_ARG,\n+\tNULL\n+};\n+\n+static struct ether_addr base_eth_addr = {\n+\t.addr_bytes = {\n+\t\t0x56 /* V */,\n+\t\t0x44 /* D */,\n+\t\t0x50 /* P */,\n+\t\t0x41 /* A */,\n+\t\t0x00,\n+\t\t0x00\n+\t}\n+};\n+\n+struct virtio_pci_info {\n+\tstruct rte_pci_device pdev;\n+\tuint64_t    req_features;\n+\tuint32_t    notify_off_multiplier;\n+\tstruct virtio_pci_common_cfg *common_cfg;\n+\tuint8_t     *isr;\n+\tuint16_t    *notify_base;\n+\tstruct virtio_net_device_config *dev_cfg;\n+\tuint16_t    *notify_addr[MAX_QUEUES * 2];\n+\tint vfio_container_fd;\n+\tint vfio_group_fd;\n+\tint vfio_dev_fd;\n+\tpthread_t tid;\t/* thread for notify relay */\n+\tint epfd;\n+};\n+\n+struct vdpa_virtio_pci_internal {\n+\tchar *dev_name;\n+\tuint16_t max_queues;\n+\tuint16_t max_devices;\n+\tuint64_t features;\n+\tstruct rte_vdpa_eng_addr eng_addr;\n+\tint eid;\n+\tint vid;\n+\tstruct virtio_pci_info vpci;\n+\trte_atomic32_t started;\n+\trte_atomic32_t dev_attached;\n+\trte_atomic32_t running;\n+\trte_spinlock_t lock;\n+};\n+\n+struct internal_list {\n+\tTAILQ_ENTRY(internal_list) next;\n+\tstruct rte_eth_dev *eth_dev;\n+};\n+\n+TAILQ_HEAD(internal_list_head, internal_list);\n+static struct internal_list_head internal_list =\n+\tTAILQ_HEAD_INITIALIZER(internal_list);\n+\n+static pthread_mutex_t internal_list_lock = PTHREAD_MUTEX_INITIALIZER;\n+\n+static struct rte_eth_link vdpa_link = {\n+\t\t.link_speed = 10000,\n+\t\t.link_duplex = ETH_LINK_FULL_DUPLEX,\n+\t\t.link_status = ETH_LINK_DOWN\n+};\n+\n+static struct internal_list *\n+find_internal_resource_by_eid(int eid)\n+{\n+\tint found = 0;\n+\tstruct internal_list *list;\n+\tstruct vdpa_virtio_pci_internal *internal;\n+\n+\tpthread_mutex_lock(&internal_list_lock);\n+\n+\tTAILQ_FOREACH(list, &internal_list, next) {\n+\t\tinternal = list->eth_dev->data->dev_private;\n+\t\tif (eid == internal->eid) {\n+\t\t\tfound = 1;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tpthread_mutex_unlock(&internal_list_lock);\n+\n+\tif (!found)\n+\t\treturn NULL;\n+\n+\treturn list;\n+}\n+\n+static struct internal_list *\n+find_internal_resource_by_eng_addr(struct rte_vdpa_eng_addr *addr)\n+{\n+\tint found = 0;\n+\tstruct internal_list *list;\n+\tstruct vdpa_virtio_pci_internal *internal;\n+\n+\tpthread_mutex_lock(&internal_list_lock);\n+\n+\tTAILQ_FOREACH(list, &internal_list, next) {\n+\t\tinternal = list->eth_dev->data->dev_private;\n+\t\tif (addr == &internal->eng_addr) {\n+\t\t\tfound = 1;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tpthread_mutex_unlock(&internal_list_lock);\n+\n+\tif (!found)\n+\t\treturn NULL;\n+\n+\treturn list;\n+}\n+\n+static int\n+check_pci_dev(struct rte_pci_device *dev)\n+{\n+\tchar filename[PATH_MAX];\n+\tchar dev_dir[PATH_MAX];\n+\tchar driver[PATH_MAX];\n+\tint ret;\n+\n+\tsnprintf(dev_dir, sizeof(dev_dir), \"%s/\" PCI_PRI_FMT,\n+\t\t\trte_pci_get_sysfs_path(),\n+\t\t\tdev->addr.domain, dev->addr.bus,\n+\t\t\tdev->addr.devid, dev->addr.function);\n+\tif (access(dev_dir, R_OK) != 0) {\n+\t\tRTE_LOG(ERR, PMD, \"%s not exist\\n\", dev_dir);\n+\t\treturn -1;\n+\t}\n+\n+\t/* parse resources */\n+\tsnprintf(filename, sizeof(filename), \"%s/resource\", dev_dir);\n+\tif (pci_parse_sysfs_resource(filename, dev) < 0) {\n+\t\tRTE_LOG(ERR, PMD, \"cannot parse resource: %s\\n\", filename);\n+\t\treturn -1;\n+\t}\n+\n+\t/* parse driver */\n+\tsnprintf(filename, sizeof(filename), \"%s/driver\", dev_dir);\n+\tret = pci_get_kernel_driver_by_path(filename, driver);\n+\tif (ret != 0) {\n+\t\tRTE_LOG(ERR, PMD, \"Fail to get kernel driver: %s\\n\", filename);\n+\t\treturn -1;\n+\t}\n+\n+\tif (strcmp(driver, \"vfio-pci\") != 0) {\n+\t\tRTE_LOG(ERR, PMD, \"kernel driver %s is not vfio-pci\\n\", driver);\n+\t\treturn -1;\n+\t}\n+\treturn 0;\n+}\n+\n+static int\n+vdpa_vfio_get_group_fd(int iommu_group_no)\n+{\n+\tchar filename[PATH_MAX];\n+\tint vfio_group_fd;\n+\n+\tsnprintf(filename, sizeof(filename), VFIO_GROUP_FMT, iommu_group_no);\n+\tvfio_group_fd = open(filename, O_RDWR);\n+\tif (vfio_group_fd < 0) {\n+\t\tif (errno != ENOENT) {\n+\t\t\tRTE_LOG(ERR, PMD, \"cannot open %s: %s\\n\", filename,\n+\t\t\t\tstrerror(errno));\n+\t\t\treturn -1;\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n+\treturn vfio_group_fd;\n+}\n+\n+static int\n+vfio_setup_device(const char *sysfs_base, const char *dev_addr,\n+\t\t  int *vfio_dev_fd, struct vfio_device_info *device_info,\n+\t\t  struct virtio_pci_info *vpci)\n+{\n+\tstruct vfio_group_status group_status = {\n+\t\t.argsz = sizeof(group_status)\n+\t};\n+\tint vfio_container_fd = -1;\n+\tint vfio_group_fd = -1;\n+\tint iommu_group_no;\n+\tint ret;\n+\n+\tvfio_container_fd = vfio_get_container_fd();\n+\n+\t/* check if we have VFIO driver enabled */\n+\tif (vfio_container_fd < 0) {\n+\t\tRTE_LOG(ERR, PMD, \"failed to open VFIO container\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\tret = ioctl(vfio_container_fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU);\n+\tif (ret < 0) {\n+\t\tRTE_LOG(ERR, PMD, \"VFIO_TYPE1_IOMMU not supported\\n\");\n+\t\tgoto err;\n+\t}\n+\n+\t/* get group number */\n+\tret = vfio_get_group_no(sysfs_base, dev_addr, &iommu_group_no);\n+\tif (ret <= 0) {\n+\t\tRTE_LOG(ERR, PMD, \"%s: unable to find IOMMU group\\n\", dev_addr);\n+\t\tgoto err;\n+\t}\n+\n+\t/* get the actual group fd */\n+\tvfio_group_fd = vdpa_vfio_get_group_fd(iommu_group_no);\n+\tRTE_LOG(INFO, PMD, \"\\n%s group no %d group fd %d\\n\",\n+\t\t\tdev_addr, iommu_group_no, vfio_group_fd);\n+\tif (vfio_group_fd <= 0)\n+\t\tgoto err;\n+\n+\t/* check if the group is viable */\n+\tret = ioctl(vfio_group_fd, VFIO_GROUP_GET_STATUS, &group_status);\n+\tif (ret) {\n+\t\tRTE_LOG(ERR, PMD, \"%s cannot get group status, error: %s\\n\",\n+\t\t\t\tdev_addr, strerror(errno));\n+\t\tgoto err;\n+\t} else if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)) {\n+\t\tRTE_LOG(ERR, PMD, \"%s VFIO group is not viable\\n\", dev_addr);\n+\t\tgoto err;\n+\t}\n+\n+\t/* check if group does not have a container yet */\n+\tif (!(group_status.flags & VFIO_GROUP_FLAGS_CONTAINER_SET)) {\n+\t\t/* add group to a container */\n+\t\tret = ioctl(vfio_group_fd, VFIO_GROUP_SET_CONTAINER,\n+\t\t\t\t&vfio_container_fd);\n+\t\tif (ret) {\n+\t\t\tRTE_LOG(ERR, PMD, \"cannot add VFIO group to container, \"\n+\t\t\t\t\t\"error: %s\\n\", strerror(errno));\n+\t\t\tgoto err;\n+\t\t}\n+\t\tRTE_LOG(INFO, PMD, \"vfio_group_fd %d set container_fd %d\\n\",\n+\t\t\t\tvfio_group_fd, vfio_container_fd);\n+\t} else {\n+\t\tRTE_LOG(ERR, PMD, \"%s has a container already\\n\", dev_addr);\n+\t\tgoto err;\n+\t}\n+\n+\tret = ioctl(vfio_container_fd, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU);\n+\tif (ret) {\n+\t\tRTE_LOG(ERR, PMD, \"%s set IOMMU type failed, error: %s\\n\",\n+\t\t\t\tdev_addr, strerror(errno));\n+\t\tgoto err;\n+\t}\n+\n+\t/* get a file descriptor for the device */\n+\t*vfio_dev_fd = ioctl(vfio_group_fd, VFIO_GROUP_GET_DEVICE_FD, dev_addr);\n+\tif (*vfio_dev_fd < 0) {\n+\t\tRTE_LOG(ERR, PMD, \"%s cannot get vfio_dev_fd, error: %s\\n\",\n+\t\t\t\tdev_addr, strerror(errno));\n+\t\tgoto err;\n+\t}\n+\n+\tret = ioctl(*vfio_dev_fd, VFIO_DEVICE_GET_INFO, device_info);\n+\tif (ret) {\n+\t\tRTE_LOG(ERR, PMD, \"%s cannot get device info, error: %s\\n\",\n+\t\t\t\tdev_addr, strerror(errno));\n+\t\tclose(*vfio_dev_fd);\n+\t\tgoto err;\n+\t}\n+\n+\tvpci->vfio_container_fd = vfio_container_fd;\n+\tvpci->vfio_group_fd = vfio_group_fd;\n+\treturn 0;\n+\n+err:\n+\tif (vfio_container_fd >= 0)\n+\t\tclose(vfio_container_fd);\n+\tif (vfio_group_fd > 0)\n+\t\tclose(vfio_group_fd);\n+\treturn -1;\n+}\n+\n+static int\n+virtio_pci_vfio_map_resource(struct virtio_pci_info *vpci)\n+{\n+\tstruct rte_pci_device *pdev = &vpci->pdev;\n+\tstruct vfio_device_info device_info = { .argsz = sizeof(device_info) };\n+\tchar pci_addr[PATH_MAX] = {0};\n+\tstruct rte_pci_addr *loc = &pdev->addr;\n+\tint i, ret, nb_maps;\n+\tint vfio_dev_fd;\n+\tuint32_t ioport_bar;\n+\tstruct pci_msix_table msix_table;\n+\n+\t/* store PCI address string */\n+\tsnprintf(pci_addr, sizeof(pci_addr), PCI_PRI_FMT,\n+\t\t\tloc->domain, loc->bus, loc->devid, loc->function);\n+\n+\tret = vfio_setup_device(rte_pci_get_sysfs_path(), pci_addr,\n+\t\t\t&vfio_dev_fd, &device_info, vpci);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = pci_vfio_get_msix_bar(vfio_dev_fd, &msix_table);\n+\tif (ret < 0) {\n+\t\tRTE_LOG(ERR, PMD, \"%s cannot get MSI-X BAR number\\n\", pci_addr);\n+\t\tgoto fail;\n+\t}\n+\n+\t/* get number of regions (up to BAR5) */\n+\tnb_maps = RTE_MIN((int)device_info.num_regions,\n+\t\t\t\tVFIO_PCI_BAR5_REGION_INDEX + 1);\n+\n+\t/* map BARs */\n+\tfor (i = 0; i < nb_maps; i++) {\n+\t\tstruct vfio_region_info reg = { .argsz = sizeof(reg) };\n+\t\tvoid *bar_addr;\n+\n+\t\treg.index = i;\n+\t\tret = ioctl(vfio_dev_fd, VFIO_DEVICE_GET_REGION_INFO, &reg);\n+\t\tif (ret) {\n+\t\t\tRTE_LOG(ERR, PMD, \"%s cannot get region info, \"\n+\t\t\t\t\t\"error: %s\\n\",\n+\t\t\t\t\tpci_addr, strerror(errno));\n+\t\t\tgoto fail;\n+\t\t}\n+\n+\t\tret = pread(vfio_dev_fd, &ioport_bar, sizeof(ioport_bar),\n+\t\t\t    VFIO_GET_REGION_ADDR(VFIO_PCI_CONFIG_REGION_INDEX) +\n+\t\t\t    PCI_BASE_ADDRESS_0 + i * 4);\n+\t\tif (ret != sizeof(ioport_bar)) {\n+\t\t\tRTE_LOG(ERR, PMD, \"cannot read command (%x) from \"\n+\t\t\t\t\"config space\\n\", PCI_BASE_ADDRESS_0 + i * 4);\n+\t\t\tgoto fail;\n+\t\t}\n+\n+\t\t/* check for io port region */\n+\t\tif (ioport_bar & PCI_BASE_ADDRESS_SPACE_IO)\n+\t\t\tcontinue;\n+\n+\t\t/* skip non-mmapable BARs */\n+\t\tif ((reg.flags & VFIO_REGION_INFO_FLAG_MMAP) == 0)\n+\t\t\tcontinue;\n+\n+\t\tif (i == msix_table.bar_index)\n+\t\t\tcontinue;\n+\n+\t\t/* try mapping somewhere close to the end of hugepages */\n+\t\tif (pci_map_addr == NULL)\n+\t\t\tpci_map_addr = pci_find_max_end_va();\n+\n+\t\tbar_addr = pci_map_addr;\n+\t\tpci_map_addr = RTE_PTR_ADD(bar_addr, (size_t)reg.size);\n+\n+\t\t/* reserve the address using an inaccessible mapping */\n+\t\tbar_addr = mmap(bar_addr, reg.size, 0, MAP_PRIVATE |\n+\t\t\t\tMAP_ANONYMOUS, -1, 0);\n+\t\tif (bar_addr != MAP_FAILED) {\n+\t\t\tvoid *map_addr = NULL;\n+\t\t\tif (reg.size)\n+\t\t\t\tmap_addr = pci_map_resource(bar_addr,\n+\t\t\t\t\t\tvfio_dev_fd,\n+\t\t\t\t\t\treg.offset, reg.size,\n+\t\t\t\t\t\tMAP_FIXED);\n+\n+\t\t\tif (map_addr == MAP_FAILED || !map_addr) {\n+\t\t\t\tmunmap(bar_addr, reg.size);\n+\t\t\t\tbar_addr = MAP_FAILED;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (bar_addr == MAP_FAILED) {\n+\t\t\tRTE_LOG(ERR, PMD, \"%s mapping BAR%d failed: %s\\n\",\n+\t\t\t\t\tpci_addr, i, strerror(errno));\n+\t\t\tgoto fail;\n+\t\t}\n+\t\tpdev->mem_resource[i].addr = bar_addr;\n+\t}\n+\n+\tif (pci_rte_vfio_setup_device(pdev, vfio_dev_fd) < 0) {\n+\t\tRTE_LOG(ERR, PMD, \"%s failed to set up device\\n\", pci_addr);\n+\t\tgoto fail;\n+\t}\n+\n+\tvpci->vfio_dev_fd = vfio_dev_fd;\n+\treturn 0;\n+\n+fail:\n+\tclose(vfio_dev_fd);\n+\treturn -1;\n+}\n+\n+static void *\n+get_cap_addr(struct rte_pci_device *dev, struct virtio_pci_cap *cap)\n+{\n+\tuint8_t bar = cap->bar;\n+\tuint32_t length = cap->length;\n+\tuint32_t offset = cap->offset;\n+\tuint8_t *base;\n+\n+\tif (bar > 5) {\n+\t\tRTE_LOG(ERR, PMD, \"invalid bar: %u\\n\", bar);\n+\t\treturn NULL;\n+\t}\n+\n+\tif (offset + length < offset) {\n+\t\tRTE_LOG(ERR, PMD, \"offset(%u) + length(%u) overflows\\n\",\n+\t\t\toffset, length);\n+\t\treturn NULL;\n+\t}\n+\n+\tif (offset + length > dev->mem_resource[bar].len) {\n+\t\tRTE_LOG(ERR, PMD, \"invalid cap: overflows bar size: %u > %lu\\n\",\n+\t\t\toffset + length, dev->mem_resource[bar].len);\n+\t\treturn NULL;\n+\t}\n+\n+\tbase = dev->mem_resource[bar].addr;\n+\tif (base == NULL) {\n+\t\tRTE_LOG(ERR, PMD, \"bar %u base addr is NULL\", bar);\n+\t\treturn NULL;\n+\t}\n+\n+\treturn base + offset;\n+}\n+\n+static int\n+virtio_pci_map(struct virtio_pci_info *vpci)\n+{\n+\tuint8_t pos;\n+\tstruct virtio_pci_cap cap;\n+\tstruct rte_pci_device *dev = &vpci->pdev;\n+\tint ret;\n+\n+\tif (virtio_pci_vfio_map_resource(vpci)) {\n+\t\tRTE_LOG(ERR, PMD, \"failed to map pci device\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\tret = rte_pci_read_config(dev, &pos, sizeof(pos), PCI_CAPABILITY_LIST);\n+\tif (ret < 0) {\n+\t\tRTE_LOG(ERR, PMD, \"failed to read pci capability list\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\twhile (pos) {\n+\t\tret = rte_pci_read_config(dev, &cap, sizeof(cap), pos);\n+\t\tif (ret < 0) {\n+\t\t\tRTE_LOG(ERR, PMD, \"failed to read cap at pos: %x\", pos);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (cap.cap_vndr != PCI_CAP_ID_VNDR)\n+\t\t\tgoto next;\n+\n+\t\tRTE_LOG(INFO, PMD, \"cfg type: %u, bar: %u, offset: %u, \"\n+\t\t\t\t\"len: %u\\n\", cap.cfg_type, cap.bar,\n+\t\t\t\tcap.offset, cap.length);\n+\n+\t\tswitch (cap.cfg_type) {\n+\t\tcase VIRTIO_PCI_CAP_COMMON_CFG:\n+\t\t\tvpci->common_cfg = get_cap_addr(dev, &cap);\n+\t\t\tbreak;\n+\t\tcase VIRTIO_PCI_CAP_NOTIFY_CFG:\n+\t\t\trte_pci_read_config(dev, &vpci->notify_off_multiplier,\n+\t\t\t\t\t\t4, pos + sizeof(cap));\n+\t\t\tvpci->notify_base = get_cap_addr(dev, &cap);\n+\t\t\tbreak;\n+\t\tcase VIRTIO_PCI_CAP_ISR_CFG:\n+\t\t\tvpci->isr = get_cap_addr(dev, &cap);\n+\t\t\tbreak;\n+\t\tcase VIRTIO_PCI_CAP_DEVICE_CFG:\n+\t\t\tvpci->dev_cfg = get_cap_addr(dev, &cap);\n+\t\t\tbreak;\n+\t\t}\n+next:\n+\t\tpos = cap.cap_next;\n+\t}\n+\n+\tif (vpci->common_cfg == NULL || vpci->notify_base == NULL ||\n+\t\t\tvpci->isr == NULL || vpci->dev_cfg == NULL) {\n+\t\tRTE_LOG(ERR, PMD, \"capability incomplete\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\tRTE_LOG(INFO, PMD, \"capability mapping:\\ncommon cfg: %p\\n\"\n+\t\t\t\"notify base: %p\\nisr cfg: %p\\ndevice cfg: %p\\n\"\n+\t\t\t\"multiplier: %u\\n\",\n+\t\t\tvpci->common_cfg, vpci->dev_cfg,\n+\t\t\tvpci->isr, vpci->notify_base,\n+\t\t\tvpci->notify_off_multiplier);\n+\n+\treturn 0;\n+}\n+\n+static int\n+virtio_pci_dma_map(struct vdpa_virtio_pci_internal *internal)\n+{\n+\tuint32_t i;\n+\tint ret = 0;\n+\tstruct rte_vhost_memory *mem = NULL;\n+\tint vfio_container_fd;\n+\n+\tret = rte_vhost_get_mem_table(internal->vid, &mem);\n+\tif (ret < 0) {\n+\t\tRTE_LOG(ERR, PMD, \"failed to get VM memory layout\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\tvfio_container_fd = internal->vpci.vfio_container_fd;\n+\n+\tfor (i = 0; i < mem->nregions; i++) {\n+\t\tstruct vfio_iommu_type1_dma_map dma_map;\n+\t\tstruct rte_vhost_mem_region *reg;\n+\t\treg = &mem->regions[i];\n+\n+\t\tRTE_LOG(INFO, PMD, \"region %u: HVA 0x%lx, GPA 0x%lx, \"\n+\t\t\t\"size 0x%lx\\n\", i, reg->host_user_addr,\n+\t\t\treg->guest_phys_addr, reg->size);\n+\n+\t\tmemset(&dma_map, 0, sizeof(dma_map));\n+\t\tdma_map.argsz = sizeof(struct vfio_iommu_type1_dma_map);\n+\t\tdma_map.vaddr = reg->host_user_addr;\n+\t\tdma_map.size = reg->size;\n+\t\tdma_map.iova = reg->guest_phys_addr;\n+\t\tdma_map.flags = VFIO_DMA_MAP_FLAG_READ |\n+\t\t\t\tVFIO_DMA_MAP_FLAG_WRITE;\n+\n+\t\tret = ioctl(vfio_container_fd, VFIO_IOMMU_MAP_DMA, &dma_map);\n+\t\tif (ret) {\n+\t\t\tRTE_LOG(ERR, PMD, \"cannot set up DMA remapping, \"\n+\t\t\t\t\"error: %s\\n\", strerror(errno));\n+\t\t\tgoto exit;\n+\t\t}\n+\t}\n+\n+exit:\n+\tif (mem)\n+\t\tfree(mem);\n+\treturn ret;\n+}\n+\n+static int\n+virtio_pci_dma_unmap(struct vdpa_virtio_pci_internal *internal)\n+{\n+\tuint32_t i;\n+\tint ret = 0;\n+\tstruct rte_vhost_memory *mem = NULL;\n+\tint vfio_container_fd;\n+\n+\tret = rte_vhost_get_mem_table(internal->vid, &mem);\n+\tif (ret < 0) {\n+\t\tRTE_LOG(ERR, PMD, \"failed to get VM memory layout\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\tvfio_container_fd = internal->vpci.vfio_container_fd;\n+\n+\tfor (i = 0; i < mem->nregions; i++) {\n+\t\tstruct vfio_iommu_type1_dma_unmap dma_unmap;\n+\t\tstruct rte_vhost_mem_region *reg;\n+\t\treg = &mem->regions[i];\n+\n+\t\tmemset(&dma_unmap, 0, sizeof(dma_unmap));\n+\t\tdma_unmap.argsz = sizeof(struct vfio_iommu_type1_dma_unmap);\n+\t\tdma_unmap.size = reg->size;\n+\t\tdma_unmap.iova = reg->guest_phys_addr;\n+\t\tdma_unmap.flags = 0;\n+\n+\t\tret = ioctl(vfio_container_fd, VFIO_IOMMU_UNMAP_DMA,\n+\t\t\t\t&dma_unmap);\n+\t\tif (ret) {\n+\t\t\tRTE_LOG(ERR, PMD, \"cannot unset DMA remapping, \"\n+\t\t\t\t\"error: %s\\n\", strerror(errno));\n+\t\t\tgoto exit;\n+\t\t}\n+\t}\n+\n+exit:\n+\tif (mem)\n+\t\tfree(mem);\n+\treturn ret;\n+}\n+\n+static uint8_t\n+virtio_get_status(struct virtio_pci_info *vpci)\n+{\n+\treturn rte_read8(&vpci->common_cfg->device_status);\n+}\n+\n+static void\n+virtio_set_status(struct virtio_pci_info *vpci, uint8_t status)\n+{\n+\trte_write8(status, &vpci->common_cfg->device_status);\n+}\n+\n+static void\n+vdpa_virtio_reset(struct virtio_pci_info *vpci)\n+{\n+\t/* 0 means reset */\n+\tvirtio_set_status(vpci, 0);\n+\n+\t/* flush status write */\n+\twhile (virtio_get_status(vpci))\n+\t\trte_delay_ms(1);\n+}\n+\n+static void\n+vdpa_virtio_set_status(struct virtio_pci_info *vpci, uint8_t status)\n+{\n+\tif (status != 0)\n+\t\tstatus |= virtio_get_status(vpci);\n+\n+\tvirtio_set_status(vpci, status);\n+\tvirtio_get_status(vpci);\n+}\n+\n+static uint64_t\n+virtio_get_features(struct virtio_pci_info *vpci)\n+{\n+\tuint32_t features_lo, features_hi;\n+\tstruct virtio_pci_common_cfg *cfg = vpci->common_cfg;\n+\n+\trte_write32(0, &cfg->device_feature_select);\n+\tfeatures_lo = rte_read32(&cfg->device_feature);\n+\n+\trte_write32(1, &cfg->device_feature_select);\n+\tfeatures_hi = rte_read32(&cfg->device_feature);\n+\n+\treturn ((uint64_t)features_hi << 32) | features_lo;\n+}\n+\n+static void\n+vdpa_set_features(struct virtio_pci_info *vpci, uint64_t features)\n+{\n+\tstruct virtio_pci_common_cfg *cfg = vpci->common_cfg;\n+\n+\t/* enable device DMA with iova */\n+\tfeatures |= (1ULL << VIRTIO_F_IOMMU_PLATFORM);\n+\n+\trte_write32(0, &cfg->guest_feature_select);\n+\trte_write32(features & ((1ULL << 32) - 1), &cfg->guest_feature);\n+\n+\trte_write32(1, &cfg->guest_feature_select);\n+\trte_write32(features >> 32, &cfg->guest_feature);\n+}\n+\n+static int\n+vdpa_virtio_config_features(struct virtio_pci_info *vpci, uint64_t req_features)\n+{\n+\tuint64_t host_features;\n+\n+\thost_features = virtio_get_features(vpci);\n+\tvpci->req_features = req_features & host_features;\n+\n+\tvdpa_set_features(vpci, vpci->req_features);\n+\tvdpa_virtio_set_status(vpci, VIRTIO_CONFIG_S_FEATURES_OK);\n+\n+\tif (!(virtio_get_status(vpci) & VIRTIO_CONFIG_S_FEATURES_OK)) {\n+\t\tRTE_LOG(ERR, PMD, \"failed to set FEATURES_OK status\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static uint64_t\n+qva_to_gpa(int vid, uint64_t qva)\n+{\n+\tstruct rte_vhost_memory *mem = NULL;\n+\tstruct rte_vhost_mem_region *reg;\n+\tuint32_t i;\n+\tuint64_t gpa = 0;\n+\n+\tif (rte_vhost_get_mem_table(vid, &mem) < 0)\n+\t\tgoto exit;\n+\n+\tfor (i = 0; i < mem->nregions; i++) {\n+\t\treg = &mem->regions[i];\n+\n+\t\tif (qva >= reg->host_user_addr &&\n+\t\t\t\tqva < reg->host_user_addr + reg->size) {\n+\t\t\tgpa = qva - reg->host_user_addr + reg->guest_phys_addr;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+exit:\n+\tif (gpa == 0)\n+\t\trte_panic(\"failed to get gpa\\n\");\n+\tif (mem)\n+\t\tfree(mem);\n+\treturn gpa;\n+}\n+\n+static void\n+io_write64_twopart(uint64_t val, uint32_t *lo, uint32_t *hi)\n+{\n+\trte_write32(val & ((1ULL << 32) - 1), lo);\n+\trte_write32(val >> 32, hi);\n+}\n+\n+static int\n+vdpa_virtio_dev_enable(struct vdpa_virtio_pci_internal *internal)\n+{\n+\tstruct virtio_pci_info *vpci;\n+\tstruct virtio_pci_common_cfg *cfg;\n+\tuint64_t desc_addr, avail_addr, used_addr;\n+\tuint32_t i, nr_vring;\n+\tuint16_t notify_off;\n+\tstruct rte_vhost_vring vq;\n+\n+\tvpci = &internal->vpci;\n+\tcfg = vpci->common_cfg;\n+\tnr_vring = rte_vhost_get_vring_num(internal->vid);\n+\n+\trte_write16(0, &cfg->msix_config);\n+\tif (rte_read16(&cfg->msix_config) == VIRTIO_MSI_NO_VECTOR) {\n+\t\tRTE_LOG(ERR, PMD, \"msix vec alloc failed for device config\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\tfor (i = 0; i < nr_vring; i++) {\n+\t\trte_vhost_get_vhost_vring(internal->vid, i, &vq);\n+\t\tdesc_addr = qva_to_gpa(internal->vid, (uint64_t)vq.desc);\n+\t\tavail_addr = qva_to_gpa(internal->vid, (uint64_t)vq.avail);\n+\t\tused_addr = qva_to_gpa(internal->vid, (uint64_t)vq.used);\n+\n+\t\trte_write16(i, &cfg->queue_select);\n+\t\tio_write64_twopart(desc_addr, &cfg->queue_desc_lo,\n+\t\t\t\t&cfg->queue_desc_hi);\n+\t\tio_write64_twopart(avail_addr, &cfg->queue_avail_lo,\n+\t\t\t\t&cfg->queue_avail_hi);\n+\t\tio_write64_twopart(used_addr, &cfg->queue_used_lo,\n+\t\t\t\t&cfg->queue_used_hi);\n+\t\trte_write16(vq.size, &cfg->queue_size);\n+\n+\t\trte_write16(i + 1, &cfg->queue_msix_vector);\n+\t\tif (rte_read16(&cfg->queue_msix_vector) ==\n+\t\t\t\tVIRTIO_MSI_NO_VECTOR) {\n+\t\t\tRTE_LOG(ERR, PMD, \"queue %u, msix vec alloc failed\\n\",\n+\t\t\t\t\ti);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tnotify_off = rte_read16(&cfg->queue_notify_off);\n+\t\tvpci->notify_addr[i] = (void *)((uint8_t *)vpci->notify_base +\n+\t\t\t\tnotify_off * vpci->notify_off_multiplier);\n+\t\trte_write16(1, &cfg->queue_enable);\n+\n+\t\tRTE_LOG(INFO, PMD, \"queue %u addresses:\\n\"\n+\t\t\t\t\"desc_addr: 0x%lx\\tavail_addr: 0x%lx\\tused_addr: 0x%lx\\n\"\n+\t\t\t\t\"queue size: %u\\t\\tnotify addr: %p\\tnotify offset: %u\\n\",\n+\t\t\t\ti, desc_addr, avail_addr, used_addr,\n+\t\t\t\tvq.size, vpci->notify_addr[i], notify_off);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void\n+vdpa_virtio_dev_disable(struct vdpa_virtio_pci_internal *internal)\n+{\n+\tuint32_t i, nr_vring;\n+\tstruct virtio_pci_info *vpci;\n+\tstruct virtio_pci_common_cfg *cfg;\n+\n+\tvpci = &internal->vpci;\n+\tcfg = vpci->common_cfg;\n+\tnr_vring = rte_vhost_get_vring_num(internal->vid);\n+\n+\trte_write16(VIRTIO_MSI_NO_VECTOR, &cfg->msix_config);\n+\tfor (i = 0; i < nr_vring; i++) {\n+\t\trte_write16(i, &cfg->queue_select);\n+\t\trte_write16(0, &cfg->queue_enable);\n+\t\trte_write16(VIRTIO_MSI_NO_VECTOR, &cfg->queue_msix_vector);\n+\t}\n+}\n+\n+static int\n+vdpa_virtio_pci_start(struct vdpa_virtio_pci_internal *internal)\n+{\n+\tstruct virtio_pci_info *vpci;\n+\tuint64_t features;\n+\n+\tvpci = &internal->vpci;\n+\n+\trte_vhost_get_negotiated_features(internal->vid, &features);\n+\n+\t/* Reset the device although not necessary at startup. */\n+\tvdpa_virtio_reset(vpci);\n+\n+\t/* Tell the host we've noticed this device. */\n+\tvdpa_virtio_set_status(vpci, VIRTIO_CONFIG_S_ACKNOWLEDGE);\n+\n+\t/* Tell the host we've known how to drive the device. */\n+\tvdpa_virtio_set_status(vpci, VIRTIO_CONFIG_S_DRIVER);\n+\n+\tif (vdpa_virtio_config_features(vpci, features) < 0)\n+\t\treturn -1;\n+\n+\tif (vdpa_virtio_dev_enable(internal) < 0)\n+\t\treturn -1;\n+\n+\tvdpa_virtio_set_status(vpci, VIRTIO_CONFIG_S_DRIVER_OK);\n+\treturn 0;\n+}\n+\n+static void\n+vdpa_virtio_pci_stop(struct vdpa_virtio_pci_internal *internal)\n+{\n+\tstruct virtio_pci_info *vpci;\n+\n+\tvpci = &internal->vpci;\n+\tvdpa_virtio_dev_disable(internal);\n+\tvdpa_virtio_reset(vpci);\n+}\n+\n+static int\n+vdpa_enable_vfio_intr(struct vdpa_virtio_pci_internal *internal)\n+{\n+\tint ret;\n+\tuint32_t i, nr_vring;\n+\tchar irq_set_buf[MSIX_IRQ_SET_BUF_LEN];\n+\tstruct vfio_irq_set *irq_set;\n+\tint *fd_ptr;\n+\tstruct virtio_pci_info *vpci;\n+\tstruct rte_vhost_vring vring;\n+\n+\tvpci = &internal->vpci;\n+\tnr_vring = rte_vhost_get_vring_num(internal->vid);\n+\n+\tirq_set = (struct vfio_irq_set *)irq_set_buf;\n+\tirq_set->argsz = sizeof(irq_set_buf);\n+\tirq_set->count = nr_vring + 1;\n+\tirq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD |\n+\t\t\t VFIO_IRQ_SET_ACTION_TRIGGER;\n+\tirq_set->index = VFIO_PCI_MSIX_IRQ_INDEX;\n+\tirq_set->start = 0;\n+\tfd_ptr = (int *)&irq_set->data;\n+\tfd_ptr[RTE_INTR_VEC_ZERO_OFFSET] = vpci->pdev.intr_handle.fd;\n+\n+\tfor (i = 0; i < nr_vring; i++) {\n+\t\trte_vhost_get_vhost_vring(internal->vid, i, &vring);\n+\t\tfd_ptr[RTE_INTR_VEC_RXTX_OFFSET + i] = vring.callfd;\n+\t}\n+\n+\tret = ioctl(vpci->vfio_dev_fd, VFIO_DEVICE_SET_IRQS, irq_set);\n+\tif (ret) {\n+\t\tRTE_LOG(ERR, PMD, \"Error enabling MSI-X interrupts: %s\\n\",\n+\t\t\t\tstrerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int\n+vdpa_disable_vfio_intr(struct vdpa_virtio_pci_internal *internal)\n+{\n+\tint ret;\n+\tchar irq_set_buf[MSIX_IRQ_SET_BUF_LEN];\n+\tstruct vfio_irq_set *irq_set;\n+\tstruct virtio_pci_info *vpci;\n+\n+\tvpci = &internal->vpci;\n+\n+\tirq_set = (struct vfio_irq_set *)irq_set_buf;\n+\tirq_set->argsz = sizeof(irq_set_buf);\n+\tirq_set->count = 0;\n+\tirq_set->flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER;\n+\tirq_set->index = VFIO_PCI_MSIX_IRQ_INDEX;\n+\tirq_set->start = 0;\n+\n+\tret = ioctl(vpci->vfio_dev_fd, VFIO_DEVICE_SET_IRQS, irq_set);\n+\tif (ret) {\n+\t\tRTE_LOG(ERR, PMD, \"Error disabling MSI-X interrupts: %s\\n\",\n+\t\t\t\tstrerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void *\n+notify_relay(void *arg)\n+{\n+\tint i, kickfd, epfd, nfds = 0;\n+\tstruct virtio_pci_info *vpci;\n+\tuint32_t qid, q_num;\n+\tstruct epoll_event events[MAX_QUEUES * 2];\n+\tstruct epoll_event ev;\n+\tuint64_t buf;\n+\tint nbytes;\n+\tstruct rte_vhost_vring vring;\n+\tstruct vdpa_virtio_pci_internal *internal = arg;\n+\n+\tvpci = &internal->vpci;\n+\tq_num = rte_vhost_get_vring_num(internal->vid);\n+\n+\tepfd = epoll_create(MAX_QUEUES * 2);\n+\tif (epfd < 0) {\n+\t\tRTE_LOG(ERR, PMD, \"failed to create epoll instance\\n\");\n+\t\treturn NULL;\n+\t}\n+\tvpci->epfd = epfd;\n+\n+\tfor (qid = 0; qid < q_num; qid++) {\n+\t\tev.events = EPOLLIN | EPOLLPRI;\n+\t\trte_vhost_get_vhost_vring(internal->vid, qid, &vring);\n+\t\tev.data.u64 = qid | (uint64_t)vring.kickfd << 32;\n+\t\tif (epoll_ctl(epfd, EPOLL_CTL_ADD, vring.kickfd, &ev) < 0) {\n+\t\t\tRTE_LOG(ERR, PMD, \"epoll add error, %s\\n\",\n+\t\t\t\t\tstrerror(errno));\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\n+\tfor (;;) {\n+\t\tnfds = epoll_wait(epfd, events, q_num, -1);\n+\t\tif (nfds < 0) {\n+\t\t\tif (errno == EINTR)\n+\t\t\t\tcontinue;\n+\t\t\tRTE_LOG(ERR, PMD, \"epoll_wait return fail\\n\");\n+\t\t\treturn NULL;\n+\t\t}\n+\n+\t\tfor (i = 0; i < nfds; i++) {\n+\t\t\tqid = events[i].data.u32;\n+\t\t\tkickfd = (uint32_t)(events[i].data.u64 >> 32);\n+\t\t\tdo {\n+\t\t\t\tnbytes = read(kickfd, &buf, 8);\n+\t\t\t\tif (nbytes < 0) {\n+\t\t\t\t\tif (errno == EINTR ||\n+\t\t\t\t\t    errno == EWOULDBLOCK ||\n+\t\t\t\t\t    errno == EAGAIN)\n+\t\t\t\t\t\tcontinue;\n+\t\t\t\t\tRTE_LOG(INFO, PMD, \"Error reading \"\n+\t\t\t\t\t\t\"kickfd: %s\\n\",\n+\t\t\t\t\t\tstrerror(errno));\n+\t\t\t\t}\n+\t\t\t\tbreak;\n+\t\t\t} while (1);\n+\n+\t\t\trte_write16(qid, vpci->notify_addr[qid]);\n+\t\t}\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static int\n+setup_notify_relay(struct vdpa_virtio_pci_internal *internal)\n+{\n+\tint ret;\n+\n+\tret = pthread_create(&internal->vpci.tid, NULL, notify_relay,\n+\t\t\t     (void *)internal);\n+\tif (ret) {\n+\t\tRTE_LOG(ERR, PMD, \"failed to create notify relay pthread\\n\");\n+\t\treturn -1;\n+\t}\n+\treturn 0;\n+}\n+\n+static int\n+unset_notify_relay(struct vdpa_virtio_pci_internal *internal)\n+{\n+\tstruct virtio_pci_info *vpci;\n+\tvoid *status;\n+\n+\tvpci = &internal->vpci;\n+\tif (vpci->tid) {\n+\t\tpthread_cancel(vpci->tid);\n+\t\tpthread_join(vpci->tid, &status);\n+\t}\n+\tvpci->tid = 0;\n+\n+\tif (vpci->epfd >= 0)\n+\t\tclose(vpci->epfd);\n+\tvpci->epfd = -1;\n+\n+\treturn 0;\n+}\n+\n+static int\n+update_datapath(struct rte_eth_dev *eth_dev)\n+{\n+\tstruct vdpa_virtio_pci_internal *internal;\n+\tint ret;\n+\n+\tinternal = eth_dev->data->dev_private;\n+\trte_spinlock_lock(&internal->lock);\n+\n+\tif (!rte_atomic32_read(&internal->running) &&\n+\t    (rte_atomic32_read(&internal->started) &&\n+\t     rte_atomic32_read(&internal->dev_attached))) {\n+\t\tret = virtio_pci_dma_map(internal);\n+\t\tif (ret)\n+\t\t\tgoto err;\n+\n+\t\tret = vdpa_enable_vfio_intr(internal);\n+\t\tif (ret)\n+\t\t\tgoto err;\n+\n+\t\tret = setup_notify_relay(internal);\n+\t\tif (ret)\n+\t\t\tgoto err;\n+\n+\t\tret = vdpa_virtio_pci_start(internal);\n+\t\tif (ret)\n+\t\t\tgoto err;\n+\n+\t\trte_atomic32_set(&internal->running, 1);\n+\t} else if (rte_atomic32_read(&internal->running) &&\n+\t\t   (!rte_atomic32_read(&internal->started) ||\n+\t\t    !rte_atomic32_read(&internal->dev_attached))) {\n+\t\tvdpa_virtio_pci_stop(internal);\n+\n+\t\tret = unset_notify_relay(internal);\n+\t\tif (ret)\n+\t\t\tgoto err;\n+\n+\t\tret = vdpa_disable_vfio_intr(internal);\n+\t\tif (ret)\n+\t\t\tgoto err;\n+\n+\t\tret = virtio_pci_dma_unmap(internal);\n+\t\tif (ret)\n+\t\t\tgoto err;\n+\n+\t\trte_atomic32_set(&internal->running, 0);\n+\t}\n+\n+\trte_spinlock_unlock(&internal->lock);\n+\treturn 0;\n+err:\n+\trte_spinlock_unlock(&internal->lock);\n+\treturn ret;\n+}\n+\n+static int\n+vdpa_virtio_dev_config(int vid)\n+{\n+\tint eid;\n+\tstruct internal_list *list;\n+\tstruct rte_eth_dev *eth_dev;\n+\tstruct vdpa_virtio_pci_internal *internal;\n+\n+\teid = rte_vhost_get_vdpa_eid(vid);\n+\tlist = find_internal_resource_by_eid(eid);\n+\tif (list == NULL) {\n+\t\tRTE_LOG(ERR, PMD, \"Invalid engine id: %d\\n\", eid);\n+\t\treturn -1;\n+\t}\n+\n+\teth_dev = list->eth_dev;\n+\tinternal = eth_dev->data->dev_private;\n+\n+\teth_dev->data->dev_link.link_status = ETH_LINK_UP;\n+\n+\trte_atomic32_set(&internal->dev_attached, 1);\n+\tupdate_datapath(eth_dev);\n+\n+\treturn 0;\n+}\n+\n+static int\n+vdpa_virtio_dev_close(int vid)\n+{\n+\tint eid;\n+\tstruct internal_list *list;\n+\tstruct rte_eth_dev *eth_dev;\n+\tstruct vdpa_virtio_pci_internal *internal;\n+\n+\teid = rte_vhost_get_vdpa_eid(vid);\n+\tlist = find_internal_resource_by_eid(eid);\n+\tif (list == NULL) {\n+\t\tRTE_LOG(ERR, PMD, \"Invalid engine id: %d\\n\", eid);\n+\t\treturn -1;\n+\t}\n+\n+\teth_dev = list->eth_dev;\n+\tinternal = eth_dev->data->dev_private;\n+\n+\teth_dev->data->dev_link.link_status = ETH_LINK_DOWN;\n+\n+\trte_atomic32_set(&internal->dev_attached, 0);\n+\tupdate_datapath(eth_dev);\n+\n+\treturn 0;\n+}\n+\n+static void\n+vfio_close_fds(struct virtio_pci_info *vpci)\n+{\n+\tif (vpci->vfio_dev_fd >= 0)\n+\t\tclose(vpci->vfio_dev_fd);\n+\tif (vpci->vfio_group_fd >= 0)\n+\t\tclose(vpci->vfio_group_fd);\n+\tif (vpci->vfio_container_fd >= 0)\n+\t\tclose(vpci->vfio_container_fd);\n+\n+\tvpci->vfio_dev_fd = -1;\n+\tvpci->vfio_group_fd = -1;\n+\tvpci->vfio_container_fd = -1;\n+}\n+\n+static int\n+vdpa_virtio_eng_init(int eid, struct rte_vdpa_eng_addr *addr)\n+{\n+\tstruct internal_list *list;\n+\tstruct vdpa_virtio_pci_internal *internal;\n+\tstruct virtio_pci_info *vpci;\n+\tuint64_t features;\n+\n+\tlist = find_internal_resource_by_eng_addr(addr);\n+\tif (list == NULL) {\n+\t\tRTE_LOG(ERR, PMD, \"Invalid engine addr\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\tinternal = list->eth_dev->data->dev_private;\n+\tvpci = &internal->vpci;\n+\n+\tvpci->vfio_dev_fd = -1;\n+\tvpci->vfio_group_fd = -1;\n+\tvpci->vfio_container_fd = -1;\n+\n+\tif (check_pci_dev(&vpci->pdev) < 0)\n+\t\treturn -1;\n+\n+\tif (virtio_pci_map(vpci) < 0)\n+\t\tgoto err;\n+\n+\tinternal->eid = eid;\n+\tinternal->max_devices = 1;\n+\tinternal->max_queues = MAX_QUEUES;\n+\tfeatures = virtio_get_features(&internal->vpci);\n+\tif ((features & (1ULL << VIRTIO_F_IOMMU_PLATFORM)) == 0) {\n+\t\tRTE_LOG(ERR, PMD, \"VIRTIO_F_IOMMU_PLATFORM feature is required \"\n+\t\t\t\t\"to support DMA with IOVA\");\n+\t\tgoto err;\n+\t}\n+\n+\t/* We need the nested VM's driver to use GPA */\n+\tinternal->features = (features & ~(1ULL << VIRTIO_F_IOMMU_PLATFORM)) |\n+\t\t\t  (1ULL << RTE_VHOST_USER_F_PROTOCOL_FEATURES);\n+\treturn 0;\n+\n+err:\n+\tvfio_close_fds(vpci);\n+\treturn -1;\n+}\n+\n+static int\n+vdpa_virtio_eng_uninit(int eid)\n+{\n+\tstruct internal_list *list;\n+\tstruct vdpa_virtio_pci_internal *internal;\n+\n+\tlist = find_internal_resource_by_eid(eid);\n+\tif (list == NULL) {\n+\t\tRTE_LOG(ERR, PMD, \"Invalid engine id %d\\n\", eid);\n+\t\treturn -1;\n+\t}\n+\n+\tinternal = list->eth_dev->data->dev_private;\n+\tvfio_close_fds(&internal->vpci);\n+\treturn 0;\n+}\n+\n+#define VDPA_SUPPORTED_PROTOCOL_FEATURES \\\n+\t\t(1ULL << RTE_VHOST_USER_PROTOCOL_F_REPLY_ACK)\n+static int\n+vdpa_virtio_info_query(int eid, struct rte_vdpa_eng_attr *attr)\n+{\n+\tstruct internal_list *list;\n+\tstruct vdpa_virtio_pci_internal *internal;\n+\n+\tlist = find_internal_resource_by_eid(eid);\n+\tif (list == NULL) {\n+\t\tRTE_LOG(ERR, PMD, \"Invalid engine id: %d\\n\", eid);\n+\t\treturn -1;\n+\t}\n+\n+\tinternal = list->eth_dev->data->dev_private;\n+\tattr->dev_num = internal->max_devices;\n+\tattr->queue_num = internal->max_queues;\n+\tattr->features = internal->features;\n+\tattr->protocol_features = VDPA_SUPPORTED_PROTOCOL_FEATURES;\n+\n+\treturn 0;\n+}\n+\n+struct rte_vdpa_eng_driver vdpa_virtio_pci_driver = {\n+\t.name = \"vdpa_virtio_pci\",\n+\t.eng_ops = {\n+\t\t.eng_init = vdpa_virtio_eng_init,\n+\t\t.eng_uninit = vdpa_virtio_eng_uninit,\n+\t\t.info_query = vdpa_virtio_info_query,\n+\t},\n+\t.dev_ops = {\n+\t\t.dev_conf = vdpa_virtio_dev_config,\n+\t\t.dev_close = vdpa_virtio_dev_close,\n+\t\t.vring_state_set = NULL,\n+\t\t.feature_set = NULL,\n+\t\t.migration_done = NULL,\n+\t},\n+};\n+\n+RTE_VDPA_REGISTER_DRIVER(vdpa_virtio_pci, vdpa_virtio_pci_driver);\n+\n+static int\n+eth_dev_start(struct rte_eth_dev *dev)\n+{\n+\tstruct vdpa_virtio_pci_internal *internal;\n+\n+\tinternal = dev->data->dev_private;\n+\trte_atomic32_set(&internal->started, 1);\n+\tupdate_datapath(dev);\n+\n+\treturn 0;\n+}\n+\n+static void\n+eth_dev_stop(struct rte_eth_dev *dev)\n+{\n+\tstruct vdpa_virtio_pci_internal *internal;\n+\n+\tinternal = dev->data->dev_private;\n+\trte_atomic32_set(&internal->started, 0);\n+\tupdate_datapath(dev);\n+}\n+\n+static void\n+eth_dev_close(struct rte_eth_dev *dev)\n+{\n+\tstruct vdpa_virtio_pci_internal *internal;\n+\tstruct internal_list *list;\n+\n+\tinternal = dev->data->dev_private;\n+\teth_dev_stop(dev);\n+\n+\tlist = find_internal_resource_by_eng_addr(&internal->eng_addr);\n+\tif (list == NULL) {\n+\t\tRTE_LOG(ERR, PMD, \"Invalid engine addr\\n\");\n+\t\treturn;\n+\t}\n+\n+\trte_vdpa_unregister_engine(internal->eid);\n+\n+\tpthread_mutex_lock(&internal_list_lock);\n+\tTAILQ_REMOVE(&internal_list, list, next);\n+\tpthread_mutex_unlock(&internal_list_lock);\n+\trte_free(list);\n+\n+\trte_free(dev->data->mac_addrs);\n+\tfree(internal->dev_name);\n+\trte_free(internal);\n+\n+\tdev->data->dev_private = NULL;\n+}\n+\n+static int\n+eth_dev_configure(struct rte_eth_dev *dev __rte_unused)\n+{\n+\treturn 0;\n+}\n+\n+static void\n+eth_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)\n+{\n+\tstruct vdpa_virtio_pci_internal *internal;\n+\n+\tinternal = dev->data->dev_private;\n+\tif (internal == NULL) {\n+\t\tRTE_LOG(ERR, PMD, \"Invalid device specified\\n\");\n+\t\treturn;\n+\t}\n+\n+\tdev_info->max_mac_addrs = 1;\n+\tdev_info->max_rx_pktlen = (uint32_t)-1;\n+\tdev_info->max_rx_queues = internal->max_queues;\n+\tdev_info->max_tx_queues = internal->max_queues;\n+\tdev_info->min_rx_bufsize = 0;\n+}\n+\n+static int\n+eth_rx_queue_setup(struct rte_eth_dev *dev __rte_unused,\n+\t\t   uint16_t rx_queue_id __rte_unused,\n+\t\t   uint16_t nb_rx_desc __rte_unused,\n+\t\t   unsigned int socket_id __rte_unused,\n+\t\t   const struct rte_eth_rxconf *rx_conf __rte_unused,\n+\t\t   struct rte_mempool *mb_pool __rte_unused)\n+{\n+\treturn 0;\n+}\n+\n+static int\n+eth_tx_queue_setup(struct rte_eth_dev *dev __rte_unused,\n+\t\t   uint16_t tx_queue_id __rte_unused,\n+\t\t   uint16_t nb_tx_desc __rte_unused,\n+\t\t   unsigned int socket_id __rte_unused,\n+\t\t   const struct rte_eth_txconf *tx_conf __rte_unused)\n+{\n+\treturn 0;\n+}\n+\n+static void\n+eth_queue_release(void *q __rte_unused)\n+{\n+}\n+\n+static uint16_t\n+eth_vdpa_virtio_pci_rx(void *q __rte_unused,\n+\t\t       struct rte_mbuf **bufs __rte_unused,\n+\t\t       uint16_t nb_bufs __rte_unused)\n+{\n+\treturn 0;\n+}\n+\n+static uint16_t\n+eth_vdpa_virtio_pci_tx(void *q __rte_unused,\n+\t\t       struct rte_mbuf **bufs __rte_unused,\n+\t\t       uint16_t nb_bufs __rte_unused)\n+{\n+\treturn 0;\n+}\n+\n+static int\n+eth_link_update(struct rte_eth_dev *dev __rte_unused,\n+\t\tint wait_to_complete __rte_unused)\n+{\n+\treturn 0;\n+}\n+\n+static const struct eth_dev_ops ops = {\n+\t.dev_start = eth_dev_start,\n+\t.dev_stop = eth_dev_stop,\n+\t.dev_close = eth_dev_close,\n+\t.dev_configure = eth_dev_configure,\n+\t.dev_infos_get = eth_dev_info,\n+\t.rx_queue_setup = eth_rx_queue_setup,\n+\t.tx_queue_setup = eth_tx_queue_setup,\n+\t.rx_queue_release = eth_queue_release,\n+\t.tx_queue_release = eth_queue_release,\n+\t.link_update = eth_link_update,\n+};\n+\n+static int\n+eth_dev_vdpa_virtio_pci_create(struct rte_vdev_device *dev,\n+\t\tstruct rte_pci_addr *pci_addr)\n+{\n+\tconst char *name = rte_vdev_device_name(dev);\n+\tstruct rte_eth_dev *eth_dev = NULL;\n+\tstruct ether_addr *eth_addr = NULL;\n+\tstruct vdpa_virtio_pci_internal *internal = NULL;\n+\tstruct internal_list *list = NULL;\n+\tstruct rte_eth_dev_data *data = NULL;\n+\n+\tlist = rte_zmalloc_socket(name, sizeof(*list), 0,\n+\t\t\tdev->device.numa_node);\n+\tif (list == NULL)\n+\t\tgoto error;\n+\n+\t/* reserve an ethdev entry */\n+\teth_dev = rte_eth_vdev_allocate(dev, sizeof(*internal));\n+\tif (eth_dev == NULL)\n+\t\tgoto error;\n+\n+\teth_addr = rte_zmalloc_socket(name, sizeof(*eth_addr), 0,\n+\t\t\tdev->device.numa_node);\n+\tif (eth_addr == NULL)\n+\t\tgoto error;\n+\n+\t*eth_addr = base_eth_addr;\n+\teth_addr->addr_bytes[5] = eth_dev->data->port_id;\n+\n+\tinternal = eth_dev->data->dev_private;\n+\tinternal->dev_name = strdup(name);\n+\tif (internal->dev_name == NULL)\n+\t\tgoto error;\n+\n+\tinternal->eng_addr.pci_addr = *pci_addr;\n+\tinternal->vpci.pdev.addr = *pci_addr;\n+\trte_spinlock_init(&internal->lock);\n+\n+\tlist->eth_dev = eth_dev;\n+\tpthread_mutex_lock(&internal_list_lock);\n+\tTAILQ_INSERT_TAIL(&internal_list, list, next);\n+\tpthread_mutex_unlock(&internal_list_lock);\n+\n+\tdata = eth_dev->data;\n+\tdata->nb_rx_queues = MAX_QUEUES;\n+\tdata->nb_tx_queues = MAX_QUEUES;\n+\tdata->dev_link = vdpa_link;\n+\tdata->mac_addrs = eth_addr;\n+\tdata->dev_flags = RTE_ETH_DEV_INTR_LSC;\n+\teth_dev->dev_ops = &ops;\n+\n+\t/* assign rx and tx ops, could be used as vDPA fallback */\n+\teth_dev->rx_pkt_burst = eth_vdpa_virtio_pci_rx;\n+\teth_dev->tx_pkt_burst = eth_vdpa_virtio_pci_tx;\n+\n+\tif (rte_vdpa_register_engine(vdpa_virtio_pci_driver.name,\n+\t\t\t\t&internal->eng_addr) < 0)\n+\t\tgoto error;\n+\n+\treturn 0;\n+\n+error:\n+\trte_free(list);\n+\trte_free(eth_addr);\n+\tif (internal && internal->dev_name)\n+\t\tfree(internal->dev_name);\n+\trte_free(internal);\n+\tif (eth_dev)\n+\t\trte_eth_dev_release_port(eth_dev);\n+\n+\treturn -1;\n+}\n+\n+static int\n+get_pci_addr(const char *key __rte_unused, const char *value, void *extra_args)\n+{\n+\tif (value == NULL || extra_args == NULL)\n+\t\treturn -1;\n+\n+\treturn parse_pci_addr_format(value, strlen(value), extra_args);\n+}\n+\n+static int\n+rte_vdpa_virtio_pci_probe(struct rte_vdev_device *dev)\n+{\n+\tstruct rte_kvargs *kvlist = NULL;\n+\tint ret = 0;\n+\tstruct rte_pci_addr pci_addr;\n+\n+\tRTE_LOG(INFO, PMD, \"Initializing vdpa_virtio_pci for %s\\n\",\n+\t\trte_vdev_device_name(dev));\n+\n+\tkvlist = rte_kvargs_parse(rte_vdev_device_args(dev), valid_arguments);\n+\tif (kvlist == NULL)\n+\t\treturn -1;\n+\n+\tif (rte_kvargs_count(kvlist, ETH_VDPA_VIRTIO_PCI_BDF_ARG) == 1) {\n+\t\tret = rte_kvargs_process(kvlist, ETH_VDPA_VIRTIO_PCI_BDF_ARG,\n+\t\t\t\t&get_pci_addr, &pci_addr);\n+\t\tif (ret < 0)\n+\t\t\tgoto out_free;\n+\t} else {\n+\t\tret = -1;\n+\t\tgoto out_free;\n+\t}\n+\n+\teth_dev_vdpa_virtio_pci_create(dev, &pci_addr);\n+\n+out_free:\n+\trte_kvargs_free(kvlist);\n+\treturn ret;\n+}\n+\n+static int\n+rte_vdpa_virtio_pci_remove(struct rte_vdev_device *dev)\n+{\n+\tconst char *name;\n+\tstruct rte_eth_dev *eth_dev = NULL;\n+\n+\tname = rte_vdev_device_name(dev);\n+\tRTE_LOG(INFO, PMD, \"Un-Initializing vdpa_virtio_pci for %s\\n\", name);\n+\n+\t/* find an ethdev entry */\n+\teth_dev = rte_eth_dev_allocated(name);\n+\tif (eth_dev == NULL)\n+\t\treturn -ENODEV;\n+\n+\teth_dev_close(eth_dev);\n+\trte_free(eth_dev->data);\n+\trte_eth_dev_release_port(eth_dev);\n+\n+\treturn 0;\n+}\n+\n+static struct rte_vdev_driver vdpa_virtio_pci_drv = {\n+\t.probe = rte_vdpa_virtio_pci_probe,\n+\t.remove = rte_vdpa_virtio_pci_remove,\n+};\n+\n+RTE_PMD_REGISTER_VDEV(net_vdpa_virtio_pci, vdpa_virtio_pci_drv);\n+RTE_PMD_REGISTER_ALIAS(net_vdpa_virtio_pci, eth_vdpa_virtio_pci);\n+RTE_PMD_REGISTER_PARAM_STRING(net_vdpa_virtio_pci,\n+\t\"bdf=<bdf>\");\ndiff --git a/drivers/net/vdpa_virtio_pci/rte_vdpa_virtio_pci_version.map b/drivers/net/vdpa_virtio_pci/rte_vdpa_virtio_pci_version.map\nnew file mode 100644\nindex 000000000..33d237913\n--- /dev/null\n+++ b/drivers/net/vdpa_virtio_pci/rte_vdpa_virtio_pci_version.map\n@@ -0,0 +1,4 @@\n+EXPERIMENTAL {\n+\n+\tlocal: *;\n+};\ndiff --git a/mk/rte.app.mk b/mk/rte.app.mk\nindex 3eb41d176..44e87f4d9 100644\n--- a/mk/rte.app.mk\n+++ b/mk/rte.app.mk\n@@ -171,6 +171,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD) += -lrte_pmd_vdev_netvsc\n _LDLIBS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD)     += -lrte_pmd_virtio\n ifeq ($(CONFIG_RTE_LIBRTE_VHOST),y)\n _LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_VHOST)      += -lrte_pmd_vhost\n+_LDLIBS-$(CONFIG_RTE_LIBRTE_VDPA_VIRTIO_PCI)      += -lrte_vdpa_virtio_pci\n endif # $(CONFIG_RTE_LIBRTE_VHOST)\n _LDLIBS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD)    += -lrte_pmd_vmxnet3_uio\n \n",
    "prefixes": [
        "dpdk-dev",
        "2/3"
    ]
}