get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 44060,
    "url": "http://patches.dpdk.org/api/patches/44060/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20180830223512.21297-6-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": "<20180830223512.21297-6-stephen@networkplumber.org>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20180830223512.21297-6-stephen@networkplumber.org",
    "date": "2018-08-30T22:35:12",
    "name": "[5/5] net/netvsc: integrated VF support",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": true,
    "hash": "5bcb2b574c5aa9883e688cbf5455bd67e41fa9df",
    "submitter": {
        "id": 27,
        "url": "http://patches.dpdk.org/api/people/27/?format=api",
        "name": "Stephen Hemminger",
        "email": "stephen@networkplumber.org"
    },
    "delegate": {
        "id": 319,
        "url": "http://patches.dpdk.org/api/users/319/?format=api",
        "username": "fyigit",
        "first_name": "Ferruh",
        "last_name": "Yigit",
        "email": "ferruh.yigit@amd.com"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20180830223512.21297-6-stephen@networkplumber.org/mbox/",
    "series": [
        {
            "id": 1120,
            "url": "http://patches.dpdk.org/api/series/1120/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=1120",
            "date": "2018-08-30T22:35:07",
            "name": "netvsc changes for 18.11",
            "version": 1,
            "mbox": "http://patches.dpdk.org/series/1120/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/44060/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/44060/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 4C3C85F45;\n\tFri, 31 Aug 2018 00:35:33 +0200 (CEST)",
            "from mail-pg1-f193.google.com (mail-pg1-f193.google.com\n\t[209.85.215.193]) by dpdk.org (Postfix) with ESMTP id 0174F5F16\n\tfor <dev@dpdk.org>; Fri, 31 Aug 2018 00:35:26 +0200 (CEST)",
            "by mail-pg1-f193.google.com with SMTP id l63-v6so32371pga.7\n\tfor <dev@dpdk.org>; Thu, 30 Aug 2018 15:35:26 -0700 (PDT)",
            "from xeon-e3.lan (204-195-22-127.wavecable.com. [204.195.22.127])\n\tby smtp.gmail.com with ESMTPSA id\n\tr19-v6sm11746033pgg.39.2018.08.30.15.35.24\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);\n\tThu, 30 Aug 2018 15:35:24 -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=JZhFUS65XPaBLAkrMvVgBwaMwE7RK9chTiKfCSlJ3rk=;\n\tb=dTmrPpHGc62YIq0viWNs8ak+g6oWK/HprqKF5+XjlpvnkK8goU2b8jw1MXkevFpxaq\n\tJTn4DhbnRmFMqYeRBtb7MQWjAnK2BZUUhrTKA3HkVkDXXV6+Q2sTMXXO/Ko8ZRSbopi8\n\txbhte10wQayVkvy+C3m7OK15QoPsJdhiC/YBt3LoGxSRKo7w7PaOCta0s6UDhuJFhIQS\n\tYgquqrO+VYeENGRdWkha7j453oOln6j1zr0b9FdaJNjQbtKjnZsEEgYBLnvvVc3c/5L2\n\taxPKJy60OxUqe58fjC6CIn7AF7zKhZz31Zw+HxnQu1v+8wrRRFraf5VavyLS8G8F2JYN\n\t905g==",
        "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=JZhFUS65XPaBLAkrMvVgBwaMwE7RK9chTiKfCSlJ3rk=;\n\tb=W2Hamzr5Iz7v2UhDMPhNQmJCyCR0j+Tzdv7oOJx9lMMRTJUeJ0Sg/7wh86nkJIwKfO\n\ttMv0oM0+fbm16y7CUdz59k/NiFiB1BrRgmuvdCa/7qYQNjEriJdDwStDqewWxZRPpfFY\n\tz4SHDhafl3H01b70aEbvIY833wt0NY/MvffBWk5VKfCiwdZ8O+X046ZTrqs8CiCEugbb\n\t393Cd5aTqAHFKCTHEurO9qvTqN5fUDaz3lDrLSeRGkQ1uOWEJRCLhZxvEJPABxma+3Qu\n\tG6+UAclhpwewPchEKcqoFA3wA1uLvrCK1Ner+pSLVktUBGbRVUvRtZM14+vnw8LpbzD0\n\tz1qQ==",
        "X-Gm-Message-State": "APzg51DWGurDaxZnJoO8M/gC0j8fNrzItjd3hQ0ahjSKR7/7ShzpD9Sb\n\tiB8JvOZd7dM65rXxf+Dddw2DVISeCfI/YA==",
        "X-Google-Smtp-Source": "ANB0VdZIYiwVBsxNjxLg2ThrEYRVrtkipAePL9OdyhxMXLk5wiTEA/+UZHRGPPmFXrDX3R3zH1W1yA==",
        "X-Received": "by 2002:a63:4207:: with SMTP id\n\tp7-v6mr11512097pga.201.1535668525507; \n\tThu, 30 Aug 2018 15:35:25 -0700 (PDT)",
        "From": "Stephen Hemminger <stephen@networkplumber.org>",
        "To": "dev@dpdk.org",
        "Cc": "Stephen Hemminger <sthemmin@microsoft.com>",
        "Date": "Thu, 30 Aug 2018 15:35:12 -0700",
        "Message-Id": "<20180830223512.21297-6-stephen@networkplumber.org>",
        "X-Mailer": "git-send-email 2.18.0",
        "In-Reply-To": "<20180830223512.21297-1-stephen@networkplumber.org>",
        "References": "<20180830223512.21297-1-stephen@networkplumber.org>",
        "Subject": "[dpdk-dev] [PATCH 5/5] net/netvsc: integrated VF support",
        "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://mails.dpdk.org/options/dev>,\n\t<mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n\t<mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "From: Stephen Hemminger <sthemmin@microsoft.com>\n\nIntegrate accelerated networking support into netvsc PMD.\nThis allows netvsc to manage VF without using failsafe or vdev_netvsc.\nFor the exception vswitch path some tests like transmit\nget a 22% increase in packets/sec.\nFor the VF path, the code is slightly shorter but has no\nreal change in performance.\n\nPro:\n   * using netvsc is more like other DPDK NIC's\n   * the exception packet uses less CPU\n   * much smaller code size\n   * no locking required on VF transmit/receive path\n   * no legacy Linux network device to get mangled by userspace\n   * much simpler (1K vs 9K) LOC\n   * unified extended statistics\n\nCon:\n   * using netvsc has more complex startup model\n   * no bifurcated driver support\n   * no flow support (since host does not have flow API).\n   * no tunnel offload support\n   * no receive interrupt support\n\nSigned-off-by: Stephen Hemminger <sthemmin@microsoft.com>\n---\n devtools/checkpatches.sh               |   2 +-\n doc/guides/nics/netvsc.rst             |  12 +-\n doc/guides/rel_notes/release_18_11.rst |  14 +-\n drivers/net/netvsc/Makefile            |   1 +\n drivers/net/netvsc/hn_ethdev.c         | 109 +++++-\n drivers/net/netvsc/hn_nvs.c            |  11 +-\n drivers/net/netvsc/hn_nvs.h            |   9 +\n drivers/net/netvsc/hn_rndis.c          |  31 ++\n drivers/net/netvsc/hn_rndis.h          |   1 +\n drivers/net/netvsc/hn_rxtx.c           | 136 ++++---\n drivers/net/netvsc/hn_var.h            |  43 ++-\n drivers/net/netvsc/hn_vf.c             | 512 +++++++++++++++++++++++++\n drivers/net/netvsc/meson.build         |   2 +-\n 13 files changed, 799 insertions(+), 84 deletions(-)\n create mode 100644 drivers/net/netvsc/hn_vf.c",
    "diff": "diff --git a/devtools/checkpatches.sh b/devtools/checkpatches.sh\nindex ba795ad1dc36..db7afc43d4c0 100755\n--- a/devtools/checkpatches.sh\n+++ b/devtools/checkpatches.sh\n@@ -1,4 +1,4 @@\n-#! /bin/sh\n+#! /bin/bash\n # SPDX-License-Identifier: BSD-3-Clause\n # Copyright 2015 6WIND S.A.\n \ndiff --git a/doc/guides/nics/netvsc.rst b/doc/guides/nics/netvsc.rst\nindex c5f9b7c6fa1e..87fabf5b8c80 100644\n--- a/doc/guides/nics/netvsc.rst\n+++ b/doc/guides/nics/netvsc.rst\n@@ -34,14 +34,10 @@ In this release, the hyper PMD driver provides the basic functionality of packet\n *   The maximum number of queues is limited by the host (currently 64).\n     When used with 4.16 kernel only a single queue is available.\n \n-.. note::\n-   This driver is intended for use with **Hyper-V only** and is\n-   not recommended for use on Azure because accelerated Networking\n-   (SR-IOV) is not supported.\n-\n-   On Azure, use the :doc:`vdev_netvsc` which\n-   automatically configures the necessary TAP and failsave drivers.\n-\n+*   This driver supports SR-IOV network acceleration.\n+    If SR-IOV is enabled then the driver will transparently manage the interface,\n+    and send and receive packets using the VF path.\n+    The VDEV_NETVSC and FAILSAFE drivers are *not* used when using netvsc PMD.\n \n Installation\n ------------\ndiff --git a/doc/guides/rel_notes/release_18_11.rst b/doc/guides/rel_notes/release_18_11.rst\nindex 3ae6b3f58b46..95c0801d0e6e 100644\n--- a/doc/guides/rel_notes/release_18_11.rst\n+++ b/doc/guides/rel_notes/release_18_11.rst\n@@ -54,6 +54,12 @@ New Features\n      Also, make sure to start the actual text at the margin.\n      =========================================================\n \n+* **Added support for SR-IOV in netvsc PMD.**\n+\n+  The ``netvsc`` poll mode driver now supports the Accelerated Networking\n+  SR-IOV option in Hyper-V and Azure. This is an alternative to the previous\n+  vdev_netvsc, tap, and failsafe drivers combination.\n+\n \n API Changes\n -----------\n@@ -122,7 +128,7 @@ The libraries prepended with a plus sign were incremented in this version.\n      librte_bus_fslmc.so.1\n      librte_bus_pci.so.1\n      librte_bus_vdev.so.1\n-     librte_bus_vmbus.so.1\n+   + librte_bus_vmbus.so.1\n      librte_cfgfile.so.2\n      librte_cmdline.so.2\n      librte_common_octeontx.so.1\n@@ -159,6 +165,7 @@ The libraries prepended with a plus sign were incremented in this version.\n      librte_pmd_ring.so.2\n      librte_pmd_softnic.so.1\n      librte_pmd_vhost.so.2\n+   + librte_pmd_netvsc.so.1\n      librte_port.so.3\n      librte_power.so.1\n      librte_rawdev.so.1\n@@ -185,6 +192,10 @@ Known Issues\n    Also, make sure to start the actual text at the margin.\n    =========================================================\n \n+* When using SR-IOV (VF) support with netvsc PMD and the Mellanox mlx5 bifurcated\n+  driver; the Linux netvsc device must be brought up before the netvsc device is\n+  unbound and passed to the DPDK.\n+\n \n Tested Platforms\n ----------------\n@@ -204,4 +215,3 @@ Tested Platforms\n    This section is a comment. Do not overwrite or remove it.\n    Also, make sure to start the actual text at the margin.\n    =========================================================\n-\ndiff --git a/drivers/net/netvsc/Makefile b/drivers/net/netvsc/Makefile\nindex 3c713af3c8fc..71482591a9c1 100644\n--- a/drivers/net/netvsc/Makefile\n+++ b/drivers/net/netvsc/Makefile\n@@ -15,6 +15,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_NETVSC_PMD) += hn_ethdev.c\n SRCS-$(CONFIG_RTE_LIBRTE_NETVSC_PMD) += hn_rxtx.c\n SRCS-$(CONFIG_RTE_LIBRTE_NETVSC_PMD) += hn_rndis.c\n SRCS-$(CONFIG_RTE_LIBRTE_NETVSC_PMD) += hn_nvs.c\n+SRCS-$(CONFIG_RTE_LIBRTE_NETVSC_PMD) += hn_vf.c\n \n LDLIBS += -lrte_eal -lrte_mbuf -lrte_mempool -lrte_ring\n LDLIBS += -lrte_ethdev -lrte_net -lrte_kvargs\ndiff --git a/drivers/net/netvsc/hn_ethdev.c b/drivers/net/netvsc/hn_ethdev.c\nindex 63e04a453546..0aa5dab1b4b4 100644\n--- a/drivers/net/netvsc/hn_ethdev.c\n+++ b/drivers/net/netvsc/hn_ethdev.c\n@@ -193,7 +193,7 @@ static int hn_parse_args(const struct rte_eth_dev *dev)\n  */\n int\n hn_dev_link_update(struct rte_eth_dev *dev,\n-\t\t   __rte_unused int wait_to_complete)\n+\t\t   int wait_to_complete)\n {\n \tstruct hn_data *hv = dev->data->dev_private;\n \tstruct rte_eth_link link, old;\n@@ -207,6 +207,8 @@ hn_dev_link_update(struct rte_eth_dev *dev,\n \n \thn_rndis_get_linkspeed(hv);\n \n+\thn_vf_link_update(dev, wait_to_complete);\n+\n \tlink = (struct rte_eth_link) {\n \t\t.link_duplex = ETH_LINK_FULL_DUPLEX,\n \t\t.link_autoneg = ETH_LINK_SPEED_FIXED,\n@@ -245,6 +247,7 @@ static void hn_dev_info_get(struct rte_eth_dev *dev,\n \tdev_info->max_tx_queues = hv->max_queues;\n \n \thn_rndis_get_offload(hv, dev_info);\n+\thn_vf_info_get(hv, dev_info);\n }\n \n static void\n@@ -395,7 +398,7 @@ static int hn_dev_configure(struct rte_eth_dev *dev)\n \t\t}\n \t}\n \n-\treturn 0;\n+\treturn hn_vf_configure(dev, dev_conf);\n }\n \n static int hn_dev_stats_get(struct rte_eth_dev *dev,\n@@ -403,6 +406,8 @@ static int hn_dev_stats_get(struct rte_eth_dev *dev,\n {\n \tunsigned int i;\n \n+\thn_vf_stats_get(dev, stats);\n+\n \tfor (i = 0; i < dev->data->nb_tx_queues; i++) {\n \t\tconst struct hn_tx_queue *txq = dev->data->tx_queues[i];\n \n@@ -465,18 +470,38 @@ hn_dev_stats_reset(struct rte_eth_dev *dev)\n \t}\n }\n \n+static void\n+hn_dev_xstats_reset(struct rte_eth_dev *dev)\n+{\n+\thn_dev_stats_reset(dev);\n+\thn_vf_xstats_reset(dev);\n+}\n+\n+static int\n+hn_dev_xstats_count(struct rte_eth_dev *dev)\n+{\n+\tint ret, count;\n+\n+\tcount = dev->data->nb_tx_queues * RTE_DIM(hn_stat_strings);\n+\tcount += dev->data->nb_rx_queues * RTE_DIM(hn_stat_strings);\n+\n+\tret = hn_vf_xstats_get_names(dev, NULL, 0);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\treturn count + ret;\n+}\n+\n static int\n hn_dev_xstats_get_names(struct rte_eth_dev *dev,\n \t\t\tstruct rte_eth_xstat_name *xstats_names,\n-\t\t\t__rte_unused unsigned int limit)\n+\t\t\tunsigned int limit)\n {\n \tunsigned int i, t, count = 0;\n-\n-\tPMD_INIT_FUNC_TRACE();\n+\tint ret;\n \n \tif (!xstats_names)\n-\t\treturn dev->data->nb_tx_queues * RTE_DIM(hn_stat_strings)\n-\t\t\t+ dev->data->nb_rx_queues * RTE_DIM(hn_stat_strings);\n+\t\treturn hn_dev_xstats_count(dev);\n \n \t/* Note: limit checked in rte_eth_xstats_names() */\n \tfor (i = 0; i < dev->data->nb_tx_queues; i++) {\n@@ -485,6 +510,9 @@ hn_dev_xstats_get_names(struct rte_eth_dev *dev,\n \t\tif (!txq)\n \t\t\tcontinue;\n \n+\t\tif (count >= limit)\n+\t\t\tbreak;\n+\n \t\tfor (t = 0; t < RTE_DIM(hn_stat_strings); t++)\n \t\t\tsnprintf(xstats_names[count++].name,\n \t\t\t\t RTE_ETH_XSTATS_NAME_SIZE,\n@@ -497,6 +525,9 @@ hn_dev_xstats_get_names(struct rte_eth_dev *dev,\n \t\tif (!rxq)\n \t\t\tcontinue;\n \n+\t\tif (count >= limit)\n+\t\t\tbreak;\n+\n \t\tfor (t = 0; t < RTE_DIM(hn_stat_strings); t++)\n \t\t\tsnprintf(xstats_names[count++].name,\n \t\t\t\t RTE_ETH_XSTATS_NAME_SIZE,\n@@ -504,7 +535,12 @@ hn_dev_xstats_get_names(struct rte_eth_dev *dev,\n \t\t\t\t hn_stat_strings[t].name);\n \t}\n \n-\treturn count;\n+\tret = hn_vf_xstats_get_names(dev, xstats_names + count,\n+\t\t\t\t     limit - count);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\treturn count + ret;\n }\n \n static int\n@@ -513,11 +549,9 @@ hn_dev_xstats_get(struct rte_eth_dev *dev,\n \t\t  unsigned int n)\n {\n \tunsigned int i, t, count = 0;\n-\n-\tconst unsigned int nstats =\n-\t\tdev->data->nb_tx_queues * RTE_DIM(hn_stat_strings)\n-\t\t+ dev->data->nb_rx_queues * RTE_DIM(hn_stat_strings);\n+\tconst unsigned int nstats = hn_dev_xstats_count(dev);\n \tconst char *stats;\n+\tint ret;\n \n \tPMD_INIT_FUNC_TRACE();\n \n@@ -548,20 +582,33 @@ hn_dev_xstats_get(struct rte_eth_dev *dev,\n \t\t\t\t(stats + hn_stat_strings[t].offset);\n \t}\n \n-\treturn count;\n+\tret = hn_vf_xstats_get(dev, xstats + count, n - count);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\treturn count + ret;\n }\n \n static int\n hn_dev_start(struct rte_eth_dev *dev)\n {\n \tstruct hn_data *hv = dev->data->dev_private;\n+\tint error;\n \n \tPMD_INIT_FUNC_TRACE();\n \n-\treturn hn_rndis_set_rxfilter(hv,\n-\t\t\t\t     NDIS_PACKET_TYPE_BROADCAST |\n-\t\t\t\t     NDIS_PACKET_TYPE_ALL_MULTICAST |\n-\t\t\t\t     NDIS_PACKET_TYPE_DIRECTED);\n+\terror = hn_rndis_set_rxfilter(hv,\n+\t\t\t\t      NDIS_PACKET_TYPE_BROADCAST |\n+\t\t\t\t      NDIS_PACKET_TYPE_ALL_MULTICAST |\n+\t\t\t\t      NDIS_PACKET_TYPE_DIRECTED);\n+\tif (error)\n+\t\treturn error;\n+\n+\terror = hn_vf_start(dev);\n+\tif (error)\n+\t\thn_rndis_set_rxfilter(hv, 0);\n+\n+\treturn error;\n }\n \n static void\n@@ -572,12 +619,15 @@ hn_dev_stop(struct rte_eth_dev *dev)\n \tPMD_INIT_FUNC_TRACE();\n \n \thn_rndis_set_rxfilter(hv, 0);\n+\thn_vf_stop(dev);\n }\n \n static void\n hn_dev_close(struct rte_eth_dev *dev __rte_unused)\n {\n \tPMD_INIT_LOG(DEBUG, \"close\");\n+\n+\thn_vf_close(dev);\n }\n \n static const struct eth_dev_ops hn_eth_dev_ops = {\n@@ -586,8 +636,7 @@ static const struct eth_dev_ops hn_eth_dev_ops = {\n \t.dev_stop\t\t= hn_dev_stop,\n \t.dev_close\t\t= hn_dev_close,\n \t.dev_infos_get\t\t= hn_dev_info_get,\n-\t.txq_info_get\t\t= hn_dev_tx_queue_info,\n-\t.rxq_info_get\t\t= hn_dev_rx_queue_info,\n+\t.dev_supported_ptypes_get = hn_vf_supported_ptypes,\n \t.promiscuous_enable     = hn_dev_promiscuous_enable,\n \t.promiscuous_disable    = hn_dev_promiscuous_disable,\n \t.allmulticast_enable    = hn_dev_allmulticast_enable,\n@@ -599,10 +648,10 @@ static const struct eth_dev_ops hn_eth_dev_ops = {\n \t.rx_queue_release\t= hn_dev_rx_queue_release,\n \t.link_update\t\t= hn_dev_link_update,\n \t.stats_get\t\t= hn_dev_stats_get,\n+\t.stats_reset            = hn_dev_stats_reset,\n \t.xstats_get\t\t= hn_dev_xstats_get,\n \t.xstats_get_names\t= hn_dev_xstats_get_names,\n-\t.stats_reset            = hn_dev_stats_reset,\n-\t.xstats_reset\t\t= hn_dev_stats_reset,\n+\t.xstats_reset\t\t= hn_dev_xstats_reset,\n };\n \n /*\n@@ -680,6 +729,14 @@ eth_hn_dev_init(struct rte_eth_dev *eth_dev)\n \tif (err)\n \t\treturn err;\n \n+\tstrlcpy(hv->owner.name, eth_dev->device->name,\n+\t\tRTE_ETH_MAX_OWNER_NAME_LEN);\n+\terr = rte_eth_dev_owner_new(&hv->owner.id);\n+\tif (err) {\n+\t\tPMD_INIT_LOG(ERR, \"Can not get owner id\");\n+\t\treturn err;\n+\t}\n+\n \t/* Initialize primary channel input for control operations */\n \terr = rte_vmbus_chan_open(vmbus, &hv->channels[0]);\n \tif (err)\n@@ -715,6 +772,15 @@ eth_hn_dev_init(struct rte_eth_dev *eth_dev)\n \n \thv->max_queues = RTE_MIN(rxr_cnt, (unsigned int)max_chan);\n \n+\t/* If VF was reported but not added, do it now */\n+\tif (hv->vf_present && !hv->vf_dev) {\n+\t\tPMD_INIT_LOG(DEBUG, \"Adding VF device\");\n+\n+\t\terr = hn_vf_add(eth_dev, hv);\n+\t\tif (err)\n+\t\t\tgoto failed;\n+\t}\n+\n \treturn 0;\n \n failed:\n@@ -744,6 +810,7 @@ eth_hn_dev_uninit(struct rte_eth_dev *eth_dev)\n \thn_detach(hv);\n \trte_vmbus_chan_close(hv->primary->chan);\n \trte_free(hv->primary);\n+\trte_eth_dev_owner_delete(hv->owner.id);\n \n \teth_dev->data->mac_addrs = NULL;\n \ndiff --git a/drivers/net/netvsc/hn_nvs.c b/drivers/net/netvsc/hn_nvs.c\nindex a458bb720f82..9690c5f8a3e7 100644\n--- a/drivers/net/netvsc/hn_nvs.c\n+++ b/drivers/net/netvsc/hn_nvs.c\n@@ -532,10 +532,19 @@ void\n hn_nvs_set_datapath(struct hn_data *hv, uint32_t path)\n {\n \tstruct hn_nvs_datapath dp;\n+\tint error;\n+\n+\tPMD_DRV_LOG(DEBUG, \"set datapath %s\",\n+\t\t    path ? \"VF\" : \"Synthetic\");\n \n \tmemset(&dp, 0, sizeof(dp));\n \tdp.type = NVS_TYPE_SET_DATAPATH;\n \tdp.active_path = path;\n \n-\thn_nvs_req_send(hv, &dp, sizeof(dp));\n+\terror = hn_nvs_req_send(hv, &dp, sizeof(dp));\n+\tif (error) {\n+\t\tPMD_DRV_LOG(ERR,\n+\t\t\t    \"send set datapath failed: %d\",\n+\t\t\t    error);\n+\t}\n }\ndiff --git a/drivers/net/netvsc/hn_nvs.h b/drivers/net/netvsc/hn_nvs.h\nindex 984a9c11c51d..2563fd8d8669 100644\n--- a/drivers/net/netvsc/hn_nvs.h\n+++ b/drivers/net/netvsc/hn_nvs.h\n@@ -105,6 +105,12 @@ struct hn_nvs_ndis_init {\n \tuint8_t\t\trsvd[28];\n } __rte_packed;\n \n+struct hn_nvs_vf_association {\n+\tuint32_t\ttype;\t/* NVS_TYPE_VFASSOC_NOTE */\n+\tuint32_t\tallocated;\n+\tuint32_t\tserial;\n+} __rte_packed;\n+\n #define NVS_DATAPATH_SYNTHETIC\t0\n #define NVS_DATAPATH_VF\t\t1\n \n@@ -207,6 +213,9 @@ void\thn_nvs_detach(struct hn_data *hv);\n void\thn_nvs_ack_rxbuf(struct vmbus_channel *chan, uint64_t tid);\n int\thn_nvs_alloc_subchans(struct hn_data *hv, uint32_t *nsubch);\n void\thn_nvs_set_datapath(struct hn_data *hv, uint32_t path);\n+void\thn_nvs_handle_vfassoc(struct rte_eth_dev *dev,\n+\t\t\t      const struct vmbus_chanpkt_hdr *hdr,\n+\t\t\t      const void *data);\n \n static inline int\n hn_nvs_send(struct vmbus_channel *chan, uint16_t flags,\ndiff --git a/drivers/net/netvsc/hn_rndis.c b/drivers/net/netvsc/hn_rndis.c\nindex a5d205ce2636..0610da3e139d 100644\n--- a/drivers/net/netvsc/hn_rndis.c\n+++ b/drivers/net/netvsc/hn_rndis.c\n@@ -914,6 +914,37 @@ int hn_rndis_get_offload(struct hn_data *hv,\n \treturn 0;\n }\n \n+uint32_t\n+hn_rndis_get_ptypes(struct hn_data *hv)\n+{\n+\tstruct ndis_offload hwcaps;\n+\tuint32_t ptypes;\n+\tint error;\n+\n+\tmemset(&hwcaps, 0, sizeof(hwcaps));\n+\n+\terror = hn_rndis_query_hwcaps(hv, &hwcaps);\n+\tif (error) {\n+\t\tPMD_DRV_LOG(ERR, \"hwcaps query failed: %d\", error);\n+\t\treturn RTE_PTYPE_L2_ETHER;\n+\t}\n+\n+\tptypes = RTE_PTYPE_L2_ETHER;\n+\n+\tif (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4)\n+\t\tptypes |= RTE_PTYPE_L3_IPV4;\n+\n+\tif ((hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_TCP4) ||\n+\t    (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_TCP6))\n+\t\tptypes |= RTE_PTYPE_L4_TCP;\n+\n+\tif ((hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_UDP4) ||\n+\t    (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_UDP6))\n+\t\tptypes |= RTE_PTYPE_L4_UDP;\n+\n+\treturn ptypes;\n+}\n+\n int\n hn_rndis_set_rxfilter(struct hn_data *hv, uint32_t filter)\n {\ndiff --git a/drivers/net/netvsc/hn_rndis.h b/drivers/net/netvsc/hn_rndis.h\nindex 01b5120631b1..319b497a7a70 100644\n--- a/drivers/net/netvsc/hn_rndis.h\n+++ b/drivers/net/netvsc/hn_rndis.h\n@@ -24,6 +24,7 @@ int\thn_rndis_query_rsscaps(struct hn_data *hv,\n \t\t\t       unsigned int *rxr_cnt0);\n int\thn_rndis_conf_rss(struct hn_data *hv,\n \t\t\t  const struct rte_eth_rss_conf *rss_conf);\n+uint32_t hn_rndis_get_ptypes(struct hn_data *hv);\n \n #ifdef RTE_LIBRTE_NETVSC_DEBUG_DUMP\n void hn_rndis_dump(const void *buf);\ndiff --git a/drivers/net/netvsc/hn_rxtx.c b/drivers/net/netvsc/hn_rxtx.c\nindex ad22a95f6c27..f4a36641b6c3 100644\n--- a/drivers/net/netvsc/hn_rxtx.c\n+++ b/drivers/net/netvsc/hn_rxtx.c\n@@ -217,6 +217,7 @@ hn_dev_tx_queue_setup(struct rte_eth_dev *dev,\n \tstruct hn_data *hv = dev->data->dev_private;\n \tstruct hn_tx_queue *txq;\n \tuint32_t tx_free_thresh;\n+\tint err;\n \n \tPMD_INIT_FUNC_TRACE();\n \n@@ -246,8 +247,14 @@ hn_dev_tx_queue_setup(struct rte_eth_dev *dev,\n \n \thn_reset_txagg(txq);\n \n-\tdev->data->tx_queues[queue_idx] = txq;\n+\terr = hn_vf_tx_queue_setup(dev, queue_idx, nb_desc,\n+\t\t\t\t     socket_id, tx_conf);\n+\tif (err) {\n+\t\trte_free(txq);\n+\t\treturn err;\n+\t}\n \n+\tdev->data->tx_queues[queue_idx] = txq;\n \treturn 0;\n }\n \n@@ -270,17 +277,6 @@ hn_dev_tx_queue_release(void *arg)\n \trte_free(txq);\n }\n \n-void\n-hn_dev_tx_queue_info(struct rte_eth_dev *dev, uint16_t queue_idx,\n-\t\t     struct rte_eth_txq_info *qinfo)\n-{\n-\tstruct hn_data *hv = dev->data->dev_private;\n-\tstruct hn_tx_queue *txq = dev->data->rx_queues[queue_idx];\n-\n-\tqinfo->conf.tx_free_thresh = txq->free_thresh;\n-\tqinfo->nb_desc = hv->tx_pool->size;\n-}\n-\n static void\n hn_nvs_send_completed(struct rte_eth_dev *dev, uint16_t queue_id,\n \t\t      unsigned long xactid, const struct hn_nvs_rndis_ack *ack)\n@@ -713,6 +709,35 @@ hn_nvs_handle_rxbuf(struct rte_eth_dev *dev,\n \thn_rx_buf_release(rxb);\n }\n \n+/*\n+ * Called when NVS inband events are received.\n+ * Send up a two part message with port_id and the NVS message\n+ * to the pipe to the netvsc-vf-event control thread.\n+ */\n+static void hn_nvs_handle_notify(struct rte_eth_dev *dev,\n+\t\t\t\t const struct vmbus_chanpkt_hdr *pkt,\n+\t\t\t\t const void *data)\n+{\n+\tconst struct hn_nvs_hdr *hdr = data;\n+\n+\tswitch (hdr->type) {\n+\tcase NVS_TYPE_TXTBL_NOTE:\n+\t\t/* Transmit indirection table has locking problems\n+\t\t * in DPDK and therefore not implemented\n+\t\t */\n+\t\tPMD_DRV_LOG(DEBUG, \"host notify of transmit indirection table\");\n+\t\tbreak;\n+\n+\tcase NVS_TYPE_VFASSOC_NOTE:\n+\t\thn_nvs_handle_vfassoc(dev, pkt, data);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\tPMD_DRV_LOG(INFO,\n+\t\t\t    \"got notify, nvs type %u\", hdr->type);\n+\t}\n+}\n+\n struct hn_rx_queue *hn_rx_queue_alloc(struct hn_data *hv,\n \t\t\t\t      uint16_t queue_id,\n \t\t\t\t      unsigned int socket_id)\n@@ -744,13 +769,14 @@ int\n hn_dev_rx_queue_setup(struct rte_eth_dev *dev,\n \t\t      uint16_t queue_idx, uint16_t nb_desc,\n \t\t      unsigned int socket_id,\n-\t\t      const struct rte_eth_rxconf *rx_conf __rte_unused,\n+\t\t      const struct rte_eth_rxconf *rx_conf,\n \t\t      struct rte_mempool *mp)\n {\n \tstruct hn_data *hv = dev->data->dev_private;\n \tchar ring_name[RTE_RING_NAMESIZE];\n \tstruct hn_rx_queue *rxq;\n \tunsigned int count;\n+\tint error = -ENOMEM;\n \n \tPMD_INIT_FUNC_TRACE();\n \n@@ -780,6 +806,11 @@ hn_dev_rx_queue_setup(struct rte_eth_dev *dev,\n \tif (!rxq->rx_ring)\n \t\tgoto fail;\n \n+\terror = hn_vf_rx_queue_setup(dev, queue_idx, nb_desc,\n+\t\t\t\t     socket_id, rx_conf, mp);\n+\tif (error)\n+\t\tgoto fail;\n+\n \tdev->data->rx_queues[queue_idx] = rxq;\n \treturn 0;\n \n@@ -787,7 +818,7 @@ hn_dev_rx_queue_setup(struct rte_eth_dev *dev,\n \trte_ring_free(rxq->rx_ring);\n \trte_free(rxq->event_buf);\n \trte_free(rxq);\n-\treturn -ENOMEM;\n+\treturn error;\n }\n \n void\n@@ -804,6 +835,9 @@ hn_dev_rx_queue_release(void *arg)\n \trxq->rx_ring = NULL;\n \trxq->mb_pool = NULL;\n \n+\thn_vf_rx_queue_release(rxq->hv, rxq->queue_id);\n+\n+\t/* Keep primary queue to allow for control operations */\n \tif (rxq != rxq->hv->primary) {\n \t\trte_free(rxq->event_buf);\n \t\trte_free(rxq);\n@@ -818,32 +852,6 @@ hn_dev_tx_done_cleanup(void *arg, uint32_t free_cnt)\n \treturn hn_process_events(txq->hv, txq->queue_id, free_cnt);\n }\n \n-void\n-hn_dev_rx_queue_info(struct rte_eth_dev *dev, uint16_t queue_idx,\n-\t\t     struct rte_eth_rxq_info *qinfo)\n-{\n-\tstruct hn_rx_queue *rxq = dev->data->rx_queues[queue_idx];\n-\n-\tqinfo->mp = rxq->mb_pool;\n-\tqinfo->scattered_rx = 1;\n-\tqinfo->nb_desc = rte_ring_get_capacity(rxq->rx_ring);\n-}\n-\n-static void\n-hn_nvs_handle_notify(const struct vmbus_chanpkt_hdr *pkthdr,\n-\t\t     const void *data)\n-{\n-\tconst struct hn_nvs_hdr *hdr = data;\n-\n-\tif (unlikely(vmbus_chanpkt_datalen(pkthdr) < sizeof(*hdr))) {\n-\t\tPMD_DRV_LOG(ERR, \"invalid nvs notify\");\n-\t\treturn;\n-\t}\n-\n-\tPMD_DRV_LOG(INFO,\n-\t\t    \"got notify, nvs type %u\", hdr->type);\n-}\n-\n /*\n  * Process pending events on the channel.\n  * Called from both Rx queue poll and Tx cleanup\n@@ -916,7 +924,7 @@ uint32_t hn_process_events(struct hn_data *hv, uint16_t queue_id,\n \t\t\tbreak;\n \n \t\tcase VMBUS_CHANPKT_TYPE_INBAND:\n-\t\t\thn_nvs_handle_notify(pkt, data);\n+\t\t\thn_nvs_handle_notify(dev, pkt, data);\n \t\t\tbreak;\n \n \t\tdefault:\n@@ -1275,7 +1283,9 @@ uint16_t\n hn_xmit_pkts(void *ptxq, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)\n {\n \tstruct hn_tx_queue *txq = ptxq;\n+\tuint16_t queue_id = txq->queue_id;\n \tstruct hn_data *hv = txq->hv;\n+\tstruct rte_eth_dev *vf_dev;\n \tbool need_sig = false;\n \tuint16_t nb_tx;\n \tint ret;\n@@ -1283,6 +1293,15 @@ hn_xmit_pkts(void *ptxq, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)\n \tif (unlikely(hv->closed))\n \t\treturn 0;\n \n+\t/* Transmit over VF if present and up */\n+\tvf_dev = hv->vf_dev;\n+\trte_compiler_barrier();\n+\tif (vf_dev && vf_dev->data->dev_started) {\n+\t\tvoid *sub_q = vf_dev->data->tx_queues[queue_id];\n+\n+\t\treturn (*vf_dev->tx_pkt_burst)(sub_q, tx_pkts, nb_pkts);\n+\t}\n+\n \tif (rte_mempool_avail_count(hv->tx_pool) <= txq->free_thresh)\n \t\thn_process_events(hv, txq->queue_id, 0);\n \n@@ -1304,7 +1323,7 @@ hn_xmit_pkts(void *ptxq, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)\n \t\t\tif (unlikely(!pkt))\n \t\t\t\tbreak;\n \n-\t\t\thn_encap(pkt, txq->queue_id, m);\n+\t\t\thn_encap(pkt, queue_id, m);\n \t\t\thn_append_to_chim(txq, pkt, m);\n \n \t\t\trte_pktmbuf_free(m);\n@@ -1331,7 +1350,7 @@ hn_xmit_pkts(void *ptxq, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)\n \t\t\ttxd->data_size += m->pkt_len;\n \t\t\t++txd->packets;\n \n-\t\t\thn_encap(pkt, txq->queue_id, m);\n+\t\t\thn_encap(pkt, queue_id, m);\n \n \t\t\tret = hn_xmit_sg(txq, txd, m, &need_sig);\n \t\t\tif (unlikely(ret != 0)) {\n@@ -1360,15 +1379,36 @@ hn_recv_pkts(void *prxq, struct rte_mbuf **rx_pkts, uint16_t nb_pkts)\n {\n \tstruct hn_rx_queue *rxq = prxq;\n \tstruct hn_data *hv = rxq->hv;\n+\tstruct rte_eth_dev *vf_dev;\n+\tuint16_t nb_rcv;\n \n \tif (unlikely(hv->closed))\n \t\treturn 0;\n \n-\t/* If ring is empty then process more */\n-\tif (rte_ring_count(rxq->rx_ring) < nb_pkts)\n+\tvf_dev = hv->vf_dev;\n+\trte_compiler_barrier();\n+\n+\tif (vf_dev && vf_dev->data->dev_started) {\n+\t\t/* Normally, with SR-IOV the ring buffer will be empty */\n \t\thn_process_events(hv, rxq->queue_id, 0);\n \n-\t/* Get mbufs off staging ring */\n-\treturn rte_ring_sc_dequeue_burst(rxq->rx_ring, (void **)rx_pkts,\n-\t\t\t\t\t nb_pkts, NULL);\n+\t\t/* Get mbufs some bufs off of staging ring */\n+\t\tnb_rcv = rte_ring_sc_dequeue_burst(rxq->rx_ring,\n+\t\t\t\t\t\t   (void **)rx_pkts,\n+\t\t\t\t\t\t   nb_pkts / 2, NULL);\n+\t\t/* And rest off of VF */\n+\t\tnb_rcv += rte_eth_rx_burst(vf_dev->data->port_id,\n+\t\t\t\t\t   rxq->queue_id,\n+\t\t\t\t\t   rx_pkts + nb_rcv, nb_pkts - nb_rcv);\n+\t} else {\n+\t\t/* If receive ring is not full then get more */\n+\t\tif (rte_ring_count(rxq->rx_ring) < nb_pkts)\n+\t\t\thn_process_events(hv, rxq->queue_id, 0);\n+\n+\t\tnb_rcv = rte_ring_sc_dequeue_burst(rxq->rx_ring,\n+\t\t\t\t\t\t   (void **)rx_pkts,\n+\t\t\t\t\t\t   nb_pkts, NULL);\n+\t}\n+\n+\treturn nb_rcv;\n }\ndiff --git a/drivers/net/netvsc/hn_var.h b/drivers/net/netvsc/hn_var.h\nindex 17b67941dc83..b8d9e5d5c470 100644\n--- a/drivers/net/netvsc/hn_var.h\n+++ b/drivers/net/netvsc/hn_var.h\n@@ -94,8 +94,11 @@ struct hn_rx_bufinfo {\n struct hn_data {\n \tstruct rte_vmbus_device *vmbus;\n \tstruct hn_rx_queue *primary;\n+\tstruct rte_eth_dev *vf_dev;\t\t/* Subordinate device */\n+\trte_spinlock_t  vf_lock;\n \tuint16_t\tport_id;\n \tbool\t\tclosed;\n+\tbool\t\tvf_present;\n \tuint32_t\tlink_status;\n \tuint32_t\tlink_speed;\n \n@@ -124,6 +127,10 @@ struct hn_data {\n \tuint8_t\t\trndis_resp[256];\n \n \tstruct ether_addr mac_addr;\n+\n+\tstruct rte_eth_dev_owner owner;\n+\tstruct rte_intr_handle vf_intr;\n+\n \tstruct vmbus_channel *channels[HN_MAX_CHANNELS];\n };\n \n@@ -160,5 +167,37 @@ int\thn_dev_rx_queue_setup(struct rte_eth_dev *dev,\n \t\t\t      const struct rte_eth_rxconf *rx_conf,\n \t\t\t      struct rte_mempool *mp);\n void\thn_dev_rx_queue_release(void *arg);\n-void\thn_dev_rx_queue_info(struct rte_eth_dev *dev, uint16_t queue_idx,\n-\t\t\t     struct rte_eth_rxq_info *qinfo);\n+\n+void\thn_vf_info_get(struct hn_data *hv,\n+\t\t       struct rte_eth_dev_info *info);\n+int\thn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv);\n+int\thn_vf_configure(struct rte_eth_dev *dev,\n+\t\t\tconst struct rte_eth_conf *dev_conf);\n+const uint32_t *hn_vf_supported_ptypes(struct rte_eth_dev *dev);\n+int\thn_vf_start(struct rte_eth_dev *dev);\n+void\thn_vf_reset(struct rte_eth_dev *dev);\n+void\thn_vf_stop(struct rte_eth_dev *dev);\n+void\thn_vf_close(struct rte_eth_dev *dev);\n+int\thn_vf_link_update(struct rte_eth_dev *dev,\n+\t\t\t  int wait_to_complete);\n+int\thn_vf_tx_queue_setup(struct rte_eth_dev *dev,\n+\t\t\t     uint16_t queue_idx, uint16_t nb_desc,\n+\t\t\t     unsigned int socket_id,\n+\t\t\t     const struct rte_eth_txconf *tx_conf);\n+void\thn_vf_tx_queue_release(struct hn_data *hv, uint16_t queue_id);\n+int\thn_vf_rx_queue_setup(struct rte_eth_dev *dev,\n+\t\t\t     uint16_t queue_idx, uint16_t nb_desc,\n+\t\t\t     unsigned int socket_id,\n+\t\t\t     const struct rte_eth_rxconf *rx_conf,\n+\t\t\t     struct rte_mempool *mp);\n+void\thn_vf_rx_queue_release(struct hn_data *hv, uint16_t queue_id);\n+\n+int\thn_vf_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats);\n+void\thn_vf_stats_reset(struct rte_eth_dev *dev);\n+int\thn_vf_xstats_get_names(struct rte_eth_dev *dev,\n+\t\t\t       struct rte_eth_xstat_name *xstats_names,\n+\t\t\t       unsigned int size);\n+int\thn_vf_xstats_get(struct rte_eth_dev *dev,\n+\t\t\t struct rte_eth_xstat *xstats,\n+\t\t\t unsigned int n);\n+void\thn_vf_xstats_reset(struct rte_eth_dev *dev);\ndiff --git a/drivers/net/netvsc/hn_vf.c b/drivers/net/netvsc/hn_vf.c\nnew file mode 100644\nindex 000000000000..c68d180fd894\n--- /dev/null\n+++ b/drivers/net/netvsc/hn_vf.c\n@@ -0,0 +1,512 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright (c) 2018 Microsoft Corp.\n+ * All rights reserved.\n+ */\n+\n+#include <stdio.h>\n+#include <stdint.h>\n+#include <string.h>\n+#include <stdbool.h>\n+#include <errno.h>\n+#include <unistd.h>\n+#include <dirent.h>\n+#include <sys/types.h>\n+#include <sys/fcntl.h>\n+#include <sys/uio.h>\n+\n+#include <rte_ether.h>\n+#include <rte_ethdev.h>\n+#include <rte_ethdev_driver.h>\n+#include <rte_lcore.h>\n+#include <rte_memory.h>\n+#include <rte_bus_vmbus.h>\n+#include <rte_pci.h>\n+#include <rte_bus_pci.h>\n+#include <rte_log.h>\n+#include <rte_string_fns.h>\n+\n+#include \"hn_logs.h\"\n+#include \"hn_var.h\"\n+#include \"hn_nvs.h\"\n+\n+/* Search for VF with matching MAC address, return port id */\n+static int hn_vf_match(const struct rte_eth_dev *dev)\n+{\n+\tconst struct ether_addr *mac = dev->data->mac_addrs;\n+\tchar buf[32];\n+\tint i;\n+\n+\tether_format_addr(buf, sizeof(buf), mac);\n+\tRTE_ETH_FOREACH_DEV(i) {\n+\t\tconst struct rte_eth_dev *vf_dev = &rte_eth_devices[i];\n+\t\tconst struct ether_addr *vf_mac = vf_dev->data->mac_addrs;\n+\n+\t\tif (vf_dev == dev)\n+\t\t\tcontinue;\n+\n+\t\tether_format_addr(buf, sizeof(buf), vf_mac);\n+\t\tif (is_same_ether_addr(mac, vf_mac))\n+\t\t\treturn i;\n+\t}\n+\treturn -ENOENT;\n+}\n+\n+/*\n+ * Attach new PCI VF device and return the port_id\n+ */\n+static int hn_vf_attach(struct hn_data *hv, uint16_t port_id,\n+\t\t\tstruct rte_eth_dev **vf_dev)\n+{\n+\tstruct rte_eth_dev_owner owner = { .id = RTE_ETH_DEV_NO_OWNER };\n+\tint ret;\n+\n+\tret = rte_eth_dev_owner_get(port_id, &owner);\n+\tif (ret < 0) {\n+\t\tPMD_DRV_LOG(ERR, \"Can not find owner for port %d\", port_id);\n+\t\treturn ret;\n+\t}\n+\n+\tif (owner.id != RTE_ETH_DEV_NO_OWNER) {\n+\t\tPMD_DRV_LOG(ERR, \"Port %u already owned by other device %s\",\n+\t\t\t    port_id, owner.name);\n+\t\treturn -EBUSY;\n+\t}\n+\n+\tret = rte_eth_dev_owner_set(port_id, &hv->owner);\n+\tif (ret < 0) {\n+\t\tPMD_DRV_LOG(ERR, \"Can set owner for port %d\", port_id);\n+\t\treturn ret;\n+\t}\n+\n+\tPMD_DRV_LOG(DEBUG, \"Attach VF device %u\", port_id);\n+\trte_smp_wmb();\n+\t*vf_dev = &rte_eth_devices[port_id];\n+\treturn 0;\n+}\n+\n+/* Add new VF device to synthetic device */\n+int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv)\n+{\n+\tint port, err;\n+\n+\tport = hn_vf_match(dev);\n+\tif (port < 0) {\n+\t\tPMD_DRV_LOG(NOTICE, \"No matching MAC found\");\n+\t\treturn port;\n+\t}\n+\n+\trte_spinlock_lock(&hv->vf_lock);\n+\tif (hv->vf_dev) {\n+\t\tPMD_DRV_LOG(ERR, \"VF already attached\");\n+\t\terr = -EBUSY;\n+\t} else {\n+\t\terr = hn_vf_attach(hv, port, &hv->vf_dev);\n+\t}\n+\n+\tif (err == 0) {\n+\t\tdev->data->dev_flags |= RTE_ETH_DEV_INTR_LSC;\n+\t\thv->vf_intr = (struct rte_intr_handle) {\n+\t\t\t.fd = -1,\n+\t\t\t.type = RTE_INTR_HANDLE_EXT,\n+\t\t};\n+\t\tdev->intr_handle = &hv->vf_intr;\n+\t\thn_nvs_set_datapath(hv, NVS_DATAPATH_VF);\n+\t}\n+\trte_spinlock_unlock(&hv->vf_lock);\n+\n+\treturn err;\n+}\n+\n+/* Remove new VF device */\n+static void hn_vf_remove(struct hn_data *hv)\n+{\n+\tstruct rte_eth_dev *vf_dev;\n+\n+\trte_spinlock_lock(&hv->vf_lock);\n+\tvf_dev = hv->vf_dev;\n+\tif (!vf_dev) {\n+\t\tPMD_DRV_LOG(ERR, \"VF path not active\");\n+\t\trte_spinlock_unlock(&hv->vf_lock);\n+\t\treturn;\n+\t}\n+\n+\t/* Stop incoming packets from arriving on VF */\n+\thn_nvs_set_datapath(hv, NVS_DATAPATH_SYNTHETIC);\n+\thv->vf_dev = NULL;\n+\n+\t/* Give back ownership */\n+\trte_eth_dev_owner_unset(vf_dev->data->port_id, hv->owner.id);\n+\trte_spinlock_unlock(&hv->vf_lock);\n+}\n+\n+/* Handle VF association message from host */\n+void\n+hn_nvs_handle_vfassoc(struct rte_eth_dev *dev,\n+\t\t      const struct vmbus_chanpkt_hdr *hdr,\n+\t\t      const void *data)\n+{\n+\tstruct hn_data *hv = dev->data->dev_private;\n+\tconst struct hn_nvs_vf_association *vf_assoc = data;\n+\n+\tif (unlikely(vmbus_chanpkt_datalen(hdr) < sizeof(*vf_assoc))) {\n+\t\tPMD_DRV_LOG(ERR, \"invalid vf association NVS\");\n+\t\treturn;\n+\t}\n+\n+\tPMD_DRV_LOG(DEBUG, \"VF serial %u %s port %u\",\n+\t\t    vf_assoc->serial,\n+\t\t    vf_assoc->allocated ? \"add to\" : \"remove from\",\n+\t\t    dev->data->port_id);\n+\n+\thv->vf_present = vf_assoc->allocated;\n+\n+\tif (dev->state != RTE_ETH_DEV_ATTACHED)\n+\t\treturn;\n+\n+\tif (vf_assoc->allocated)\n+\t\thn_vf_add(dev, hv);\n+\telse\n+\t\thn_vf_remove(hv);\n+}\n+\n+/*\n+ * Merge the info from the VF and synthetic path.\n+ * use the default config of the VF\n+ * and the minimum number of queues and buffer sizes.\n+ */\n+static void hn_vf_info_merge(struct rte_eth_dev *vf_dev,\n+\t\t\t     struct rte_eth_dev_info *info)\n+{\n+\tstruct rte_eth_dev_info vf_info;\n+\n+\trte_eth_dev_info_get(vf_dev->data->port_id, &vf_info);\n+\n+\tinfo->speed_capa = vf_info.speed_capa;\n+\tinfo->default_rxportconf = vf_info.default_rxportconf;\n+\tinfo->default_txportconf = vf_info.default_txportconf;\n+\n+\tinfo->max_rx_queues = RTE_MIN(vf_info.max_rx_queues,\n+\t\t\t\t      info->max_rx_queues);\n+\tinfo->rx_offload_capa &= vf_info.rx_offload_capa;\n+\tinfo->rx_queue_offload_capa &= vf_info.rx_queue_offload_capa;\n+\tinfo->flow_type_rss_offloads &= vf_info.flow_type_rss_offloads;\n+\n+\tinfo->max_tx_queues = RTE_MIN(vf_info.max_tx_queues,\n+\t\t\t\t      info->max_tx_queues);\n+\tinfo->tx_offload_capa &= vf_info.tx_offload_capa;\n+\tinfo->tx_queue_offload_capa &= vf_info.tx_queue_offload_capa;\n+\n+\tinfo->min_rx_bufsize = RTE_MAX(vf_info.min_rx_bufsize,\n+\t\t\t\t       info->min_rx_bufsize);\n+\tinfo->max_rx_pktlen  = RTE_MAX(vf_info.max_rx_pktlen,\n+\t\t\t\t       info->max_rx_pktlen);\n+}\n+\n+void hn_vf_info_get(struct hn_data *hv, struct rte_eth_dev_info *info)\n+{\n+\tstruct rte_eth_dev *vf_dev;\n+\n+\trte_spinlock_lock(&hv->vf_lock);\n+\tvf_dev = hv->vf_dev;\n+\tif (vf_dev)\n+\t\thn_vf_info_merge(vf_dev, info);\n+\trte_spinlock_unlock(&hv->vf_lock);\n+}\n+\n+int hn_vf_link_update(struct rte_eth_dev *dev,\n+\t\t      int wait_to_complete)\n+{\n+\tstruct hn_data *hv = dev->data->dev_private;\n+\tstruct rte_eth_dev *vf_dev;\n+\tint ret = 0;\n+\n+\trte_spinlock_lock(&hv->vf_lock);\n+\tvf_dev = hv->vf_dev;\n+\tif (vf_dev && vf_dev->dev_ops->link_update)\n+\t\tret = (*vf_dev->dev_ops->link_update)(dev, wait_to_complete);\n+\trte_spinlock_unlock(&hv->vf_lock);\n+\n+\treturn ret;\n+}\n+\n+/* called when VF has link state interrupts enabled */\n+static int hn_vf_lsc_event(uint16_t port_id __rte_unused,\n+\t\t\t   enum rte_eth_event_type event,\n+\t\t\t   void *cb_arg, void *out __rte_unused)\n+{\n+\tstruct rte_eth_dev *dev = cb_arg;\n+\n+\tif (event != RTE_ETH_EVENT_INTR_LSC)\n+\t\treturn 0;\n+\n+\t/* if link state has changed pass on */\n+\tif (hn_dev_link_update(dev, 0) == 0)\n+\t\treturn 0; /* no change */\n+\n+\treturn _rte_eth_dev_callback_process(dev,\n+\t\t\t\t\t     RTE_ETH_EVENT_INTR_LSC,\n+\t\t\t\t\t     NULL);\n+}\n+\n+static int _hn_vf_configure(struct rte_eth_dev *dev,\n+\t\t\t    struct rte_eth_dev *vf_dev,\n+\t\t\t    const struct rte_eth_conf *dev_conf)\n+{\n+\tstruct rte_eth_conf vf_conf = *dev_conf;\n+\tuint16_t vf_port = vf_dev->data->port_id;\n+\tint ret;\n+\n+\tif (dev_conf->intr_conf.lsc &&\n+\t    (vf_dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC)) {\n+\t\tPMD_DRV_LOG(DEBUG, \"enabling LSC for VF %u\",\n+\t\t\t    vf_port);\n+\t\tvf_conf.intr_conf.lsc = 1;\n+\t} else {\n+\t\tPMD_DRV_LOG(DEBUG, \"disabling LSC for VF %u\",\n+\t\t\t    vf_port);\n+\t\tvf_conf.intr_conf.lsc = 0;\n+\t}\n+\n+\tret = rte_eth_dev_configure(vf_port,\n+\t\t\t\t    dev->data->nb_rx_queues,\n+\t\t\t\t    dev->data->nb_tx_queues,\n+\t\t\t\t    &vf_conf);\n+\tif (ret) {\n+\t\tPMD_DRV_LOG(ERR,\n+\t\t\t    \"VF configuration failed: %d\", ret);\n+\t} else if (vf_conf.intr_conf.lsc) {\n+\t\tret = rte_eth_dev_callback_register(vf_port,\n+\t\t\t\t\t\t    RTE_ETH_DEV_INTR_LSC,\n+\t\t\t\t\t\t    hn_vf_lsc_event, dev);\n+\t\tif (ret)\n+\t\t\tPMD_DRV_LOG(ERR,\n+\t\t\t\t    \"Failed to register LSC callback for VF %u\",\n+\t\t\t\t    vf_port);\n+\t}\n+\treturn ret;\n+}\n+\n+/*\n+ * Configure VF if present.\n+ * Force VF to have same number of queues as synthetic device\n+ */\n+int hn_vf_configure(struct rte_eth_dev *dev,\n+\t\t    const struct rte_eth_conf *dev_conf)\n+{\n+\tstruct hn_data *hv = dev->data->dev_private;\n+\tstruct rte_eth_dev *vf_dev;\n+\tint ret = 0;\n+\n+\trte_spinlock_lock(&hv->vf_lock);\n+\tvf_dev = hv->vf_dev;\n+\tif (vf_dev)\n+\t\tret = _hn_vf_configure(dev, vf_dev, dev_conf);\n+\trte_spinlock_unlock(&hv->vf_lock);\n+\treturn ret;\n+}\n+\n+const uint32_t *hn_vf_supported_ptypes(struct rte_eth_dev *dev)\n+{\n+\tstruct hn_data *hv = dev->data->dev_private;\n+\tstruct rte_eth_dev *vf_dev;\n+\tconst uint32_t *ptypes = NULL;\n+\n+\trte_spinlock_lock(&hv->vf_lock);\n+\tvf_dev = hv->vf_dev;\n+\tif (vf_dev && vf_dev->dev_ops->dev_supported_ptypes_get)\n+\t\tptypes = (*vf_dev->dev_ops->dev_supported_ptypes_get)(vf_dev);\n+\trte_spinlock_unlock(&hv->vf_lock);\n+\n+\treturn ptypes;\n+}\n+\n+int hn_vf_start(struct rte_eth_dev *dev)\n+{\n+\tstruct hn_data *hv = dev->data->dev_private;\n+\tstruct rte_eth_dev *vf_dev;\n+\tint ret = 0;\n+\n+\trte_spinlock_lock(&hv->vf_lock);\n+\tvf_dev = hv->vf_dev;\n+\tif (vf_dev)\n+\t\tret = rte_eth_dev_start(vf_dev->data->port_id);\n+\trte_spinlock_unlock(&hv->vf_lock);\n+\treturn ret;\n+}\n+\n+void hn_vf_stop(struct rte_eth_dev *dev)\n+{\n+\tstruct hn_data *hv = dev->data->dev_private;\n+\tstruct rte_eth_dev *vf_dev;\n+\n+\trte_spinlock_lock(&hv->vf_lock);\n+\tvf_dev = hv->vf_dev;\n+\tif (vf_dev)\n+\t\trte_eth_dev_stop(vf_dev->data->port_id);\n+\trte_spinlock_unlock(&hv->vf_lock);\n+}\n+\n+/* If VF is present, then cascade configuration down */\n+#define VF_ETHDEV_FUNC(dev, func)\t\t\t\t\\\n+\t{\t\t\t\t\t\t\t\\\n+\t\tstruct hn_data *hv = (dev)->data->dev_private;\t\\\n+\t\tstruct rte_eth_dev *vf_dev;\t\t\t\\\n+\t\trte_spinlock_lock(&hv->vf_lock);\t\t\\\n+\t\tvf_dev = hv->vf_dev;\t\t\t\t\\\n+\t\tif (vf_dev)\t\t\t\t\t\\\n+\t\t\tfunc(vf_dev->data->port_id);\t\t\\\n+\t\trte_spinlock_unlock(&hv->vf_lock);\t\t\\\n+\t}\n+\n+void hn_vf_reset(struct rte_eth_dev *dev)\n+{\n+\tVF_ETHDEV_FUNC(dev, rte_eth_dev_reset);\n+}\n+\n+void hn_vf_close(struct rte_eth_dev *dev)\n+{\n+\tVF_ETHDEV_FUNC(dev, rte_eth_dev_close);\n+}\n+\n+void hn_vf_stats_reset(struct rte_eth_dev *dev)\n+{\n+\tVF_ETHDEV_FUNC(dev, rte_eth_stats_reset);\n+}\n+\n+int hn_vf_tx_queue_setup(struct rte_eth_dev *dev,\n+\t\t\t uint16_t queue_idx, uint16_t nb_desc,\n+\t\t\t unsigned int socket_id,\n+\t\t\t const struct rte_eth_txconf *tx_conf)\n+{\n+\tstruct hn_data *hv = dev->data->dev_private;\n+\tstruct rte_eth_dev *vf_dev;\n+\tint ret = 0;\n+\n+\trte_spinlock_lock(&hv->vf_lock);\n+\tvf_dev = hv->vf_dev;\n+\tif (vf_dev)\n+\t\tret = rte_eth_tx_queue_setup(vf_dev->data->port_id,\n+\t\t\t\t\t     queue_idx, nb_desc,\n+\t\t\t\t\t     socket_id, tx_conf);\n+\trte_spinlock_unlock(&hv->vf_lock);\n+\treturn ret;\n+}\n+\n+void hn_vf_tx_queue_release(struct hn_data *hv, uint16_t queue_id)\n+{\n+\tstruct rte_eth_dev *vf_dev;\n+\n+\trte_spinlock_lock(&hv->vf_lock);\n+\tvf_dev = hv->vf_dev;\n+\tif (vf_dev && vf_dev->dev_ops->tx_queue_release) {\n+\t\tvoid *subq = vf_dev->data->tx_queues[queue_id];\n+\n+\t\t(*vf_dev->dev_ops->tx_queue_release)(subq);\n+\t}\n+\n+\trte_spinlock_unlock(&hv->vf_lock);\n+}\n+\n+int hn_vf_rx_queue_setup(struct rte_eth_dev *dev,\n+\t\t\t uint16_t queue_idx, uint16_t nb_desc,\n+\t\t\t unsigned int socket_id,\n+\t\t\t const struct rte_eth_rxconf *rx_conf,\n+\t\t\t struct rte_mempool *mp)\n+{\n+\tstruct hn_data *hv = dev->data->dev_private;\n+\tstruct rte_eth_dev *vf_dev;\n+\tint ret = 0;\n+\n+\trte_spinlock_lock(&hv->vf_lock);\n+\tvf_dev = hv->vf_dev;\n+\tif (vf_dev)\n+\t\tret = rte_eth_rx_queue_setup(vf_dev->data->port_id,\n+\t\t\t\t\t     queue_idx, nb_desc,\n+\t\t\t\t\t     socket_id, rx_conf, mp);\n+\trte_spinlock_unlock(&hv->vf_lock);\n+\treturn ret;\n+}\n+\n+void hn_vf_rx_queue_release(struct hn_data *hv, uint16_t queue_id)\n+{\n+\tstruct rte_eth_dev *vf_dev;\n+\n+\trte_spinlock_lock(&hv->vf_lock);\n+\tvf_dev = hv->vf_dev;\n+\tif (vf_dev && vf_dev->dev_ops->rx_queue_release) {\n+\t\tvoid *subq = vf_dev->data->rx_queues[queue_id];\n+\n+\t\t(*vf_dev->dev_ops->rx_queue_release)(subq);\n+\t}\n+\trte_spinlock_unlock(&hv->vf_lock);\n+}\n+\n+int hn_vf_stats_get(struct rte_eth_dev *dev,\n+\t\t    struct rte_eth_stats *stats)\n+{\n+\tstruct hn_data *hv = dev->data->dev_private;\n+\tstruct rte_eth_dev *vf_dev;\n+\tint ret = 0;\n+\n+\trte_spinlock_lock(&hv->vf_lock);\n+\tvf_dev = hv->vf_dev;\n+\tif (vf_dev)\n+\t\tret = rte_eth_stats_get(vf_dev->data->port_id, stats);\n+\trte_spinlock_unlock(&hv->vf_lock);\n+\treturn ret;\n+}\n+\n+int hn_vf_xstats_get_names(struct rte_eth_dev *dev,\n+\t\t\t   struct rte_eth_xstat_name *names,\n+\t\t\t   unsigned int n)\n+{\n+\tstruct hn_data *hv = dev->data->dev_private;\n+\tstruct rte_eth_dev *vf_dev;\n+\tint i, count = 0;\n+\tchar tmp[RTE_ETH_XSTATS_NAME_SIZE];\n+\n+\trte_spinlock_lock(&hv->vf_lock);\n+\tvf_dev = hv->vf_dev;\n+\tif (vf_dev && vf_dev->dev_ops->xstats_get_names)\n+\t\tcount = vf_dev->dev_ops->xstats_get_names(vf_dev, names, n);\n+\trte_spinlock_unlock(&hv->vf_lock);\n+\n+\t/* add vf_ prefix to xstat names */\n+\tif (names) {\n+\t\tfor (i = 0; i < count; i++) {\n+\t\t\tsnprintf(tmp, sizeof(tmp), \"vf_%s\", names[i].name);\n+\t\t\tstrlcpy(names[i].name, tmp, sizeof(names[i].name));\n+\t\t}\n+\t}\n+\n+\treturn count;\n+}\n+\n+int hn_vf_xstats_get(struct rte_eth_dev *dev,\n+\t\t     struct rte_eth_xstat *xstats,\n+\t\t     unsigned int n)\n+{\n+\tstruct hn_data *hv = dev->data->dev_private;\n+\tstruct rte_eth_dev *vf_dev;\n+\tint count = 0;\n+\n+\trte_spinlock_lock(&hv->vf_lock);\n+\tvf_dev = hv->vf_dev;\n+\tif (vf_dev && vf_dev->dev_ops->xstats_get)\n+\t\tcount = vf_dev->dev_ops->xstats_get(vf_dev, xstats, n);\n+\trte_spinlock_unlock(&hv->vf_lock);\n+\n+\treturn count;\n+}\n+\n+void hn_vf_xstats_reset(struct rte_eth_dev *dev)\n+{\n+\tstruct hn_data *hv = dev->data->dev_private;\n+\tstruct rte_eth_dev *vf_dev;\n+\n+\trte_spinlock_lock(&hv->vf_lock);\n+\tvf_dev = hv->vf_dev;\n+\tif (vf_dev && vf_dev->dev_ops->xstats_reset)\n+\t\tvf_dev->dev_ops->xstats_reset(vf_dev);\n+\trte_spinlock_unlock(&hv->vf_lock);\n+}\ndiff --git a/drivers/net/netvsc/meson.build b/drivers/net/netvsc/meson.build\nindex a717cdd48365..c8426971624f 100644\n--- a/drivers/net/netvsc/meson.build\n+++ b/drivers/net/netvsc/meson.build\n@@ -3,7 +3,7 @@\n \n build = dpdk_conf.has('RTE_LIBRTE_VMBUS_BUS')\n version = 2\n-sources = files('hn_ethdev.c', 'hn_rxtx.c', 'hn_rndis.c', 'hn_nvs.c')\n+sources = files('hn_ethdev.c', 'hn_rxtx.c', 'hn_rndis.c', 'hn_nvs.c', 'hn_vf.c')\n \n deps += ['bus_vmbus' ]\n \n",
    "prefixes": [
        "5/5"
    ]
}