get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 26604,
    "url": "http://patches.dpdk.org/api/patches/26604/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/fa03f1cb8972d92da3bc80d5a43530323e93b738.1499385282.git.gaetan.rivet@6wind.com/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<fa03f1cb8972d92da3bc80d5a43530323e93b738.1499385282.git.gaetan.rivet@6wind.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/fa03f1cb8972d92da3bc80d5a43530323e93b738.1499385282.git.gaetan.rivet@6wind.com",
    "date": "2017-07-07T00:09:25",
    "name": "[dpdk-dev,v6,14/22] net/failsafe: add fail-safe PMD",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "eb0724afb44b144517b01f1d5212757c91d15435",
    "submitter": {
        "id": 269,
        "url": "http://patches.dpdk.org/api/people/269/?format=api",
        "name": "Gaëtan Rivet",
        "email": "gaetan.rivet@6wind.com"
    },
    "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/fa03f1cb8972d92da3bc80d5a43530323e93b738.1499385282.git.gaetan.rivet@6wind.com/mbox/",
    "series": [],
    "comments": "http://patches.dpdk.org/api/patches/26604/comments/",
    "check": "fail",
    "checks": "http://patches.dpdk.org/api/patches/26604/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 [IPv6:::1])\n\tby dpdk.org (Postfix) with ESMTP id 7D4567CFB;\n\tFri,  7 Jul 2017 02:10:43 +0200 (CEST)",
            "from mail-wr0-f176.google.com (mail-wr0-f176.google.com\n\t[209.85.128.176]) by dpdk.org (Postfix) with ESMTP id 09A847CB4\n\tfor <dev@dpdk.org>; Fri,  7 Jul 2017 02:10:16 +0200 (CEST)",
            "by mail-wr0-f176.google.com with SMTP id r103so24426865wrb.0\n\tfor <dev@dpdk.org>; Thu, 06 Jul 2017 17:10:16 -0700 (PDT)",
            "from bidouze.dev.6wind.com (host.78.145.23.62.rev.coltfrance.com.\n\t[62.23.145.78]) by smtp.gmail.com with ESMTPSA id\n\tf45sm2391166wrf.2.2017.07.06.17.10.13\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128);\n\tThu, 06 Jul 2017 17:10:14 -0700 (PDT)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=6wind-com.20150623.gappssmtp.com; s=20150623;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references\n\t:in-reply-to:references;\n\tbh=n8oQhQWbBK++c0lAmHNHg1HYZFSkxIJykp3alQo0EQw=;\n\tb=t9oZgEGYoeMuFyzX10M4FdvagBT0Dh5zHUazV58PfxYhX8Vy5KVCP+xS15zdlPwlN5\n\t9RS1q53X87NLBbV3xT3uD2UzKHmqeZhb+SYc21LRYqyPPQj38T4yd3WDUfiPdyxsex4Y\n\tenkUcp2eP9kT319R1rIKS4AckGUNmSEV2iETmyO3YfB+w7Sz91vgD+9/aNNw1uQxaWuj\n\tdGsriNLv0/QVET7aBh/1fUbxdEvArSF6MDwubAucB7k38BwvH+5XbshHkYWGqnKMWU+I\n\tjF5Q9zm1eJftVJaz2DE3nd+oaUqt+5QZhp9gGkzGP1IfXyixh1Y56XieV4BrpEkVOsSg\n\tj+rQ==",
        "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:in-reply-to:references;\n\tbh=n8oQhQWbBK++c0lAmHNHg1HYZFSkxIJykp3alQo0EQw=;\n\tb=dVt2N+oXCjWq6+jde3aglNgsyRdYNwSEUr+rPAENdi5nIyBVozvMGvzewsss1XqXxO\n\t8erY4gZaWaGSxomQzMcmSIufMcK3pcprla0cr2W3/U2pvbtaVJTOnfE4N3rdVlwzU8Hi\n\tQOxyCpLdQRVoaVcrgBZAKNMfEWXDQmBFCjSn47WBXfw42Ud+BU7D7MMS9IkfN0gWQFPG\n\tFDaahJkgI1tYMw7T+Of0y0QThEY2Nq9CD/bt0XJfLXO0Idmq1dIlcqhj9yRh+uPW4uYa\n\t51CLkq8WVr7HAMeL4HfwgdFDLzyN5+fJSNFAspYPTWGj7sqOy8frRtCKGNHgSOLujKK6\n\ts1TA==",
        "X-Gm-Message-State": "AKS2vOwXuRnVIaPCw+qVvNHoxJFCoFk+KKRVd9W89y2GpJguwejJzDTQ\n\tLg9bmauUCxOrhak/Hws=",
        "X-Received": "by 10.223.171.69 with SMTP id r5mr45286580wrc.57.1499386215556; \n\tThu, 06 Jul 2017 17:10:15 -0700 (PDT)",
        "From": "Gaetan Rivet <gaetan.rivet@6wind.com>",
        "To": "dev@dpdk.org",
        "Cc": "Gaetan Rivet <gaetan.rivet@6wind.com>",
        "Date": "Fri,  7 Jul 2017 02:09:25 +0200",
        "Message-Id": "<fa03f1cb8972d92da3bc80d5a43530323e93b738.1499385282.git.gaetan.rivet@6wind.com>",
        "X-Mailer": "git-send-email 2.1.4",
        "In-Reply-To": [
            "<cover.1499385281.git.gaetan.rivet@6wind.com>",
            "<cover.1499385281.git.gaetan.rivet@6wind.com>"
        ],
        "References": [
            "<cover.1496877105.git.gaetan.rivet@6wind.com>\n\t<cover.1499385281.git.gaetan.rivet@6wind.com>",
            "<cover.1499385281.git.gaetan.rivet@6wind.com>"
        ],
        "Subject": "[dpdk-dev] [PATCH v6 14/22] net/failsafe: add fail-safe PMD",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<http://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": "<http://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": "Introduce the fail-safe poll mode driver initialization and enable its\nbuild infrastructure.\n\nThis PMD allows for applications to benefit from true hot-plugging\nsupport without having to implement it.\n\nIt intercepts and manages Ethernet device removal events issued by\nslave PMDs and re-initializes them transparently when brought back.\nIt also allows defining a contingency to the removal of a device, by\ndesignating a fail-over device that will take on transmitting operations\nif the preferred device is removed.\n\nApplications only see a fail-safe instance, without caring for\nunderlying activity ensuring their continued operations.\n\nSigned-off-by: Gaetan Rivet <gaetan.rivet@6wind.com>\nAcked-by: Olga Shern <olgas@mellanox.com>\n---\n MAINTAINERS                                       |   5 +\n config/common_base                                |   6 +\n doc/guides/nics/fail_safe.rst                     | 133 +++++\n doc/guides/nics/features/failsafe.ini             |  24 +\n doc/guides/nics/index.rst                         |   1 +\n drivers/net/Makefile                              |   2 +\n drivers/net/failsafe/Makefile                     |  76 +++\n drivers/net/failsafe/failsafe.c                   | 231 ++++++++\n drivers/net/failsafe/failsafe_args.c              | 331 +++++++++++\n drivers/net/failsafe/failsafe_eal.c               | 154 +++++\n drivers/net/failsafe/failsafe_ops.c               | 663 ++++++++++++++++++++++\n drivers/net/failsafe/failsafe_private.h           | 227 ++++++++\n drivers/net/failsafe/failsafe_rxtx.c              | 107 ++++\n drivers/net/failsafe/rte_pmd_failsafe_version.map |   4 +\n mk/rte.app.mk                                     |   1 +\n 15 files changed, 1965 insertions(+)\n create mode 100644 doc/guides/nics/fail_safe.rst\n create mode 100644 doc/guides/nics/features/failsafe.ini\n create mode 100644 drivers/net/failsafe/Makefile\n create mode 100644 drivers/net/failsafe/failsafe.c\n create mode 100644 drivers/net/failsafe/failsafe_args.c\n create mode 100644 drivers/net/failsafe/failsafe_eal.c\n create mode 100644 drivers/net/failsafe/failsafe_ops.c\n create mode 100644 drivers/net/failsafe/failsafe_private.h\n create mode 100644 drivers/net/failsafe/failsafe_rxtx.c\n create mode 100644 drivers/net/failsafe/rte_pmd_failsafe_version.map",
    "diff": "diff --git a/MAINTAINERS b/MAINTAINERS\nindex b4424ea..5ae007f 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -336,6 +336,11 @@ F: drivers/net/enic/\n F: doc/guides/nics/enic.rst\n F: doc/guides/nics/features/enic.ini\n \n+Fail-safe PMD\n+M: Gaetan Rivet <gaetan.rivet@6wind.com>\n+F: drivers/net/failsafe/\n+F: doc/guides/nics/fail_safe.rst\n+\n Intel e1000\n M: Wenzhuo Lu <wenzhuo.lu@intel.com>\n F: drivers/net/e1000/\ndiff --git a/config/common_base b/config/common_base\nindex 660588a..f402c4b 100644\n--- a/config/common_base\n+++ b/config/common_base\n@@ -420,6 +420,12 @@ CONFIG_RTE_LIBRTE_PMD_XENVIRT=n\n CONFIG_RTE_LIBRTE_PMD_NULL=y\n \n #\n+# Compile fail-safe PMD\n+#\n+CONFIG_RTE_LIBRTE_PMD_FAILSAFE=y\n+CONFIG_RTE_LIBRTE_PMD_FAILSAFE_DEBUG=n\n+\n+#\n # Do prefetch of packet data within PMD driver receive function\n #\n CONFIG_RTE_PMD_PACKET_PREFETCH=y\ndiff --git a/doc/guides/nics/fail_safe.rst b/doc/guides/nics/fail_safe.rst\nnew file mode 100644\nindex 0000000..056f85f\n--- /dev/null\n+++ b/doc/guides/nics/fail_safe.rst\n@@ -0,0 +1,133 @@\n+..  BSD LICENSE\n+    Copyright 2017 6WIND S.A.\n+\n+    Redistribution and use in source and binary forms, with or without\n+    modification, are permitted provided that the following conditions\n+    are met:\n+\n+    * Redistributions of source code must retain the above copyright\n+    notice, this list of conditions and the following disclaimer.\n+    * Redistributions in binary form must reproduce the above copyright\n+    notice, this list of conditions and the following disclaimer in\n+    the documentation and/or other materials provided with the\n+    distribution.\n+    * Neither the name of 6WIND S.A. nor the names of its\n+    contributors may be used to endorse or promote products derived\n+    from this software without specific prior written permission.\n+\n+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+    \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+\n+Fail-safe poll mode driver library\n+==================================\n+\n+The Fail-safe poll mode driver library (**librte_pmd_failsafe**) is a virtual\n+device that allows using any device supporting hotplug (sudden device removal\n+and plugging on its bus), without modifying other components relying on such\n+device (application, other PMDs).\n+\n+Additionally to the Seamless Hotplug feature, the Fail-safe PMD offers the\n+ability to redirect operations to secondary devices when the primary has been\n+removed from the system.\n+\n+.. note::\n+\n+   The library is enabled by default. You can enable it or disable it manually\n+   by setting the ``CONFIG_RTE_LIBRTE_PMD_FAILSAFE`` configuration option.\n+\n+Features\n+--------\n+\n+The Fail-safe PMD only supports a limited set of features. If you plan to use a\n+device underneath the Fail-safe PMD with a specific feature, this feature must\n+be supported by the Fail-safe PMD to avoid throwing any error.\n+\n+Check the feature matrix for the complete set of supported features.\n+\n+Compilation options\n+-------------------\n+\n+These options can be modified in the ``$RTE_TARGET/build/.config`` file.\n+\n+- ``CONFIG_RTE_LIBRTE_PMD_FAILSAFE`` (default **y**)\n+\n+  Toggle compiling librte_pmd_failsafe itself.\n+\n+- ``CONFIG_RTE_LIBRTE_PMD_FAILSAFE_DEBUG`` (default **n**)\n+\n+  Toggle debugging code.\n+\n+Using the Fail-safe PMD from the EAL command line\n+-------------------------------------------------\n+\n+The Fail-safe PMD can be used like most other DPDK virtual devices, by passing a\n+``--vdev`` parameter to the EAL when starting the application. The device name\n+must start with the *net_failsafe* prefix, followed by numbers or letters. This\n+name must be unique for each device. Each fail-safe instance must have at least one\n+sub-device, up to ``RTE_MAX_ETHPORTS-1``.\n+\n+A sub-device can be any legal DPDK device, including possibly another fail-safe\n+instance.\n+\n+Fail-safe command line parameters\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+\n+- **dev(<iface>)** parameter\n+\n+  This parameter allows the user to define a sub-device. The ``<iface>`` part of\n+  this parameter must be a valid device definition. It could be the argument\n+  provided to a ``-w`` PCI device specification or the argument that would be\n+  given to a ``--vdev`` parameter (including a fail-safe).\n+  Enclosing the device definition within parenthesis here allows using\n+  additional sub-device parameters if need be. They will be passed on to the\n+  sub-device.\n+\n+- **mac** parameter [MAC address]\n+\n+  This parameter allows the user to set a default MAC address to the fail-safe\n+  and all of its sub-devices.\n+  If no default mac address is provided, the fail-safe PMD will read the MAC\n+  address of the first of its sub-device to be successfully probed and use it as\n+  its default MAC address, trying to set it to all of its other sub-devices.\n+  If no sub-device was successfully probed at initialization, then a random MAC\n+  address is generated, that will be subsequently applied to all sub-device once\n+  they are probed.\n+\n+Usage example\n+~~~~~~~~~~~~~\n+\n+This section shows some example of using **testpmd** with a fail-safe PMD.\n+\n+#. Request huge pages:\n+\n+   .. code-block:: console\n+\n+      echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages\n+\n+#. Start testpmd\n+\n+   .. code-block:: console\n+\n+      $RTE_TARGET/build/app/testpmd -c 0xff -n 4 --no-pci \\\n+         --vdev='net_failsafe0,mac=de:ad:be:ef:01:02,dev(84:00.0),dev(net_ring0,nodeaction=r1:0:CREATE)' -- \\\n+         -i\n+\n+Using the Fail-safe PMD from an application\n+-------------------------------------------\n+\n+This driver strives to be as seamless as possible to existing applications, in\n+order to propose the hotplug functionality in the easiest way possible.\n+\n+Care must be taken, however, to respect the **ether** API concerning device\n+access, and in particular, using the ``RTE_ETH_FOREACH_DEV`` macro to iterate\n+over ethernet devices, instead of directly accessing them or by writing one's\n+own device iterator.\ndiff --git a/doc/guides/nics/features/failsafe.ini b/doc/guides/nics/features/failsafe.ini\nnew file mode 100644\nindex 0000000..3c52823\n--- /dev/null\n+++ b/doc/guides/nics/features/failsafe.ini\n@@ -0,0 +1,24 @@\n+;\n+; Supported features of the 'fail-safe' poll mode driver.\n+;\n+; Refer to default.ini for the full list of available PMD features.\n+;\n+[Features]\n+Link status          = Y\n+Queue start/stop     = Y\n+MTU update           = Y\n+Jumbo frame          = Y\n+Promiscuous mode     = Y\n+Allmulticast mode    = Y\n+Unicast MAC filter   = Y\n+Multicast MAC filter = Y\n+VLAN filter          = Y\n+Packet type parsing  = Y\n+Basic stats          = Y\n+Stats per queue      = Y\n+ARMv7                = Y\n+ARMv8                = Y\n+Power8               = Y\n+x86-32               = Y\n+x86-64               = Y\n+Usage doc            = Y\ndiff --git a/doc/guides/nics/index.rst b/doc/guides/nics/index.rst\nindex 240d082..17eaaf4 100644\n--- a/doc/guides/nics/index.rst\n+++ b/doc/guides/nics/index.rst\n@@ -64,6 +64,7 @@ Network Interface Controller Drivers\n     vhost\n     vmxnet3\n     pcap_ring\n+    fail_safe\n \n **Figures**\n \ndiff --git a/drivers/net/Makefile b/drivers/net/Makefile\nindex 35ed813..d33c959 100644\n--- a/drivers/net/Makefile\n+++ b/drivers/net/Makefile\n@@ -59,6 +59,8 @@ DIRS-$(CONFIG_RTE_LIBRTE_ENA_PMD) += ena\n DEPDIRS-ena = $(core-libs)\n DIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic\n DEPDIRS-enic = $(core-libs) librte_hash\n+DIRS-$(CONFIG_RTE_LIBRTE_PMD_FAILSAFE) += failsafe\n+DEPDIRS-failsafe = $(core-libs)\n DIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k\n DEPDIRS-fm10k = $(core-libs) librte_hash\n DIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += i40e\ndiff --git a/drivers/net/failsafe/Makefile b/drivers/net/failsafe/Makefile\nnew file mode 100644\nindex 0000000..c759035\n--- /dev/null\n+++ b/drivers/net/failsafe/Makefile\n@@ -0,0 +1,76 @@\n+#   BSD LICENSE\n+#\n+#   Copyright 2017 6WIND S.A.\n+#   Copyright 2017 Mellanox.\n+#\n+#   Redistribution and use in source and binary forms, with or without\n+#   modification, are permitted provided that the following conditions\n+#   are met:\n+#\n+#     * Redistributions of source code must retain the above copyright\n+#       notice, this list of conditions and the following disclaimer.\n+#     * Redistributions in binary form must reproduce the above copyright\n+#       notice, this list of conditions and the following disclaimer in\n+#       the documentation and/or other materials provided with the\n+#       distribution.\n+#     * Neither the name of 6WIND S.A. nor the names of its\n+#       contributors may be used to endorse or promote products derived\n+#       from this software without specific prior written permission.\n+#\n+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+#   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+\n+include $(RTE_SDK)/mk/rte.vars.mk\n+\n+# Library name\n+LIB = librte_pmd_failsafe.a\n+\n+EXPORT_MAP := rte_pmd_failsafe_version.map\n+\n+LIBABIVER := 1\n+\n+# Sources are stored in SRCS-y\n+SRCS-$(CONFIG_RTE_LIBRTE_PMD_FAILSAFE) += failsafe.c\n+SRCS-$(CONFIG_RTE_LIBRTE_PMD_FAILSAFE) += failsafe_args.c\n+SRCS-$(CONFIG_RTE_LIBRTE_PMD_FAILSAFE) += failsafe_eal.c\n+SRCS-$(CONFIG_RTE_LIBRTE_PMD_FAILSAFE) += failsafe_ops.c\n+SRCS-$(CONFIG_RTE_LIBRTE_PMD_FAILSAFE) += failsafe_rxtx.c\n+\n+# No exported include files\n+\n+# This lib depends upon:\n+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_FAILSAFE) += lib/librte_eal\n+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_FAILSAFE) += lib/librte_ether\n+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_FAILSAFE) += lib/librte_kvargs\n+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_FAILSAFE) += lib/librte_mbuf\n+\n+ifneq ($(DEBUG),)\n+CONFIG_RTE_LIBRTE_PMD_FAILSAFE_DEBUG := y\n+endif\n+\n+# Basic CFLAGS:\n+CFLAGS += -std=gnu99 -Wall -Wextra\n+CFLAGS += -I.\n+CFLAGS += -D_DEFAULT_SOURCE\n+CFLAGS += -D_XOPEN_SOURCE=700\n+CFLAGS += $(WERROR_FLAGS)\n+CFLAGS += -Wno-strict-prototypes\n+CFLAGS += -pedantic -DPEDANTIC\n+\n+ifeq ($(CONFIG_RTE_LIBRTE_PMD_FAILSAFE_DEBUG),y)\n+CFLAGS += -g -UNDEBUG\n+else\n+CFLAGS += -O3\n+CFLAGS += -DNDEBUG\n+endif\n+\n+include $(RTE_SDK)/mk/rte.lib.mk\ndiff --git a/drivers/net/failsafe/failsafe.c b/drivers/net/failsafe/failsafe.c\nnew file mode 100644\nindex 0000000..7cf33e8\n--- /dev/null\n+++ b/drivers/net/failsafe/failsafe.c\n@@ -0,0 +1,231 @@\n+/*-\n+ *   BSD LICENSE\n+ *\n+ *   Copyright 2017 6WIND S.A.\n+ *   Copyright 2017 Mellanox.\n+ *\n+ *   Redistribution and use in source and binary forms, with or without\n+ *   modification, are permitted provided that the following conditions\n+ *   are met:\n+ *\n+ *     * Redistributions of source code must retain the above copyright\n+ *       notice, this list of conditions and the following disclaimer.\n+ *     * Redistributions in binary form must reproduce the above copyright\n+ *       notice, this list of conditions and the following disclaimer in\n+ *       the documentation and/or other materials provided with the\n+ *       distribution.\n+ *     * Neither the name of 6WIND S.A. nor the names of its\n+ *       contributors may be used to endorse or promote products derived\n+ *       from this software without specific prior written permission.\n+ *\n+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ *   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+#include <rte_alarm.h>\n+#include <rte_malloc.h>\n+#include <rte_ethdev.h>\n+#include <rte_ethdev_vdev.h>\n+#include <rte_devargs.h>\n+#include <rte_kvargs.h>\n+#include <rte_vdev.h>\n+\n+#include \"failsafe_private.h\"\n+\n+const char pmd_failsafe_driver_name[] = FAILSAFE_DRIVER_NAME;\n+static const struct rte_eth_link eth_link = {\n+\t.link_speed = ETH_SPEED_NUM_10G,\n+\t.link_duplex = ETH_LINK_FULL_DUPLEX,\n+\t.link_status = ETH_LINK_UP,\n+\t.link_autoneg = ETH_LINK_SPEED_AUTONEG,\n+};\n+\n+static int\n+fs_sub_device_create(struct rte_eth_dev *dev,\n+\t\tconst char *params)\n+{\n+\tuint8_t nb_subs;\n+\tint ret;\n+\n+\tret = failsafe_args_count_subdevice(dev, params);\n+\tif (ret)\n+\t\treturn ret;\n+\tif (PRIV(dev)->subs_tail > FAILSAFE_MAX_ETHPORTS) {\n+\t\tERROR(\"Cannot allocate more than %d ports\",\n+\t\t\tFAILSAFE_MAX_ETHPORTS);\n+\t\treturn -ENOSPC;\n+\t}\n+\tnb_subs = PRIV(dev)->subs_tail;\n+\tPRIV(dev)->subs = rte_zmalloc(NULL,\n+\t\t\tsizeof(struct sub_device) * nb_subs,\n+\t\t\tRTE_CACHE_LINE_SIZE);\n+\tif (PRIV(dev)->subs == NULL) {\n+\t\tERROR(\"Could not allocate sub_devices\");\n+\t\treturn -ENOMEM;\n+\t}\n+\treturn 0;\n+}\n+\n+static void\n+fs_sub_device_free(struct rte_eth_dev *dev)\n+{\n+\trte_free(PRIV(dev)->subs);\n+}\n+\n+static int\n+fs_eth_dev_create(struct rte_vdev_device *vdev)\n+{\n+\tstruct rte_eth_dev *dev;\n+\tstruct ether_addr *mac;\n+\tstruct fs_priv *priv;\n+\tstruct sub_device *sdev;\n+\tconst char *params;\n+\tunsigned int socket_id;\n+\tuint8_t i;\n+\tint ret;\n+\n+\tdev = NULL;\n+\tpriv = NULL;\n+\tparams = rte_vdev_device_args(vdev);\n+\tsocket_id = rte_socket_id();\n+\tINFO(\"Creating fail-safe device on NUMA socket %u\",\n+\t     socket_id);\n+\tdev = rte_eth_vdev_allocate(vdev, sizeof(*priv));\n+\tif (dev == NULL) {\n+\t\tERROR(\"Unable to allocate rte_eth_dev\");\n+\t\treturn -1;\n+\t}\n+\tpriv = dev->data->dev_private;\n+\tPRIV(dev)->dev = dev;\n+\tdev->dev_ops = &failsafe_ops;\n+\tTAILQ_INIT(&dev->link_intr_cbs);\n+\tdev->data->dev_flags = 0x0;\n+\tdev->data->mac_addrs = &PRIV(dev)->mac_addrs[0];\n+\tdev->data->dev_link = eth_link;\n+\tPRIV(dev)->nb_mac_addr = 1;\n+\tdev->rx_pkt_burst = (eth_rx_burst_t)&failsafe_rx_burst;\n+\tdev->tx_pkt_burst = (eth_tx_burst_t)&failsafe_tx_burst;\n+\tif (params == NULL) {\n+\t\tERROR(\"This PMD requires sub-devices, none provided\");\n+\t\tgoto free_dev;\n+\t}\n+\tret = fs_sub_device_create(dev, params);\n+\tif (ret) {\n+\t\tERROR(\"Could not allocate sub_devices\");\n+\t\tgoto free_dev;\n+\t}\n+\tret = failsafe_args_parse(dev, params);\n+\tif (ret)\n+\t\tgoto free_subs;\n+\tret = failsafe_eal_init(dev);\n+\tif (ret)\n+\t\tgoto free_args;\n+\tmac = &dev->data->mac_addrs[0];\n+\tif (mac_from_arg) {\n+\t\t/*\n+\t\t * If MAC address was provided as a parameter,\n+\t\t * apply to all probed slaves.\n+\t\t */\n+\t\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_PROBED) {\n+\t\t\tret = rte_eth_dev_default_mac_addr_set(PORT_ID(sdev),\n+\t\t\t\t\t\t\t       mac);\n+\t\t\tif (ret) {\n+\t\t\t\tERROR(\"Failed to set default MAC address\");\n+\t\t\t\tgoto free_args;\n+\t\t\t}\n+\t\t}\n+\t} else {\n+\t\t/*\n+\t\t * Use the ether_addr from first probed\n+\t\t * device, either preferred or fallback.\n+\t\t */\n+\t\tFOREACH_SUBDEV(sdev, i, dev)\n+\t\t\tif (sdev->state >= DEV_PROBED) {\n+\t\t\t\tether_addr_copy(&ETH(sdev)->data->mac_addrs[0],\n+\t\t\t\t\t\tmac);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t/*\n+\t\t * If no device has been probed and no ether_addr\n+\t\t * has been provided on the command line, use a random\n+\t\t * valid one.\n+\t\t * It will be applied during future slave state syncs to\n+\t\t * probed slaves.\n+\t\t */\n+\t\tif (i == priv->subs_tail)\n+\t\t\teth_random_addr(&mac->addr_bytes[0]);\n+\t}\n+\tINFO(\"MAC address is %02x:%02x:%02x:%02x:%02x:%02x\",\n+\t\tmac->addr_bytes[0], mac->addr_bytes[1],\n+\t\tmac->addr_bytes[2], mac->addr_bytes[3],\n+\t\tmac->addr_bytes[4], mac->addr_bytes[5]);\n+\treturn 0;\n+free_args:\n+\tfailsafe_args_free(dev);\n+free_subs:\n+\tfs_sub_device_free(dev);\n+free_dev:\n+\trte_eth_dev_release_port(dev);\n+\treturn -1;\n+}\n+\n+static int\n+fs_rte_eth_free(const char *name)\n+{\n+\tstruct rte_eth_dev *dev;\n+\tint ret;\n+\n+\tdev = rte_eth_dev_allocated(name);\n+\tif (dev == NULL)\n+\t\treturn -ENODEV;\n+\tret = failsafe_eal_uninit(dev);\n+\tif (ret)\n+\t\tERROR(\"Error while uninitializing sub-EAL\");\n+\tfailsafe_args_free(dev);\n+\tfs_sub_device_free(dev);\n+\trte_free(PRIV(dev));\n+\trte_eth_dev_release_port(dev);\n+\treturn ret;\n+}\n+\n+static int\n+rte_pmd_failsafe_probe(struct rte_vdev_device *vdev)\n+{\n+\tconst char *name;\n+\n+\tname = rte_vdev_device_name(vdev);\n+\tif (vdev == NULL)\n+\t\treturn -EINVAL;\n+\tINFO(\"Initializing \" FAILSAFE_DRIVER_NAME \" for %s\",\n+\t\t\tname);\n+\treturn fs_eth_dev_create(vdev);\n+}\n+\n+static int\n+rte_pmd_failsafe_remove(struct rte_vdev_device *vdev)\n+{\n+\tconst char *name;\n+\n+\tname = rte_vdev_device_name(vdev);\n+\tif (name == NULL)\n+\t\treturn -EINVAL;\n+\tINFO(\"Uninitializing \" FAILSAFE_DRIVER_NAME \" for %s\", name);\n+\treturn fs_rte_eth_free(name);\n+}\n+\n+static struct rte_vdev_driver failsafe_drv = {\n+\t.probe = rte_pmd_failsafe_probe,\n+\t.remove = rte_pmd_failsafe_remove,\n+};\n+\n+RTE_PMD_REGISTER_VDEV(net_failsafe, failsafe_drv);\n+RTE_PMD_REGISTER_ALIAS(net_failsafe, eth_failsafe);\n+RTE_PMD_REGISTER_PARAM_STRING(net_failsafe, PMD_FAILSAFE_PARAM_STRING);\ndiff --git a/drivers/net/failsafe/failsafe_args.c b/drivers/net/failsafe/failsafe_args.c\nnew file mode 100644\nindex 0000000..f07d26e\n--- /dev/null\n+++ b/drivers/net/failsafe/failsafe_args.c\n@@ -0,0 +1,331 @@\n+/*-\n+ *   BSD LICENSE\n+ *\n+ *   Copyright 2017 6WIND S.A.\n+ *   Copyright 2017 Mellanox.\n+ *\n+ *   Redistribution and use in source and binary forms, with or without\n+ *   modification, are permitted provided that the following conditions\n+ *   are met:\n+ *\n+ *     * Redistributions of source code must retain the above copyright\n+ *       notice, this list of conditions and the following disclaimer.\n+ *     * Redistributions in binary form must reproduce the above copyright\n+ *       notice, this list of conditions and the following disclaimer in\n+ *       the documentation and/or other materials provided with the\n+ *       distribution.\n+ *     * Neither the name of 6WIND S.A. nor the names of its\n+ *       contributors may be used to endorse or promote products derived\n+ *       from this software without specific prior written permission.\n+ *\n+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ *   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+#include <string.h>\n+#include <errno.h>\n+\n+#include <rte_devargs.h>\n+#include <rte_malloc.h>\n+#include <rte_kvargs.h>\n+\n+#include \"failsafe_private.h\"\n+\n+#define DEVARGS_MAXLEN 4096\n+\n+/* Callback used when a new device is found in devargs */\n+typedef int (parse_cb)(struct rte_eth_dev *dev, const char *params,\n+\t\tuint8_t head);\n+\n+int mac_from_arg;\n+\n+const char *pmd_failsafe_init_parameters[] = {\n+\tPMD_FAILSAFE_MAC_KVARG,\n+\tNULL,\n+};\n+\n+/*\n+ * input: text.\n+ * output: 0: if text[0] != '(',\n+ *         0: if there are no corresponding ')'\n+ *         n: distance to corresponding ')' otherwise\n+ */\n+static size_t\n+closing_paren(const char *text)\n+{\n+\tint nb_open = 0;\n+\tsize_t i = 0;\n+\n+\twhile (text[i] != '\\0') {\n+\t\tif (text[i] == '(')\n+\t\t\tnb_open++;\n+\t\tif (text[i] == ')')\n+\t\t\tnb_open--;\n+\t\tif (nb_open == 0)\n+\t\t\treturn i;\n+\t\ti++;\n+\t}\n+\treturn 0;\n+}\n+\n+static int\n+fs_parse_device(struct sub_device *sdev, char *args)\n+{\n+\tstruct rte_devargs *d;\n+\tint ret;\n+\n+\td = &sdev->devargs;\n+\tDEBUG(\"%s\", args);\n+\tret = rte_eal_devargs_parse(args, d);\n+\tif (ret) {\n+\t\tDEBUG(\"devargs parsing failed with code %d\", ret);\n+\t\treturn ret;\n+\t}\n+\tsdev->bus = d->bus;\n+\tsdev->state = DEV_PARSED;\n+\treturn 0;\n+}\n+\n+static int\n+fs_parse_device_param(struct rte_eth_dev *dev, const char *param,\n+\t\tuint8_t head)\n+{\n+\tstruct fs_priv *priv;\n+\tstruct sub_device *sdev;\n+\tchar *args = NULL;\n+\tsize_t a, b;\n+\tint ret;\n+\n+\tpriv = PRIV(dev);\n+\ta = 0;\n+\tb = 0;\n+\tret = 0;\n+\twhile  (param[b] != '(' &&\n+\t\tparam[b] != '\\0')\n+\t\tb++;\n+\ta = b;\n+\tb += closing_paren(&param[b]);\n+\tif (a == b) {\n+\t\tERROR(\"Dangling parenthesis\");\n+\t\treturn -EINVAL;\n+\t}\n+\ta += 1;\n+\targs = strndup(&param[a], b - a);\n+\tif (args == NULL) {\n+\t\tERROR(\"Not enough memory for parameter parsing\");\n+\t\treturn -ENOMEM;\n+\t}\n+\tsdev = &priv->subs[head];\n+\tif (strncmp(param, \"dev\", 3) == 0) {\n+\t\tret = fs_parse_device(sdev, args);\n+\t\tif (ret)\n+\t\t\tgoto free_args;\n+\t} else {\n+\t\tERROR(\"Unrecognized device type: %.*s\", (int)b, param);\n+\t\treturn -EINVAL;\n+\t}\n+free_args:\n+\tfree(args);\n+\treturn ret;\n+}\n+\n+static int\n+fs_parse_sub_devices(parse_cb *cb,\n+\t\tstruct rte_eth_dev *dev, const char *params)\n+{\n+\tsize_t a, b;\n+\tuint8_t head;\n+\tint ret;\n+\n+\ta = 0;\n+\thead = 0;\n+\tret = 0;\n+\twhile (params[a] != '\\0') {\n+\t\tb = a;\n+\t\twhile (params[b] != '(' &&\n+\t\t       params[b] != ',' &&\n+\t\t       params[b] != '\\0')\n+\t\t\tb++;\n+\t\tif (b == a) {\n+\t\t\tERROR(\"Invalid parameter\");\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t\tif (params[b] == ',') {\n+\t\t\ta = b + 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (params[b] == '(') {\n+\t\t\tsize_t start = b;\n+\n+\t\t\tb += closing_paren(&params[b]);\n+\t\t\tif (b == start) {\n+\t\t\t\tERROR(\"Dangling parenthesis\");\n+\t\t\t\treturn -EINVAL;\n+\t\t\t}\n+\t\t\tret = (*cb)(dev, &params[a], head);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t\thead += 1;\n+\t\t\tb += 1;\n+\t\t\tif (params[b] == '\\0')\n+\t\t\t\treturn 0;\n+\t\t}\n+\t\ta = b + 1;\n+\t}\n+\treturn 0;\n+}\n+\n+static int\n+fs_remove_sub_devices_definition(char params[DEVARGS_MAXLEN])\n+{\n+\tchar buffer[DEVARGS_MAXLEN] = {0};\n+\tsize_t a, b;\n+\tint i;\n+\n+\ta = 0;\n+\ti = 0;\n+\twhile (params[a] != '\\0') {\n+\t\tb = a;\n+\t\twhile (params[b] != '(' &&\n+\t\t       params[b] != ',' &&\n+\t\t       params[b] != '\\0')\n+\t\t\tb++;\n+\t\tif (b == a) {\n+\t\t\tERROR(\"Invalid parameter\");\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t\tif (params[b] == ',' || params[b] == '\\0')\n+\t\t\ti += snprintf(&buffer[i], b - a + 1, \"%s\", &params[a]);\n+\t\tif (params[b] == '(') {\n+\t\t\tsize_t start = b;\n+\t\t\tb += closing_paren(&params[b]);\n+\t\t\tif (b == start)\n+\t\t\t\treturn -EINVAL;\n+\t\t\tb += 1;\n+\t\t\tif (params[b] == '\\0')\n+\t\t\t\tgoto out;\n+\t\t}\n+\t\ta = b + 1;\n+\t}\n+out:\n+\tsnprintf(params, DEVARGS_MAXLEN, \"%s\", buffer);\n+\treturn 0;\n+}\n+\n+static int\n+fs_get_mac_addr_arg(const char *key __rte_unused,\n+\t\tconst char *value, void *out)\n+{\n+\tstruct ether_addr *ea = out;\n+\tint ret;\n+\n+\tif ((value == NULL) || (out == NULL))\n+\t\treturn -EINVAL;\n+\tret = sscanf(value, \"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\",\n+\t\t&ea->addr_bytes[0], &ea->addr_bytes[1],\n+\t\t&ea->addr_bytes[2], &ea->addr_bytes[3],\n+\t\t&ea->addr_bytes[4], &ea->addr_bytes[5]);\n+\treturn ret != ETHER_ADDR_LEN;\n+}\n+\n+int\n+failsafe_args_parse(struct rte_eth_dev *dev, const char *params)\n+{\n+\tstruct fs_priv *priv;\n+\tchar mut_params[DEVARGS_MAXLEN] = \"\";\n+\tstruct rte_kvargs *kvlist = NULL;\n+\tunsigned int arg_count;\n+\tsize_t n;\n+\tint ret;\n+\n+\tif (dev == NULL || params == NULL)\n+\t\treturn -EINVAL;\n+\tpriv = PRIV(dev);\n+\tret = 0;\n+\tpriv->subs_tx = FAILSAFE_MAX_ETHPORTS;\n+\t/* default parameters */\n+\tmac_from_arg = 0;\n+\tn = snprintf(mut_params, sizeof(mut_params), \"%s\", params);\n+\tif (n >= sizeof(mut_params)) {\n+\t\tERROR(\"Parameter string too long (>=%zu)\",\n+\t\t\t\tsizeof(mut_params));\n+\t\treturn -ENOMEM;\n+\t}\n+\tret = fs_parse_sub_devices(fs_parse_device_param,\n+\t\t\t\t   dev, params);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\tret = fs_remove_sub_devices_definition(mut_params);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\tif (strnlen(mut_params, sizeof(mut_params)) > 0) {\n+\t\tkvlist = rte_kvargs_parse(mut_params,\n+\t\t\t\tpmd_failsafe_init_parameters);\n+\t\tif (kvlist == NULL) {\n+\t\t\tERROR(\"Error parsing parameters, usage:\\n\"\n+\t\t\t\tPMD_FAILSAFE_PARAM_STRING);\n+\t\t\treturn -1;\n+\t\t}\n+\t\t/* MAC addr */\n+\t\targ_count = rte_kvargs_count(kvlist,\n+\t\t\t\tPMD_FAILSAFE_MAC_KVARG);\n+\t\tif (arg_count == 1) {\n+\t\t\tret = rte_kvargs_process(kvlist,\n+\t\t\t\t\tPMD_FAILSAFE_MAC_KVARG,\n+\t\t\t\t\t&fs_get_mac_addr_arg,\n+\t\t\t\t\t&dev->data->mac_addrs[0]);\n+\t\t\tif (ret < 0)\n+\t\t\t\tgoto free_kvlist;\n+\t\t\tmac_from_arg = 1;\n+\t\t}\n+\t}\n+free_kvlist:\n+\trte_kvargs_free(kvlist);\n+\treturn ret;\n+}\n+\n+void\n+failsafe_args_free(struct rte_eth_dev *dev)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\n+\tFOREACH_SUBDEV(sdev, i, dev) {\n+\t\tfree(sdev->devargs.args);\n+\t\tsdev->devargs.args = NULL;\n+\t}\n+}\n+\n+static int\n+fs_count_device(struct rte_eth_dev *dev, const char *param,\n+\t\tuint8_t head __rte_unused)\n+{\n+\tsize_t b = 0;\n+\n+\twhile  (param[b] != '(' &&\n+\t\tparam[b] != '\\0')\n+\t\tb++;\n+\tif (strncmp(param, \"dev\", b) &&\n+\t    strncmp(param, \"exec\", b)) {\n+\t\tERROR(\"Unrecognized device type: %.*s\", (int)b, param);\n+\t\treturn -EINVAL;\n+\t}\n+\tPRIV(dev)->subs_tail += 1;\n+\treturn 0;\n+}\n+\n+int\n+failsafe_args_count_subdevice(struct rte_eth_dev *dev,\n+\t\t\tconst char *params)\n+{\n+\treturn fs_parse_sub_devices(fs_count_device,\n+\t\t\t\t    dev, params);\n+}\ndiff --git a/drivers/net/failsafe/failsafe_eal.c b/drivers/net/failsafe/failsafe_eal.c\nnew file mode 100644\nindex 0000000..6c3a811\n--- /dev/null\n+++ b/drivers/net/failsafe/failsafe_eal.c\n@@ -0,0 +1,154 @@\n+/*-\n+ *   BSD LICENSE\n+ *\n+ *   Copyright 2017 6WIND S.A.\n+ *   Copyright 2017 Mellanox.\n+ *\n+ *   Redistribution and use in source and binary forms, with or without\n+ *   modification, are permitted provided that the following conditions\n+ *   are met:\n+ *\n+ *     * Redistributions of source code must retain the above copyright\n+ *       notice, this list of conditions and the following disclaimer.\n+ *     * Redistributions in binary form must reproduce the above copyright\n+ *       notice, this list of conditions and the following disclaimer in\n+ *       the documentation and/or other materials provided with the\n+ *       distribution.\n+ *     * Neither the name of 6WIND S.A. nor the names of its\n+ *       contributors may be used to endorse or promote products derived\n+ *       from this software without specific prior written permission.\n+ *\n+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ *   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+#include <rte_malloc.h>\n+\n+#include \"failsafe_private.h\"\n+\n+static struct rte_eth_dev *\n+fs_find_ethdev(const struct rte_device *dev)\n+{\n+\tstruct rte_eth_dev *eth_dev;\n+\tuint8_t i;\n+\n+\tRTE_ETH_FOREACH_DEV(i) {\n+\t\teth_dev = &rte_eth_devices[i];\n+\t\tif (eth_dev->device == dev)\n+\t\t\treturn eth_dev;\n+\t}\n+\treturn NULL;\n+}\n+\n+static int\n+fs_bus_init(struct rte_eth_dev *dev)\n+{\n+\tstruct sub_device *sdev;\n+\tstruct rte_device *rdev;\n+\tstruct rte_devargs *da;\n+\tuint8_t i;\n+\tint ret;\n+\n+\tFOREACH_SUBDEV(sdev, i, dev) {\n+\t\tif (sdev->state != DEV_PARSED)\n+\t\t\tcontinue;\n+\t\tda = &sdev->devargs;\n+\t\trdev = rte_eal_hotplug_add(da->bus->name,\n+\t\t\t\t\t   da->name,\n+\t\t\t\t\t   da->args);\n+\t\tret = rdev ? 0 : -rte_errno;\n+\t\tif (ret) {\n+\t\t\tERROR(\"sub_device %d probe failed %s%s%s\", i,\n+\t\t\t      errno ? \"(\" : \"\",\n+\t\t\t      errno ? strerror(rte_errno) : \"\",\n+\t\t\t      errno ? \")\" : \"\");\n+\t\t\tcontinue;\n+\t\t}\n+\t\tETH(sdev) = fs_find_ethdev(rdev);\n+\t\tif (ETH(sdev) == NULL) {\n+\t\t\tERROR(\"sub_device %d init went wrong\", i);\n+\t\t\treturn -ENODEV;\n+\t\t}\n+\t\tsdev->dev = ETH(sdev)->device;\n+\t\tETH(sdev)->state = RTE_ETH_DEV_DEFERRED;\n+\t\tsdev->state = DEV_PROBED;\n+\t}\n+\treturn 0;\n+}\n+\n+int\n+failsafe_eal_init(struct rte_eth_dev *dev)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\tint ret;\n+\n+\tret = fs_bus_init(dev);\n+\tif (ret)\n+\t\treturn ret;\n+\t/*\n+\t * We only update TX_SUBDEV if we are not started.\n+\t * If a sub_device is emitting, we will switch the TX_SUBDEV to the\n+\t * preferred port only upon starting it, so that the switch is smoother.\n+\t */\n+\tif (PREFERRED_SUBDEV(dev)->state >= DEV_PROBED) {\n+\t\tif (TX_SUBDEV(dev) != PREFERRED_SUBDEV(dev) &&\n+\t\t    (TX_SUBDEV(dev) == NULL ||\n+\t\t     (TX_SUBDEV(dev) && TX_SUBDEV(dev)->state < DEV_STARTED))) {\n+\t\t\tDEBUG(\"Switching tx_dev to preferred sub_device\");\n+\t\t\tPRIV(dev)->subs_tx = 0;\n+\t\t}\n+\t} else {\n+\t\tif ((TX_SUBDEV(dev) && TX_SUBDEV(dev)->state < DEV_PROBED) ||\n+\t\t    TX_SUBDEV(dev) == NULL) {\n+\t\t\t/* Using first probed device */\n+\t\t\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_PROBED) {\n+\t\t\t\tDEBUG(\"Switching tx_dev to sub_device %d\",\n+\t\t\t\t      i);\n+\t\t\t\tPRIV(dev)->subs_tx = i;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+static int\n+fs_bus_uninit(struct rte_eth_dev *dev)\n+{\n+\tstruct sub_device *sdev = NULL;\n+\tuint8_t i;\n+\tint ret;\n+\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_PROBED) {\n+\t\tret = rte_eal_hotplug_remove(sdev->bus->name,\n+\t\t\t\t\t     sdev->dev->name);\n+\t\tif (ret) {\n+\t\t\tERROR(\"Failed to remove requested device %s\",\n+\t\t\t      sdev->dev->name);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tsdev->state = DEV_PROBED - 1;\n+\t}\n+\treturn 0;\n+}\n+\n+int\n+failsafe_eal_uninit(struct rte_eth_dev *dev)\n+{\n+\tint ret;\n+\n+\tret = fs_bus_uninit(dev);\n+\tif (ret)\n+\t\treturn ret;\n+\treturn 0;\n+}\ndiff --git a/drivers/net/failsafe/failsafe_ops.c b/drivers/net/failsafe/failsafe_ops.c\nnew file mode 100644\nindex 0000000..693162e\n--- /dev/null\n+++ b/drivers/net/failsafe/failsafe_ops.c\n@@ -0,0 +1,663 @@\n+/*-\n+ *   BSD LICENSE\n+ *\n+ *   Copyright 2017 6WIND S.A.\n+ *   Copyright 2017 Mellanox.\n+ *\n+ *   Redistribution and use in source and binary forms, with or without\n+ *   modification, are permitted provided that the following conditions\n+ *   are met:\n+ *\n+ *     * Redistributions of source code must retain the above copyright\n+ *       notice, this list of conditions and the following disclaimer.\n+ *     * Redistributions in binary form must reproduce the above copyright\n+ *       notice, this list of conditions and the following disclaimer in\n+ *       the documentation and/or other materials provided with the\n+ *       distribution.\n+ *     * Neither the name of 6WIND S.A. nor the names of its\n+ *       contributors may be used to endorse or promote products derived\n+ *       from this software without specific prior written permission.\n+ *\n+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ *   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+#include <assert.h>\n+#include <stdint.h>\n+#include <rte_ethdev.h>\n+#include <rte_malloc.h>\n+\n+#include \"failsafe_private.h\"\n+\n+static struct rte_eth_dev_info default_infos = {\n+\t.driver_name = pmd_failsafe_driver_name,\n+\t/* Max possible number of elements */\n+\t.max_rx_pktlen = UINT32_MAX,\n+\t.max_rx_queues = RTE_MAX_QUEUES_PER_PORT,\n+\t.max_tx_queues = RTE_MAX_QUEUES_PER_PORT,\n+\t.max_mac_addrs = FAILSAFE_MAX_ETHADDR,\n+\t.max_hash_mac_addrs = UINT32_MAX,\n+\t.max_vfs = UINT16_MAX,\n+\t.max_vmdq_pools = UINT16_MAX,\n+\t.rx_desc_lim = {\n+\t\t.nb_max = UINT16_MAX,\n+\t\t.nb_min = 0,\n+\t\t.nb_align = 1,\n+\t\t.nb_seg_max = UINT16_MAX,\n+\t\t.nb_mtu_seg_max = UINT16_MAX,\n+\t},\n+\t.tx_desc_lim = {\n+\t\t.nb_max = UINT16_MAX,\n+\t\t.nb_min = 0,\n+\t\t.nb_align = 1,\n+\t\t.nb_seg_max = UINT16_MAX,\n+\t\t.nb_mtu_seg_max = UINT16_MAX,\n+\t},\n+\t/* Set of understood capabilities */\n+\t.rx_offload_capa = 0x0,\n+\t.tx_offload_capa = 0x0,\n+\t.flow_type_rss_offloads = 0x0,\n+};\n+\n+static int\n+fs_dev_configure(struct rte_eth_dev *dev)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\tint ret;\n+\n+\tFOREACH_SUBDEV(sdev, i, dev) {\n+\t\tif (sdev->state != DEV_PROBED)\n+\t\t\tcontinue;\n+\t\tDEBUG(\"Configuring sub-device %d\", i);\n+\t\tret = rte_eth_dev_configure(PORT_ID(sdev),\n+\t\t\t\t\tdev->data->nb_rx_queues,\n+\t\t\t\t\tdev->data->nb_tx_queues,\n+\t\t\t\t\t&dev->data->dev_conf);\n+\t\tif (ret) {\n+\t\t\tERROR(\"Could not configure sub_device %d\", i);\n+\t\t\treturn ret;\n+\t\t}\n+\t\tsdev->state = DEV_ACTIVE;\n+\t}\n+\treturn 0;\n+}\n+\n+static int\n+fs_dev_start(struct rte_eth_dev *dev)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\tint ret;\n+\n+\tFOREACH_SUBDEV(sdev, i, dev) {\n+\t\tif (sdev->state != DEV_ACTIVE)\n+\t\t\tcontinue;\n+\t\tDEBUG(\"Starting sub_device %d\", i);\n+\t\tret = rte_eth_dev_start(PORT_ID(sdev));\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\tsdev->state = DEV_STARTED;\n+\t}\n+\tif (PREFERRED_SUBDEV(dev)->state == DEV_STARTED) {\n+\t\tif (TX_SUBDEV(dev) != PREFERRED_SUBDEV(dev)) {\n+\t\t\tDEBUG(\"Switching tx_dev to preferred sub_device\");\n+\t\t\tPRIV(dev)->subs_tx = 0;\n+\t\t}\n+\t} else {\n+\t\tif ((TX_SUBDEV(dev) && TX_SUBDEV(dev)->state < DEV_STARTED) ||\n+\t\t    TX_SUBDEV(dev) == NULL) {\n+\t\t\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_STARTED) {\n+\t\t\t\tDEBUG(\"Switching tx_dev to sub_device %d\", i);\n+\t\t\t\tPRIV(dev)->subs_tx = i;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+static void\n+fs_dev_stop(struct rte_eth_dev *dev)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_STARTED) {\n+\t\trte_eth_dev_stop(PORT_ID(sdev));\n+\t\tsdev->state = DEV_STARTED - 1;\n+\t}\n+}\n+\n+static int\n+fs_dev_set_link_up(struct rte_eth_dev *dev)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\tint ret;\n+\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {\n+\t\tDEBUG(\"Calling rte_eth_dev_set_link_up on sub_device %d\", i);\n+\t\tret = rte_eth_dev_set_link_up(PORT_ID(sdev));\n+\t\tif (ret) {\n+\t\t\tERROR(\"Operation rte_eth_dev_set_link_up failed for sub_device %d\"\n+\t\t\t      \" with error %d\", i, ret);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+static int\n+fs_dev_set_link_down(struct rte_eth_dev *dev)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\tint ret;\n+\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {\n+\t\tDEBUG(\"Calling rte_eth_dev_set_link_down on sub_device %d\", i);\n+\t\tret = rte_eth_dev_set_link_down(PORT_ID(sdev));\n+\t\tif (ret) {\n+\t\t\tERROR(\"Operation rte_eth_dev_set_link_down failed for sub_device %d\"\n+\t\t\t      \" with error %d\", i, ret);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+static void fs_dev_free_queues(struct rte_eth_dev *dev);\n+static void\n+fs_dev_close(struct rte_eth_dev *dev)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {\n+\t\tDEBUG(\"Closing sub_device %d\", i);\n+\t\trte_eth_dev_close(PORT_ID(sdev));\n+\t\tsdev->state = DEV_ACTIVE - 1;\n+\t}\n+\tfs_dev_free_queues(dev);\n+}\n+\n+static void\n+fs_rx_queue_release(void *queue)\n+{\n+\tstruct rte_eth_dev *dev;\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\tstruct rxq *rxq;\n+\n+\tif (queue == NULL)\n+\t\treturn;\n+\trxq = queue;\n+\tdev = rxq->priv->dev;\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE)\n+\t\tSUBOPS(sdev, rx_queue_release)\n+\t\t\t(ETH(sdev)->data->rx_queues[rxq->qid]);\n+\tdev->data->rx_queues[rxq->qid] = NULL;\n+\trte_free(rxq);\n+}\n+\n+static int\n+fs_rx_queue_setup(struct rte_eth_dev *dev,\n+\t\tuint16_t rx_queue_id,\n+\t\tuint16_t nb_rx_desc,\n+\t\tunsigned int socket_id,\n+\t\tconst struct rte_eth_rxconf *rx_conf,\n+\t\tstruct rte_mempool *mb_pool)\n+{\n+\tstruct sub_device *sdev;\n+\tstruct rxq *rxq;\n+\tuint8_t i;\n+\tint ret;\n+\n+\trxq = dev->data->rx_queues[rx_queue_id];\n+\tif (rxq != NULL) {\n+\t\tfs_rx_queue_release(rxq);\n+\t\tdev->data->rx_queues[rx_queue_id] = NULL;\n+\t}\n+\trxq = rte_zmalloc(NULL, sizeof(*rxq),\n+\t\t\t  RTE_CACHE_LINE_SIZE);\n+\tif (rxq == NULL)\n+\t\treturn -ENOMEM;\n+\trxq->qid = rx_queue_id;\n+\trxq->socket_id = socket_id;\n+\trxq->info.mp = mb_pool;\n+\trxq->info.conf = *rx_conf;\n+\trxq->info.nb_desc = nb_rx_desc;\n+\trxq->priv = PRIV(dev);\n+\tdev->data->rx_queues[rx_queue_id] = rxq;\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {\n+\t\tret = rte_eth_rx_queue_setup(PORT_ID(sdev),\n+\t\t\t\trx_queue_id,\n+\t\t\t\tnb_rx_desc, socket_id,\n+\t\t\t\trx_conf, mb_pool);\n+\t\tif (ret) {\n+\t\t\tERROR(\"RX queue setup failed for sub_device %d\", i);\n+\t\t\tgoto free_rxq;\n+\t\t}\n+\t}\n+\treturn 0;\n+free_rxq:\n+\tfs_rx_queue_release(rxq);\n+\treturn ret;\n+}\n+\n+static void\n+fs_tx_queue_release(void *queue)\n+{\n+\tstruct rte_eth_dev *dev;\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\tstruct txq *txq;\n+\n+\tif (queue == NULL)\n+\t\treturn;\n+\ttxq = queue;\n+\tdev = txq->priv->dev;\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE)\n+\t\tSUBOPS(sdev, tx_queue_release)\n+\t\t\t(ETH(sdev)->data->tx_queues[txq->qid]);\n+\tdev->data->tx_queues[txq->qid] = NULL;\n+\trte_free(txq);\n+}\n+\n+static int\n+fs_tx_queue_setup(struct rte_eth_dev *dev,\n+\t\tuint16_t tx_queue_id,\n+\t\tuint16_t nb_tx_desc,\n+\t\tunsigned int socket_id,\n+\t\tconst struct rte_eth_txconf *tx_conf)\n+{\n+\tstruct sub_device *sdev;\n+\tstruct txq *txq;\n+\tuint8_t i;\n+\tint ret;\n+\n+\ttxq = dev->data->tx_queues[tx_queue_id];\n+\tif (txq != NULL) {\n+\t\tfs_tx_queue_release(txq);\n+\t\tdev->data->tx_queues[tx_queue_id] = NULL;\n+\t}\n+\ttxq = rte_zmalloc(\"ethdev TX queue\", sizeof(*txq),\n+\t\t\t  RTE_CACHE_LINE_SIZE);\n+\tif (txq == NULL)\n+\t\treturn -ENOMEM;\n+\ttxq->qid = tx_queue_id;\n+\ttxq->socket_id = socket_id;\n+\ttxq->info.conf = *tx_conf;\n+\ttxq->info.nb_desc = nb_tx_desc;\n+\ttxq->priv = PRIV(dev);\n+\tdev->data->tx_queues[tx_queue_id] = txq;\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {\n+\t\tret = rte_eth_tx_queue_setup(PORT_ID(sdev),\n+\t\t\t\ttx_queue_id,\n+\t\t\t\tnb_tx_desc, socket_id,\n+\t\t\t\ttx_conf);\n+\t\tif (ret) {\n+\t\t\tERROR(\"TX queue setup failed for sub_device %d\", i);\n+\t\t\tgoto free_txq;\n+\t\t}\n+\t}\n+\treturn 0;\n+free_txq:\n+\tfs_tx_queue_release(txq);\n+\treturn ret;\n+}\n+\n+static void\n+fs_dev_free_queues(struct rte_eth_dev *dev)\n+{\n+\tuint16_t i;\n+\n+\tfor (i = 0; i < dev->data->nb_rx_queues; i++) {\n+\t\tfs_rx_queue_release(dev->data->rx_queues[i]);\n+\t\tdev->data->rx_queues[i] = NULL;\n+\t}\n+\tdev->data->nb_rx_queues = 0;\n+\tfor (i = 0; i < dev->data->nb_tx_queues; i++) {\n+\t\tfs_tx_queue_release(dev->data->tx_queues[i]);\n+\t\tdev->data->tx_queues[i] = NULL;\n+\t}\n+\tdev->data->nb_tx_queues = 0;\n+}\n+\n+static void\n+fs_promiscuous_enable(struct rte_eth_dev *dev)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE)\n+\t\trte_eth_promiscuous_enable(PORT_ID(sdev));\n+}\n+\n+static void\n+fs_promiscuous_disable(struct rte_eth_dev *dev)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE)\n+\t\trte_eth_promiscuous_disable(PORT_ID(sdev));\n+}\n+\n+static void\n+fs_allmulticast_enable(struct rte_eth_dev *dev)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE)\n+\t\trte_eth_allmulticast_enable(PORT_ID(sdev));\n+}\n+\n+static void\n+fs_allmulticast_disable(struct rte_eth_dev *dev)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE)\n+\t\trte_eth_allmulticast_disable(PORT_ID(sdev));\n+}\n+\n+static int\n+fs_link_update(struct rte_eth_dev *dev,\n+\t\tint wait_to_complete)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\tint ret;\n+\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {\n+\t\tDEBUG(\"Calling link_update on sub_device %d\", i);\n+\t\tret = (SUBOPS(sdev, link_update))(ETH(sdev), wait_to_complete);\n+\t\tif (ret && ret != -1) {\n+\t\t\tERROR(\"Link update failed for sub_device %d with error %d\",\n+\t\t\t      i, ret);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\tif (TX_SUBDEV(dev)) {\n+\t\tstruct rte_eth_link *l1;\n+\t\tstruct rte_eth_link *l2;\n+\n+\t\tl1 = &dev->data->dev_link;\n+\t\tl2 = &ETH(TX_SUBDEV(dev))->data->dev_link;\n+\t\tif (memcmp(l1, l2, sizeof(*l1))) {\n+\t\t\t*l1 = *l2;\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\treturn -1;\n+}\n+\n+static void\n+fs_stats_get(struct rte_eth_dev *dev,\n+\t     struct rte_eth_stats *stats)\n+{\n+\tmemset(stats, 0, sizeof(*stats));\n+\tif (TX_SUBDEV(dev) == NULL)\n+\t\treturn;\n+\trte_eth_stats_get(PORT_ID(TX_SUBDEV(dev)), stats);\n+}\n+\n+static void\n+fs_stats_reset(struct rte_eth_dev *dev)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE)\n+\t\trte_eth_stats_reset(PORT_ID(sdev));\n+}\n+\n+/**\n+ * Fail-safe dev_infos_get rules:\n+ *\n+ * No sub_device:\n+ *   Numerables:\n+ *      Use the maximum possible values for any field, so as not\n+ *      to impede any further configuration effort.\n+ *   Capabilities:\n+ *      Limits capabilities to those that are understood by the\n+ *      fail-safe PMD. This understanding stems from the fail-safe\n+ *      being capable of verifying that the related capability is\n+ *      expressed within the device configuration (struct rte_eth_conf).\n+ *\n+ * At least one probed sub_device:\n+ *   Numerables:\n+ *      Uses values from the active probed sub_device\n+ *      The rationale here is that if any sub_device is less capable\n+ *      (for example concerning the number of queues) than the active\n+ *      sub_device, then its subsequent configuration will fail.\n+ *      It is impossible to foresee this failure when the failing sub_device\n+ *      is supposed to be plugged-in later on, so the configuration process\n+ *      is the single point of failure and error reporting.\n+ *   Capabilities:\n+ *      Uses a logical AND of RX capabilities among\n+ *      all sub_devices and the default capabilities.\n+ *      Uses a logical AND of TX capabilities among\n+ *      the active probed sub_device and the default capabilities.\n+ *\n+ */\n+static void\n+fs_dev_infos_get(struct rte_eth_dev *dev,\n+\t\t  struct rte_eth_dev_info *infos)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\n+\tsdev = TX_SUBDEV(dev);\n+\tif (sdev == NULL) {\n+\t\tDEBUG(\"No probed device, using default infos\");\n+\t\trte_memcpy(&PRIV(dev)->infos, &default_infos,\n+\t\t\t   sizeof(default_infos));\n+\t} else {\n+\t\tuint32_t rx_offload_capa;\n+\n+\t\trx_offload_capa = default_infos.rx_offload_capa;\n+\t\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_PROBED) {\n+\t\t\trte_eth_dev_info_get(PORT_ID(sdev),\n+\t\t\t\t\t&PRIV(dev)->infos);\n+\t\t\trx_offload_capa &= PRIV(dev)->infos.rx_offload_capa;\n+\t\t}\n+\t\tsdev = TX_SUBDEV(dev);\n+\t\trte_eth_dev_info_get(PORT_ID(sdev), &PRIV(dev)->infos);\n+\t\tPRIV(dev)->infos.rx_offload_capa = rx_offload_capa;\n+\t\tPRIV(dev)->infos.tx_offload_capa &=\n+\t\t\t\t\tdefault_infos.tx_offload_capa;\n+\t\tPRIV(dev)->infos.flow_type_rss_offloads &=\n+\t\t\t\t\tdefault_infos.flow_type_rss_offloads;\n+\t}\n+\trte_memcpy(infos, &PRIV(dev)->infos, sizeof(*infos));\n+}\n+\n+static const uint32_t *\n+fs_dev_supported_ptypes_get(struct rte_eth_dev *dev)\n+{\n+\tstruct sub_device *sdev;\n+\tstruct rte_eth_dev *edev;\n+\n+\tsdev = TX_SUBDEV(dev);\n+\tif (sdev == NULL)\n+\t\treturn NULL;\n+\tedev = ETH(sdev);\n+\t/* ENOTSUP: counts as no supported ptypes */\n+\tif (SUBOPS(sdev, dev_supported_ptypes_get) == NULL)\n+\t\treturn NULL;\n+\t/*\n+\t * The API does not permit to do a clean AND of all ptypes,\n+\t * It is also incomplete by design and we do not really care\n+\t * to have a best possible value in this context.\n+\t * We just return the ptypes of the device of highest\n+\t * priority, usually the PREFERRED device.\n+\t */\n+\treturn SUBOPS(sdev, dev_supported_ptypes_get)(edev);\n+}\n+\n+static int\n+fs_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\tint ret;\n+\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {\n+\t\tDEBUG(\"Calling rte_eth_dev_set_mtu on sub_device %d\", i);\n+\t\tret = rte_eth_dev_set_mtu(PORT_ID(sdev), mtu);\n+\t\tif (ret) {\n+\t\t\tERROR(\"Operation rte_eth_dev_set_mtu failed for sub_device %d\"\n+\t\t\t      \" with error %d\", i, ret);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+static int\n+fs_vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\tint ret;\n+\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {\n+\t\tDEBUG(\"Calling rte_eth_dev_vlan_filter on sub_device %d\", i);\n+\t\tret = rte_eth_dev_vlan_filter(PORT_ID(sdev), vlan_id, on);\n+\t\tif (ret) {\n+\t\t\tERROR(\"Operation rte_eth_dev_vlan_filter failed for sub_device %d\"\n+\t\t\t      \" with error %d\", i, ret);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+static int\n+fs_flow_ctrl_get(struct rte_eth_dev *dev,\n+\t\tstruct rte_eth_fc_conf *fc_conf)\n+{\n+\tstruct sub_device *sdev;\n+\n+\tsdev = TX_SUBDEV(dev);\n+\tif (sdev == NULL)\n+\t\treturn 0;\n+\tif (SUBOPS(sdev, flow_ctrl_get) == NULL)\n+\t\treturn -ENOTSUP;\n+\treturn SUBOPS(sdev, flow_ctrl_get)(ETH(sdev), fc_conf);\n+}\n+\n+static int\n+fs_flow_ctrl_set(struct rte_eth_dev *dev,\n+\t\tstruct rte_eth_fc_conf *fc_conf)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\tint ret;\n+\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {\n+\t\tDEBUG(\"Calling rte_eth_dev_flow_ctrl_set on sub_device %d\", i);\n+\t\tret = rte_eth_dev_flow_ctrl_set(PORT_ID(sdev), fc_conf);\n+\t\tif (ret) {\n+\t\t\tERROR(\"Operation rte_eth_dev_flow_ctrl_set failed for sub_device %d\"\n+\t\t\t      \" with error %d\", i, ret);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+static void\n+fs_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\n+\t/* No check: already done within the rte_eth_dev_mac_addr_remove\n+\t * call for the fail-safe device.\n+\t */\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE)\n+\t\trte_eth_dev_mac_addr_remove(PORT_ID(sdev),\n+\t\t\t\t&dev->data->mac_addrs[index]);\n+\tPRIV(dev)->mac_addr_pool[index] = 0;\n+}\n+\n+static int\n+fs_mac_addr_add(struct rte_eth_dev *dev,\n+\t\tstruct ether_addr *mac_addr,\n+\t\tuint32_t index,\n+\t\tuint32_t vmdq)\n+{\n+\tstruct sub_device *sdev;\n+\tint ret;\n+\tuint8_t i;\n+\n+\tassert(index < FAILSAFE_MAX_ETHADDR);\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {\n+\t\tret = rte_eth_dev_mac_addr_add(PORT_ID(sdev), mac_addr, vmdq);\n+\t\tif (ret) {\n+\t\t\tERROR(\"Operation rte_eth_dev_mac_addr_add failed for sub_device %\"\n+\t\t\t      PRIu8 \" with error %d\", i, ret);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\tif (index >= PRIV(dev)->nb_mac_addr) {\n+\t\tDEBUG(\"Growing mac_addrs array\");\n+\t\tPRIV(dev)->nb_mac_addr = index;\n+\t}\n+\tPRIV(dev)->mac_addr_pool[index] = vmdq;\n+\treturn 0;\n+}\n+\n+static void\n+fs_mac_addr_set(struct rte_eth_dev *dev, struct ether_addr *mac_addr)\n+{\n+\tstruct sub_device *sdev;\n+\tuint8_t i;\n+\n+\tFOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE)\n+\t\trte_eth_dev_default_mac_addr_set(PORT_ID(sdev), mac_addr);\n+}\n+\n+const struct eth_dev_ops failsafe_ops = {\n+\t.dev_configure = fs_dev_configure,\n+\t.dev_start = fs_dev_start,\n+\t.dev_stop = fs_dev_stop,\n+\t.dev_set_link_down = fs_dev_set_link_down,\n+\t.dev_set_link_up = fs_dev_set_link_up,\n+\t.dev_close = fs_dev_close,\n+\t.promiscuous_enable = fs_promiscuous_enable,\n+\t.promiscuous_disable = fs_promiscuous_disable,\n+\t.allmulticast_enable = fs_allmulticast_enable,\n+\t.allmulticast_disable = fs_allmulticast_disable,\n+\t.link_update = fs_link_update,\n+\t.stats_get = fs_stats_get,\n+\t.stats_reset = fs_stats_reset,\n+\t.dev_infos_get = fs_dev_infos_get,\n+\t.dev_supported_ptypes_get = fs_dev_supported_ptypes_get,\n+\t.mtu_set = fs_mtu_set,\n+\t.vlan_filter_set = fs_vlan_filter_set,\n+\t.rx_queue_setup = fs_rx_queue_setup,\n+\t.tx_queue_setup = fs_tx_queue_setup,\n+\t.rx_queue_release = fs_rx_queue_release,\n+\t.tx_queue_release = fs_tx_queue_release,\n+\t.flow_ctrl_get = fs_flow_ctrl_get,\n+\t.flow_ctrl_set = fs_flow_ctrl_set,\n+\t.mac_addr_remove = fs_mac_addr_remove,\n+\t.mac_addr_add = fs_mac_addr_add,\n+\t.mac_addr_set = fs_mac_addr_set,\n+};\ndiff --git a/drivers/net/failsafe/failsafe_private.h b/drivers/net/failsafe/failsafe_private.h\nnew file mode 100644\nindex 0000000..e7a7592\n--- /dev/null\n+++ b/drivers/net/failsafe/failsafe_private.h\n@@ -0,0 +1,227 @@\n+/*-\n+ *   BSD LICENSE\n+ *\n+ *   Copyright 2017 6WIND S.A.\n+ *   Copyright 2017 Mellanox.\n+ *\n+ *   Redistribution and use in source and binary forms, with or without\n+ *   modification, are permitted provided that the following conditions\n+ *   are met:\n+ *\n+ *     * Redistributions of source code must retain the above copyright\n+ *       notice, this list of conditions and the following disclaimer.\n+ *     * Redistributions in binary form must reproduce the above copyright\n+ *       notice, this list of conditions and the following disclaimer in\n+ *       the documentation and/or other materials provided with the\n+ *       distribution.\n+ *     * Neither the name of 6WIND S.A. nor the names of its\n+ *       contributors may be used to endorse or promote products derived\n+ *       from this software without specific prior written permission.\n+ *\n+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ *   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+#ifndef _RTE_ETH_FAILSAFE_PRIVATE_H_\n+#define _RTE_ETH_FAILSAFE_PRIVATE_H_\n+\n+#include <rte_dev.h>\n+#include <rte_ethdev.h>\n+#include <rte_devargs.h>\n+\n+#define FAILSAFE_DRIVER_NAME \"Fail-safe PMD\"\n+\n+#define PMD_FAILSAFE_MAC_KVARG \"mac\"\n+#define PMD_FAILSAFE_PARAM_STRING\t\\\n+\t\"dev(<ifc>),\"\t\t\t\\\n+\t\"mac=mac_addr\"\t\t\t\\\n+\t\"\"\n+\n+#define FAILSAFE_PLUGIN_DEFAULT_TIMEOUT_MS 2000\n+\n+#define FAILSAFE_MAX_ETHPORTS 2\n+#define FAILSAFE_MAX_ETHADDR 128\n+\n+/* TYPES */\n+\n+struct rxq {\n+\tstruct fs_priv *priv;\n+\tuint16_t qid;\n+\t/* id of last sub_device polled */\n+\tuint8_t last_polled;\n+\tunsigned int socket_id;\n+\tstruct rte_eth_rxq_info info;\n+};\n+\n+struct txq {\n+\tstruct fs_priv *priv;\n+\tuint16_t qid;\n+\tunsigned int socket_id;\n+\tstruct rte_eth_txq_info info;\n+};\n+\n+enum dev_state {\n+\tDEV_UNDEFINED = 0,\n+\tDEV_PARSED,\n+\tDEV_PROBED,\n+\tDEV_ACTIVE,\n+\tDEV_STARTED,\n+};\n+\n+struct sub_device {\n+\t/* Exhaustive DPDK device description */\n+\tstruct rte_devargs devargs;\n+\tstruct rte_bus *bus;\n+\tstruct rte_device *dev;\n+\tstruct rte_eth_dev *edev;\n+\t/* Device state machine */\n+\tenum dev_state state;\n+};\n+\n+struct fs_priv {\n+\tstruct rte_eth_dev *dev;\n+\t/*\n+\t * Set of sub_devices.\n+\t * subs[0] is the preferred device\n+\t * any other is just another slave\n+\t */\n+\tstruct sub_device *subs;\n+\tuint8_t subs_head; /* if head == tail, no subs */\n+\tuint8_t subs_tail; /* first invalid */\n+\tuint8_t subs_tx; /* current emitting device */\n+\tuint8_t current_probed;\n+\t/* current number of mac_addr slots allocated. */\n+\tuint32_t nb_mac_addr;\n+\tstruct ether_addr mac_addrs[FAILSAFE_MAX_ETHADDR];\n+\tuint32_t mac_addr_pool[FAILSAFE_MAX_ETHADDR];\n+\t/* current capabilities */\n+\tstruct rte_eth_dev_info infos;\n+};\n+\n+/* RX / TX */\n+\n+uint16_t failsafe_rx_burst(void *rxq,\n+\t\tstruct rte_mbuf **rx_pkts, uint16_t nb_pkts);\n+uint16_t failsafe_tx_burst(void *txq,\n+\t\tstruct rte_mbuf **tx_pkts, uint16_t nb_pkts);\n+\n+/* ARGS */\n+\n+int failsafe_args_parse(struct rte_eth_dev *dev, const char *params);\n+void failsafe_args_free(struct rte_eth_dev *dev);\n+int failsafe_args_count_subdevice(struct rte_eth_dev *dev, const char *params);\n+\n+/* EAL */\n+\n+int failsafe_eal_init(struct rte_eth_dev *dev);\n+int failsafe_eal_uninit(struct rte_eth_dev *dev);\n+\n+/* GLOBALS */\n+\n+extern const char pmd_failsafe_driver_name[];\n+extern const struct eth_dev_ops failsafe_ops;\n+extern int mac_from_arg;\n+\n+/* HELPERS */\n+\n+/* dev: (struct rte_eth_dev *) fail-safe device */\n+#define PRIV(dev) \\\n+\t((struct fs_priv *)(dev)->data->dev_private)\n+\n+/* sdev: (struct sub_device *) */\n+#define ETH(sdev) \\\n+\t((sdev)->edev)\n+\n+/* sdev: (struct sub_device *) */\n+#define PORT_ID(sdev) \\\n+\t(ETH(sdev)->data->port_id)\n+\n+/**\n+ * Stateful iterator construct over fail-safe sub-devices:\n+ * s:     (struct sub_device *), iterator\n+ * i:     (uint8_t), increment\n+ * dev:   (struct rte_eth_dev *), fail-safe ethdev\n+ * state: (enum dev_state), minimum acceptable device state\n+ */\n+#define FOREACH_SUBDEV_ST(s, i, dev, state)\t\t\t\t\\\n+\tfor (i = fs_find_next((dev), 0, state);\t\t\t\t\\\n+\t     i < PRIV(dev)->subs_tail && (s = &PRIV(dev)->subs[i]);\t\\\n+\t     i = fs_find_next((dev), i + 1, state))\n+\n+/**\n+ * Iterator construct over fail-safe sub-devices:\n+ * s:   (struct sub_device *), iterator\n+ * i:   (uint8_t), increment\n+ * dev: (struct rte_eth_dev *), fail-safe ethdev\n+ */\n+#define FOREACH_SUBDEV(s, i, dev)\t\t\t\\\n+\tFOREACH_SUBDEV_ST(s, i, dev, DEV_UNDEFINED)\n+\n+/* dev: (struct rte_eth_dev *) fail-safe device */\n+#define PREFERRED_SUBDEV(dev) \\\n+\t(&PRIV(dev)->subs[0])\n+\n+/* dev: (struct rte_eth_dev *) fail-safe device */\n+#define TX_SUBDEV(dev)\t\t\t\t\t\t\t  \\\n+\t(PRIV(dev)->subs_tx >= PRIV(dev)->subs_tail\t\t   ? NULL \\\n+\t : (PRIV(dev)->subs[PRIV(dev)->subs_tx].state < DEV_PROBED ? NULL \\\n+\t : &PRIV(dev)->subs[PRIV(dev)->subs_tx]))\n+\n+/**\n+ * s:   (struct sub_device *)\n+ * ops: (struct eth_dev_ops) member\n+ */\n+#define SUBOPS(s, ops) \\\n+\t(ETH(s)->dev_ops->ops)\n+\n+#ifndef NDEBUG\n+#include <stdio.h>\n+#define DEBUG__(m, ...)\t\t\t\t\t\t\\\n+\t(fprintf(stderr, \"%s:%d: %s(): \" m \"%c\",\t\t\\\n+\t\t __FILE__, __LINE__, __func__, __VA_ARGS__),\t\\\n+\t (void)0)\n+#define DEBUG_(...)\t\t\t\t\\\n+\t(errno = ((int []){\t\t\t\\\n+\t\t*(volatile int *)&errno,\t\\\n+\t\t(DEBUG__(__VA_ARGS__), 0)\t\\\n+\t})[0])\n+#define DEBUG(...) DEBUG_(__VA_ARGS__, '\\n')\n+#define INFO(...) DEBUG(__VA_ARGS__)\n+#define WARN(...) DEBUG(__VA_ARGS__)\n+#define ERROR(...) DEBUG(__VA_ARGS__)\n+#else\n+#define DEBUG(...) ((void)0)\n+#define LOG__(level, m, ...) \\\n+\tRTE_LOG(level, PMD, \"net_failsafe: \" m \"%c\", __VA_ARGS__)\n+#define LOG_(level, ...) LOG__(level, __VA_ARGS__, '\\n')\n+#define INFO(...) LOG_(INFO, __VA_ARGS__)\n+#define WARN(...) LOG_(WARNING, \"WARNING: \" __VA_ARGS__)\n+#define ERROR(...) LOG_(ERR, \"ERROR: \" __VA_ARGS__)\n+#endif\n+\n+/* inlined functions */\n+\n+static inline uint8_t\n+fs_find_next(struct rte_eth_dev *dev, uint8_t sid,\n+\t\tenum dev_state min_state)\n+{\n+\twhile (sid < PRIV(dev)->subs_tail) {\n+\t\tif (PRIV(dev)->subs[sid].state >= min_state)\n+\t\t\tbreak;\n+\t\tsid++;\n+\t}\n+\tif (sid >= PRIV(dev)->subs_tail)\n+\t\treturn PRIV(dev)->subs_tail;\n+\treturn sid;\n+}\n+\n+#endif /* _RTE_ETH_FAILSAFE_PRIVATE_H_ */\ndiff --git a/drivers/net/failsafe/failsafe_rxtx.c b/drivers/net/failsafe/failsafe_rxtx.c\nnew file mode 100644\nindex 0000000..a45b4e5\n--- /dev/null\n+++ b/drivers/net/failsafe/failsafe_rxtx.c\n@@ -0,0 +1,107 @@\n+/*-\n+ *   BSD LICENSE\n+ *\n+ *   Copyright 2017 6WIND S.A.\n+ *   Copyright 2017 Mellanox.\n+ *\n+ *   Redistribution and use in source and binary forms, with or without\n+ *   modification, are permitted provided that the following conditions\n+ *   are met:\n+ *\n+ *     * Redistributions of source code must retain the above copyright\n+ *       notice, this list of conditions and the following disclaimer.\n+ *     * Redistributions in binary form must reproduce the above copyright\n+ *       notice, this list of conditions and the following disclaimer in\n+ *       the documentation and/or other materials provided with the\n+ *       distribution.\n+ *     * Neither the name of 6WIND S.A. nor the names of its\n+ *       contributors may be used to endorse or promote products derived\n+ *       from this software without specific prior written permission.\n+ *\n+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ *   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+#include <rte_mbuf.h>\n+#include <rte_ethdev.h>\n+\n+#include \"failsafe_private.h\"\n+\n+/*\n+ * TODO: write fast version,\n+ * without additional checks, to be activated once\n+ * everything has been verified to comply.\n+ */\n+uint16_t\n+failsafe_rx_burst(void *queue,\n+\t\t  struct rte_mbuf **rx_pkts,\n+\t\t  uint16_t nb_pkts)\n+{\n+\tstruct fs_priv *priv;\n+\tstruct sub_device *sdev;\n+\tstruct rxq *rxq;\n+\tvoid *sub_rxq;\n+\tuint16_t nb_rx;\n+\tuint8_t nb_polled, nb_subs;\n+\tuint8_t i;\n+\n+\trxq = queue;\n+\tpriv = rxq->priv;\n+\tnb_subs = priv->subs_tail - priv->subs_head;\n+\tnb_polled = 0;\n+\tfor (i = rxq->last_polled; nb_polled < nb_subs; nb_polled++) {\n+\t\ti++;\n+\t\tif (i == priv->subs_tail)\n+\t\t\ti = priv->subs_head;\n+\t\tsdev = &priv->subs[i];\n+\t\tif (unlikely(ETH(sdev) == NULL))\n+\t\t\tcontinue;\n+\t\tif (unlikely(ETH(sdev)->rx_pkt_burst == NULL))\n+\t\t\tcontinue;\n+\t\tif (unlikely(sdev->state != DEV_STARTED))\n+\t\t\tcontinue;\n+\t\tsub_rxq = ETH(sdev)->data->rx_queues[rxq->qid];\n+\t\tnb_rx = ETH(sdev)->\n+\t\t\trx_pkt_burst(sub_rxq, rx_pkts, nb_pkts);\n+\t\tif (nb_rx) {\n+\t\t\trxq->last_polled = i;\n+\t\t\treturn nb_rx;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+/*\n+ * TODO: write fast version,\n+ * without additional checks, to be activated once\n+ * everything has been verified to comply.\n+ */\n+uint16_t\n+failsafe_tx_burst(void *queue,\n+\t\t  struct rte_mbuf **tx_pkts,\n+\t\t  uint16_t nb_pkts)\n+{\n+\tstruct sub_device *sdev;\n+\tstruct txq *txq;\n+\tvoid *sub_txq;\n+\n+\ttxq = queue;\n+\tsdev = TX_SUBDEV(txq->priv->dev);\n+\tif (unlikely(sdev == NULL))\n+\t\treturn 0;\n+\tif (unlikely(ETH(sdev) == NULL))\n+\t\treturn 0;\n+\tif (unlikely(ETH(sdev)->tx_pkt_burst == NULL))\n+\t\treturn 0;\n+\tsub_txq = ETH(sdev)->data->tx_queues[txq->qid];\n+\treturn ETH(sdev)->tx_pkt_burst(sub_txq, tx_pkts, nb_pkts);\n+}\ndiff --git a/drivers/net/failsafe/rte_pmd_failsafe_version.map b/drivers/net/failsafe/rte_pmd_failsafe_version.map\nnew file mode 100644\nindex 0000000..b6d2840\n--- /dev/null\n+++ b/drivers/net/failsafe/rte_pmd_failsafe_version.map\n@@ -0,0 +1,4 @@\n+DPDK_17.08 {\n+\n+\tlocal: *;\n+};\ndiff --git a/mk/rte.app.mk b/mk/rte.app.mk\nindex 7d71a49..ffc089c 100644\n--- a/mk/rte.app.mk\n+++ b/mk/rte.app.mk\n@@ -120,6 +120,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_E1000_PMD)      += -lrte_pmd_e1000\n _LDLIBS-$(CONFIG_RTE_LIBRTE_ENA_PMD)        += -lrte_pmd_ena\n _LDLIBS-$(CONFIG_RTE_LIBRTE_ENIC_PMD)       += -lrte_pmd_enic\n _LDLIBS-$(CONFIG_RTE_LIBRTE_FM10K_PMD)      += -lrte_pmd_fm10k\n+_LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_FAILSAFE)   += -lrte_pmd_failsafe\n _LDLIBS-$(CONFIG_RTE_LIBRTE_I40E_PMD)       += -lrte_pmd_i40e\n _LDLIBS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD)      += -lrte_pmd_ixgbe\n ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)\n",
    "prefixes": [
        "dpdk-dev",
        "v6",
        "14/22"
    ]
}