From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <matan@mellanox.com>
Received: from EUR02-HE1-obe.outbound.protection.outlook.com
 (mail-eopbgr10064.outbound.protection.outlook.com [40.107.1.64])
 by dpdk.org (Postfix) with ESMTP id 354621B2EF
 for <dev@dpdk.org>; Thu, 18 Jan 2018 14:52:04 +0100 (CET)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Mellanox.com;
 s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version;
 bh=sj1sfOkHSnabXiWRwHkETv01QtbEB5YtE2EVse5Qwfs=;
 b=j2odN0Oy7gy9Ot68VWi21bj24CMPPvtKu2dKgNbFklTH3s4+HCxeVg1Zuy7e/Gw1o3DqMNgQiU8Uoy9YQ0rQA06XOnA3lRKbAY/EnqKhs7pUcYUPC7+pfaqe/vvzkoUrydCPL/su5kweb4DIA62brM7Gj4yIEJJRNI9F2+qiruM=
Authentication-Results: spf=none (sender IP is )
 smtp.mailfrom=matan@mellanox.com; 
Received: from mellanox.com (37.142.13.130) by
 AM6PR0502MB3798.eurprd05.prod.outlook.com (2603:10a6:209:a::27) with
 Microsoft SMTP Server (version=TLS1_2,
 cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.407.7; Thu, 18
 Jan 2018 13:52:02 +0000
From: Matan Azrad <matan@mellanox.com>
To: Ferruh Yigit <ferruh.yigit@intel.com>,
 Adrien Mazarguil <adrien.mazarguil@6wind.com>,
 Gaetan Rivet <gaetan.rivet@6wind.com>
Cc: Thomas Monjalon <thomas@monjalon.net>,
	dev@dpdk.org
Date: Thu, 18 Jan 2018 13:51:43 +0000
Message-Id: <1516283506-21198-6-git-send-email-matan@mellanox.com>
X-Mailer: git-send-email 1.8.3.1
In-Reply-To: <1516283506-21198-1-git-send-email-matan@mellanox.com>
References: <1516269709-15252-1-git-send-email-matan@mellanox.com>
 <1516283506-21198-1-git-send-email-matan@mellanox.com>
MIME-Version: 1.0
Content-Type: text/plain
X-Originating-IP: [37.142.13.130]
X-ClientProxiedBy: VI1PR08CA0115.eurprd08.prod.outlook.com
 (2603:10a6:800:d4::17) To AM6PR0502MB3798.eurprd05.prod.outlook.com
 (2603:10a6:209:a::27)
X-MS-PublicTrafficType: Email
X-MS-Office365-Filtering-HT: Tenant
X-MS-Office365-Filtering-Correlation-Id: 6cd011ee-6c21-44fc-c6b1-08d55e7aa73d
X-Microsoft-Antispam: UriScan:; BCL:0; PCL:0;
 RULEID:(7020095)(4652020)(4534125)(4602075)(4627221)(201703031133081)(201702281549075)(48565401081)(5600026)(4604075)(2017052603307)(7153060)(7193020);
 SRVR:AM6PR0502MB3798; 
X-Microsoft-Exchange-Diagnostics: 1; AM6PR0502MB3798;
 3:ZcA+6L4MJpnlF6uKc58WknU5BWmrNeYmWDx0A/L51pvz7TGXiLXRjOl1MZHR+yRbb7uXOpaWfjPLh+ekF6dwfLjMIqTs6ETEK+EIL21hSRvdMQiMLUTUwyec0mw10us4TRgt3ujGMHQB/Wmr0WggpVqkFLL7O9prKlC1Ujn652vUlA1jbIpiCrqAks9rArEfTrKbef8SslWdAzFSFPV8RqzpZnu88F9w/FqzHMRPOOV4Zp/Lb/Ikyck8wrHSWgkK;
 25:DnvkQwXgkJ4xsFKdBocd3+6FYdB6sk41B8l2l26bgKiyG4J0Pfdc2ksUtqthvOaO+UXFm19fRJGV77Hg2mdjoVff9H1hEFlkBNtfETCKSrVUj2i6RXVEZziS2rjbiWYH7C7rk147xiDB3qV5/K/6tg+3Us9dAK8JykCl1OckH6fog7x7BWcihefPYGuSUGyR4x2ZC/VN9dL0MCo8f38xdOQ7eehO3xTVuA4QQ07x4l+pQbkhpKT2CQ4PIDSi2lfHZTy6GJemVZ5k4DlIJjq2BylivFkmyknTJ22/PmQZ2HjGE0VnHwFU3YeOc0Y1OsyMLKfLqZr+yO/pGjBNUi9cVA==;
 31:2QIiWuKopnVMXoumHiNV0426xdA8J5B8jNsC7pZTxOZbTMzYzTbvfzIjiG64bhHX9g6ZP+zCxfU5Vst1OnNCDLQ05C0cg3K2UEcLhZ7mqc6ogEApqex4JE4Uh4/KGDH+J6PG95CO0lLbY+JK9Rwfa0aQBIsQ7wruO6J8UoBuDavaiOlgWwXId7zOsFkEftmfdubwH2kCBy8K2njlvwOSV3o8hN/+psBggAFlVwDHFUw=
X-MS-TrafficTypeDiagnostic: AM6PR0502MB3798:
X-LD-Processed: a652971c-7d2e-4d9b-a6a4-d149256f461b,ExtAddr
X-Microsoft-Exchange-Diagnostics: 1; AM6PR0502MB3798;
 20:Tgxhs3U/1XCVkY574Y21soaABCVn6jEVljmO+8Rjf9Wu5fZF5l7O4xLYhdhUEEjp5sgamGFFoXDCRKTzfmgIDR7USb5wuiS2ntNCoUv++bxhOLD159evKvDCbF/4fdXs6vDcGnN3idKKIRkvroZA6Dptv5rDcy4wwZdQcnsW/SX6rv22pygmSPesJtNbEaPP7BAUNn5GxaMIS3ANHeMyrjX7D1amE/BeVQJk6KhUnOeum2u2BwAZ/shY2WIULhDrM3tHqZ7w+k16PlsxgdSNtb6E9s3s/WIrK18nHKLZTlhSePZdJBPXvCg1+m8ZFnT3UVqPnUjS9z8A2A7g1ocDtpJkna2NsGe4OMP4Yb/a0T/snwJx95lgMWS7c27L0hx4l0cYa1wFKLteLNLINMvncQ556Lm+mavbs7ckZC0R/IgsPRnChoCVjHcxyo86SPnmDtiGksKR3I6i8UBRCzuuZXaK5R0ekdWvVNK6uUZfL3Gdw2oleZPobwAUBgJbEROH;
 4:8sWWLEiNAUM0Uzc5FqfJkrjIEwfrsbHG4NIckkuA6jeMSTLYIzq0upUGoeNkNITaClr02HH/UNMNfI37z81c7anO9zAVdGbIvoYtk8SE7tEvpxo8+UYqG7szNs3GxgBsjch1Y6Bc0hgC3yOUfjtqV9Tt41NHgkVemmz8nn4w9aLlQw5ABt5XmIjjAl2ZTp1SJQx8wgtmVbrFYHdN/3gT1UNPxzde/8z6r45FhANpcR16Y4WYu1eL6wV/1SbRvaO2gx4UoNF3K0U79xARml/2Re28387OHmafcqpmtAOh5TkaLhMvEu+HfiGQSPuY6rSaKeQwqgy3Pa0hXFaRysEbbvMF/TBNhv2M2aEjoHW+8gg=
X-Microsoft-Antispam-PRVS: <AM6PR0502MB3798AE12EEDF3B5FD1C8003DD2E80@AM6PR0502MB3798.eurprd05.prod.outlook.com>
X-Exchange-Antispam-Report-Test: UriScan:(28532068793085)(278428928389397);
X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0;
 RULEID:(6040470)(2401047)(8121501046)(5005006)(3002001)(10201501046)(3231023)(944501161)(93006095)(93001095)(6055026)(6041268)(20161123562045)(20161123560045)(201703131423095)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(20161123564045)(20161123558120)(6072148)(201708071742011);
 SRVR:AM6PR0502MB3798; BCL:0; PCL:0; RULEID:(100000803101)(100110400095);
 SRVR:AM6PR0502MB3798; 
X-Forefront-PRVS: 05568D1FF7
X-Forefront-Antispam-Report: SFV:NSPM;
 SFS:(10009020)(366004)(396003)(346002)(39380400002)(39860400002)(376002)(199004)(189003)(386003)(2906002)(51416003)(81156014)(81166006)(5660300001)(305945005)(4326008)(21086003)(7696005)(106356001)(8676002)(68736007)(26005)(69596002)(6306002)(76176011)(59450400001)(105586002)(86362001)(97736004)(3846002)(6116002)(575784001)(52116002)(53936002)(6666003)(50226002)(55016002)(4720700003)(33026002)(7736002)(2950100002)(36756003)(47776003)(478600001)(16526018)(8936002)(66066001)(316002)(966005)(110136005)(48376002)(50466002)(16586007)(5890100001)(25786009);
 DIR:OUT; SFP:1101; SCL:1; SRVR:AM6PR0502MB3798; H:mellanox.com; FPR:; SPF:None;
 PTR:InfoNoRecords; A:1; MX:1; LANG:en; 
Received-SPF: None (protection.outlook.com: mellanox.com does not designate
 permitted sender hosts)
X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; AM6PR0502MB3798;
 23:aBBzhKScuCFwLMK3nba01CHMHxA+QOGuMfpdbJ4?=
 =?us-ascii?Q?ybk6MTNLn5bfwdKG94hGuXqtauyiJBrrBitA12GxTeBPEpigioSPGL9riH++?=
 =?us-ascii?Q?xxtob2gosg40XPb5v5OkWJpCtSZ2Un0Ro6jleQk5kzFwS24GQmJ2vcRcEOuY?=
 =?us-ascii?Q?ryfwuVrzxMMLrkFLlKvMeeSeB/RTYUt8jJYE3bdd8v4wWC8wVjkCbCJB2Dgi?=
 =?us-ascii?Q?oGssqahpF6OQXajnMV1t3P4Rah4ukuWTW0HejE8+H5Wqm0jrvG15DIuVQ7yW?=
 =?us-ascii?Q?FJbstkt57e6XwnTCjziGOwpZA5u5coWivcYbndeO9qObQ92OKNXb7nqss1W8?=
 =?us-ascii?Q?tku/vBco7zfXEJSgojFSVOMsvr98KqoxRfHI/aSmhh1Y0i+hoP8s2vj5Pp2c?=
 =?us-ascii?Q?OCz/VuNiDPLwNhO/mrRXbKJvpxo/1G7ndMoPvr2MoiL2rR5PuaHNSowU8dW8?=
 =?us-ascii?Q?JjWZVojSN715CS5NAKDxTCM45v51B8TpRz+hrI0iKUzDsW0XrEt/3+i6dFM5?=
 =?us-ascii?Q?BnN5oJvF0G64GlMTwaTwP9IYuRS7x1S0ziZ/r08YwlKudNT8yl8mKWvovQxI?=
 =?us-ascii?Q?qXUrpHOr7kN3REXVYRmZjxIc/C2G0px+owXbhd/eXn3ZaKXdsNCooVHXNlCk?=
 =?us-ascii?Q?FCFG30V04OWOVf0aJGxysMRxRclIRppsi+b8Gqo1nGpYgLC8+8+sulYqabe/?=
 =?us-ascii?Q?rapE6VbHyLkFu72wOWMhQ5AOGRQCyPFtOcg9h5gFz6n+numzP0HSSNk/i/Pg?=
 =?us-ascii?Q?sIzDZwf3uSDu+LQr+QvdSmOfxeEekSrN64GZNWjbjDQpi13zTA1rTOk2I6bJ?=
 =?us-ascii?Q?Y4bjqfKE1znEjgGAkHMyQ/c5PRHZe6awomJCuYaL/m1E+VemazLrT2S5JyF2?=
 =?us-ascii?Q?GisNtfs5rpTHFBxhcJBRFLbxRYho8Tql6PT+nRxtUVYGNYDEp+U9NQ4DNyaM?=
 =?us-ascii?Q?Xwe9olyD2jhZgaaqa459YJnY8BIJzS0mRTXXAjG50nC5DgVrkuz1hj/kWl/s?=
 =?us-ascii?Q?Oim1H0GfXwdSkloB7nz6Sb9sB0EWoNuV7py5wdo7QAL/G3xfPpJrLlRnKDDu?=
 =?us-ascii?Q?UdkP96BdDGyZDOja0IlGQEuIXLFNOfDt6X7K/VzFmVCA7NZA+wEyMUxeMxcQ?=
 =?us-ascii?Q?qPbGgfKk8QwiT8xmG2NoQraqOuSUQM6q+b5M3NpTbJwiROFxtwG545Y/PtC1?=
 =?us-ascii?Q?FUFdpSH5yrc5Lp4V3PJ8iWtYOy5qcSi7rdpvRUUGX+PWDiOx119jCMOn1ujW?=
 =?us-ascii?Q?KwoT6H+pJvhWnKtXOvayqJqjWw3rhJd30LtKuYOxv?=
X-Microsoft-Exchange-Diagnostics: 1; AM6PR0502MB3798;
 6:NwqtTOEqi0gE1L5F/jNQvJ0nuT+zFzgofRYtAztRboTe5mBGsQzE6exNv+viqE1xpoCp68F+Ou/3crvmXV4VbKCIi9ZLIog76DpI1J66Hg8NbtVyiinHtuNCxnTSBbgYAqwXEKvZV/oKjW5/Vdo1WGeYipD39P43cEQ2jhVZBpmK7uq3U8NCDf6fxgeGolP3PsfInecMb4/ITfGtvEMTE366VJAHr9o7lWvnqL0bkqT9+OdCUNNO7ULNVmxE+S4BIjQv05yEfjA6BbHcESV9dtZTmXBEXdLUzmzO3D+LMwgHiMHezDdDnKK25rfDzZ2pHbar8c1ynyiqE3rQESozfnIG6/Ozmpw7DHs5HLoT274=;
 5:WNY1tnKAkR36uBF7DtWMM4C1zglnT37DtWVgvcNN+a6dPQWYlXK/CJf8mXLzq0uuSeHozSJu1Qneuvsj0U62xWGrCo5WF5EoJK9J9RR3HQExMqmBgtzBpJlFdQxaJzHw3FaJl/vbGf99mFUW08purQLpMEWzJJmsdW4FDnOmcf0=;
 24:384bxwl8mpkhmVWsCJxSYrPMFBuFsnNGXMbWo90Dao2vsfnpFILChrVjsAXuMagWk4YFhU8i+lIrWk0wh64sjjLLWoutexUsMokIBj5vRM8=;
 7:WT99XFKwdNNdcXyX6NX1zFjdgGBUxQzbkfDqf4DDFYizCRj1aRkioCb2Tcqywl7yd192xRajo/YpmwOp2rnyjv+lqkvReE3cTolrYmnHHlvfv9eqXPXBMIt9rk4suTW7+jAgG2uic7EkyY/uMR+1kDq4D22txShRybRlozZE7qtFCQH6kGWanm7TI0JvuXLUhDYocSrU04tA7n0BIqFIbxuWB/FdeS9C3MfLYsc+lX6qcMlycfJ0xf3FsoRVczDO
SpamDiagnosticOutput: 1:99
SpamDiagnosticMetadata: NSPM
X-OriginatorOrg: Mellanox.com
X-MS-Exchange-CrossTenant-OriginalArrivalTime: 18 Jan 2018 13:52:02.0470 (UTC)
X-MS-Exchange-CrossTenant-Network-Message-Id: 6cd011ee-6c21-44fc-c6b1-08d55e7aa73d
X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted
X-MS-Exchange-CrossTenant-Id: a652971c-7d2e-4d9b-a6a4-d149256f461b
X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM6PR0502MB3798
Subject: [dpdk-dev] [PATCH v6 5/8] net/vdev_netvsc: implement core
	functionality
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>,
 <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>,
 <mailto:dev-request@dpdk.org?subject=subscribe>
X-List-Received-Date: Thu, 18 Jan 2018 13:52:04 -0000

As described in more details in the attached documentation (see patch
contents), this virtual device driver manages NetVSC interfaces in virtual
machines hosted by Hyper-V/Azure platforms.

This driver does not manage traffic nor Ethernet devices directly; it acts
as a thin configuration layer that automatically instantiates and controls
fail-safe PMD instances combining tap and PCI sub-devices, so that each
NetVSC interface is exposed as a single consolidated port to DPDK
applications.

PCI sub-devices being hot-pluggable (e.g. during VM migration),
applications automatically benefit from increased throughput when present
and automatic fallback on NetVSC otherwise without interruption thanks to
fail-safe's hot-plug handling.

Once initialized, the sole job of the vdev_netvsc driver is to regularly
scan for PCI devices to associate with NetVSC interfaces and feed their
addresses to corresponding fail-safe instances.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Signed-off-by: Matan Azrad <matan@mellanox.com>
---
 doc/guides/nics/vdev_netvsc.rst       |  70 +++++
 drivers/net/vdev_netvsc/Makefile      |   4 +
 drivers/net/vdev_netvsc/vdev_netvsc.c | 550 +++++++++++++++++++++++++++++++++-
 3 files changed, 623 insertions(+), 1 deletion(-)

diff --git a/doc/guides/nics/vdev_netvsc.rst b/doc/guides/nics/vdev_netvsc.rst
index a952908..fde1fb8 100644
--- a/doc/guides/nics/vdev_netvsc.rst
+++ b/doc/guides/nics/vdev_netvsc.rst
@@ -12,9 +12,79 @@ platforms.
 
 .. _Hyper-V: https://docs.microsoft.com/en-us/windows-hardware/drivers/network/overview-of-hyper-v
 
+Implementation details
+----------------------
+
+Each instance of this driver effectively needs to drive two devices: the
+NetVSC interface proper and its SR-IOV VF (referred to as "physical" from
+this point on) counterpart sharing the same MAC address.
+
+Physical devices are part of the host system and cannot be maintained during
+VM migration. From a VM standpoint they appear as hot-plug devices that come
+and go without prior notice.
+
+When the physical device is present, egress and most of the ingress traffic
+flows through it; only multicasts and other hypervisor control still flow
+through NetVSC. Otherwise, NetVSC acts as a fallback for all traffic.
+
+To avoid unnecessary code duplication and ensure maximum performance,
+handling of physical devices is left to their original PMDs; this virtual
+device driver (also known as *vdev*) manages other PMDs as summarized by the
+following block diagram::
+
+         .------------------.
+         | DPDK application |
+         `--------+---------'
+                  |
+           .------+------.
+           | DPDK ethdev |
+           `------+------'       Control
+                  |                 |
+     .------------+------------.    v    .--------------------.
+     |       failsafe PMD      +---------+ vdev_netvsc driver |
+     `--+-------------------+--'         `--------------------'
+        |                   |
+        |          .........|.........
+        |          :        |        :
+   .----+----.     :   .----+----.   :
+   | tap PMD |     :   | any PMD |   :
+   `----+----'     :   `----+----'   : <-- Hot-pluggable
+        |          :        |        :
+ .------+-------.  :  .-----+-----.  :
+ | NetVSC-based |  :  | SR-IOV VF |  :
+ |   netdevice  |  :  |   device  |  :
+ `--------------'  :  `-----------'  :
+                   :.................:
+
+
+This driver implementation may be temporary and should be improved or removed
+either when hot-plug will be fully supported in EAL and bus drivers or when
+a new NetVSC driver will be integrated.
+
 Build options
 -------------
 
 - ``CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD`` (default ``y``)
 
    Toggle compilation of this driver.
+
+Run-time parameters
+-------------------
+
+To invoke this driver, applications have to explicitly provide the
+``--vdev=net_vdev_netvsc`` EAL option.
+
+The following device parameters are supported:
+
+- ``iface`` [string]
+
+  Provide a specific NetVSC interface (netdevice) name to attach this driver
+  to. Can be provided multiple times for additional instances.
+
+- ``mac`` [string]
+
+  Same as ``iface`` except a suitable NetVSC interface is located using its
+  MAC address.
+
+Not specifying either ``iface`` or ``mac`` makes this driver attach itself to
+all NetVSC interfaces found on the system.
diff --git a/drivers/net/vdev_netvsc/Makefile b/drivers/net/vdev_netvsc/Makefile
index 2fb059d..f2b2ac5 100644
--- a/drivers/net/vdev_netvsc/Makefile
+++ b/drivers/net/vdev_netvsc/Makefile
@@ -13,6 +13,9 @@ EXPORT_MAP := rte_pmd_vdev_netvsc_version.map
 CFLAGS += -O3
 CFLAGS += -g
 CFLAGS += -std=c11 -pedantic -Wall -Wextra
+CFLAGS += -D_XOPEN_SOURCE=600
+CFLAGS += -D_BSD_SOURCE
+CFLAGS += -D_DEFAULT_SOURCE
 CFLAGS += $(WERROR_FLAGS)
 
 # Dependencies.
@@ -20,6 +23,7 @@ LDLIBS += -lrte_bus_vdev
 LDLIBS += -lrte_eal
 LDLIBS += -lrte_ethdev
 LDLIBS += -lrte_kvargs
+LDLIBS += -lrte_net
 
 # Source files.
 SRCS-$(CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD) += vdev_netvsc.c
diff --git a/drivers/net/vdev_netvsc/vdev_netvsc.c b/drivers/net/vdev_netvsc/vdev_netvsc.c
index e895b32..21c3265 100644
--- a/drivers/net/vdev_netvsc/vdev_netvsc.c
+++ b/drivers/net/vdev_netvsc/vdev_netvsc.c
@@ -3,17 +3,42 @@
  * Copyright 2017 Mellanox Technologies, Ltd.
  */
 
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/sockios.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/ip.h>
+#include <stdarg.h>
 #include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <unistd.h>
 
+#include <rte_alarm.h>
+#include <rte_bus.h>
 #include <rte_bus_vdev.h>
 #include <rte_common.h>
 #include <rte_config.h>
+#include <rte_dev.h>
+#include <rte_errno.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
 #include <rte_kvargs.h>
 #include <rte_log.h>
 
 #define VDEV_NETVSC_DRIVER net_vdev_netvsc
 #define VDEV_NETVSC_ARG_IFACE "iface"
 #define VDEV_NETVSC_ARG_MAC "mac"
+#define VDEV_NETVSC_PROBE_MS 1000
+
+#define NETVSC_CLASS_ID "{f8615163-df3e-46c5-913f-f2d2f965ed0e}"
 
 #define DRV_LOG(level, ...) \
 	rte_log(RTE_LOG_ ## level, \
@@ -25,12 +50,495 @@
 /** Driver-specific log messages type. */
 static int vdev_netvsc_logtype;
 
+/** Context structure for a vdev_netvsc instance. */
+struct vdev_netvsc_ctx {
+	LIST_ENTRY(vdev_netvsc_ctx) entry; /**< Next entry in list. */
+	unsigned int id;		   /**< Unique ID. */
+	char name[64];			   /**< Unique name. */
+	char devname[64];		   /**< Fail-safe instance name. */
+	char devargs[256];		   /**< Fail-safe device arguments. */
+	char if_name[IF_NAMESIZE];	   /**< NetVSC netdevice name. */
+	unsigned int if_index;		   /**< NetVSC netdevice index. */
+	struct ether_addr if_addr;	   /**< NetVSC MAC address. */
+	int pipe[2];			   /**< Fail-safe communication pipe. */
+	char yield[256];		   /**< PCI sub-device arguments. */
+};
+
+/** Context list is common to all driver instances. */
+static LIST_HEAD(, vdev_netvsc_ctx) vdev_netvsc_ctx_list =
+	LIST_HEAD_INITIALIZER(vdev_netvsc_ctx_list);
+
+/** Number of entries in context list. */
+static unsigned int vdev_netvsc_ctx_count;
+
 /** Number of driver instances relying on context list. */
 static unsigned int vdev_netvsc_ctx_inst;
 
 /**
+ * Destroy a vdev_netvsc context instance.
+ *
+ * @param ctx
+ *   Context to destroy.
+ */
+static void
+vdev_netvsc_ctx_destroy(struct vdev_netvsc_ctx *ctx)
+{
+	if (ctx->pipe[0] != -1)
+		close(ctx->pipe[0]);
+	if (ctx->pipe[1] != -1)
+		close(ctx->pipe[1]);
+	free(ctx);
+}
+
+/**
+ * Iterate over system network interfaces.
+ *
+ * This function runs a given callback function for each netdevice found on
+ * the system.
+ *
+ * @param func
+ *   Callback function pointer. List traversal is aborted when this function
+ *   returns a nonzero value.
+ * @param ...
+ *   Variable parameter list passed as @p va_list to @p func.
+ *
+ * @return
+ *   0 when the entire list is traversed successfully, a negative error code
+ *   in case or failure, or the nonzero value returned by @p func when list
+ *   traversal is aborted.
+ */
+static int
+vdev_netvsc_foreach_iface(int (*func)(const struct if_nameindex *iface,
+				      const struct ether_addr *eth_addr,
+				      va_list ap), ...)
+{
+	struct if_nameindex *iface = if_nameindex();
+	int s = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+	unsigned int i;
+	int ret = 0;
+
+	if (!iface) {
+		ret = -ENOBUFS;
+		DRV_LOG(ERR, "cannot retrieve system network interfaces");
+		goto error;
+	}
+	if (s == -1) {
+		ret = -errno;
+		DRV_LOG(ERR, "cannot open socket: %s", rte_strerror(errno));
+		goto error;
+	}
+	for (i = 0; iface[i].if_name; ++i) {
+		struct ifreq req;
+		struct ether_addr eth_addr;
+		va_list ap;
+
+		strncpy(req.ifr_name, iface[i].if_name, sizeof(req.ifr_name));
+		if (ioctl(s, SIOCGIFHWADDR, &req) == -1) {
+			DRV_LOG(WARNING, "cannot retrieve information about"
+					 " interface \"%s\": %s",
+					 req.ifr_name, rte_strerror(errno));
+			continue;
+		}
+		if (req.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+			DRV_LOG(DEBUG, "interface %s is non-ethernet device",
+				req.ifr_name);
+			continue;
+		}
+		memcpy(eth_addr.addr_bytes, req.ifr_hwaddr.sa_data,
+		       RTE_DIM(eth_addr.addr_bytes));
+		va_start(ap, func);
+		ret = func(&iface[i], &eth_addr, ap);
+		va_end(ap);
+		if (ret)
+			break;
+	}
+error:
+	if (s != -1)
+		close(s);
+	if (iface)
+		if_freenameindex(iface);
+	return ret;
+}
+
+/**
+ * Determine if a network interface is NetVSC.
+ *
+ * @param[in] iface
+ *   Pointer to netdevice description structure (name and index).
+ *
+ * @return
+ *   A nonzero value when interface is detected as NetVSC. In case of error,
+ *   rte_errno is updated and 0 returned.
+ */
+static int
+vdev_netvsc_iface_is_netvsc(const struct if_nameindex *iface)
+{
+	static const char temp[] = "/sys/class/net/%s/device/class_id";
+	char path[sizeof(temp) + IF_NAMESIZE];
+	FILE *f;
+	int ret;
+	int len = 0;
+
+	ret = snprintf(path, sizeof(path), temp, iface->if_name);
+	if (ret == -1 || (size_t)ret >= sizeof(path)) {
+		rte_errno = ENOBUFS;
+		return 0;
+	}
+	f = fopen(path, "r");
+	if (!f) {
+		rte_errno = errno;
+		return 0;
+	}
+	ret = fscanf(f, NETVSC_CLASS_ID "%n", &len);
+	if (ret == EOF)
+		rte_errno = errno;
+	ret = len == (int)strlen(NETVSC_CLASS_ID);
+	fclose(f);
+	return ret;
+}
+
+/**
+ * Retrieve network interface data from sysfs symbolic link.
+ *
+ * @param[out] buf
+ *   Output data buffer.
+ * @param size
+ *   Output buffer size.
+ * @param[in] if_name
+ *   Netdevice name.
+ * @param[in] relpath
+ *   Symbolic link path relative to netdevice sysfs entry.
+ *
+ * @return
+ *   0 on success, a negative error code otherwise.
+ */
+static int
+vdev_netvsc_sysfs_readlink(char *buf, size_t size, const char *if_name,
+			   const char *relpath)
+{
+	int ret;
+
+	ret = snprintf(buf, size, "/sys/class/net/%s/%s", if_name, relpath);
+	if (ret == -1 || (size_t)ret >= size)
+		return -ENOBUFS;
+	ret = readlink(buf, buf, size);
+	if (ret == -1)
+		return -errno;
+	if ((size_t)ret >= size - 1)
+		return -ENOBUFS;
+	buf[ret] = '\0';
+	return 0;
+}
+
+/**
+ * Probe a network interface to associate with vdev_netvsc context.
+ *
+ * This function determines if the network device matches the properties of
+ * the NetVSC interface associated with the vdev_netvsc context and
+ * communicates its bus address to the fail-safe PMD instance if so.
+ *
+ * It is normally used with vdev_netvsc_foreach_iface().
+ *
+ * @param[in] iface
+ *   Pointer to netdevice description structure (name and index).
+ * @param[in] eth_addr
+ *   MAC address associated with @p iface.
+ * @param ap
+ *   Variable arguments list comprising:
+ *
+ *   - struct vdev_netvsc_ctx *ctx:
+ *     Context to associate network interface with.
+ *
+ * @return
+ *   A nonzero value when interface matches, 0 otherwise or in case of
+ *   error.
+ */
+static int
+vdev_netvsc_device_probe(const struct if_nameindex *iface,
+		    const struct ether_addr *eth_addr,
+		    va_list ap)
+{
+	struct vdev_netvsc_ctx *ctx = va_arg(ap, struct vdev_netvsc_ctx *);
+	char buf[RTE_MAX(sizeof(ctx->yield), 256u)];
+	const char *addr;
+	size_t len;
+	int ret;
+
+	/* Skip non-matching or unwanted NetVSC interfaces. */
+	if (ctx->if_index == iface->if_index) {
+		if (!strcmp(ctx->if_name, iface->if_name))
+			return 0;
+		DRV_LOG(DEBUG,
+			"NetVSC interface \"%s\" (index %u) renamed \"%s\"",
+			ctx->if_name, ctx->if_index, iface->if_name);
+		strncpy(ctx->if_name, iface->if_name, sizeof(ctx->if_name));
+		return 0;
+	}
+	if (vdev_netvsc_iface_is_netvsc(iface))
+		return 0;
+	if (!is_same_ether_addr(eth_addr, &ctx->if_addr))
+		return 0;
+	/* Look for associated PCI device. */
+	ret = vdev_netvsc_sysfs_readlink(buf, sizeof(buf), iface->if_name,
+					 "device/subsystem");
+	if (ret)
+		return 0;
+	addr = strrchr(buf, '/');
+	addr = addr ? addr + 1 : buf;
+	if (strcmp(addr, "pci"))
+		return 0;
+	ret = vdev_netvsc_sysfs_readlink(buf, sizeof(buf), iface->if_name,
+					 "device");
+	if (ret)
+		return 0;
+	addr = strrchr(buf, '/');
+	addr = addr ? addr + 1 : buf;
+	len = strlen(addr);
+	if (!len)
+		return 0;
+	/* Send PCI device argument to fail-safe PMD instance. */
+	if (strcmp(addr, ctx->yield))
+		DRV_LOG(DEBUG, "associating PCI device \"%s\" with NetVSC"
+			" interface \"%s\" (index %u)", addr, ctx->if_name,
+			ctx->if_index);
+	memmove(buf, addr, len + 1);
+	addr = buf;
+	buf[len] = '\n';
+	ret = write(ctx->pipe[1], addr, len + 1);
+	buf[len] = '\0';
+	if (ret == -1) {
+		if (errno == EINTR || errno == EAGAIN)
+			return 1;
+		DRV_LOG(WARNING, "cannot associate PCI device name \"%s\" with"
+			" interface \"%s\": %s", addr, ctx->if_name,
+			rte_strerror(errno));
+		return 1;
+	}
+	if ((size_t)ret != len + 1) {
+		/*
+		 * Attempt to override previous partial write, no need to
+		 * recover if that fails.
+		 */
+		ret = write(ctx->pipe[1], "\n", 1);
+		(void)ret;
+		return 1;
+	}
+	fsync(ctx->pipe[1]);
+	memcpy(ctx->yield, addr, len + 1);
+	return 1;
+}
+
+/**
+ * Alarm callback that regularly probes system network interfaces.
+ *
+ * This callback runs at a frequency determined by VDEV_NETVSC_PROBE_MS as
+ * long as an vdev_netvsc context instance exists.
+ *
+ * @param arg
+ *   Ignored.
+ */
+static void
+vdev_netvsc_alarm(__rte_unused void *arg)
+{
+	struct vdev_netvsc_ctx *ctx;
+	int ret;
+
+	LIST_FOREACH(ctx, &vdev_netvsc_ctx_list, entry) {
+		ret = vdev_netvsc_foreach_iface(vdev_netvsc_device_probe, ctx);
+		if (ret)
+			break;
+	}
+	if (!vdev_netvsc_ctx_count)
+		return;
+	ret = rte_eal_alarm_set(VDEV_NETVSC_PROBE_MS * 1000,
+				vdev_netvsc_alarm, NULL);
+	if (ret < 0) {
+		DRV_LOG(ERR, "unable to reschedule alarm callback: %s",
+			rte_strerror(-ret));
+	}
+}
+
+/**
+ * Probe a NetVSC interface to generate a vdev_netvsc context from.
+ *
+ * This function instantiates vdev_netvsc contexts either for all NetVSC
+ * devices found on the system or only a subset provided as device
+ * arguments.
+ *
+ * It is normally used with vdev_netvsc_foreach_iface().
+ *
+ * @param[in] iface
+ *   Pointer to netdevice description structure (name and index).
+ * @param[in] eth_addr
+ *   MAC address associated with @p iface.
+ * @param ap
+ *   Variable arguments list comprising:
+ *
+ *   - const char *name:
+ *     Name associated with current driver instance.
+ *
+ *   - struct rte_kvargs *kvargs:
+ *     Device arguments provided to current driver instance.
+ *
+ *   - unsigned int specified:
+ *     Number of specific netdevices provided as device arguments.
+ *
+ *   - unsigned int *matched:
+ *     The number of specified netdevices matched by this function.
+ *
+ * @return
+ *   A nonzero value when interface matches, 0 otherwise or in case of
+ *   error.
+ */
+static int
+vdev_netvsc_netvsc_probe(const struct if_nameindex *iface,
+			 const struct ether_addr *eth_addr,
+			 va_list ap)
+{
+	const char *name = va_arg(ap, const char *);
+	struct rte_kvargs *kvargs = va_arg(ap, struct rte_kvargs *);
+	unsigned int specified = va_arg(ap, unsigned int);
+	unsigned int *matched = va_arg(ap, unsigned int *);
+	unsigned int i;
+	struct vdev_netvsc_ctx *ctx;
+	int ret;
+
+	/* Probe all interfaces when none are specified. */
+	if (specified) {
+		for (i = 0; i != kvargs->count; ++i) {
+			const struct rte_kvargs_pair *pair = &kvargs->pairs[i];
+
+			if (!strcmp(pair->key, VDEV_NETVSC_ARG_IFACE)) {
+				if (!strcmp(pair->value, iface->if_name))
+					break;
+			} else if (!strcmp(pair->key, VDEV_NETVSC_ARG_MAC)) {
+				struct ether_addr tmp;
+
+				if (sscanf(pair->value,
+					   "%" SCNx8 ":%" SCNx8 ":%" SCNx8 ":"
+					   "%" SCNx8 ":%" SCNx8 ":%" SCNx8,
+					   &tmp.addr_bytes[0],
+					   &tmp.addr_bytes[1],
+					   &tmp.addr_bytes[2],
+					   &tmp.addr_bytes[3],
+					   &tmp.addr_bytes[4],
+					   &tmp.addr_bytes[5]) != 6) {
+					DRV_LOG(ERR,
+						"invalid MAC address format"
+						" \"%s\"",
+						pair->value);
+					return -EINVAL;
+				}
+				if (is_same_ether_addr(eth_addr, &tmp))
+					break;
+			}
+		}
+		if (i == kvargs->count)
+			return 0;
+		++(*matched);
+	}
+	/* Weed out interfaces already handled. */
+	LIST_FOREACH(ctx, &vdev_netvsc_ctx_list, entry)
+		if (ctx->if_index == iface->if_index)
+			break;
+	if (ctx) {
+		if (!specified)
+			return 0;
+		DRV_LOG(WARNING,
+			"interface \"%s\" (index %u) is already handled,"
+			" skipping",
+			iface->if_name, iface->if_index);
+		return 0;
+	}
+	if (!vdev_netvsc_iface_is_netvsc(iface)) {
+		if (!specified)
+			return 0;
+		DRV_LOG(WARNING,
+			"interface \"%s\" (index %u) is not NetVSC,"
+			" skipping",
+			iface->if_name, iface->if_index);
+		return 0;
+	}
+	/* Create interface context. */
+	ctx = calloc(1, sizeof(*ctx));
+	if (!ctx) {
+		ret = -errno;
+		DRV_LOG(ERR, "cannot allocate context for interface \"%s\": %s",
+			iface->if_name, rte_strerror(errno));
+		goto error;
+	}
+	ctx->id = vdev_netvsc_ctx_count;
+	strncpy(ctx->if_name, iface->if_name, sizeof(ctx->if_name));
+	ctx->if_index = iface->if_index;
+	ctx->if_addr = *eth_addr;
+	ctx->pipe[0] = -1;
+	ctx->pipe[1] = -1;
+	ctx->yield[0] = '\0';
+	if (pipe(ctx->pipe) == -1) {
+		ret = -errno;
+		DRV_LOG(ERR,
+			"cannot allocate control pipe for interface \"%s\": %s",
+			ctx->if_name, rte_strerror(errno));
+		goto error;
+	}
+	for (i = 0; i != RTE_DIM(ctx->pipe); ++i) {
+		int flf = fcntl(ctx->pipe[i], F_GETFL);
+
+		if (flf != -1 &&
+		    fcntl(ctx->pipe[i], F_SETFL, flf | O_NONBLOCK) != -1)
+			continue;
+		ret = -errno;
+		DRV_LOG(ERR, "cannot toggle non-blocking flag on control file"
+			" descriptor #%u (%d): %s", i, ctx->pipe[i],
+			rte_strerror(errno));
+		goto error;
+	}
+	/* Generate virtual device name and arguments. */
+	i = 0;
+	ret = snprintf(ctx->name, sizeof(ctx->name), "%s_id%u",
+		       name, ctx->id);
+	if (ret == -1 || (size_t)ret >= sizeof(ctx->name))
+		++i;
+	ret = snprintf(ctx->devname, sizeof(ctx->devname), "net_failsafe_%s",
+		       ctx->name);
+	if (ret == -1 || (size_t)ret >= sizeof(ctx->devname))
+		++i;
+	ret = snprintf(ctx->devargs, sizeof(ctx->devargs),
+		       "fd(%d),dev(net_tap_%s,remote=%s)",
+		       ctx->pipe[0], ctx->name, ctx->if_name);
+	if (ret == -1 || (size_t)ret >= sizeof(ctx->devargs))
+		++i;
+	if (i) {
+		ret = -ENOBUFS;
+		DRV_LOG(ERR, "generated virtual device name or argument list"
+			" too long for interface \"%s\"", ctx->if_name);
+		goto error;
+	}
+	/* Request virtual device generation. */
+	DRV_LOG(DEBUG, "generating virtual device \"%s\" with arguments \"%s\"",
+		ctx->devname, ctx->devargs);
+	vdev_netvsc_foreach_iface(vdev_netvsc_device_probe, ctx);
+	ret = rte_eal_hotplug_add("vdev", ctx->devname, ctx->devargs);
+	if (ret)
+		goto error;
+	LIST_INSERT_HEAD(&vdev_netvsc_ctx_list, ctx, entry);
+	++vdev_netvsc_ctx_count;
+	DRV_LOG(DEBUG, "added NetVSC interface \"%s\" to context list",
+		ctx->if_name);
+	return 0;
+error:
+	if (ctx)
+		vdev_netvsc_ctx_destroy(ctx);
+	return ret;
+}
+
+/**
  * Probe NetVSC interfaces.
  *
+ * This function probes system netdevices according to the specified device
+ * arguments and starts a periodic alarm callback to notify the resulting
+ * fail-safe PMD instances of their sub-devices whereabouts.
+ *
  * @param dev
  *   Virtual device context for driver instance.
  *
@@ -49,12 +557,40 @@
 	const char *args = rte_vdev_device_args(dev);
 	struct rte_kvargs *kvargs = rte_kvargs_parse(args ? args : "",
 						     vdev_netvsc_arg);
+	unsigned int specified = 0;
+	unsigned int matched = 0;
+	unsigned int i;
+	int ret;
 
 	DRV_LOG(DEBUG, "invoked as \"%s\", using arguments \"%s\"", name, args);
 	if (!kvargs) {
 		DRV_LOG(ERR, "cannot parse arguments list");
 		goto error;
 	}
+	for (i = 0; i != kvargs->count; ++i) {
+		const struct rte_kvargs_pair *pair = &kvargs->pairs[i];
+
+		if (!strcmp(pair->key, VDEV_NETVSC_ARG_IFACE) ||
+		    !strcmp(pair->key, VDEV_NETVSC_ARG_MAC))
+			++specified;
+	}
+	rte_eal_alarm_cancel(vdev_netvsc_alarm, NULL);
+	/* Gather interfaces. */
+	ret = vdev_netvsc_foreach_iface(vdev_netvsc_netvsc_probe, name, kvargs,
+					specified, &matched);
+	if (ret < 0)
+		goto error;
+	if (matched < specified)
+		DRV_LOG(WARNING,
+			"some of the specified parameters did not match"
+			" recognized network interfaces");
+	ret = rte_eal_alarm_set(VDEV_NETVSC_PROBE_MS * 1000,
+				vdev_netvsc_alarm, NULL);
+	if (ret < 0) {
+		DRV_LOG(ERR, "unable to schedule alarm callback: %s",
+			rte_strerror(-ret));
+		goto error;
+	}
 error:
 	if (kvargs)
 		rte_kvargs_free(kvargs);
@@ -65,6 +601,9 @@
 /**
  * Remove driver instance.
  *
+ * The alarm callback and underlying vdev_netvsc context instances are only
+ * destroyed after the last PMD instance is removed.
+ *
  * @param dev
  *   Virtual device context for driver instance.
  *
@@ -74,7 +613,16 @@
 static int
 vdev_netvsc_vdev_remove(__rte_unused struct rte_vdev_device *dev)
 {
-	--vdev_netvsc_ctx_inst;
+	if (--vdev_netvsc_ctx_inst)
+		return 0;
+	rte_eal_alarm_cancel(vdev_netvsc_alarm, NULL);
+	while (!LIST_EMPTY(&vdev_netvsc_ctx_list)) {
+		struct vdev_netvsc_ctx *ctx = LIST_FIRST(&vdev_netvsc_ctx_list);
+
+		LIST_REMOVE(ctx, entry);
+		--vdev_netvsc_ctx_count;
+		vdev_netvsc_ctx_destroy(ctx);
+	}
 	return 0;
 }
 
-- 
1.8.3.1