get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 41016,
    "url": "http://patches.dpdk.org/api/patches/41016/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20180612232939.24793-3-stephen@networkplumber.org/",
    "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": "<20180612232939.24793-3-stephen@networkplumber.org>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20180612232939.24793-3-stephen@networkplumber.org",
    "date": "2018-06-12T23:29:37",
    "name": "[v11,2/4] bus/vmbus: add hyper-v virtual bus support",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "775eb4506762fad165a615d9a19712bb2010b3e0",
    "submitter": {
        "id": 27,
        "url": "http://patches.dpdk.org/api/people/27/?format=api",
        "name": "Stephen Hemminger",
        "email": "stephen@networkplumber.org"
    },
    "delegate": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20180612232939.24793-3-stephen@networkplumber.org/mbox/",
    "series": [
        {
            "id": 98,
            "url": "http://patches.dpdk.org/api/series/98/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=98",
            "date": "2018-06-12T23:29:35",
            "name": "Hyper-V/Azure netvsc PMD and bus support",
            "version": 11,
            "mbox": "http://patches.dpdk.org/series/98/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/41016/comments/",
    "check": "fail",
    "checks": "http://patches.dpdk.org/api/patches/41016/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 53F561EBE9;\n\tWed, 13 Jun 2018 01:29:49 +0200 (CEST)",
            "from mail-pg0-f67.google.com (mail-pg0-f67.google.com\n\t[74.125.83.67]) by dpdk.org (Postfix) with ESMTP id D4A931EBB0\n\tfor <dev@dpdk.org>; Wed, 13 Jun 2018 01:29:46 +0200 (CEST)",
            "by mail-pg0-f67.google.com with SMTP id a14-v6so303844pgw.10\n\tfor <dev@dpdk.org>; Tue, 12 Jun 2018 16:29:46 -0700 (PDT)",
            "from xeon-e3.lan (204-195-35-107.wavecable.com. [204.195.35.107])\n\tby smtp.gmail.com with ESMTPSA id\n\t29-v6sm1592088pfj.14.2018.06.12.16.29.42\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);\n\tTue, 12 Jun 2018 16:29:43 -0700 (PDT)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=networkplumber-org.20150623.gappssmtp.com; s=20150623;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references;\n\tbh=zfFfvhT4OwZ7S3qIyNLX9WaQuUSX4XP0gWwFGaOksyM=;\n\tb=o+PvTDZ4/4oHB5GJfeSuPrE6Y6WZv6rE5tKp6QFseaX+Wvc3vel56C0QfV+ReBC4iM\n\tLDrmz33XEyiLupSOcevUxrMd6QSQSQ9H8TNJMuIHQUuAjrh+GUHTi323ncOa1FO6JPFx\n\tR4ob25ijMPoOKKg2JBiAzlXD+/0Y48VLHBU55bRQN7nqXvs95xTG77NIt4g0Vh/vc4fp\n\twLd/6AxBqSSN9E6pQZ+mBB+2ky1zxeDK8eV2mGeEZkfi0PjIs5QWVpdWZWMUX6mJavhE\n\tBWXQHcejQFnNxftV0fxXAozC3lb8b7INfzDgbQ4ZjwW2OtFX1arkYsVD40k+Nk6NDk58\n\teg6A==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references;\n\tbh=zfFfvhT4OwZ7S3qIyNLX9WaQuUSX4XP0gWwFGaOksyM=;\n\tb=CIZvng9PY8VsJ5CB378HwKmTxbvpHkD8zpfDJ2jB1Sxwv/6i+yse3EIcYTWV0sHT2V\n\tO7GCfbJu78v5jFbLRopkc1VJ+hEVtwishsAY/+TE0Ik6anVopvkc7Zds6B6xuZjIl4SM\n\t6nk4Z41T3n9v2l6zkeIIRnOv89f/ZNJSPFSYuK/SiS01epUrL+s4CqFoAYFADpwtVWrN\n\tQzlDZ776O7lyzvCDOdFMac4AjeGNt2lsrNLNCNy1LcDhu/9g5UtSuTTnJxaFezryNg5C\n\tIEwZrUxgpVm+H2UdkLw5IUnKEdxbbq+W7WyQz9XMFiNHxysohcf/iJI3hCjAUc+npf9t\n\tW2yw==",
        "X-Gm-Message-State": "APt69E0VmvkTUgZ0U4kv5SQm6VR289HhzLEGa8w2xtIMjRfG8ls8afAI\n\tc2G3udgttTLVPxbF/8yqyL20ach84Xw=",
        "X-Google-Smtp-Source": "ADUXVKJsy8Fcmd9BVPBZOKlxg4NAeZf/1CsER4zvF9Z0JpJAKMTqSp8r/ym2ynvBmkrxpVdm3CN1Zw==",
        "X-Received": "by 2002:a65:524d:: with SMTP id\n\tq13-v6mr2036657pgp.244.1528846184292; \n\tTue, 12 Jun 2018 16:29:44 -0700 (PDT)",
        "From": "Stephen Hemminger <stephen@networkplumber.org>",
        "To": "dev@dpdk.org",
        "Cc": "Stephen Hemminger <stephen@networkplumber.org>,\n\tStephen Hemminger <sthemmin@microsoft.com>",
        "Date": "Tue, 12 Jun 2018 16:29:37 -0700",
        "Message-Id": "<20180612232939.24793-3-stephen@networkplumber.org>",
        "X-Mailer": "git-send-email 2.17.1",
        "In-Reply-To": "<20180612232939.24793-1-stephen@networkplumber.org>",
        "References": "<20180612232939.24793-1-stephen@networkplumber.org>",
        "Subject": "[dpdk-dev] [PATCH v11 2/4] bus/vmbus: add hyper-v virtual bus\n\tsupport",
        "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 patch adds support for an additional bus type Virtual Machine BUS\n(VMBUS) on Microsoft Hyper-V in Windows 10, Windows Server 2016\nand Azure. Most of this code was extracted from FreeBSD and some of\nthis is from earlier code donated by Brocade.\n\nOnly Linux is supported at present, but the code is split\nto allow future FreeBSD and Windows support.\n\nThe bus support relies on the uio_hv_generic driver from Linux\nkernel 4.16. Multiple queue support requires additional sysfs\ninterfaces which is in kernel 5.0 (a.k.a 4.17).\n\nSigned-off-by: Stephen Hemminger <sthemmin@microsoft.com>\n---\n MAINTAINERS                                 |   3 +\n config/common_base                          |   5 +\n drivers/bus/Makefile                        |   1 +\n drivers/bus/meson.build                     |   2 +-\n drivers/bus/vmbus/Makefile                  |  36 ++\n drivers/bus/vmbus/linux/Makefile            |   3 +\n drivers/bus/vmbus/linux/vmbus_bus.c         | 355 +++++++++++++++++\n drivers/bus/vmbus/linux/vmbus_uio.c         | 390 +++++++++++++++++++\n drivers/bus/vmbus/meson.build               |  18 +\n drivers/bus/vmbus/private.h                 | 132 +++++++\n drivers/bus/vmbus/rte_bus_vmbus.h           | 396 +++++++++++++++++++\n drivers/bus/vmbus/rte_bus_vmbus_version.map |  28 ++\n drivers/bus/vmbus/rte_vmbus_reg.h           | 344 +++++++++++++++++\n drivers/bus/vmbus/vmbus_bufring.c           | 241 ++++++++++++\n drivers/bus/vmbus/vmbus_channel.c           | 406 ++++++++++++++++++++\n drivers/bus/vmbus/vmbus_common.c            | 286 ++++++++++++++\n drivers/bus/vmbus/vmbus_common_uio.c        | 232 +++++++++++\n mk/rte.app.mk                               |   1 +\n 18 files changed, 2878 insertions(+), 1 deletion(-)\n create mode 100644 drivers/bus/vmbus/Makefile\n create mode 100644 drivers/bus/vmbus/linux/Makefile\n create mode 100644 drivers/bus/vmbus/linux/vmbus_bus.c\n create mode 100644 drivers/bus/vmbus/linux/vmbus_uio.c\n create mode 100644 drivers/bus/vmbus/meson.build\n create mode 100644 drivers/bus/vmbus/private.h\n create mode 100644 drivers/bus/vmbus/rte_bus_vmbus.h\n create mode 100644 drivers/bus/vmbus/rte_bus_vmbus_version.map\n create mode 100644 drivers/bus/vmbus/rte_vmbus_reg.h\n create mode 100644 drivers/bus/vmbus/vmbus_bufring.c\n create mode 100644 drivers/bus/vmbus/vmbus_channel.c\n create mode 100644 drivers/bus/vmbus/vmbus_common.c\n create mode 100644 drivers/bus/vmbus/vmbus_common_uio.c",
    "diff": "diff --git a/MAINTAINERS b/MAINTAINERS\nindex 4667fa7fbcb1..e9e0f9c188fe 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -421,6 +421,9 @@ F: drivers/bus/pci/\n VDEV bus driver\n F: drivers/bus/vdev/\n \n+VMBUS bus driver\n+M: Stephen Hemminger <sthemmin@microsoft.com>\n+F: drivers/bus/vmbus/\n \n Networking Drivers\n ------------------\ndiff --git a/config/common_base b/config/common_base\nindex 6b0d1cbbb76c..e4e30ba50437 100644\n--- a/config/common_base\n+++ b/config/common_base\n@@ -402,6 +402,11 @@ CONFIG_RTE_LIBRTE_PMD_FAILSAFE=y\n CONFIG_RTE_LIBRTE_MVPP2_PMD=n\n \n #\n+# Compile support for VMBus library\n+#\n+CONFIG_RTE_LIBRTE_VMBUS=n\n+\n+\n # Compile virtual device driver for NetVSC on Hyper-V/Azure\n #\n CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD=n\ndiff --git a/drivers/bus/Makefile b/drivers/bus/Makefile\nindex ef7f24751974..cea3b55e60c9 100644\n--- a/drivers/bus/Makefile\n+++ b/drivers/bus/Makefile\n@@ -10,5 +10,6 @@ endif\n DIRS-$(CONFIG_RTE_LIBRTE_IFPGA_BUS) += ifpga\n DIRS-$(CONFIG_RTE_LIBRTE_PCI_BUS) += pci\n DIRS-$(CONFIG_RTE_LIBRTE_VDEV_BUS) += vdev\n+DIRS-$(CONFIG_RTE_LIBRTE_VMBUS) += vmbus\n \n include $(RTE_SDK)/mk/rte.subdir.mk\ndiff --git a/drivers/bus/meson.build b/drivers/bus/meson.build\nindex 52c755dcfd8e..80de2d91d52d 100644\n--- a/drivers/bus/meson.build\n+++ b/drivers/bus/meson.build\n@@ -1,7 +1,7 @@\n # SPDX-License-Identifier: BSD-3-Clause\n # Copyright(c) 2017 Intel Corporation\n \n-drivers = ['dpaa', 'fslmc', 'ifpga', 'pci', 'vdev']\n+drivers = ['dpaa', 'fslmc', 'ifpga', 'pci', 'vdev', 'vmbus']\n std_deps = ['eal']\n config_flag_fmt = 'RTE_LIBRTE_@0@_BUS'\n driver_name_fmt = 'rte_bus_@0@'\ndiff --git a/drivers/bus/vmbus/Makefile b/drivers/bus/vmbus/Makefile\nnew file mode 100644\nindex 000000000000..bd18a71154af\n--- /dev/null\n+++ b/drivers/bus/vmbus/Makefile\n@@ -0,0 +1,36 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+\n+include $(RTE_SDK)/mk/rte.vars.mk\n+\n+LIB = librte_bus_vmbus.a\n+LIBABIVER := 1\n+EXPORT_MAP := rte_bus_vmbus_version.map\n+\n+CFLAGS += -I$(SRCDIR)\n+CFLAGS += -O3 $(WERROR_FLAGS)\n+CFLAGS += -DALLOW_EXPERIMENTAL_API\n+\n+ifneq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),)\n+SYSTEM := linux\n+endif\n+ifneq ($(CONFIG_RTE_EXEC_ENV_BSDAPP),)\n+$(error \"VMBUS not implemented for BSD yet\")\n+endif\n+\n+CFLAGS += -I$(RTE_SDK)/drivers/bus/vmbus/$(SYSTEM)\n+CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common\n+CFLAGS += -I$(RTE_SDK)/lib/librte_eal/$(SYSTEM)app/eal\n+\n+LDLIBS += -lrte_eal -lrte_mbuf -lrte_mempool -lrte_ring\n+LDLIBS += -lrte_ethdev -luuid\n+\n+include $(RTE_SDK)/drivers/bus/vmbus/$(SYSTEM)/Makefile\n+SRCS-$(CONFIG_RTE_LIBRTE_VMBUS) := $(addprefix $(SYSTEM)/,$(SRCS))\n+SRCS-$(CONFIG_RTE_LIBRTE_VMBUS) += vmbus_common.c\n+SRCS-$(CONFIG_RTE_LIBRTE_VMBUS) += vmbus_channel.c vmbus_bufring.c\n+SRCS-$(CONFIG_RTE_LIBRTE_VMBUS) += vmbus_common_uio.c\n+\n+SYMLINK-$(CONFIG_RTE_LIBRTE_VMBUS)-include += rte_bus_vmbus.h\n+SYMLINK-$(CONFIG_RTE_LIBRTE_VMBUS)-include += rte_vmbus_reg.h\n+\n+include $(RTE_SDK)/mk/rte.lib.mk\ndiff --git a/drivers/bus/vmbus/linux/Makefile b/drivers/bus/vmbus/linux/Makefile\nnew file mode 100644\nindex 000000000000..ef0d30b2d3aa\n--- /dev/null\n+++ b/drivers/bus/vmbus/linux/Makefile\n@@ -0,0 +1,3 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+\n+SRCS += vmbus_bus.c vmbus_uio.c\ndiff --git a/drivers/bus/vmbus/linux/vmbus_bus.c b/drivers/bus/vmbus/linux/vmbus_bus.c\nnew file mode 100644\nindex 000000000000..52d6a3c05306\n--- /dev/null\n+++ b/drivers/bus/vmbus/linux/vmbus_bus.c\n@@ -0,0 +1,355 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright (c) 2018, Microsoft Corporation.\n+ * All Rights Reserved.\n+ */\n+\n+#include <string.h>\n+#include <unistd.h>\n+#include <dirent.h>\n+#include <fcntl.h>\n+#include <sys/mman.h>\n+#include <sys/stat.h>\n+\n+#include <rte_eal.h>\n+#include <rte_uuid.h>\n+#include <rte_tailq.h>\n+#include <rte_log.h>\n+#include <rte_devargs.h>\n+#include <rte_memory.h>\n+#include <rte_malloc.h>\n+#include <rte_bus_vmbus.h>\n+\n+#include \"eal_filesystem.h\"\n+#include \"private.h\"\n+\n+/** Pathname of VMBUS devices directory. */\n+#define SYSFS_VMBUS_DEVICES \"/sys/bus/vmbus/devices\"\n+\n+extern struct rte_vmbus_bus rte_vmbus_bus;\n+\n+/* Read sysfs file to get UUID */\n+static int\n+parse_sysfs_uuid(const char *filename, rte_uuid_t uu)\n+{\n+\tchar buf[BUFSIZ];\n+\tchar *cp, *in = buf;\n+\tFILE *f;\n+\n+\tf = fopen(filename, \"r\");\n+\tif (f == NULL) {\n+\t\tVMBUS_LOG(ERR, \"cannot open sysfs value %s: %s\",\n+\t\t\t  filename, strerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\tif (fgets(buf, sizeof(buf), f) == NULL) {\n+\t\tVMBUS_LOG(ERR, \"cannot read sysfs value %s\",\n+\t\t\t\tfilename);\n+\t\tfclose(f);\n+\t\treturn -1;\n+\t}\n+\tfclose(f);\n+\n+\tcp = strchr(buf, '\\n');\n+\tif (cp)\n+\t\t*cp = '\\0';\n+\n+\t/* strip { } notation */\n+\tif (buf[0] == '{') {\n+\t\tin = buf + 1;\n+\t\tcp = strchr(in, '}');\n+\t\tif (cp)\n+\t\t\t*cp = '\\0';\n+\t}\n+\n+\tif (rte_uuid_parse(in, uu) < 0) {\n+\t\tVMBUS_LOG(ERR, \"%s %s not a valid UUID\",\n+\t\t\tfilename, buf);\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int\n+get_sysfs_string(const char *filename, char *buf, size_t buflen)\n+{\n+\tchar *cp;\n+\tFILE *f;\n+\n+\tf = fopen(filename, \"r\");\n+\tif (f == NULL) {\n+\t\tVMBUS_LOG(ERR, \"cannot open sysfs value %s:%s\",\n+\t\t\t  filename, strerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\tif (fgets(buf, buflen, f) == NULL) {\n+\t\tVMBUS_LOG(ERR, \"cannot read sysfs value %s\",\n+\t\t\t\tfilename);\n+\t\tfclose(f);\n+\t\treturn -1;\n+\t}\n+\tfclose(f);\n+\n+\t/* remove trailing newline */\n+\tcp = memchr(buf, '\\n', buflen);\n+\tif (cp)\n+\t\t*cp = '\\0';\n+\n+\treturn 0;\n+}\n+\n+static int\n+vmbus_get_uio_dev(const struct rte_vmbus_device *dev,\n+\t\t  char *dstbuf, size_t buflen)\n+{\n+\tchar dirname[PATH_MAX];\n+\tunsigned int uio_num;\n+\tstruct dirent *e;\n+\tDIR *dir;\n+\n+\t/* Assume recent kernel where uio is in uio/uioX */\n+\tsnprintf(dirname, sizeof(dirname),\n+\t\t SYSFS_VMBUS_DEVICES \"/%s/uio\", dev->device.name);\n+\n+\tdir = opendir(dirname);\n+\tif (dir == NULL)\n+\t\treturn -1; /* Not a UIO device */\n+\n+\t/* take the first file starting with \"uio\" */\n+\twhile ((e = readdir(dir)) != NULL) {\n+\t\tconst int prefix_len = 3;\n+\t\tchar *endptr;\n+\n+\t\tif (strncmp(e->d_name, \"uio\", prefix_len) != 0)\n+\t\t\tcontinue;\n+\n+\t\t/* try uio%d */\n+\t\terrno = 0;\n+\t\tuio_num = strtoull(e->d_name + prefix_len, &endptr, 10);\n+\t\tif (errno == 0 && endptr != (e->d_name + prefix_len)) {\n+\t\t\tsnprintf(dstbuf, buflen, \"%s/uio%u\", dirname, uio_num);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tclosedir(dir);\n+\n+\tif (e == NULL)\n+\t\treturn -1;\n+\n+\treturn uio_num;\n+}\n+\n+/* Check map names with kernel names */\n+static const char *map_names[VMBUS_MAX_RESOURCE] = {\n+\t[HV_TXRX_RING_MAP] = \"txrx_rings\",\n+\t[HV_INT_PAGE_MAP]  = \"int_page\",\n+\t[HV_MON_PAGE_MAP]  = \"monitor_page\",\n+\t[HV_RECV_BUF_MAP]  = \"recv:\",\n+\t[HV_SEND_BUF_MAP]  = \"send:\",\n+};\n+\n+\n+/* map the resources of a vmbus device in virtual memory */\n+int\n+rte_vmbus_map_device(struct rte_vmbus_device *dev)\n+{\n+\tchar uioname[PATH_MAX], filename[PATH_MAX];\n+\tchar dirname[PATH_MAX], mapname[64];\n+\tint i;\n+\n+\tdev->uio_num = vmbus_get_uio_dev(dev, uioname, sizeof(uioname));\n+\tif (dev->uio_num < 0) {\n+\t\tVMBUS_LOG(DEBUG, \"Not managed by UIO driver, skipped\");\n+\t\treturn 1;\n+\t}\n+\n+\t/* Extract resource value */\n+\tfor (i = 0; i < VMBUS_MAX_RESOURCE; i++) {\n+\t\tstruct rte_mem_resource *res = &dev->resource[i];\n+\t\tunsigned long len, gpad = 0;\n+\t\tchar *cp;\n+\n+\t\tsnprintf(dirname, sizeof(dirname),\n+\t\t\t \"%s/maps/map%d\", uioname, i);\n+\n+\t\tsnprintf(filename, sizeof(filename),\n+\t\t\t \"%s/name\", dirname);\n+\n+\t\tif (get_sysfs_string(filename, mapname, sizeof(mapname)) < 0) {\n+\t\t\tVMBUS_LOG(ERR, \"could not read %s\", filename);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (strncmp(map_names[i], mapname, strlen(map_names[i])) != 0) {\n+\t\t\tVMBUS_LOG(ERR,\n+\t\t\t\t\"unexpected resource %s (expected %s)\",\n+\t\t\t\tmapname, map_names[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tsnprintf(filename, sizeof(filename),\n+\t\t\t \"%s/size\", dirname);\n+\t\tif (eal_parse_sysfs_value(filename, &len) < 0) {\n+\t\t\tVMBUS_LOG(ERR,\n+\t\t\t\t\"could not read %s\", filename);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tres->len = len;\n+\n+\t\t/* both send and receive buffers have gpad in name */\n+\t\tcp = memchr(mapname, ':', sizeof(mapname));\n+\t\tif (cp)\n+\t\t\tgpad = strtoul(cp+1, NULL, 0);\n+\n+\t\t/* put the GPAD value in physical address */\n+\t\tres->phys_addr = gpad;\n+\t}\n+\n+\treturn vmbus_uio_map_resource(dev);\n+}\n+\n+void\n+rte_vmbus_unmap_device(struct rte_vmbus_device *dev)\n+{\n+\tvmbus_uio_unmap_resource(dev);\n+}\n+\n+/* Scan one vmbus sysfs entry, and fill the devices list from it. */\n+static int\n+vmbus_scan_one(const char *name)\n+{\n+\tstruct rte_vmbus_device *dev, *dev2;\n+\tchar filename[PATH_MAX];\n+\tchar dirname[PATH_MAX];\n+\tunsigned long tmp;\n+\n+\tdev = calloc(1, sizeof(*dev));\n+\tif (dev == NULL)\n+\t\treturn -1;\n+\n+\tdev->device.name = strdup(name);\n+\tif (!dev->device.name)\n+\t\tgoto error;\n+\n+\t/* sysfs base directory\n+\t *   /sys/bus/vmbus/devices/7a08391f-f5a0-4ac0-9802-d13fd964f8df\n+\t * or on older kernel\n+\t *   /sys/bus/vmbus/devices/vmbus_1\n+\t */\n+\tsnprintf(dirname, sizeof(dirname), \"%s/%s\",\n+\t\t SYSFS_VMBUS_DEVICES, name);\n+\n+\t/* get device id */\n+\tsnprintf(filename, sizeof(filename), \"%s/device_id\", dirname);\n+\tif (parse_sysfs_uuid(filename, dev->device_id) < 0)\n+\t\tgoto error;\n+\n+\t/* get device class  */\n+\tsnprintf(filename, sizeof(filename), \"%s/class_id\", dirname);\n+\tif (parse_sysfs_uuid(filename, dev->class_id) < 0)\n+\t\tgoto error;\n+\n+\t/* get relid */\n+\tsnprintf(filename, sizeof(filename), \"%s/id\", dirname);\n+\tif (eal_parse_sysfs_value(filename, &tmp) < 0)\n+\t\tgoto error;\n+\tdev->relid = tmp;\n+\n+\t/* get monitor id */\n+\tsnprintf(filename, sizeof(filename), \"%s/monitor_id\", dirname);\n+\tif (eal_parse_sysfs_value(filename, &tmp) < 0)\n+\t\tgoto error;\n+\tdev->monitor_id = tmp;\n+\n+\t/* get numa node (if present) */\n+\tsnprintf(filename, sizeof(filename), \"%s/numa_node\",\n+\t\t dirname);\n+\n+\tif (access(filename, R_OK) == 0) {\n+\t\tif (eal_parse_sysfs_value(filename, &tmp) < 0)\n+\t\t\tgoto error;\n+\t\tdev->device.numa_node = tmp;\n+\t} else {\n+\t\t/* if no NUMA support, set default to 0 */\n+\t\tdev->device.numa_node = SOCKET_ID_ANY;\n+\t}\n+\n+\t/* device is valid, add in list (sorted) */\n+\tVMBUS_LOG(DEBUG, \"Adding vmbus device %s\", name);\n+\n+\tTAILQ_FOREACH(dev2, &rte_vmbus_bus.device_list, next) {\n+\t\tint ret;\n+\n+\t\tret = rte_uuid_compare(dev->device_id, dev2->device_id);\n+\t\tif (ret > 0)\n+\t\t\tcontinue;\n+\n+\t\tif (ret < 0) {\n+\t\t\tvmbus_insert_device(dev2, dev);\n+\t\t} else { /* already registered */\n+\t\t\tVMBUS_LOG(NOTICE,\n+\t\t\t\t\"%s already registered\", name);\n+\t\t\tfree(dev);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n+\tvmbus_add_device(dev);\n+\treturn 0;\n+error:\n+\tVMBUS_LOG(DEBUG, \"failed\");\n+\n+\tfree(dev);\n+\treturn -1;\n+}\n+\n+/*\n+ * Scan the content of the vmbus, and the devices in the devices list\n+ */\n+int\n+rte_vmbus_scan(void)\n+{\n+\tstruct dirent *e;\n+\tDIR *dir;\n+\n+\tdir = opendir(SYSFS_VMBUS_DEVICES);\n+\tif (dir == NULL) {\n+\t\tif (errno == ENOENT)\n+\t\t\treturn 0;\n+\n+\t\tVMBUS_LOG(ERR, \"opendir %s failed: %s\",\n+\t\t\t  SYSFS_VMBUS_DEVICES, strerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\twhile ((e = readdir(dir)) != NULL) {\n+\t\tif (e->d_name[0] == '.')\n+\t\t\tcontinue;\n+\n+\t\tif (vmbus_scan_one(e->d_name) < 0)\n+\t\t\tgoto error;\n+\t}\n+\tclosedir(dir);\n+\treturn 0;\n+\n+error:\n+\tclosedir(dir);\n+\treturn -1;\n+}\n+\n+void rte_vmbus_irq_mask(struct rte_vmbus_device *device)\n+{\n+\tvmbus_uio_irq_control(device, 1);\n+}\n+\n+void rte_vmbus_irq_unmask(struct rte_vmbus_device *device)\n+{\n+\tvmbus_uio_irq_control(device, 0);\n+}\n+\n+int rte_vmbus_irq_read(struct rte_vmbus_device *device)\n+{\n+\treturn vmbus_uio_irq_read(device);\n+}\ndiff --git a/drivers/bus/vmbus/linux/vmbus_uio.c b/drivers/bus/vmbus/linux/vmbus_uio.c\nnew file mode 100644\nindex 000000000000..b0f8ebaea6ea\n--- /dev/null\n+++ b/drivers/bus/vmbus/linux/vmbus_uio.c\n@@ -0,0 +1,390 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright (c) 2018, Microsoft Corporation.\n+ * All Rights Reserved.\n+ */\n+\n+#include <string.h>\n+#include <unistd.h>\n+#include <fcntl.h>\n+#include <dirent.h>\n+#include <inttypes.h>\n+#include <sys/stat.h>\n+#include <sys/mman.h>\n+\n+#include <rte_log.h>\n+#include <rte_bus.h>\n+#include <rte_memory.h>\n+#include <rte_eal_memconfig.h>\n+#include <rte_common.h>\n+#include <rte_malloc.h>\n+#include <rte_bus_vmbus.h>\n+\n+#include \"private.h\"\n+\n+/** Pathname of VMBUS devices directory. */\n+#define SYSFS_VMBUS_DEVICES \"/sys/bus/vmbus/devices\"\n+\n+static void *vmbus_map_addr;\n+\n+/* Control interrupts */\n+void vmbus_uio_irq_control(struct rte_vmbus_device *dev, int32_t onoff)\n+{\n+\tif (write(dev->intr_handle.fd, &onoff, sizeof(onoff)) < 0) {\n+\t\tVMBUS_LOG(ERR, \"cannot write to %d:%s\",\n+\t\t\tdev->intr_handle.fd, strerror(errno));\n+\t}\n+}\n+\n+int vmbus_uio_irq_read(struct rte_vmbus_device *dev)\n+{\n+\tint32_t count;\n+\n+\tif (read(dev->intr_handle.fd, &count, sizeof(count)) < 0) {\n+\t\tVMBUS_LOG(ERR, \"cannot read to %d:%s\",\n+\t\t\tdev->intr_handle.fd, strerror(errno));\n+\t\tcount = -errno;\n+\t}\n+\n+\treturn count;\n+}\n+\n+void\n+vmbus_uio_free_resource(struct rte_vmbus_device *dev,\n+\t\tstruct mapped_vmbus_resource *uio_res)\n+{\n+\trte_free(uio_res);\n+\n+\tif (dev->intr_handle.uio_cfg_fd >= 0) {\n+\t\tclose(dev->intr_handle.uio_cfg_fd);\n+\t\tdev->intr_handle.uio_cfg_fd = -1;\n+\t}\n+\n+\tif (dev->intr_handle.fd >= 0) {\n+\t\tclose(dev->intr_handle.fd);\n+\t\tdev->intr_handle.fd = -1;\n+\t\tdev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;\n+\t}\n+}\n+\n+int\n+vmbus_uio_alloc_resource(struct rte_vmbus_device *dev,\n+\t\t\t struct mapped_vmbus_resource **uio_res)\n+{\n+\tchar devname[PATH_MAX]; /* contains the /dev/uioX */\n+\n+\t/* save fd if in primary process */\n+\tsnprintf(devname, sizeof(devname), \"/dev/uio%u\", dev->uio_num);\n+\tdev->intr_handle.fd = open(devname, O_RDWR);\n+\tif (dev->intr_handle.fd < 0) {\n+\t\tVMBUS_LOG(ERR, \"Cannot open %s: %s\",\n+\t\t\tdevname, strerror(errno));\n+\t\tgoto error;\n+\t}\n+\tdev->intr_handle.type = RTE_INTR_HANDLE_UIO_INTX;\n+\n+\t/* allocate the mapping details for secondary processes*/\n+\t*uio_res = rte_zmalloc(\"UIO_RES\", sizeof(**uio_res), 0);\n+\tif (*uio_res == NULL) {\n+\t\tVMBUS_LOG(ERR, \"cannot store uio mmap details\");\n+\t\tgoto error;\n+\t}\n+\n+\tstrncpy((*uio_res)->path, devname, PATH_MAX);\n+\trte_uuid_copy((*uio_res)->id, dev->device_id);\n+\n+\treturn 0;\n+\n+error:\n+\tvmbus_uio_free_resource(dev, *uio_res);\n+\treturn -1;\n+}\n+\n+static int\n+find_max_end_va(const struct rte_memseg_list *msl, void *arg)\n+{\n+\tsize_t sz = msl->memseg_arr.len * msl->page_sz;\n+\tvoid *end_va = RTE_PTR_ADD(msl->base_va, sz);\n+\tvoid **max_va = arg;\n+\n+\tif (*max_va < end_va)\n+\t\t*max_va = end_va;\n+\treturn 0;\n+}\n+\n+/*\n+ * TODO: this should be part of memseg api.\n+ *       code is duplicated from PCI.\n+ */\n+static void *\n+vmbus_find_max_end_va(void)\n+{\n+\tvoid *va = NULL;\n+\n+\trte_memseg_list_walk(find_max_end_va, &va);\n+\treturn va;\n+}\n+\n+int\n+vmbus_uio_map_resource_by_index(struct rte_vmbus_device *dev, int idx,\n+\t\t\t\tstruct mapped_vmbus_resource *uio_res,\n+\t\t\t\tint flags)\n+{\n+\tsize_t size = dev->resource[idx].len;\n+\tstruct vmbus_map *maps = uio_res->maps;\n+\tvoid *mapaddr;\n+\toff_t offset;\n+\tint fd;\n+\n+\t/* devname for mmap  */\n+\tfd = open(uio_res->path, O_RDWR);\n+\tif (fd < 0) {\n+\t\tVMBUS_LOG(ERR, \"Cannot open %s: %s\",\n+\t\t\t  uio_res->path, strerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\t/* try mapping somewhere close to the end of hugepages */\n+\tif (vmbus_map_addr == NULL)\n+\t\tvmbus_map_addr = vmbus_find_max_end_va();\n+\n+\t/* offset is special in uio it indicates which resource */\n+\toffset = idx * PAGE_SIZE;\n+\n+\tmapaddr = vmbus_map_resource(vmbus_map_addr, fd, offset, size, flags);\n+\tclose(fd);\n+\n+\tif (mapaddr == MAP_FAILED)\n+\t\treturn -1;\n+\n+\tdev->resource[idx].addr = mapaddr;\n+\tvmbus_map_addr = RTE_PTR_ADD(mapaddr, size);\n+\n+\t/* Record result of sucessful mapping for use by secondary */\n+\tmaps[idx].addr = mapaddr;\n+\tmaps[idx].size = size;\n+\n+\treturn 0;\n+}\n+\n+static int vmbus_uio_map_primary(struct vmbus_channel *chan,\n+\t\t\t\t void **ring_buf, uint32_t *ring_size)\n+{\n+\tstruct mapped_vmbus_resource *uio_res;\n+\n+\tuio_res = vmbus_uio_find_resource(chan->device);\n+\tif (!uio_res) {\n+\t\tVMBUS_LOG(ERR, \"can not find resources!\");\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tif (uio_res->nb_maps < VMBUS_MAX_RESOURCE) {\n+\t\tVMBUS_LOG(ERR, \"VMBUS: only %u resources found!\",\n+\t\t\t  uio_res->nb_maps);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t*ring_size = uio_res->maps[HV_TXRX_RING_MAP].size / 2;\n+\t*ring_buf  = uio_res->maps[HV_TXRX_RING_MAP].addr;\n+\treturn 0;\n+}\n+\n+static int vmbus_uio_map_subchan(const struct rte_vmbus_device *dev,\n+\t\t\t\t const struct vmbus_channel *chan,\n+\t\t\t\t void **ring_buf, uint32_t *ring_size)\n+{\n+\tchar ring_path[PATH_MAX];\n+\tsize_t file_size;\n+\tstruct stat sb;\n+\tint fd;\n+\n+\tsnprintf(ring_path, sizeof(ring_path),\n+\t\t \"%s/%s/channels/%u/ring\",\n+\t\t SYSFS_VMBUS_DEVICES, dev->device.name,\n+\t\t chan->relid);\n+\n+\tfd = open(ring_path, O_RDWR);\n+\tif (fd < 0) {\n+\t\tVMBUS_LOG(ERR, \"Cannot open %s: %s\",\n+\t\t\t  ring_path, strerror(errno));\n+\t\treturn -errno;\n+\t}\n+\n+\tif (fstat(fd, &sb) < 0) {\n+\t\tVMBUS_LOG(ERR, \"Cannot state %s: %s\",\n+\t\t\t  ring_path, strerror(errno));\n+\t\tclose(fd);\n+\t\treturn -errno;\n+\t}\n+\tfile_size = sb.st_size;\n+\n+\tif (file_size == 0 || (file_size & (PAGE_SIZE - 1))) {\n+\t\tVMBUS_LOG(ERR, \"incorrect size %s: %zu\",\n+\t\t\t  ring_path, file_size);\n+\n+\t\tclose(fd);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t*ring_size = file_size / 2;\n+\t*ring_buf = vmbus_map_resource(vmbus_map_addr, fd,\n+\t\t\t\t       0, sb.st_size, 0);\n+\tclose(fd);\n+\n+\tif (ring_buf == MAP_FAILED)\n+\t\treturn -EIO;\n+\n+\tvmbus_map_addr = RTE_PTR_ADD(ring_buf, file_size);\n+\treturn 0;\n+}\n+\n+int vmbus_uio_map_rings(struct vmbus_channel *chan)\n+{\n+\tconst struct rte_vmbus_device *dev = chan->device;\n+\tuint32_t ring_size;\n+\tvoid *ring_buf;\n+\tint ret;\n+\n+\t/* Primary channel */\n+\tif (chan->subchannel_id == 0)\n+\t\tret = vmbus_uio_map_primary(chan, &ring_buf, &ring_size);\n+\telse\n+\t\tret = vmbus_uio_map_subchan(dev, chan, &ring_buf, &ring_size);\n+\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tvmbus_br_setup(&chan->txbr, ring_buf, ring_size);\n+\tvmbus_br_setup(&chan->rxbr, (char *)ring_buf + ring_size, ring_size);\n+\treturn 0;\n+}\n+\n+static int vmbus_uio_sysfs_read(const char *dir, const char *name,\n+\t\t\t\tunsigned long *val, unsigned long max_range)\n+{\n+\tchar path[PATH_MAX];\n+\tFILE *f;\n+\tint ret;\n+\n+\tsnprintf(path, sizeof(path), \"%s/%s\", dir, name);\n+\tf = fopen(path, \"r\");\n+\tif (!f) {\n+\t\tVMBUS_LOG(ERR, \"can't open %s:%s\",\n+\t\t\t  path, strerror(errno));\n+\t\treturn -errno;\n+\t}\n+\n+\tif (fscanf(f, \"%lu\", val) != 1)\n+\t\tret = -EIO;\n+\telse if (*val > max_range)\n+\t\tret = -ERANGE;\n+\telse\n+\t\tret = 0;\n+\tfclose(f);\n+\n+\treturn ret;\n+}\n+\n+static bool vmbus_uio_ring_present(const struct rte_vmbus_device *dev,\n+\t\t\t\t   uint32_t relid)\n+{\n+\tchar ring_path[PATH_MAX];\n+\n+\t/* Check if kernel has subchannel sysfs files */\n+\tsnprintf(ring_path, sizeof(ring_path),\n+\t\t \"%s/%s/channels/%u/ring\",\n+\t\t SYSFS_VMBUS_DEVICES, dev->device.name, relid);\n+\n+\treturn access(ring_path, R_OK|W_OK) == 0;\n+}\n+\n+bool vmbus_uio_subchannels_supported(const struct rte_vmbus_device *dev,\n+\t\t\t\t     const struct vmbus_channel *chan)\n+{\n+\treturn vmbus_uio_ring_present(dev, chan->relid);\n+}\n+\n+static bool vmbus_isnew_subchannel(struct vmbus_channel *primary,\n+\t\t\t\t   unsigned long id)\n+{\n+\tconst struct vmbus_channel *c;\n+\n+\tSTAILQ_FOREACH(c, &primary->subchannel_list, next) {\n+\t\tif (c->relid == id)\n+\t\t\treturn false;\n+\t}\n+\treturn true;\n+}\n+\n+int vmbus_uio_get_subchan(struct vmbus_channel *primary,\n+\t\t\t  struct vmbus_channel **subchan)\n+{\n+\tconst struct rte_vmbus_device *dev = primary->device;\n+\tchar chan_path[PATH_MAX], subchan_path[PATH_MAX];\n+\tstruct dirent *ent;\n+\tDIR *chan_dir;\n+\n+\tsnprintf(chan_path, sizeof(chan_path),\n+\t\t \"%s/%s/channels\",\n+\t\t SYSFS_VMBUS_DEVICES, dev->device.name);\n+\n+\tchan_dir = opendir(chan_path);\n+\tif (!chan_dir) {\n+\t\tVMBUS_LOG(ERR, \"cannot open %s: %s\",\n+\t\t\t  chan_path, strerror(errno));\n+\t\treturn -errno;\n+\t}\n+\n+\twhile ((ent = readdir(chan_dir))) {\n+\t\tunsigned long relid, subid, monid;\n+\t\tchar *endp;\n+\t\tint err;\n+\n+\t\tif (ent->d_name[0] == '.')\n+\t\t\tcontinue;\n+\n+\t\terrno = 0;\n+\t\trelid = strtoul(ent->d_name, &endp, 0);\n+\t\tif (*endp || errno != 0 || relid > UINT16_MAX) {\n+\t\t\tVMBUS_LOG(NOTICE, \"not a valid channel relid: %s\",\n+\t\t\t\t  ent->d_name);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tsnprintf(subchan_path, sizeof(subchan_path), \"%s/%lu\",\n+\t\t\t chan_path, relid);\n+\t\terr = vmbus_uio_sysfs_read(subchan_path, \"subchannel_id\",\n+\t\t\t\t\t   &subid, UINT16_MAX);\n+\t\tif (err) {\n+\t\t\tVMBUS_LOG(NOTICE, \"invalid subchannel id %lu\",\n+\t\t\t\t  subid);\n+\t\t\treturn err;\n+\t\t}\n+\n+\t\tif (subid == 0)\n+\t\t\tcontinue;\t/* skip primary channel */\n+\n+\t\tif (!vmbus_isnew_subchannel(primary, relid))\n+\t\t\tcontinue;\n+\n+\t\tif (!vmbus_uio_ring_present(dev, relid))\n+\t\t\tcontinue;\t/* Ring may not be ready yet */\n+\n+\t\terr = vmbus_uio_sysfs_read(subchan_path, \"monitor_id\",\n+\t\t\t\t\t   &monid, UINT8_MAX);\n+\t\tif (err) {\n+\t\t\tVMBUS_LOG(NOTICE, \"invalid monitor id %lu\",\n+\t\t\t\t  monid);\n+\t\t\treturn err;\n+\t\t}\n+\n+\t\terr = vmbus_chan_create(dev, relid, subid, monid, subchan);\n+\t\tif (err) {\n+\t\t\tVMBUS_LOG(NOTICE, \"subchannel setup failed\");\n+\t\t\treturn err;\n+\t\t}\n+\t\tbreak;\n+\t}\n+\tclosedir(chan_dir);\n+\n+\treturn (ent == NULL) ? -ENOENT : 0;\n+}\ndiff --git a/drivers/bus/vmbus/meson.build b/drivers/bus/vmbus/meson.build\nnew file mode 100644\nindex 000000000000..18daabecc62d\n--- /dev/null\n+++ b/drivers/bus/vmbus/meson.build\n@@ -0,0 +1,18 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+\n+allow_experimental_apis = true\n+\n+install_headers('rte_bus_vmbus.h','rte_vmbus_reg.h')\n+\n+sources = files('vmbus_common.c',\n+\t\t'vmbus_channel.c',\n+\t\t'vmbus_bufring.c',\n+\t\t'vmbus_common_uio.c')\n+\n+if host_machine.system() == 'linux'\n+\tsources += files('linux/vmbus_bus.c',\n+\t\t\t'linux/vmbus_uio.c')\n+\tincludes += include_directories('linux')\n+else\n+\tbuild = false\n+endif\ndiff --git a/drivers/bus/vmbus/private.h b/drivers/bus/vmbus/private.h\nnew file mode 100644\nindex 000000000000..9964fc42a7b5\n--- /dev/null\n+++ b/drivers/bus/vmbus/private.h\n@@ -0,0 +1,132 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright (c) 2018, Microsoft Corporation.\n+ * All Rights Reserved.\n+ */\n+\n+#ifndef _VMBUS_PRIVATE_H_\n+#define _VMBUS_PRIVATE_H_\n+\n+#include <stdbool.h>\n+#include <sys/uio.h>\n+#include <rte_log.h>\n+#include <rte_vmbus_reg.h>\n+\n+#ifndef PAGE_SIZE\n+#define PAGE_SIZE\t4096\n+#endif\n+\n+extern int vmbus_logtype_bus;\n+#define VMBUS_LOG(level, fmt, args...) \\\n+\trte_log(RTE_LOG_ ## level, vmbus_logtype_bus, \"%s(): \" fmt \"\\n\", \\\n+\t\t__func__, ##args)\n+\n+struct vmbus_br {\n+\tstruct vmbus_bufring *vbr;\n+\tuint32_t\tdsize;\n+\tuint32_t\twindex; /* next available location */\n+};\n+\n+#define UIO_NAME_MAX 64\n+\n+struct vmbus_map {\n+\tvoid *addr;\t/* user mmap of resource */\n+\tuint64_t size;\t/* length */\n+};\n+\n+/*\n+ * For multi-process we need to reproduce all vmbus mappings in secondary\n+ * processes, so save them in a tailq.\n+ */\n+struct mapped_vmbus_resource {\n+\tTAILQ_ENTRY(mapped_vmbus_resource) next;\n+\n+\trte_uuid_t id;\n+\tint nb_maps;\n+\tstruct vmbus_map maps[VMBUS_MAX_RESOURCE];\n+\tchar path[PATH_MAX];\n+};\n+\n+TAILQ_HEAD(mapped_vmbus_res_list, mapped_vmbus_resource);\n+\n+#define HV_MON_TRIG_LEN\t32\n+#define HV_MON_TRIG_MAX\t4\n+\n+struct vmbus_channel {\n+\tSTAILQ_HEAD(, vmbus_channel) subchannel_list;\n+\tSTAILQ_ENTRY(vmbus_channel) next;\n+\tconst struct rte_vmbus_device *device;\n+\n+\tstruct vmbus_br rxbr;\n+\tstruct vmbus_br txbr;\n+\n+\tuint16_t relid;\n+\tuint16_t subchannel_id;\n+\tuint8_t monitor_id;\n+};\n+\n+#define VMBUS_MAX_CHANNELS\t64\n+\n+int vmbus_chan_create(const struct rte_vmbus_device *device,\n+\t\t      uint16_t relid, uint16_t subid, uint8_t monitor_id,\n+\t\t      struct vmbus_channel **new_chan);\n+\n+void vmbus_add_device(struct rte_vmbus_device *vmbus_dev);\n+void vmbus_insert_device(struct rte_vmbus_device *exist_vmbus_dev,\n+\t\t\t struct rte_vmbus_device *new_vmbus_dev);\n+void vmbus_remove_device(struct rte_vmbus_device *vmbus_device);\n+\n+void vmbus_uio_irq_control(struct rte_vmbus_device *dev, int32_t onoff);\n+int vmbus_uio_irq_read(struct rte_vmbus_device *dev);\n+\n+int vmbus_uio_map_resource(struct rte_vmbus_device *dev);\n+void vmbus_uio_unmap_resource(struct rte_vmbus_device *dev);\n+\n+int vmbus_uio_alloc_resource(struct rte_vmbus_device *dev,\n+\t\tstruct mapped_vmbus_resource **uio_res);\n+void vmbus_uio_free_resource(struct rte_vmbus_device *dev,\n+\t\tstruct mapped_vmbus_resource *uio_res);\n+\n+struct mapped_vmbus_resource *\n+vmbus_uio_find_resource(const struct rte_vmbus_device *dev);\n+int vmbus_uio_map_resource_by_index(struct rte_vmbus_device *dev, int res_idx,\n+\t\t\t\t    struct mapped_vmbus_resource *uio_res,\n+\t\t\t\t    int flags);\n+\n+void *vmbus_map_resource(void *requested_addr, int fd, off_t offset,\n+\t\tsize_t size, int additional_flags);\n+void vmbus_unmap_resource(void *requested_addr, size_t size);\n+\n+bool vmbus_uio_subchannels_supported(const struct rte_vmbus_device *dev,\n+\t\t\t\t     const struct vmbus_channel *chan);\n+int vmbus_uio_get_subchan(struct vmbus_channel *primary,\n+\t\t\t  struct vmbus_channel **subchan);\n+int vmbus_uio_map_rings(struct vmbus_channel *chan);\n+\n+void vmbus_br_setup(struct vmbus_br *br, void *buf, unsigned int blen);\n+\n+/* Amount of space available for write */\n+static inline uint32_t\n+vmbus_br_availwrite(const struct vmbus_br *br, uint32_t windex)\n+{\n+\tuint32_t rindex = br->vbr->rindex;\n+\n+\tif (windex >= rindex)\n+\t\treturn br->dsize - (windex - rindex);\n+\telse\n+\t\treturn rindex - windex;\n+}\n+\n+static inline uint32_t\n+vmbus_br_availread(const struct vmbus_br *br)\n+{\n+\treturn br->dsize - vmbus_br_availwrite(br, br->vbr->windex);\n+}\n+\n+int vmbus_txbr_write(struct vmbus_br *tbr, const struct iovec iov[], int iovlen,\n+\t\t     bool *need_sig);\n+\n+int vmbus_rxbr_peek(const struct vmbus_br *rbr, void *data, size_t dlen);\n+\n+int vmbus_rxbr_read(struct vmbus_br *rbr, void *data, size_t dlen, size_t hlen);\n+\n+#endif /* _VMBUS_PRIVATE_H_ */\ndiff --git a/drivers/bus/vmbus/rte_bus_vmbus.h b/drivers/bus/vmbus/rte_bus_vmbus.h\nnew file mode 100644\nindex 000000000000..0100f80ff9a0\n--- /dev/null\n+++ b/drivers/bus/vmbus/rte_bus_vmbus.h\n@@ -0,0 +1,396 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright (c) 2018, Microsoft Corporation.\n+ * All Rights Reserved.\n+ */\n+\n+#ifndef _VMBUS_H_\n+#define _VMBUS_H_\n+\n+/**\n+ * @file\n+ *\n+ * VMBUS Interface\n+ */\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <limits.h>\n+#include <stdbool.h>\n+#include <errno.h>\n+#include <sys/queue.h>\n+#include <stdint.h>\n+#include <inttypes.h>\n+\n+#include <rte_compat.h>\n+#include <rte_uuid.h>\n+#include <rte_debug.h>\n+#include <rte_interrupts.h>\n+#include <rte_dev.h>\n+#include <rte_vmbus_reg.h>\n+\n+/* Forward declarations */\n+struct rte_vmbus_device;\n+struct rte_vmbus_driver;\n+struct rte_vmbus_bus;\n+struct vmbus_channel;\n+struct vmbus_mon_page;\n+\n+TAILQ_HEAD(rte_vmbus_device_list, rte_vmbus_device);\n+TAILQ_HEAD(rte_vmbus_driver_list, rte_vmbus_driver);\n+\n+/* VMBus iterators */\n+#define FOREACH_DEVICE_ON_VMBUS(p)\t\\\n+\tTAILQ_FOREACH(p, &(rte_vmbus_bus.device_list), next)\n+\n+#define FOREACH_DRIVER_ON_VMBUS(p)\t\\\n+\tTAILQ_FOREACH(p, &(rte_vmbus_bus.driver_list), next)\n+\n+/** Maximum number of VMBUS resources. */\n+enum hv_uio_map {\n+\tHV_TXRX_RING_MAP = 0,\n+\tHV_INT_PAGE_MAP,\n+\tHV_MON_PAGE_MAP,\n+\tHV_RECV_BUF_MAP,\n+\tHV_SEND_BUF_MAP\n+};\n+#define VMBUS_MAX_RESOURCE 5\n+\n+/**\n+ * A structure describing a VMBUS device.\n+ */\n+struct rte_vmbus_device {\n+\tTAILQ_ENTRY(rte_vmbus_device) next;    /**< Next probed VMBUS device */\n+\tconst struct rte_vmbus_driver *driver; /**< Associated driver */\n+\tstruct rte_device device;              /**< Inherit core device */\n+\trte_uuid_t device_id;\t\t       /**< VMBUS device id */\n+\trte_uuid_t class_id;\t\t       /**< VMBUS device type */\n+\tuint32_t relid;\t\t\t       /**< id for primary */\n+\tuint8_t monitor_id;\t\t       /**< monitor page */\n+\tint uio_num;\t\t\t       /**< UIO device number */\n+\tuint32_t *int_page;\t\t       /**< VMBUS interrupt page */\n+\tstruct vmbus_channel *primary;\t       /**< VMBUS primary channel */\n+\tstruct vmbus_mon_page *monitor_page;   /**< VMBUS monitor page */\n+\n+\tstruct rte_intr_handle intr_handle;    /**< Interrupt handle */\n+\tstruct rte_mem_resource resource[VMBUS_MAX_RESOURCE];\n+};\n+\n+/**\n+ * Initialization function for the driver called during VMBUS probing.\n+ */\n+typedef int (vmbus_probe_t)(struct rte_vmbus_driver *,\n+\t\t\t    struct rte_vmbus_device *);\n+\n+/**\n+ * Initialization function for the driver called during hot plugging.\n+ */\n+typedef int (vmbus_remove_t)(struct rte_vmbus_device *);\n+\n+/**\n+ * A structure describing a VMBUS driver.\n+ */\n+struct rte_vmbus_driver {\n+\tTAILQ_ENTRY(rte_vmbus_driver) next; /**< Next in list. */\n+\tstruct rte_driver driver;\n+\tstruct rte_vmbus_bus *bus;          /**< VM bus reference. */\n+\tvmbus_probe_t *probe;               /**< Device Probe function. */\n+\tvmbus_remove_t *remove;             /**< Device Remove function. */\n+\n+\tconst rte_uuid_t *id_table;\t    /**< ID table. */\n+};\n+\n+\n+/**\n+ * Structure describing the VM bus\n+ */\n+struct rte_vmbus_bus {\n+\tstruct rte_bus bus;               /**< Inherit the generic class */\n+\tstruct rte_vmbus_device_list device_list;  /**< List of devices */\n+\tstruct rte_vmbus_driver_list driver_list;  /**< List of drivers */\n+};\n+\n+/**\n+ * Scan the content of the VMBUS bus, and the devices in the devices\n+ * list\n+ *\n+ * @return\n+ *  0 on success, negative on error\n+ */\n+int rte_vmbus_scan(void);\n+\n+/**\n+ * Probe the VMBUS bus\n+ *\n+ * @return\n+ *   - 0 on success.\n+ *   - !0 on error.\n+ */\n+int rte_vmbus_probe(void);\n+\n+/**\n+ * Map the VMBUS device resources in user space virtual memory address\n+ *\n+ * @param dev\n+ *   A pointer to a rte_vmbus_device structure describing the device\n+ *   to use\n+ *\n+ * @return\n+ *   0 on success, negative on error and positive if no driver\n+ *   is found for the device.\n+ */\n+int rte_vmbus_map_device(struct rte_vmbus_device *dev);\n+\n+/**\n+ * Unmap this device\n+ *\n+ * @param dev\n+ *   A pointer to a rte_vmbus_device structure describing the device\n+ *   to use\n+ */\n+void rte_vmbus_unmap_device(struct rte_vmbus_device *dev);\n+\n+/**\n+ * Get connection to primary VMBUS channel\n+ *\n+ * @param device\n+ *   A pointer to a rte_vmbus_device structure describing the device\n+ * @param chan\n+ *   A pointer to a VMBUS channel pointer that will be filled.\n+ * @return\n+ *   - 0 Success; channel opened.\n+ *   - -ENOMEM: Not enough memory available.\n+ *   - -EINVAL: Regions could not be mapped.\n+ */\n+int rte_vmbus_chan_open(struct rte_vmbus_device *device,\n+\t\t\tstruct vmbus_channel **chan);\n+\n+/**\n+ * Free connection to VMBUS channel\n+ *\n+ * @param chan\n+ *    VMBUS channel\n+ */\n+void rte_vmbus_chan_close(struct vmbus_channel *chan);\n+\n+/**\n+ * Gets the maximum number of channels supported on device\n+ *\n+ * @param device\n+ *   A pointer to a rte_vmbus_device structure describing the device\n+ * @return\n+ *   Number of channels available.\n+ */\n+int rte_vmbus_max_channels(const struct rte_vmbus_device *device);\n+\n+/**\n+ * Get a connection to new secondary vmbus channel\n+ *\n+ * @param primary\n+ *   A pointer to primary VMBUS channel\n+ * @param chan\n+ *   A pointer to a secondary VMBUS channel pointer that will be filled.\n+ * @return\n+ *   - 0 Success; channel opened.\n+ *   - -ENOMEM: Not enough memory available.\n+ *   - -EINVAL: Regions could not be mapped.\n+ */\n+int rte_vmbus_subchan_open(struct vmbus_channel *primary,\n+\t\t\t   struct vmbus_channel **new_chan);\n+\n+/**\n+ * Disable IRQ for device\n+ *\n+ * @param device\n+ *    VMBUS device\n+ */\n+void rte_vmbus_irq_mask(struct rte_vmbus_device *device);\n+\n+/**\n+ * Enable IRQ for device\n+ *\n+ * @param device\n+ *    VMBUS device\n+ */\n+void rte_vmbus_irq_unmask(struct rte_vmbus_device *device);\n+\n+/**\n+ * Read (and wait) for IRQ\n+ *\n+ * @param device\n+ *    VMBUS device\n+ */\n+int rte_vmbus_irq_read(struct rte_vmbus_device *device);\n+\n+/**\n+ * Test if channel is empty\n+ *\n+ * @param channel\n+ *\tPointer to vmbus_channel structure.\n+ * @return\n+ *\tReturn true if no data present in incoming ring.\n+ */\n+bool rte_vmbus_chan_rx_empty(const struct vmbus_channel *channel);\n+\n+/**\n+ * Send the specified buffer on the given channel\n+ *\n+ * @param channel\n+ *\tPointer to vmbus_channel structure.\n+ * @param type\n+ *\tType of packet that is being send e.g. negotiate, time\n+ *\tpacket etc.\n+ * @param data\n+ *\tPointer to the buffer to send\n+ * @param dlen\n+ *\tNumber of bytes of data to send\n+ * @param xact\n+ *\tIdentifier of the request\n+ * @param flags\n+ *\tMessage type inband, rxbuf, gpa\n+ * @param need_sig\n+ *\tIs host signal tx is required (optional)\n+ *\n+ * Sends data in buffer directly to hyper-v via the vmbus\n+ */\n+int rte_vmbus_chan_send(struct vmbus_channel *channel, uint16_t type,\n+\t\t\tvoid *data, uint32_t dlen,\n+\t\t\tuint64_t xact, uint32_t flags, bool *need_sig);\n+\n+/**\n+ * Explicitly signal host that data is available\n+ *\n+ * @param\n+ *\tPointer to vmbus_channel structure.\n+ *\n+ * Used when batching multiple sends and only signaling host\n+ * after the last send.\n+ */\n+void rte_vmbus_chan_signal_tx(const struct vmbus_channel *channel);\n+\n+/* Structure for scatter/gather I/O */\n+struct iova_list {\n+\trte_iova_t\taddr;\n+\tuint32_t\tlen;\n+};\n+#define MAX_PAGE_BUFFER_COUNT\t\t32\n+\n+/**\n+ * Send a scattered buffer on the given channel\n+ *\n+ * @param channel\n+ *\tPointer to vmbus_channel structure.\n+ * @param type\n+ *\tType of packet that is being send e.g. negotiate, time\n+ *\tpacket etc.\n+ * @param gpa\n+ *\tArray of buffers to send\n+ * @param gpacnt\n+ *\tNumber of elements in iov\n+ * @param data\n+ *\tPointer to the buffer additional data to send\n+ * @param dlen\n+ *\t Maximum size of what the the buffer will hold\n+ * @param xact\n+ *\tIdentifier of the request\n+ * @param flags\n+ *\tMessage type inband, rxbuf, gpa\n+ * @param need_sig\n+ *\tIs host signal tx is required (optional)\n+ *\n+ * Sends data in buffer directly to hyper-v via the vmbus\n+ */\n+int rte_vmbus_chan_send_sglist(struct vmbus_channel *channel,\n+\t\t\t       struct vmbus_gpa gpa[], uint32_t gpacnt,\n+\t\t\t       void *data, uint32_t dlen,\n+\t\t\t       uint64_t xact, bool *need_sig);\n+/**\n+ * Receive response to request on the given channel\n+ * skips the channel header.\n+ *\n+ * @param channel\n+ *\tPointer to vmbus_channel structure.\n+ * @param data\n+ *\tPointer to the buffer you want to receive the data into.\n+ * @param len\n+ *\tPointer to size of receive buffer (in/out)\n+ * @param\n+ *\tPointer to received transaction_id\n+ * @return\n+ *   On success, returns 0\n+ *   On failure, returns negative errno.\n+ */\n+int rte_vmbus_chan_recv(struct vmbus_channel *chan,\n+\t\t\tvoid *data, uint32_t *len,\n+\t\t\tuint64_t *request_id);\n+\n+/**\n+ * Receive response to request on the given channel\n+ * includes the channel header.\n+ *\n+ * @param channel\n+ *\tPointer to vmbus_channel structure.\n+ * @param data\n+ *\tPointer to the buffer you want to receive the data into.\n+ * @param len\n+ *\tPointer to size of receive buffer (in/out)\n+ * @return\n+ *   On success, returns 0\n+ *   On failure, returns negative errno.\n+ */\n+int rte_vmbus_chan_recv_raw(struct vmbus_channel *chan,\n+\t\t\t    void *data, uint32_t *len);\n+\n+/**\n+ * Determine sub channel index of the given channel\n+ *\n+ * @param channel\n+ *\tPointer to vmbus_channel structure.\n+ * @return\n+ *   Sub channel index (0 for primary)\n+ */\n+uint16_t rte_vmbus_sub_channel_index(const struct vmbus_channel *chan);\n+\n+/**\n+ * Register a VMBUS driver.\n+ *\n+ * @param driver\n+ *   A pointer to a rte_vmbus_driver structure describing the driver\n+ *   to be registered.\n+ */\n+void rte_vmbus_register(struct rte_vmbus_driver *driver);\n+\n+/**\n+ * For debug dump contents of ring buffer.\n+ *\n+ * @param channel\n+ *\tPointer to vmbus_channel structure.\n+ */\n+void rte_vmbus_chan_dump(FILE *f, const struct vmbus_channel *chan);\n+\n+/**\n+ * Unregister a VMBUS driver.\n+ *\n+ * @param driver\n+ *   A pointer to a rte_vmbus_driver structure describing the driver\n+ *   to be unregistered.\n+ */\n+void rte_vmbus_unregister(struct rte_vmbus_driver *driver);\n+\n+/** Helper for VMBUS device registration from driver instance */\n+#define RTE_PMD_REGISTER_VMBUS(nm, vmbus_drv)\t\t\\\n+\tRTE_INIT(vmbusinitfn_ ##nm);\t\t\t\\\n+\tstatic void vmbusinitfn_ ##nm(void)\t\t\\\n+\t{\t\t\t\t\t\t\\\n+\t\t(vmbus_drv).driver.name = RTE_STR(nm);\t\\\n+\t\trte_vmbus_register(&vmbus_drv);\t\t\\\n+\t}\t\t\t\t\t\t\\\n+\tRTE_PMD_EXPORT_NAME(nm, __COUNTER__)\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif /* _VMBUS_H_ */\ndiff --git a/drivers/bus/vmbus/rte_bus_vmbus_version.map b/drivers/bus/vmbus/rte_bus_vmbus_version.map\nnew file mode 100644\nindex 000000000000..620ced9bb348\n--- /dev/null\n+++ b/drivers/bus/vmbus/rte_bus_vmbus_version.map\n@@ -0,0 +1,28 @@\n+/* SPDX-License-Identifier: BSD-3-Clause */\n+DPDK_18.08 {\n+\tglobal:\n+\n+\trte_vmbus_chan_close;\n+\trte_vmbus_chan_open;\n+\trte_vmbus_chan_recv;\n+\trte_vmbus_chan_recv_raw;\n+\trte_vmbus_chan_rx_empty;\n+\trte_vmbus_chan_send;\n+\trte_vmbus_chan_send_sglist;\n+\trte_vmbus_chan_signal_tx;\n+\trte_vmbus_irq_mask;\n+\trte_vmbus_irq_read;\n+\trte_vmbus_irq_unmask;\n+\trte_vmbus_map_device;\n+\trte_vmbus_max_channels;\n+\trte_vmbus_probe;\n+\trte_vmbus_probe_one;\n+\trte_vmbus_register;\n+\trte_vmbus_scan;\n+\trte_vmbus_sub_channel_index;\n+\trte_vmbus_subchan_open;\n+\trte_vmbus_unmap_device;\n+\trte_vmbus_unregister;\n+\n+\tlocal: *;\n+};\ndiff --git a/drivers/bus/vmbus/rte_vmbus_reg.h b/drivers/bus/vmbus/rte_vmbus_reg.h\nnew file mode 100644\nindex 000000000000..f5a0693dcbe7\n--- /dev/null\n+++ b/drivers/bus/vmbus/rte_vmbus_reg.h\n@@ -0,0 +1,344 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright (c) 2018, Microsoft Corporation.\n+ * All Rights Reserved.\n+ */\n+\n+#ifndef _VMBUS_REG_H_\n+#define _VMBUS_REG_H_\n+\n+/*\n+ * Hyper-V SynIC message format.\n+ */\n+#define VMBUS_MSG_DSIZE_MAX\t\t240\n+#define VMBUS_MSG_SIZE\t\t\t256\n+\n+struct vmbus_message {\n+\tuint32_t\ttype;\t/* HYPERV_MSGTYPE_ */\n+\tuint8_t\t\tdsize;\t/* data size */\n+\tuint8_t\t\tflags;\t/* VMBUS_MSGFLAG_ */\n+\tuint16_t\trsvd;\n+\tuint64_t\tid;\n+\tuint8_t\t\tdata[VMBUS_MSG_DSIZE_MAX];\n+} __rte_packed;\n+\n+#define VMBUS_MSGFLAG_PENDING\t\t0x01\n+\n+/*\n+ * Hyper-V Monitor Notification Facility\n+ */\n+\n+struct vmbus_mon_trig {\n+\tuint32_t\tpending;\n+\tuint32_t\tarmed;\n+} __rte_packed;\n+\n+#define VMBUS_MONTRIGS_MAX\t4\n+#define VMBUS_MONTRIG_LEN\t32\n+\n+/*\n+ * Hyper-V Monitor Notification Facility\n+ */\n+struct hyperv_mon_param {\n+\tuint32_t\tconnid;\n+\tuint16_t\tevtflag_ofs;\n+\tuint16_t\trsvd;\n+} __rte_packed;\n+\n+struct vmbus_mon_page {\n+\tuint32_t\tstate;\n+\tuint32_t\trsvd1;\n+\n+\tstruct vmbus_mon_trig trigs[VMBUS_MONTRIGS_MAX];\n+\tuint8_t\t\trsvd2[536];\n+\n+\tuint16_t\tlat[VMBUS_MONTRIGS_MAX][VMBUS_MONTRIG_LEN];\n+\tuint8_t\t\trsvd3[256];\n+\n+\tstruct hyperv_mon_param\n+\t\t\tparam[VMBUS_MONTRIGS_MAX][VMBUS_MONTRIG_LEN];\n+\tuint8_t\t\trsvd4[1984];\n+} __rte_packed;\n+\n+/*\n+ * Buffer ring\n+ */\n+\n+struct vmbus_bufring {\n+\tvolatile uint32_t windex;\n+\tvolatile uint32_t rindex;\n+\n+\t/*\n+\t * Interrupt mask {0,1}\n+\t *\n+\t * For TX bufring, host set this to 1, when it is processing\n+\t * the TX bufring, so that we can safely skip the TX event\n+\t * notification to host.\n+\t *\n+\t * For RX bufring, once this is set to 1 by us, host will not\n+\t * further dispatch interrupts to us, even if there are data\n+\t * pending on the RX bufring.  This effectively disables the\n+\t * interrupt of the channel to which this RX bufring is attached.\n+\t */\n+\tvolatile uint32_t imask;\n+\n+\t/*\n+\t * Win8 uses some of the reserved bits to implement\n+\t * interrupt driven flow management. On the send side\n+\t * we can request that the receiver interrupt the sender\n+\t * when the ring transitions from being full to being able\n+\t * to handle a message of size \"pending_send_sz\".\n+\t *\n+\t * Add necessary state for this enhancement.\n+\t */\n+\tvolatile uint32_t pending_send;\n+\tuint32_t reserved1[12];\n+\n+\tunion {\n+\t\tstruct {\n+\t\t\tuint32_t feat_pending_send_sz:1;\n+\t\t};\n+\t\tuint32_t value;\n+\t} feature_bits;\n+\n+\t/* Pad it to PAGE_SIZE so that data starts on page boundary */\n+\tuint8_t\treserved2[4028];\n+\n+\t/*\n+\t * Ring data starts here + RingDataStartOffset\n+\t * !!! DO NOT place any fields below this !!!\n+\t */\n+\tuint8_t data[0];\n+} __rte_packed;\n+\n+/*\n+ * Channel packets\n+ */\n+\n+/* Channel packet flags */\n+#define VMBUS_CHANPKT_TYPE_INBAND      0x0006\n+#define VMBUS_CHANPKT_TYPE_RXBUF       0x0007\n+#define VMBUS_CHANPKT_TYPE_GPA         0x0009\n+#define VMBUS_CHANPKT_TYPE_COMP        0x000b\n+\n+#define VMBUS_CHANPKT_FLAG_NONE        0\n+#define VMBUS_CHANPKT_FLAG_RC          0x0001  /* report completion */\n+\n+#define VMBUS_CHANPKT_SIZE_SHIFT\t3\n+#define VMBUS_CHANPKT_SIZE_ALIGN\t(1 << VMBUS_CHANPKT_SIZE_SHIFT)\n+#define VMBUS_CHANPKT_HLEN_MIN\t\t\\\n+\t(sizeof(struct vmbus_chanpkt_hdr) >> VMBUS_CHANPKT_SIZE_SHIFT)\n+\n+static inline uint32_t\n+vmbus_chanpkt_getlen(uint16_t pktlen)\n+{\n+\treturn (uint32_t)pktlen << VMBUS_CHANPKT_SIZE_SHIFT;\n+}\n+\n+/*\n+ * GPA stuffs.\n+ */\n+struct vmbus_gpa_range {\n+\tuint32_t       len;\n+\tuint32_t       ofs;\n+\tuint64_t       page[0];\n+} __rte_packed;\n+\n+/* This is actually vmbus_gpa_range.gpa_page[1] */\n+struct vmbus_gpa {\n+\tuint32_t\tlen;\n+\tuint32_t\tofs;\n+\tuint64_t\tpage;\n+} __rte_packed;\n+\n+struct vmbus_chanpkt_hdr {\n+\tuint16_t\ttype;\t/* VMBUS_CHANPKT_TYPE_ */\n+\tuint16_t\thlen;\t/* header len, in 8 bytes */\n+\tuint16_t\ttlen;\t/* total len, in 8 bytes */\n+\tuint16_t\tflags;\t/* VMBUS_CHANPKT_FLAG_ */\n+\tuint64_t\txactid;\n+} __rte_packed;\n+\n+static inline uint32_t\n+vmbus_chanpkt_datalen(const struct vmbus_chanpkt_hdr *pkt)\n+{\n+\treturn vmbus_chanpkt_getlen(pkt->tlen)\n+\t\t- vmbus_chanpkt_getlen(pkt->hlen);\n+}\n+\n+struct vmbus_chanpkt {\n+\tstruct vmbus_chanpkt_hdr hdr;\n+} __rte_packed;\n+\n+struct vmbus_rxbuf_desc {\n+\tuint32_t\tlen;\n+\tuint32_t\tofs;\n+} __rte_packed;\n+\n+struct vmbus_chanpkt_rxbuf {\n+\tstruct vmbus_chanpkt_hdr hdr;\n+\tuint16_t\trxbuf_id;\n+\tuint16_t\trsvd;\n+\tuint32_t\trxbuf_cnt;\n+\tstruct vmbus_rxbuf_desc rxbuf[];\n+} __rte_packed;\n+\n+struct vmbus_chanpkt_sglist {\n+\tstruct vmbus_chanpkt_hdr hdr;\n+\tuint32_t\trsvd;\n+\tuint32_t\tgpa_cnt;\n+\tstruct vmbus_gpa gpa[];\n+} __rte_packed;\n+\n+/*\n+ * Channel messages\n+ * - Embedded in vmbus_message.msg_data, e.g. response and notification.\n+ * - Embedded in hypercall_postmsg_in.hc_data, e.g. request.\n+ */\n+\n+#define VMBUS_CHANMSG_TYPE_CHOFFER\t\t1\t/* NOTE */\n+#define VMBUS_CHANMSG_TYPE_CHRESCIND\t\t2\t/* NOTE */\n+#define VMBUS_CHANMSG_TYPE_CHREQUEST\t\t3\t/* REQ */\n+#define VMBUS_CHANMSG_TYPE_CHOFFER_DONE\t\t4\t/* NOTE */\n+#define VMBUS_CHANMSG_TYPE_CHOPEN\t\t5\t/* REQ */\n+#define VMBUS_CHANMSG_TYPE_CHOPEN_RESP\t\t6\t/* RESP */\n+#define VMBUS_CHANMSG_TYPE_CHCLOSE\t\t7\t/* REQ */\n+#define VMBUS_CHANMSG_TYPE_GPADL_CONN\t\t8\t/* REQ */\n+#define VMBUS_CHANMSG_TYPE_GPADL_SUBCONN\t9\t/* REQ */\n+#define VMBUS_CHANMSG_TYPE_GPADL_CONNRESP\t10\t/* RESP */\n+#define VMBUS_CHANMSG_TYPE_GPADL_DISCONN\t11\t/* REQ */\n+#define VMBUS_CHANMSG_TYPE_GPADL_DISCONNRESP\t12\t/* RESP */\n+#define VMBUS_CHANMSG_TYPE_CHFREE\t\t13\t/* REQ */\n+#define VMBUS_CHANMSG_TYPE_CONNECT\t\t14\t/* REQ */\n+#define VMBUS_CHANMSG_TYPE_CONNECT_RESP\t\t15\t/* RESP */\n+#define VMBUS_CHANMSG_TYPE_DISCONNECT\t\t16\t/* REQ */\n+#define VMBUS_CHANMSG_TYPE_MAX\t\t\t22\n+\n+struct vmbus_chanmsg_hdr {\n+\tuint32_t\ttype;\t/* VMBUS_CHANMSG_TYPE_ */\n+\tuint32_t\trsvd;\n+} __rte_packed;\n+\n+/* VMBUS_CHANMSG_TYPE_CONNECT */\n+struct vmbus_chanmsg_connect {\n+\tstruct vmbus_chanmsg_hdr hdr;\n+\tuint32_t\tver;\n+\tuint32_t\trsvd;\n+\tuint64_t\tevtflags;\n+\tuint64_t\tmnf1;\n+\tuint64_t\tmnf2;\n+} __rte_packed;\n+\n+/* VMBUS_CHANMSG_TYPE_CONNECT_RESP */\n+struct vmbus_chanmsg_connect_resp {\n+\tstruct vmbus_chanmsg_hdr hdr;\n+\tuint8_t\t\tdone;\n+} __rte_packed;\n+\n+/* VMBUS_CHANMSG_TYPE_CHREQUEST */\n+struct vmbus_chanmsg_chrequest {\n+\tstruct vmbus_chanmsg_hdr hdr;\n+} __rte_packed;\n+\n+/* VMBUS_CHANMSG_TYPE_DISCONNECT */\n+struct vmbus_chanmsg_disconnect {\n+\tstruct vmbus_chanmsg_hdr hdr;\n+} __rte_packed;\n+\n+/* VMBUS_CHANMSG_TYPE_CHOPEN */\n+struct vmbus_chanmsg_chopen {\n+\tstruct vmbus_chanmsg_hdr hdr;\n+\tuint32_t\tchanid;\n+\tuint32_t\topenid;\n+\tuint32_t\tgpadl;\n+\tuint32_t\tvcpuid;\n+\tuint32_t\ttxbr_pgcnt;\n+#define VMBUS_CHANMSG_CHOPEN_UDATA_SIZE\t120\n+\tuint8_t\t\tudata[VMBUS_CHANMSG_CHOPEN_UDATA_SIZE];\n+} __rte_packed;\n+\n+/* VMBUS_CHANMSG_TYPE_CHOPEN_RESP */\n+struct vmbus_chanmsg_chopen_resp {\n+\tstruct vmbus_chanmsg_hdr hdr;\n+\tuint32_t\tchanid;\n+\tuint32_t\topenid;\n+\tuint32_t\tstatus;\n+} __rte_packed;\n+\n+/* VMBUS_CHANMSG_TYPE_GPADL_CONN */\n+struct vmbus_chanmsg_gpadl_conn {\n+\tstruct vmbus_chanmsg_hdr hdr;\n+\tuint32_t\tchanid;\n+\tuint32_t\tgpadl;\n+\tuint16_t\trange_len;\n+\tuint16_t\trange_cnt;\n+\tstruct vmbus_gpa_range range;\n+} __rte_packed;\n+\n+#define VMBUS_CHANMSG_GPADL_CONN_PGMAX\t\t26\n+\n+/* VMBUS_CHANMSG_TYPE_GPADL_SUBCONN */\n+struct vmbus_chanmsg_gpadl_subconn {\n+\tstruct vmbus_chanmsg_hdr hdr;\n+\tuint32_t\tmsgno;\n+\tuint32_t\tgpadl;\n+\tuint64_t\tgpa_page[];\n+} __rte_packed;\n+\n+#define VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX\t28\n+\n+/* VMBUS_CHANMSG_TYPE_GPADL_CONNRESP */\n+struct vmbus_chanmsg_gpadl_connresp {\n+\tstruct vmbus_chanmsg_hdr hdr;\n+\tuint32_t\tchanid;\n+\tuint32_t\tgpadl;\n+\tuint32_t\tstatus;\n+} __rte_packed;\n+\n+/* VMBUS_CHANMSG_TYPE_CHCLOSE */\n+struct vmbus_chanmsg_chclose {\n+\tstruct vmbus_chanmsg_hdr hdr;\n+\tuint32_t\tchanid;\n+} __rte_packed;\n+\n+/* VMBUS_CHANMSG_TYPE_GPADL_DISCONN */\n+struct vmbus_chanmsg_gpadl_disconn {\n+\tstruct vmbus_chanmsg_hdr hdr;\n+\tuint32_t\tchanid;\n+\tuint32_t\tgpadl;\n+} __rte_packed;\n+\n+/* VMBUS_CHANMSG_TYPE_CHFREE */\n+struct vmbus_chanmsg_chfree {\n+\tstruct vmbus_chanmsg_hdr hdr;\n+\tuint32_t\tchanid;\n+} __rte_packed;\n+\n+/* VMBUS_CHANMSG_TYPE_CHRESCIND */\n+struct vmbus_chanmsg_chrescind {\n+\tstruct vmbus_chanmsg_hdr hdr;\n+\tuint32_t\tchanid;\n+} __rte_packed;\n+\n+/* VMBUS_CHANMSG_TYPE_CHOFFER */\n+struct vmbus_chanmsg_choffer {\n+\tstruct vmbus_chanmsg_hdr hdr;\n+\trte_uuid_t\tchtype;\n+\trte_uuid_t\tchinst;\n+\tuint64_t\tchlat;\t/* unit: 100ns */\n+\tuint32_t\tchrev;\n+\tuint32_t\tsvrctx_sz;\n+\tuint16_t\tchflags;\n+\tuint16_t\tmmio_sz;\t/* unit: MB */\n+\tuint8_t\t\tudata[120];\n+\tuint16_t\tsubidx;\n+\tuint16_t\trsvd;\n+\tuint32_t\tchanid;\n+\tuint8_t\t\tmontrig;\n+\tuint8_t\t\tflags1;\t/* VMBUS_CHOFFER_FLAG1_ */\n+\tuint16_t\tflags2;\n+\tuint32_t\tconnid;\n+} __rte_packed;\n+\n+#define VMBUS_CHOFFER_FLAG1_HASMNF\t0x01\n+\n+#endif\t/* !_VMBUS_REG_H_ */\ndiff --git a/drivers/bus/vmbus/vmbus_bufring.c b/drivers/bus/vmbus/vmbus_bufring.c\nnew file mode 100644\nindex 000000000000..c2d7d8cc2254\n--- /dev/null\n+++ b/drivers/bus/vmbus/vmbus_bufring.c\n@@ -0,0 +1,241 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright (c) 2009-2012,2016 Microsoft Corp.\n+ * Copyright (c) 2012 NetApp Inc.\n+ * Copyright (c) 2012 Citrix Inc.\n+ * All rights reserved.\n+ */\n+\n+#include <unistd.h>\n+#include <stdint.h>\n+#include <stdbool.h>\n+#include <string.h>\n+#include <sys/uio.h>\n+\n+#include <rte_eal.h>\n+#include <rte_tailq.h>\n+#include <rte_log.h>\n+#include <rte_malloc.h>\n+#include <rte_bus.h>\n+#include <rte_atomic.h>\n+#include <rte_memory.h>\n+#include <rte_pause.h>\n+#include <rte_bus_vmbus.h>\n+\n+#include \"private.h\"\n+\n+/* Increase bufring index by inc with wraparound */\n+static inline uint32_t vmbus_br_idxinc(uint32_t idx, uint32_t inc, uint32_t sz)\n+{\n+\tidx += inc;\n+\tif (idx >= sz)\n+\t\tidx -= sz;\n+\n+\treturn idx;\n+}\n+\n+void vmbus_br_setup(struct vmbus_br *br, void *buf, unsigned int blen)\n+{\n+\tbr->vbr = buf;\n+\tbr->windex = br->vbr->windex;\n+\tbr->dsize = blen - sizeof(struct vmbus_bufring);\n+}\n+\n+/*\n+ * When we write to the ring buffer, check if the host needs to be\n+ * signaled.\n+ *\n+ * The contract:\n+ * - The host guarantees that while it is draining the TX bufring,\n+ *   it will set the br_imask to indicate it does not need to be\n+ *   interrupted when new data are added.\n+ * - The host guarantees that it will completely drain the TX bufring\n+ *   before exiting the read loop.  Further, once the TX bufring is\n+ *   empty, it will clear the br_imask and re-check to see if new\n+ *   data have arrived.\n+ */\n+static inline bool\n+vmbus_txbr_need_signal(const struct vmbus_br *tbr, uint32_t old_windex)\n+{\n+\trte_smp_mb();\n+\tif (tbr->vbr->imask)\n+\t\treturn false;\n+\n+\trte_smp_rmb();\n+\n+\t/*\n+\t * This is the only case we need to signal when the\n+\t * ring transitions from being empty to non-empty.\n+\t */\n+\treturn old_windex == tbr->vbr->rindex;\n+}\n+\n+static inline uint32_t\n+vmbus_txbr_copyto(const struct vmbus_br *tbr, uint32_t windex,\n+\t\t  const void *src0, uint32_t cplen)\n+{\n+\tuint8_t *br_data = tbr->vbr->data;\n+\tuint32_t br_dsize = tbr->dsize;\n+\tconst uint8_t *src = src0;\n+\n+\t/* XXX use double mapping like Linux kernel? */\n+\tif (cplen > br_dsize - windex) {\n+\t\tuint32_t fraglen = br_dsize - windex;\n+\n+\t\t/* Wrap-around detected */\n+\t\tmemcpy(br_data + windex, src, fraglen);\n+\t\tmemcpy(br_data, src + fraglen, cplen - fraglen);\n+\t} else {\n+\t\tmemcpy(br_data + windex, src, cplen);\n+\t}\n+\n+\treturn vmbus_br_idxinc(windex, cplen, br_dsize);\n+}\n+\n+/*\n+ * Write scattered channel packet to TX bufring.\n+ *\n+ * The offset of this channel packet is written as a 64bits value\n+ * immediately after this channel packet.\n+ *\n+ * The write goes through three stages:\n+ *  1. Reserve space in ring buffer for the new data.\n+ *     Writer atomically moves priv_write_index.\n+ *  2. Copy the new data into the ring.\n+ *  3. Update the tail of the ring (visible to host) that indicates\n+ *     next read location. Writer updates write_index\n+ */\n+int\n+vmbus_txbr_write(struct vmbus_br *tbr, const struct iovec iov[], int iovlen,\n+\t\t bool *need_sig)\n+{\n+\tstruct vmbus_bufring *vbr = tbr->vbr;\n+\tuint32_t ring_size = tbr->dsize;\n+\tuint32_t old_windex, next_windex, windex, total;\n+\tuint64_t save_windex;\n+\tint i;\n+\n+\ttotal = 0;\n+\tfor (i = 0; i < iovlen; i++)\n+\t\ttotal += iov[i].iov_len;\n+\ttotal += sizeof(save_windex);\n+\n+\t/* Reserve space in ring */\n+\tdo {\n+\t\tuint32_t avail;\n+\n+\t\t/* Get current free location */\n+\t\told_windex = tbr->windex;\n+\n+\t\t/* Prevent compiler reordering this with calculation */\n+\t\trte_compiler_barrier();\n+\n+\t\tavail = vmbus_br_availwrite(tbr, old_windex);\n+\n+\t\t/* If not enough space in ring, then tell caller. */\n+\t\tif (avail <= total)\n+\t\t\treturn -EAGAIN;\n+\n+\t\tnext_windex = vmbus_br_idxinc(old_windex, total, ring_size);\n+\n+\t\t/* Atomic update of next write_index for other threads */\n+\t} while (!rte_atomic32_cmpset(&tbr->windex, old_windex, next_windex));\n+\n+\t/* Space from old..new is now reserved */\n+\twindex = old_windex;\n+\tfor (i = 0; i < iovlen; i++) {\n+\t\twindex = vmbus_txbr_copyto(tbr, windex,\n+\t\t\t\t\t   iov[i].iov_base, iov[i].iov_len);\n+\t}\n+\n+\t/* Set the offset of the current channel packet. */\n+\tsave_windex = ((uint64_t)old_windex) << 32;\n+\twindex = vmbus_txbr_copyto(tbr, windex, &save_windex,\n+\t\t\t\t   sizeof(save_windex));\n+\n+\t/* The region reserved should match region used */\n+\tRTE_ASSERT(windex == next_windex);\n+\n+\t/* Ensure that data is available before updating host index */\n+\trte_smp_wmb();\n+\n+\t/* Checkin for our reservation. wait for our turn to update host */\n+\twhile (!rte_atomic32_cmpset(&vbr->windex, old_windex, next_windex))\n+\t\trte_pause();\n+\n+\t/* If host had read all data before this, then need to signal */\n+\t*need_sig |= vmbus_txbr_need_signal(tbr, old_windex);\n+\treturn 0;\n+}\n+\n+static inline uint32_t\n+vmbus_rxbr_copyfrom(const struct vmbus_br *rbr, uint32_t rindex,\n+\t\t    void *dst0, size_t cplen)\n+{\n+\tconst uint8_t *br_data = rbr->vbr->data;\n+\tuint32_t br_dsize = rbr->dsize;\n+\tuint8_t *dst = dst0;\n+\n+\tif (cplen > br_dsize - rindex) {\n+\t\tuint32_t fraglen = br_dsize - rindex;\n+\n+\t\t/* Wrap-around detected. */\n+\t\tmemcpy(dst, br_data + rindex, fraglen);\n+\t\tmemcpy(dst + fraglen, br_data, cplen - fraglen);\n+\t} else {\n+\t\tmemcpy(dst, br_data + rindex, cplen);\n+\t}\n+\n+\treturn vmbus_br_idxinc(rindex, cplen, br_dsize);\n+}\n+\n+/* Copy data from receive ring but don't change index */\n+int\n+vmbus_rxbr_peek(const struct vmbus_br *rbr, void *data, size_t dlen)\n+{\n+\tuint32_t avail;\n+\n+\t/*\n+\t * The requested data and the 64bits channel packet\n+\t * offset should be there at least.\n+\t */\n+\tavail = vmbus_br_availread(rbr);\n+\tif (avail < dlen + sizeof(uint64_t))\n+\t\treturn -EAGAIN;\n+\n+\tvmbus_rxbr_copyfrom(rbr, rbr->vbr->rindex, data, dlen);\n+\treturn 0;\n+}\n+\n+/*\n+ * Copy data from receive ring and change index\n+ * NOTE:\n+ * We assume (dlen + skip) == sizeof(channel packet).\n+ */\n+int\n+vmbus_rxbr_read(struct vmbus_br *rbr, void *data, size_t dlen, size_t skip)\n+{\n+\tstruct vmbus_bufring *vbr = rbr->vbr;\n+\tuint32_t br_dsize = rbr->dsize;\n+\tuint32_t rindex;\n+\n+\tif (vmbus_br_availread(rbr) < dlen + skip + sizeof(uint64_t))\n+\t\treturn -EAGAIN;\n+\n+\t/*\n+\t * Copy channel packet from RX bufring.\n+\t */\n+\trindex = vmbus_br_idxinc(rbr->vbr->rindex, skip, br_dsize);\n+\trindex = vmbus_rxbr_copyfrom(rbr, rindex, data, dlen);\n+\n+\t/*\n+\t * Discard this channel packet's 64bits offset, which is useless to us.\n+\t */\n+\trindex = vmbus_br_idxinc(rindex, sizeof(uint64_t), br_dsize);\n+\n+\t/* Update the read index _after_ the channel packet is fetched.\t */\n+\trte_compiler_barrier();\n+\n+\tvbr->rindex = rindex;\n+\n+\treturn 0;\n+}\ndiff --git a/drivers/bus/vmbus/vmbus_channel.c b/drivers/bus/vmbus/vmbus_channel.c\nnew file mode 100644\nindex 000000000000..f9feada9b047\n--- /dev/null\n+++ b/drivers/bus/vmbus/vmbus_channel.c\n@@ -0,0 +1,406 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright (c) 2018, Microsoft Corporation.\n+ * All Rights Reserved.\n+ */\n+\n+#include <unistd.h>\n+#include <stdint.h>\n+#include <string.h>\n+#include <sys/uio.h>\n+\n+#include <rte_eal.h>\n+#include <rte_tailq.h>\n+#include <rte_log.h>\n+#include <rte_malloc.h>\n+#include <rte_bus.h>\n+#include <rte_atomic.h>\n+#include <rte_memory.h>\n+#include <rte_bus_vmbus.h>\n+\n+#include \"private.h\"\n+\n+static inline void\n+vmbus_sync_set_bit(volatile uint32_t *addr, uint32_t mask)\n+{\n+\t/* Use GCC builtin which atomic does atomic OR operation */\n+\t__sync_or_and_fetch(addr, mask);\n+}\n+\n+static inline void\n+vmbus_send_interrupt(const struct rte_vmbus_device *dev, uint32_t relid)\n+{\n+\tuint32_t *int_addr;\n+\tuint32_t int_mask;\n+\n+\tint_addr = dev->int_page + relid / 32;\n+\tint_mask = 1u << (relid % 32);\n+\n+\tvmbus_sync_set_bit(int_addr, int_mask);\n+}\n+\n+static inline void\n+vmbus_set_monitor(const struct rte_vmbus_device *dev, uint32_t monitor_id)\n+{\n+\tuint32_t *monitor_addr, monitor_mask;\n+\tunsigned int trigger_index;\n+\n+\ttrigger_index = monitor_id / HV_MON_TRIG_LEN;\n+\tmonitor_mask = 1u << (monitor_id % HV_MON_TRIG_LEN);\n+\n+\tmonitor_addr = &dev->monitor_page->trigs[trigger_index].pending;\n+\tvmbus_sync_set_bit(monitor_addr, monitor_mask);\n+}\n+\n+static void\n+vmbus_set_event(const struct rte_vmbus_device *dev,\n+\t\tconst struct vmbus_channel *chan)\n+{\n+\tvmbus_send_interrupt(dev, chan->relid);\n+\tvmbus_set_monitor(dev, chan->monitor_id);\n+}\n+\n+/*\n+ * Notify host that there are data pending on our TX bufring.\n+ *\n+ * Since this in userspace, rely on the monitor page.\n+ * Can't do a hypercall from userspace.\n+ */\n+void\n+rte_vmbus_chan_signal_tx(const struct vmbus_channel *chan)\n+{\n+\tconst struct rte_vmbus_device *dev = chan->device;\n+\tconst struct vmbus_br *tbr = &chan->txbr;\n+\n+\t/* Make sure all updates are done before signaling host */\n+\trte_smp_wmb();\n+\n+\t/* If host is ignoring interrupts? */\n+\tif (tbr->vbr->imask)\n+\t\treturn;\n+\n+\tvmbus_set_event(dev, chan);\n+}\n+\n+\n+/* Do a simple send directly using transmit ring. */\n+int rte_vmbus_chan_send(struct vmbus_channel *chan, uint16_t type,\n+\t\t\tvoid *data, uint32_t dlen,\n+\t\t\tuint64_t xactid, uint32_t flags, bool *need_sig)\n+{\n+\tstruct vmbus_chanpkt pkt;\n+\tunsigned int pktlen, pad_pktlen;\n+\tconst uint32_t hlen = sizeof(pkt);\n+\tbool send_evt = false;\n+\tuint64_t pad = 0;\n+\tstruct iovec iov[3];\n+\tint error;\n+\n+\tpktlen = hlen + dlen;\n+\tpad_pktlen = RTE_ALIGN(pktlen, sizeof(uint64_t));\n+\n+\tpkt.hdr.type = type;\n+\tpkt.hdr.flags = flags;\n+\tpkt.hdr.hlen = hlen >> VMBUS_CHANPKT_SIZE_SHIFT;\n+\tpkt.hdr.tlen = pad_pktlen >> VMBUS_CHANPKT_SIZE_SHIFT;\n+\tpkt.hdr.xactid = xactid;\n+\n+\tiov[0].iov_base = &pkt;\n+\tiov[0].iov_len = hlen;\n+\tiov[1].iov_base = data;\n+\tiov[1].iov_len = dlen;\n+\tiov[2].iov_base = &pad;\n+\tiov[2].iov_len = pad_pktlen - pktlen;\n+\n+\terror = vmbus_txbr_write(&chan->txbr, iov, 3, &send_evt);\n+\n+\t/*\n+\t * caller sets need_sig to non-NULL if it will handle\n+\t * signaling if required later.\n+\t * if need_sig is NULL, signal now if needed.\n+\t */\n+\tif (need_sig)\n+\t\t*need_sig |= send_evt;\n+\telse if (error == 0 && send_evt)\n+\t\trte_vmbus_chan_signal_tx(chan);\n+\treturn error;\n+}\n+\n+/* Do a scatter/gather send where the descriptor points to data. */\n+int rte_vmbus_chan_send_sglist(struct vmbus_channel *chan,\n+\t\t\t       struct vmbus_gpa sg[], uint32_t sglen,\n+\t\t\t       void *data, uint32_t dlen,\n+\t\t\t       uint64_t xactid, bool *need_sig)\n+{\n+\tstruct vmbus_chanpkt_sglist pkt;\n+\tunsigned int pktlen, pad_pktlen, hlen;\n+\tbool send_evt = false;\n+\tstruct iovec iov[4];\n+\tuint64_t pad = 0;\n+\tint error;\n+\n+\thlen = offsetof(struct vmbus_chanpkt_sglist, gpa[sglen]);\n+\tpktlen = hlen + dlen;\n+\tpad_pktlen = RTE_ALIGN(pktlen, sizeof(uint64_t));\n+\n+\tpkt.hdr.type = VMBUS_CHANPKT_TYPE_GPA;\n+\tpkt.hdr.flags = VMBUS_CHANPKT_FLAG_RC;\n+\tpkt.hdr.hlen = hlen >> VMBUS_CHANPKT_SIZE_SHIFT;\n+\tpkt.hdr.tlen = pad_pktlen >> VMBUS_CHANPKT_SIZE_SHIFT;\n+\tpkt.hdr.xactid = xactid;\n+\tpkt.rsvd = 0;\n+\tpkt.gpa_cnt = sglen;\n+\n+\tiov[0].iov_base = &pkt;\n+\tiov[0].iov_len = sizeof(pkt);\n+\tiov[1].iov_base = sg;\n+\tiov[1].iov_len = sizeof(struct vmbus_gpa) * sglen;\n+\tiov[2].iov_base = data;\n+\tiov[2].iov_len = dlen;\n+\tiov[3].iov_base = &pad;\n+\tiov[3].iov_len = pad_pktlen - pktlen;\n+\n+\terror = vmbus_txbr_write(&chan->txbr, iov, 4, &send_evt);\n+\n+\t/* if caller is batching, just propagate the status */\n+\tif (need_sig)\n+\t\t*need_sig |= send_evt;\n+\telse if (error == 0 && send_evt)\n+\t\trte_vmbus_chan_signal_tx(chan);\n+\treturn error;\n+}\n+\n+bool rte_vmbus_chan_rx_empty(const struct vmbus_channel *channel)\n+{\n+\tconst struct vmbus_br *br = &channel->rxbr;\n+\n+\treturn br->vbr->rindex == br->vbr->windex;\n+}\n+\n+static int vmbus_read_and_signal(struct vmbus_channel *chan,\n+\t\t\t\t void *data, size_t dlen, size_t skip)\n+{\n+\tstruct vmbus_br *rbr = &chan->rxbr;\n+\tuint32_t write_sz, pending_sz, bytes_read;\n+\tint error;\n+\n+\t/* Record where host was when we started read (for debug) */\n+\trbr->windex = rbr->vbr->windex;\n+\n+\t/* Read data and skip packet header */\n+\terror = vmbus_rxbr_read(rbr, data, dlen, skip);\n+\tif (error)\n+\t\treturn error;\n+\n+\t/* No need for signaling on older versions */\n+\tif (!rbr->vbr->feature_bits.feat_pending_send_sz)\n+\t\treturn 0;\n+\n+\t/* Make sure reading of pending happens after new read index */\n+\trte_mb();\n+\n+\tpending_sz = rbr->vbr->pending_send;\n+\tif (!pending_sz)\n+\t\treturn 0;\n+\n+\trte_smp_rmb();\n+\twrite_sz = vmbus_br_availwrite(rbr, rbr->vbr->windex);\n+\tbytes_read = dlen + skip + sizeof(uint64_t);\n+\n+\t/* If there was space before then host was not blocked */\n+\tif (write_sz - bytes_read > pending_sz)\n+\t\treturn 0;\n+\n+\t/* If pending write will not fit */\n+\tif (write_sz <= pending_sz)\n+\t\treturn 0;\n+\n+\tvmbus_set_event(chan->device, chan);\n+\treturn 0;\n+}\n+\n+/* TODO: replace this with inplace ring buffer (no copy) */\n+int rte_vmbus_chan_recv(struct vmbus_channel *chan, void *data, uint32_t *len,\n+\t\t\tuint64_t *request_id)\n+{\n+\tstruct vmbus_chanpkt_hdr pkt;\n+\tuint32_t dlen, hlen, bufferlen = *len;\n+\tint error;\n+\n+\t*len = 0;\n+\n+\terror = vmbus_rxbr_peek(&chan->rxbr, &pkt, sizeof(pkt));\n+\tif (error)\n+\t\treturn error;\n+\n+\tif (unlikely(pkt.hlen < VMBUS_CHANPKT_HLEN_MIN)) {\n+\t\tVMBUS_LOG(ERR, \"VMBUS recv, invalid hlen %u\", pkt.hlen);\n+\t\t/* XXX this channel is dead actually. */\n+\t\treturn -EIO;\n+\t}\n+\n+\tif (unlikely(pkt.hlen > pkt.tlen)) {\n+\t\tVMBUS_LOG(ERR, \"VMBUS recv,invalid hlen %u and tlen %u\",\n+\t\t\t  pkt.hlen, pkt.tlen);\n+\t\treturn -EIO;\n+\t}\n+\n+\t/* Length are in quad words */\n+\thlen = pkt.hlen << VMBUS_CHANPKT_SIZE_SHIFT;\n+\tdlen = (pkt.tlen << VMBUS_CHANPKT_SIZE_SHIFT) - hlen;\n+\t*len = dlen;\n+\n+\t/* If caller buffer is not large enough */\n+\tif (unlikely(dlen > bufferlen))\n+\t\treturn -ENOBUFS;\n+\n+\tif (request_id)\n+\t\t*request_id = pkt.xactid;\n+\n+\t/* Read data and skip the header */\n+\treturn vmbus_read_and_signal(chan, data, dlen, hlen);\n+}\n+\n+int rte_vmbus_chan_recv_raw(struct vmbus_channel *chan,\n+\t\t\t    void *data, uint32_t *len)\n+{\n+\tstruct vmbus_chanpkt_hdr pkt;\n+\tuint32_t dlen, bufferlen = *len;\n+\tint error;\n+\n+\terror = vmbus_rxbr_peek(&chan->rxbr, &pkt, sizeof(pkt));\n+\tif (error)\n+\t\treturn error;\n+\n+\tif (unlikely(pkt.hlen < VMBUS_CHANPKT_HLEN_MIN)) {\n+\t\tVMBUS_LOG(ERR, \"VMBUS recv, invalid hlen %u\", pkt.hlen);\n+\t\t/* XXX this channel is dead actually. */\n+\t\treturn -EIO;\n+\t}\n+\n+\tif (unlikely(pkt.hlen > pkt.tlen)) {\n+\t\tVMBUS_LOG(ERR, \"VMBUS recv,invalid hlen %u and tlen %u\",\n+\t\t\tpkt.hlen, pkt.tlen);\n+\t\treturn -EIO;\n+\t}\n+\n+\t/* Length are in quad words */\n+\tdlen = pkt.tlen << VMBUS_CHANPKT_SIZE_SHIFT;\n+\t*len = dlen;\n+\n+\t/* If caller buffer is not large enough */\n+\tif (unlikely(dlen > bufferlen))\n+\t\treturn -ENOBUFS;\n+\n+\t/* Put packet header in data buffer */\n+\treturn vmbus_read_and_signal(chan, data, dlen, 0);\n+}\n+\n+int vmbus_chan_create(const struct rte_vmbus_device *device,\n+\t\t      uint16_t relid, uint16_t subid, uint8_t monitor_id,\n+\t\t      struct vmbus_channel **new_chan)\n+{\n+\tstruct vmbus_channel *chan;\n+\tint err;\n+\n+\tchan = rte_zmalloc_socket(\"VMBUS\", sizeof(*chan), RTE_CACHE_LINE_SIZE,\n+\t\t\t\t  device->device.numa_node);\n+\tif (!chan)\n+\t\treturn -ENOMEM;\n+\n+\tSTAILQ_INIT(&chan->subchannel_list);\n+\tchan->device = device;\n+\tchan->subchannel_id = subid;\n+\tchan->relid = relid;\n+\tchan->monitor_id = monitor_id;\n+\t*new_chan = chan;\n+\n+\terr = vmbus_uio_map_rings(chan);\n+\tif (err) {\n+\t\trte_free(chan);\n+\t\treturn err;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* Setup the primary channel */\n+int rte_vmbus_chan_open(struct rte_vmbus_device *device,\n+\t\t\tstruct vmbus_channel **new_chan)\n+{\n+\tint err;\n+\n+\terr = vmbus_chan_create(device, device->relid, 0,\n+\t\t\t\tdevice->monitor_id, new_chan);\n+\tif (!err)\n+\t\tdevice->primary = *new_chan;\n+\n+\treturn err;\n+}\n+\n+int rte_vmbus_max_channels(const struct rte_vmbus_device *device)\n+{\n+\tif (vmbus_uio_subchannels_supported(device, device->primary))\n+\t\treturn VMBUS_MAX_CHANNELS;\n+\telse\n+\t\treturn 1;\n+}\n+\n+/* Setup secondary channel */\n+int rte_vmbus_subchan_open(struct vmbus_channel *primary,\n+\t\t\t   struct vmbus_channel **new_chan)\n+{\n+\tstruct vmbus_channel *chan;\n+\tint err;\n+\n+\terr = vmbus_uio_get_subchan(primary, &chan);\n+\tif (err)\n+\t\treturn err;\n+\n+\tSTAILQ_INSERT_TAIL(&primary->subchannel_list, chan, next);\n+\t*new_chan = chan;\n+\treturn 0;\n+}\n+\n+uint16_t rte_vmbus_sub_channel_index(const struct vmbus_channel *chan)\n+{\n+\treturn chan->subchannel_id;\n+}\n+\n+void rte_vmbus_chan_close(struct vmbus_channel *chan)\n+{\n+\tconst struct rte_vmbus_device *device = chan->device;\n+\tstruct vmbus_channel *primary = device->primary;\n+\n+\tif (chan != primary)\n+\t\tSTAILQ_REMOVE(&primary->subchannel_list, chan,\n+\t\t\t      vmbus_channel, next);\n+\n+\trte_free(chan);\n+}\n+\n+static void vmbus_dump_ring(FILE *f, const char *id, const struct vmbus_br *br)\n+{\n+\tconst struct vmbus_bufring *vbr = br->vbr;\n+\tstruct vmbus_chanpkt_hdr pkt;\n+\n+\tfprintf(f, \"%s windex=%u rindex=%u mask=%u pending=%u feature=%#x\\n\",\n+\t\tid, vbr->windex, vbr->rindex, vbr->imask,\n+\t\tvbr->pending_send, vbr->feature_bits.value);\n+\tfprintf(f, \" size=%u avail write=%u read=%u\\n\",\n+\t\tbr->dsize, vmbus_br_availwrite(br, vbr->windex),\n+\t\tvmbus_br_availread(br));\n+\n+\tif (vmbus_rxbr_peek(br, &pkt, sizeof(pkt)) == 0)\n+\t\tfprintf(f, \"  pkt type %#x len %u flags %#x xactid %#\"PRIx64\"\\n\",\n+\t\t\tpkt.type,\n+\t\t\tpkt.tlen << VMBUS_CHANPKT_SIZE_SHIFT,\n+\t\t\tpkt.flags, pkt.xactid);\n+}\n+\n+void rte_vmbus_chan_dump(FILE *f, const struct vmbus_channel *chan)\n+{\n+\tfprintf(f, \"channel[%u] relid=%u monitor=%u\\n\",\n+\t\tchan->subchannel_id, chan->relid, chan->monitor_id);\n+\tvmbus_dump_ring(f, \"rxbr\", &chan->rxbr);\n+\tvmbus_dump_ring(f, \"txbr\", &chan->txbr);\n+}\ndiff --git a/drivers/bus/vmbus/vmbus_common.c b/drivers/bus/vmbus/vmbus_common.c\nnew file mode 100644\nindex 000000000000..c7165ad54fe2\n--- /dev/null\n+++ b/drivers/bus/vmbus/vmbus_common.c\n@@ -0,0 +1,286 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright (c) 2018, Microsoft Corporation.\n+ * All Rights Reserved.\n+ */\n+\n+#include <string.h>\n+#include <unistd.h>\n+#include <dirent.h>\n+#include <fcntl.h>\n+#include <sys/queue.h>\n+#include <sys/mman.h>\n+\n+#include <rte_log.h>\n+#include <rte_bus.h>\n+#include <rte_eal.h>\n+#include <rte_tailq.h>\n+#include <rte_devargs.h>\n+#include <rte_malloc.h>\n+#include <rte_errno.h>\n+#include <rte_memory.h>\n+#include <rte_bus_vmbus.h>\n+\n+#include \"private.h\"\n+\n+int vmbus_logtype_bus;\n+extern struct rte_vmbus_bus rte_vmbus_bus;\n+\n+/* map a particular resource from a file */\n+void *\n+vmbus_map_resource(void *requested_addr, int fd, off_t offset, size_t size,\n+\t\t   int flags)\n+{\n+\tvoid *mapaddr;\n+\n+\t/* Map the memory resource of device */\n+\tmapaddr = mmap(requested_addr, size, PROT_READ | PROT_WRITE,\n+\t\t       MAP_SHARED | flags, fd, offset);\n+\tif (mapaddr == MAP_FAILED) {\n+\t\tVMBUS_LOG(ERR,\n+\t\t\t  \"mmap(%d, %p, %zu, %ld) failed: %s\",\n+\t\t\t  fd, requested_addr, size, (long)offset,\n+\t\t\t  strerror(errno));\n+\t}\n+\treturn mapaddr;\n+}\n+\n+/* unmap a particular resource */\n+void\n+vmbus_unmap_resource(void *requested_addr, size_t size)\n+{\n+\tif (requested_addr == NULL)\n+\t\treturn;\n+\n+\t/* Unmap the VMBUS memory resource of device */\n+\tif (munmap(requested_addr, size)) {\n+\t\tVMBUS_LOG(ERR, \"munmap(%p, 0x%lx) failed: %s\",\n+\t\t\trequested_addr, (unsigned long)size,\n+\t\t\tstrerror(errno));\n+\t} else\n+\t\tVMBUS_LOG(DEBUG, \"  VMBUS memory unmapped at %p\",\n+\t\t\t  requested_addr);\n+}\n+\n+/**\n+ * Match the VMBUS driver and device using UUID table\n+ *\n+ * @param drv\n+ *\tVMBUS driver from which ID table would be extracted\n+ * @param pci_dev\n+ *\tVMBUS device to match against the driver\n+ * @return\n+ *\ttrue for successful match\n+ *\tfalse for unsuccessful match\n+ */\n+static bool\n+vmbus_match(const struct rte_vmbus_driver *dr,\n+\t    const struct rte_vmbus_device *dev)\n+{\n+\tconst rte_uuid_t *id_table;\n+\n+\tfor (id_table = dr->id_table; !rte_uuid_is_null(*id_table); ++id_table) {\n+\t\tif (rte_uuid_compare(*id_table, dev->class_id) == 0)\n+\t\t\treturn true;\n+\t}\n+\n+\treturn false;\n+}\n+\n+/*\n+ * If device ID match, call the devinit() function of the driver.\n+ */\n+static int\n+vmbus_probe_one_driver(struct rte_vmbus_driver *dr,\n+\t\t       struct rte_vmbus_device *dev)\n+{\n+\tchar guid[RTE_UUID_STRLEN];\n+\tint ret;\n+\n+\tif (!vmbus_match(dr, dev))\n+\t\treturn 1;\t /* not supported */\n+\n+\trte_uuid_unparse(dev->device_id, guid, sizeof(guid));\n+\tVMBUS_LOG(INFO, \"VMBUS device %s on NUMA socket %i\",\n+\t\t  guid, dev->device.numa_node);\n+\n+\t/* TODO add blacklisted */\n+\n+\t/* map resources for device */\n+\tret = rte_vmbus_map_device(dev);\n+\tif (ret != 0)\n+\t\treturn ret;\n+\n+\t/* reference driver structure */\n+\tdev->driver = dr;\n+\tdev->device.driver = &dr->driver;\n+\n+\tif (dev->device.numa_node < 0) {\n+\t\tVMBUS_LOG(WARNING, \"  Invalid NUMA socket, default to 0\");\n+\t\tdev->device.numa_node = 0;\n+\t}\n+\n+\t/* call the driver probe() function */\n+\tVMBUS_LOG(INFO, \"  probe driver: %s\", dr->driver.name);\n+\tret = dr->probe(dr, dev);\n+\tif (ret) {\n+\t\tdev->driver = NULL;\n+\t\trte_vmbus_unmap_device(dev);\n+\t}\n+\n+\treturn ret;\n+}\n+\n+/*\n+ * IF device class GUID mathces, call the probe function of\n+ * registere drivers for the vmbus device.\n+ * Return -1 if initialization failed,\n+ * and 1 if no driver found for this device.\n+ */\n+static int\n+vmbus_probe_all_drivers(struct rte_vmbus_device *dev)\n+{\n+\tstruct rte_vmbus_driver *dr;\n+\tint rc;\n+\n+\t/* Check if a driver is already loaded */\n+\tif (dev->driver != NULL) {\n+\t\tVMBUS_LOG(DEBUG, \"VMBUS driver already loaded\");\n+\t\treturn 0;\n+\t}\n+\n+\tFOREACH_DRIVER_ON_VMBUS(dr) {\n+\t\trc = vmbus_probe_one_driver(dr, dev);\n+\t\tif (rc < 0) /* negative is an error */\n+\t\t\treturn -1;\n+\n+\t\tif (rc > 0) /* positive driver doesn't support it */\n+\t\t\tcontinue;\n+\n+\t\treturn 0;\n+\t}\n+\treturn 1;\n+}\n+\n+/*\n+ * Scan the vmbus, and call the devinit() function for\n+ * all registered drivers that have a matching entry in its id_table\n+ * for discovered devices.\n+ */\n+int\n+rte_vmbus_probe(void)\n+{\n+\tstruct rte_vmbus_device *dev;\n+\tsize_t probed = 0, failed = 0;\n+\tchar ubuf[RTE_UUID_STRLEN];\n+\n+\tFOREACH_DEVICE_ON_VMBUS(dev) {\n+\t\tprobed++;\n+\n+\t\trte_uuid_unparse(dev->device_id, ubuf, sizeof(ubuf));\n+\n+\t\t/* TODO: add whitelist/blacklist */\n+\n+\t\tif (vmbus_probe_all_drivers(dev) < 0) {\n+\t\t\tVMBUS_LOG(NOTICE,\n+\t\t\t\t\"Requested device %s cannot be used\", ubuf);\n+\t\t\trte_errno = errno;\n+\t\t\tfailed++;\n+\t\t}\n+\t}\n+\n+\treturn (probed && probed == failed) ? -1 : 0;\n+}\n+\n+static int\n+vmbus_parse(const char *name, void *addr)\n+{\n+\trte_uuid_t guid;\n+\tint ret;\n+\n+\tret = rte_uuid_parse(name, guid);\n+\tif (ret == 0 && addr)\n+\t\tmemcpy(addr, &guid, sizeof(guid));\n+\n+\treturn ret;\n+}\n+\n+/* register vmbus driver */\n+void\n+rte_vmbus_register(struct rte_vmbus_driver *driver)\n+{\n+\tVMBUS_LOG(DEBUG,\n+\t\t\"Registered driver %s\", driver->driver.name);\n+\n+\tTAILQ_INSERT_TAIL(&rte_vmbus_bus.driver_list, driver, next);\n+\tdriver->bus = &rte_vmbus_bus;\n+}\n+\n+/* unregister vmbus driver */\n+void\n+rte_vmbus_unregister(struct rte_vmbus_driver *driver)\n+{\n+\tTAILQ_REMOVE(&rte_vmbus_bus.driver_list, driver, next);\n+\tdriver->bus = NULL;\n+}\n+\n+/* Add a device to VMBUS bus */\n+void\n+vmbus_add_device(struct rte_vmbus_device *vmbus_dev)\n+{\n+\tTAILQ_INSERT_TAIL(&rte_vmbus_bus.device_list, vmbus_dev, next);\n+}\n+\n+/* Insert a device into a predefined position in VMBUS bus */\n+void\n+vmbus_insert_device(struct rte_vmbus_device *exist_vmbus_dev,\n+\t\t      struct rte_vmbus_device *new_vmbus_dev)\n+{\n+\tTAILQ_INSERT_BEFORE(exist_vmbus_dev, new_vmbus_dev, next);\n+}\n+\n+/* Remove a device from VMBUS bus */\n+void\n+vmbus_remove_device(struct rte_vmbus_device *vmbus_dev)\n+{\n+\tTAILQ_REMOVE(&rte_vmbus_bus.device_list, vmbus_dev, next);\n+}\n+\n+/* VMBUS doesn't support hotplug */\n+static struct rte_device *\n+vmbus_find_device(const struct rte_device *start, rte_dev_cmp_t cmp,\n+\t\t  const void *data)\n+{\n+\tstruct rte_vmbus_device *dev;\n+\n+\tFOREACH_DEVICE_ON_VMBUS(dev) {\n+\t\tif (start && &dev->device == start) {\n+\t\t\tstart = NULL;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (cmp(&dev->device, data) == 0)\n+\t\t\treturn &dev->device;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+\n+struct rte_vmbus_bus rte_vmbus_bus = {\n+\t.bus = {\n+\t\t.scan = rte_vmbus_scan,\n+\t\t.probe = rte_vmbus_probe,\n+\t\t.find_device = vmbus_find_device,\n+\t\t.parse = vmbus_parse,\n+\t},\n+\t.device_list = TAILQ_HEAD_INITIALIZER(rte_vmbus_bus.device_list),\n+\t.driver_list = TAILQ_HEAD_INITIALIZER(rte_vmbus_bus.driver_list),\n+};\n+\n+RTE_REGISTER_BUS(vmbus, rte_vmbus_bus.bus);\n+\n+RTE_INIT(vmbus_init_log)\n+{\n+\tvmbus_logtype_bus = rte_log_register(\"bus.vmbus\");\n+\tif (vmbus_logtype_bus >= 0)\n+\t\trte_log_set_level(vmbus_logtype_bus, RTE_LOG_NOTICE);\n+}\ndiff --git a/drivers/bus/vmbus/vmbus_common_uio.c b/drivers/bus/vmbus/vmbus_common_uio.c\nnew file mode 100644\nindex 000000000000..5ddd36ab62d2\n--- /dev/null\n+++ b/drivers/bus/vmbus/vmbus_common_uio.c\n@@ -0,0 +1,232 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright (c) 2018, Microsoft Corporation.\n+ * All Rights Reserved.\n+ */\n+\n+#include <fcntl.h>\n+#include <string.h>\n+#include <unistd.h>\n+#include <sys/types.h>\n+#include <sys/mman.h>\n+\n+#include <rte_eal.h>\n+#include <rte_tailq.h>\n+#include <rte_log.h>\n+#include <rte_malloc.h>\n+#include <rte_bus.h>\n+#include <rte_bus_vmbus.h>\n+\n+#include \"private.h\"\n+\n+static struct rte_tailq_elem vmbus_tailq = {\n+\t.name = \"VMBUS_RESOURCE_LIST\",\n+};\n+EAL_REGISTER_TAILQ(vmbus_tailq)\n+\n+static int\n+vmbus_uio_map_secondary(struct rte_vmbus_device *dev)\n+{\n+\tint fd, i;\n+\tstruct mapped_vmbus_resource *uio_res;\n+\tstruct mapped_vmbus_res_list *uio_res_list\n+\t\t= RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);\n+\n+\tTAILQ_FOREACH(uio_res, uio_res_list, next) {\n+\n+\t\t/* skip this element if it doesn't match our UUID */\n+\t\tif (rte_uuid_compare(uio_res->id, dev->device_id) != 0)\n+\t\t\tcontinue;\n+\n+\t\t/* open /dev/uioX */\n+\t\tfd = open(uio_res->path, O_RDWR);\n+\t\tif (fd < 0) {\n+\t\t\tVMBUS_LOG(ERR, \"Cannot open %s: %s\",\n+\t\t\t\t  uio_res->path, strerror(errno));\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tfor (i = 0; i != uio_res->nb_maps; i++) {\n+\t\t\tvoid *mapaddr;\n+\n+\t\t\tmapaddr = vmbus_map_resource(uio_res->maps[i].addr,\n+\t\t\t\t\t\t     fd, 0,\n+\t\t\t\t\t\t     uio_res->maps[i].size, 0);\n+\n+\t\t\tif (mapaddr == uio_res->maps[i].addr)\n+\t\t\t\tcontinue;\n+\n+\t\t\tVMBUS_LOG(ERR,\n+\t\t\t\t  \"Cannot mmap device resource file %s to address: %p\",\n+\t\t\t\t  uio_res->path, uio_res->maps[i].addr);\n+\n+\t\t\tif (mapaddr != MAP_FAILED)\n+\t\t\t\t/* unmap addr wrongly mapped */\n+\t\t\t\tvmbus_unmap_resource(mapaddr,\n+\t\t\t\t\t\t     (size_t)uio_res->maps[i].size);\n+\n+\t\t\t/* unmap addrs correctly mapped */\n+\t\t\twhile (--i >= 0)\n+\t\t\t\tvmbus_unmap_resource(uio_res->maps[i].addr,\n+\t\t\t\t\t\t     (size_t)uio_res->maps[i].size);\n+\n+\t\t\tclose(fd);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\t/* fd is not needed in slave process, close it */\n+\t\tclose(fd);\n+\t\treturn 0;\n+\t}\n+\n+\tVMBUS_LOG(ERR,  \"Cannot find resource for device\");\n+\treturn 1;\n+}\n+\n+static int\n+vmbus_uio_map_primary(struct rte_vmbus_device *dev)\n+{\n+\tint i, ret;\n+\tstruct mapped_vmbus_resource *uio_res = NULL;\n+\tstruct mapped_vmbus_res_list *uio_res_list =\n+\t\tRTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);\n+\n+\t/* allocate uio resource */\n+\tret = vmbus_uio_alloc_resource(dev, &uio_res);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Map the resources */\n+\tfor (i = 0; i < VMBUS_MAX_RESOURCE; i++) {\n+\t\t/* skip empty BAR */\n+\t\tif (dev->resource[i].len == 0)\n+\t\t\tcontinue;\n+\n+\t\tret = vmbus_uio_map_resource_by_index(dev, i, uio_res, 0);\n+\t\tif (ret)\n+\t\t\tgoto error;\n+\t}\n+\n+\tuio_res->nb_maps = i;\n+\n+\tTAILQ_INSERT_TAIL(uio_res_list, uio_res, next);\n+\n+\treturn 0;\n+error:\n+\twhile (--i >= 0) {\n+\t\tvmbus_unmap_resource(uio_res->maps[i].addr,\n+\t\t\t\t(size_t)uio_res->maps[i].size);\n+\t}\n+\tvmbus_uio_free_resource(dev, uio_res);\n+\treturn -1;\n+}\n+\n+\n+struct mapped_vmbus_resource *\n+vmbus_uio_find_resource(const struct rte_vmbus_device *dev)\n+{\n+\tstruct mapped_vmbus_resource *uio_res;\n+\tstruct mapped_vmbus_res_list *uio_res_list =\n+\t\t\tRTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);\n+\n+\tif (dev == NULL)\n+\t\treturn NULL;\n+\n+\tTAILQ_FOREACH(uio_res, uio_res_list, next) {\n+\t\t/* skip this element if it doesn't match our VMBUS address */\n+\t\tif (rte_uuid_compare(uio_res->id, dev->device_id) == 0)\n+\t\t\treturn uio_res;\n+\t}\n+\treturn NULL;\n+}\n+\n+/* map the VMBUS resource of a VMBUS device in virtual memory */\n+int\n+vmbus_uio_map_resource(struct rte_vmbus_device *dev)\n+{\n+\tstruct mapped_vmbus_resource *uio_res;\n+\tint ret;\n+\n+\t/* TODO: handle rescind */\n+\tdev->intr_handle.fd = -1;\n+\tdev->intr_handle.uio_cfg_fd = -1;\n+\tdev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;\n+\n+\t/* secondary processes - use already recorded details */\n+\tif (rte_eal_process_type() != RTE_PROC_PRIMARY)\n+\t\tret = vmbus_uio_map_secondary(dev);\n+\telse\n+\t\tret = vmbus_uio_map_primary(dev);\n+\n+\tif (ret != 0)\n+\t\treturn ret;\n+\n+\tuio_res = vmbus_uio_find_resource(dev);\n+\tif (!uio_res) {\n+\t\tVMBUS_LOG(ERR, \"can not find resources!\");\n+\t\treturn -EIO;\n+\t}\n+\n+\tif (uio_res->nb_maps <= HV_MON_PAGE_MAP) {\n+\t\tVMBUS_LOG(ERR, \"VMBUS: only %u resources found!\",\n+\t\t\tuio_res->nb_maps);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tdev->int_page = (uint32_t *)((char *)uio_res->maps[HV_INT_PAGE_MAP].addr\n+\t\t\t\t     + (PAGE_SIZE >> 1));\n+\tdev->monitor_page = uio_res->maps[HV_MON_PAGE_MAP].addr;\n+\treturn 0;\n+}\n+\n+static void\n+vmbus_uio_unmap(struct mapped_vmbus_resource *uio_res)\n+{\n+\tint i;\n+\n+\tif (uio_res == NULL)\n+\t\treturn;\n+\n+\tfor (i = 0; i != uio_res->nb_maps; i++) {\n+\t\tvmbus_unmap_resource(uio_res->maps[i].addr,\n+\t\t\t\t     (size_t)uio_res->maps[i].size);\n+\t}\n+}\n+\n+/* unmap the VMBUS resource of a VMBUS device in virtual memory */\n+void\n+vmbus_uio_unmap_resource(struct rte_vmbus_device *dev)\n+{\n+\tstruct mapped_vmbus_resource *uio_res;\n+\tstruct mapped_vmbus_res_list *uio_res_list =\n+\t\t\tRTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);\n+\n+\tif (dev == NULL)\n+\t\treturn;\n+\n+\t/* find an entry for the device */\n+\tuio_res = vmbus_uio_find_resource(dev);\n+\tif (uio_res == NULL)\n+\t\treturn;\n+\n+\t/* secondary processes - just free maps */\n+\tif (rte_eal_process_type() != RTE_PROC_PRIMARY)\n+\t\treturn vmbus_uio_unmap(uio_res);\n+\n+\tTAILQ_REMOVE(uio_res_list, uio_res, next);\n+\n+\t/* unmap all resources */\n+\tvmbus_uio_unmap(uio_res);\n+\n+\t/* free uio resource */\n+\trte_free(uio_res);\n+\n+\t/* close fd if in primary process */\n+\tclose(dev->intr_handle.fd);\n+\tif (dev->intr_handle.uio_cfg_fd >= 0) {\n+\t\tclose(dev->intr_handle.uio_cfg_fd);\n+\t\tdev->intr_handle.uio_cfg_fd = -1;\n+\t}\n+\n+\tdev->intr_handle.fd = -1;\n+\tdev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;\n+}\ndiff --git a/mk/rte.app.mk b/mk/rte.app.mk\nindex 1e32c83e75e7..9fa82383ee8d 100644\n--- a/mk/rte.app.mk\n+++ b/mk/rte.app.mk\n@@ -169,6 +169,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_IFCVF_VDPA_PMD) += -lrte_ifcvf_vdpa\n endif # $(CONFIG_RTE_EAL_VFIO)\n endif # $(CONFIG_RTE_LIBRTE_VHOST)\n _LDLIBS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD)    += -lrte_pmd_vmxnet3_uio\n+_LDLIBS-$(CONFIG_RTE_LIBRTE_VMBUS)\t    += -lrte_bus_vmbus\n \n ifeq ($(CONFIG_RTE_LIBRTE_BBDEV),y)\n _LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_BBDEV_NULL)     += -lrte_pmd_bbdev_null\n",
    "prefixes": [
        "v11",
        "2/4"
    ]
}