get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 16556,
    "url": "http://patchwork.dpdk.org/api/patches/16556/?format=api",
    "web_url": "http://patchwork.dpdk.org/project/dpdk/patch/1476375088-40892-1-git-send-email-keith.wiles@intel.com/",
    "project": {
        "id": 1,
        "url": "http://patchwork.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": "<1476375088-40892-1-git-send-email-keith.wiles@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/1476375088-40892-1-git-send-email-keith.wiles@intel.com",
    "date": "2016-10-13T16:11:28",
    "name": "[dpdk-dev,v8] drivers/net:new PMD using tun/tap host interface",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "81ea4a793883b62b3fd1051ad26a168e1edb78ff",
    "submitter": {
        "id": 166,
        "url": "http://patchwork.dpdk.org/api/people/166/?format=api",
        "name": "Wiles, Keith",
        "email": "keith.wiles@intel.com"
    },
    "delegate": {
        "id": 10,
        "url": "http://patchwork.dpdk.org/api/users/10/?format=api",
        "username": "bruce",
        "first_name": "Bruce",
        "last_name": "Richardson",
        "email": "bruce.richardson@intel.com"
    },
    "mbox": "http://patchwork.dpdk.org/project/dpdk/patch/1476375088-40892-1-git-send-email-keith.wiles@intel.com/mbox/",
    "series": [],
    "comments": "http://patchwork.dpdk.org/api/patches/16556/comments/",
    "check": "pending",
    "checks": "http://patchwork.dpdk.org/api/patches/16556/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 8354629CA;\n\tThu, 13 Oct 2016 18:11:36 +0200 (CEST)",
            "from mga11.intel.com (mga11.intel.com [192.55.52.93])\n\tby dpdk.org (Postfix) with ESMTP id 02F982935\n\tfor <dev@dpdk.org>; Thu, 13 Oct 2016 18:11:33 +0200 (CEST)",
            "from orsmga001.jf.intel.com ([10.7.209.18])\n\tby fmsmga102.fm.intel.com with ESMTP; 13 Oct 2016 09:11:33 -0700",
            "from liuguann-mobl.amr.corp.intel.com ([10.254.12.209])\n\tby orsmga001.jf.intel.com with ESMTP; 13 Oct 2016 09:11:31 -0700"
        ],
        "X-ExtLoop1": "1",
        "X-IronPort-AV": "E=Sophos; i=\"5.31,340,1473145200\"; d=\"scan'208\";\n\ta=\"1044260616\"",
        "From": "Keith Wiles <keith.wiles@intel.com>",
        "To": "dev@dpdk.org",
        "Cc": "pmatilai@redhat.com, yuanhan.liu@linux.intel.com, ferruh.yigit@intel.com",
        "Date": "Thu, 13 Oct 2016 11:11:28 -0500",
        "Message-Id": "<1476375088-40892-1-git-send-email-keith.wiles@intel.com>",
        "X-Mailer": "git-send-email 2.8.1",
        "In-Reply-To": "<1474423220-10207-1-git-send-email-keith.wiles@intel.com>",
        "References": "<1474423220-10207-1-git-send-email-keith.wiles@intel.com>",
        "Subject": "[dpdk-dev] [PATCH v8] drivers/net:new PMD using tun/tap host\n\tinterface",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "patches and discussions about DPDK <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": "The rte_eth_tap.c PMD creates a device using TUN/TAP interfaces\non the local host. The PMD allows for DPDK and the host to\ncommunicate using a raw device interface on the host and in\nthe DPDK application. The device created is a Tap device with\na L2 packet header.\n\nv8 - Fix issue with tap_tx_queue_setup() not return zero on success.\nv7 - Reword the comment in common_base and fix the data->name issue\nv6 - fixed the checkpatch issues\nv5 - merge in changes from list review see related emails\n     fixed many minor edits\nv4 - merge with latest driver changes\nv3 - fix includes by removing ifdef for other type besides Linux\n     Fix the copyright notice in the Makefile\nv2 - merge all of the patches into one patch\n     Fix a typo on naming the tap device\n     Update the maintainers list\n\nSigned-off-by: Keith Wiles <keith.wiles@intel.com>\n---\n MAINTAINERS                             |   5 +\n config/common_base                      |   9 +\n config/common_linuxapp                  |   1 +\n doc/guides/nics/tap.rst                 | 138 ++++++\n drivers/net/Makefile                    |   1 +\n drivers/net/tap/Makefile                |  57 +++\n drivers/net/tap/rte_eth_tap.c           | 756 ++++++++++++++++++++++++++++++++\n drivers/net/tap/rte_pmd_tap_version.map |   4 +\n mk/rte.app.mk                           |   1 +\n 9 files changed, 972 insertions(+)\n create mode 100644 doc/guides/nics/tap.rst\n create mode 100644 drivers/net/tap/Makefile\n create mode 100644 drivers/net/tap/rte_eth_tap.c\n create mode 100644 drivers/net/tap/rte_pmd_tap_version.map",
    "diff": "diff --git a/MAINTAINERS b/MAINTAINERS\nindex cd8d167..f905709 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -394,6 +394,11 @@ F: doc/guides/nics/pcap_ring.rst\n F: app/test/test_pmd_ring.c\n F: app/test/test_pmd_ring_perf.c\n \n+Tap PMD\n+M: Keith Wiles <keith.wiles@intel.com>\n+F: drivers/net/tap\n+F: doc/guides/nics/tap.rst\n+\n Null Networking PMD\n M: Tetsuya Mukawa <mtetsuyah@gmail.com>\n F: drivers/net/null/\ndiff --git a/config/common_base b/config/common_base\nindex f5d2eff..47ef843 100644\n--- a/config/common_base\n+++ b/config/common_base\n@@ -592,3 +592,12 @@ CONFIG_RTE_APP_TEST_RESOURCE_TAR=n\n CONFIG_RTE_TEST_PMD=y\n CONFIG_RTE_TEST_PMD_RECORD_CORE_CYCLES=n\n CONFIG_RTE_TEST_PMD_RECORD_BURST_STATS=n\n+\n+#\n+# Compile the TAP PMD\n+#\n+# The TAP PMD is currently only built for Linux and the\n+# option is enabled by default in common_linuxapp file,\n+# set to 'n' in the common_base file.\n+#\n+CONFIG_RTE_LIBRTE_PMD_TAP=n\ndiff --git a/config/common_linuxapp b/config/common_linuxapp\nindex 2483dfa..782b503 100644\n--- a/config/common_linuxapp\n+++ b/config/common_linuxapp\n@@ -44,3 +44,4 @@ CONFIG_RTE_LIBRTE_PMD_VHOST=y\n CONFIG_RTE_LIBRTE_PMD_AF_PACKET=y\n CONFIG_RTE_LIBRTE_POWER=y\n CONFIG_RTE_VIRTIO_USER=y\n+CONFIG_RTE_LIBRTE_PMD_TAP=y\ndiff --git a/doc/guides/nics/tap.rst b/doc/guides/nics/tap.rst\nnew file mode 100644\nindex 0000000..bffc649\n--- /dev/null\n+++ b/doc/guides/nics/tap.rst\n@@ -0,0 +1,138 @@\n+..  BSD LICENSE\n+    Copyright(c) 2016 Intel Corporation. All rights reserved.\n+    All rights reserved.\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 Intel Corporation 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+Tun/Tap Poll Mode Driver\n+========================================\n+\n+The rte_eth_tap.c PMD creates a device using TUN/TAP interfaces on the local\n+host. The PMD allows for DPDK and the host to communicate using a raw device\n+interface on the host and in the DPDK application.\n+\n+The device created is a TAP device, which sends/receives packet in a raw format\n+with a L2 header. The usage for a TAP PMD is for connectivity to the local host\n+using a TAP interface. When the TAP PMD is initialized it will create a number\n+of tap devices in the host accessed via 'ifconfig -a' or 'ip' command. The\n+commands can be used to assign and query the virtual like device.\n+\n+These TAP interfaces can be used with wireshark or tcpdump or Pktgen-DPDK along\n+with being able to be used as a network connection to the DPDK application. The\n+method enable one or more interfaces is to use the --vdev=net_tap option on the\n+DPDK application  command line. Each --vdev=net_tap option give will create an\n+interface named dtap0, dtap1, ... and so forth.\n+\n+.. code-block:: console\n+\n+   The interfaced name can be changed by adding the iface=foo0\n+   e.g. --vdev=net_tap,iface=foo0 --vdev=net_tap,iface=foo1, ...\n+\n+.. code-block:: console\n+\n+   Also the speed of the interface can be changed from 10G to whatever number\n+   needed, but the interface does not enforce that speed.\n+   e.g. --vdev=net_tap,iface=foo0,speed=25000\n+\n+After the DPDK application is started you can send and receive packets on the\n+interface using the standard rx_burst/tx_burst APIs in DPDK. From the host point\n+of view you can use any host tool like tcpdump, wireshark, ping, Pktgen and\n+others to communicate with the DPDK application. The DPDK application may not\n+understand network protocols like IPv4/6, UDP or TCP unless the application has\n+been written to understand these protocols.\n+\n+If you need the interface as a real network interface meaning running and has\n+a valid IP address then you can do this with the following commands:\n+\n+.. code-block:: console\n+\n+   sudo ip link set dtap0 up; sudo ip addr add 192.168.0.250/24 dev dtap0\n+   sudo ip link set dtap1 up; sudo ip addr add 192.168.1.250/24 dev dtap1\n+\n+Please change the IP addresses as you see fit.\n+\n+If routing is enabled on the host you can also communicate with the DPDK App\n+over the internet via a standard socket layer application as long as you account\n+for the protocol handing in the application.\n+\n+If you have a Network Stack in your DPDK application or something like it you\n+can utilize that stack to handle the network protocols. Plus you would be able\n+to address the interface using an IP address assigned to the internal interface.\n+\n+A very crude test you can do the following:\n+\n+Apply the patch below and make sure you have socat installed on your system.\n+\n+Build DPDK, then pull down Pktgen and build pktgen using the DPDK SDK/Target\n+used to build the dpdk you pulled down.\n+\n+Run pktgen from the pktgen repo directory in an xterm:\n+    Note: change the -b options to blacklist all of your physical ports. The\n+          following command line is all one line.\n+\n+.. code-block:: console\n+\n+    sudo ./app/app/x86_64-native-linuxapp-gcc/app/pktgen -l 1-5 -n 4        \\\n+     --proc-type auto --log-level 8 --socket-mem 512,512 --file-prefix pg   \\\n+     --vdev=net_tap --vdev=net_tap -b 05:00.0 -b 05:00.1                    \\\n+     -b 04:00.0 -b 04:00.1 -b 04:00.2 -b 04:00.3                            \\\n+     -b 81:00.0 -b 81:00.1 -b 81:00.2 -b 81:00.3                            \\\n+      -b 82:00.0 -b 83:00.0 -- -T -P -m [2:3].0 -m [4:5].1                  \\\n+     -f themes/black-yellow.theme\n+\n+I normally put the line above into a file called doit.sh, just to allow for a\n+simple execution of the line above.\n+\n+You can leave the -f themes/black-yellow.theme off if the colors does not work\n+for your system configuration.\n+\n+Verify with 'ifconfig -a' command in a different xterm window, should have a\n+dtap0 and dtap1 interfaces created.\n+\n+Next set the links for the two interfaces to up via the commands below.\n+\n+.. code-block:: console\n+\n+    sudo ip link set dtap0 up; sudo ip addr add 192.168.0.250/24 dev dtap0\n+    sudo ip link set dtap1 up; sudo ip addr add 192.168.1.250/24 dev dtap1\n+\n+Then use socat to create a loopback for the two interfaces.\n+\n+.. code-block:: console\n+\n+    sudo socat interface:dtap0 interface:dtap1\n+\n+Then on the Pktgen command line interface you can start sending packets using\n+the commands 'start 0' and 'start 1' or you can start both at the same time\n+with 'start all'. The command 'str' is an alias for 'start all' and 'stp' is\n+an alias for 'stop all'.\n+\n+While running you should see the 64 byte counters increasing to verify the\n+traffic is being looped back. You can use 'set all size XXX' to change the\n+size of the packets after you stop the traffic. Use the pktgen 'help' command\n+to see a list of all commands. You can also use the '-f' option to load commands\n+at startup.\ndiff --git a/drivers/net/Makefile b/drivers/net/Makefile\nindex bc93230..e366a85 100644\n--- a/drivers/net/Makefile\n+++ b/drivers/net/Makefile\n@@ -51,6 +51,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += pcap\n DIRS-$(CONFIG_RTE_LIBRTE_QEDE_PMD) += qede\n DIRS-$(CONFIG_RTE_LIBRTE_PMD_RING) += ring\n DIRS-$(CONFIG_RTE_LIBRTE_PMD_SZEDATA2) += szedata2\n+DIRS-$(CONFIG_RTE_LIBRTE_PMD_TAP) += tap\n DIRS-$(CONFIG_RTE_LIBRTE_THUNDERX_NICVF_PMD) += thunderx\n DIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio\n DIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += vmxnet3\ndiff --git a/drivers/net/tap/Makefile b/drivers/net/tap/Makefile\nnew file mode 100644\nindex 0000000..e18f30c\n--- /dev/null\n+++ b/drivers/net/tap/Makefile\n@@ -0,0 +1,57 @@\n+#   BSD LICENSE\n+#\n+#   Copyright(c) 2016 Intel Corporation. All rights reserved.\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 Intel Corporation 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+#\n+# library name\n+#\n+LIB = librte_pmd_tap.a\n+\n+EXPORT_MAP := rte_pmd_tap_version.map\n+\n+LIBABIVER := 1\n+\n+CFLAGS += -O3\n+CFLAGS += $(WERROR_FLAGS)\n+\n+#\n+# all source are stored in SRCS-y\n+#\n+SRCS-$(CONFIG_RTE_LIBRTE_PMD_TAP) += rte_eth_tap.c\n+\n+# this lib depends upon:\n+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_TAP) += lib/librte_eal\n+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_TAP) += lib/librte_mbuf\n+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_TAP) += lib/librte_mempool\n+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_TAP) += lib/librte_ether\n+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_TAP) += lib/librte_kvargs\n+\n+include $(RTE_SDK)/mk/rte.lib.mk\ndiff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c\nnew file mode 100644\nindex 0000000..7f303db\n--- /dev/null\n+++ b/drivers/net/tap/rte_eth_tap.c\n@@ -0,0 +1,756 @@\n+/*-\n+ *   BSD LICENSE\n+ *\n+ *   Copyright(c) 2016 Intel Corporation. All rights reserved.\n+ *   All rights reserved.\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 Intel Corporation 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+#include <rte_malloc.h>\n+#include <rte_vdev.h>\n+#include <rte_kvargs.h>\n+\n+#include <sys/types.h>\n+#include <sys/stat.h>\n+#include <sys/socket.h>\n+#include <sys/ioctl.h>\n+#include <sys/mman.h>\n+#include <unistd.h>\n+#include <poll.h>\n+#include <arpa/inet.h>\n+#include <linux/if.h>\n+#include <linux/if_tun.h>\n+#include <linux/if_ether.h>\n+#include <fcntl.h>\n+\n+/* Linux based path to the TUN device */\n+#define TUN_TAP_DEV_PATH        \"/dev/net/tun\"\n+#define DEFAULT_TAP_NAME        \"dtap\"\n+\n+#define ETH_TAP_IFACE_ARG       \"iface\"\n+#define ETH_TAP_SPEED_ARG       \"speed\"\n+\n+#define RTE_PMD_TAP_MAX_QUEUES\t32\n+\n+static const char *valid_arguments[] = {\n+\tETH_TAP_IFACE_ARG,\n+\tETH_TAP_SPEED_ARG,\n+\tNULL\n+};\n+\n+static const char *drivername = \"Tap PMD\";\n+static int tap_unit;\n+\n+static struct rte_eth_link pmd_link = {\n+\t.link_speed = ETH_SPEED_NUM_10G,\n+\t.link_duplex = ETH_LINK_FULL_DUPLEX,\n+\t.link_status = ETH_LINK_DOWN,\n+\t.link_autoneg = ETH_LINK_SPEED_AUTONEG\n+};\n+\n+struct pkt_stats {\n+\tuint64_t opackets;\t\t/* Number of output packets */\n+\tuint64_t ipackets;\t\t/* Number of input packets */\n+\tuint64_t obytes;\t\t/* Number of bytes on output */\n+\tuint64_t ibytes;\t\t/* Number of bytes on input */\n+\tuint64_t errs;\t\t\t/* Number of error packets */\n+};\n+\n+struct rx_queue {\n+\tstruct rte_mempool *mp;\t\t/* Mempool for RX packets */\n+\tuint16_t in_port;\t\t/* Port ID */\n+\tint fd;\n+\n+\tstruct pkt_stats stats;\t\t/* Stats for this RX queue */\n+};\n+\n+struct tx_queue {\n+\tint fd;\n+\tstruct pkt_stats stats;\t\t/* Stats for this TX queue */\n+};\n+\n+struct pmd_internals {\n+\tchar name[RTE_ETH_NAME_MAX_LEN];\t/* Internal Tap device name */\n+\tuint16_t nb_queues;\t\t/* Number of queues supported */\n+\tstruct ether_addr eth_addr;\t/* Mac address of the device port */\n+\n+\tint if_index;\t\t\t/* IF_INDEX for the port */\n+\tint fds[RTE_PMD_TAP_MAX_QUEUES]; /* List of all file descriptors */\n+\n+\tstruct rx_queue rxq[RTE_PMD_TAP_MAX_QUEUES];\t/* List of RX queues */\n+\tstruct tx_queue txq[RTE_PMD_TAP_MAX_QUEUES];\t/* List of TX queues */\n+};\n+\n+/* Tun/Tap allocation routine\n+ *\n+ * name is the number of the interface to use, unless NULL to take the host\n+ * supplied name.\n+ */\n+static int\n+tun_alloc(char *name)\n+{\n+\tstruct ifreq ifr;\n+\tunsigned int features;\n+\tint fd;\n+\n+\tmemset(&ifr, 0, sizeof(struct ifreq));\n+\n+\tifr.ifr_flags = IFF_TAP | IFF_NO_PI;\n+\tif (name && name[0])\n+\t\tstrncpy(ifr.ifr_name, name, IFNAMSIZ);\n+\n+\tfd = open(TUN_TAP_DEV_PATH, O_RDWR);\n+\tif (fd < 0) {\n+\t\tRTE_LOG(ERR, PMD, \"Unable to create TAP interface\");\n+\t\tgoto error;\n+\t}\n+\n+\t/* Grab the TUN features to verify we can work */\n+\tif (ioctl(fd, TUNGETFEATURES, &features) < 0) {\n+\t\tRTE_LOG(ERR, PMD, \"Unable to get TUN/TAP features\\n\");\n+\t\tgoto error;\n+\t}\n+\tRTE_LOG(DEBUG, PMD, \"TUN/TAP Features %08x\\n\", features);\n+\n+\tif (!(features & IFF_MULTI_QUEUE) && (RTE_PMD_TAP_MAX_QUEUES > 1)) {\n+\t\tRTE_LOG(DEBUG, PMD, \"TUN/TAP device only one queue\\n\");\n+\t\tgoto error;\n+\t} else if ((features & IFF_ONE_QUEUE) &&\n+\t\t\t(RTE_PMD_TAP_MAX_QUEUES == 1)) {\n+\t\tifr.ifr_flags |= IFF_ONE_QUEUE;\n+\t\tRTE_LOG(DEBUG, PMD, \"Single queue only support\\n\");\n+\t} else {\n+\t\tifr.ifr_flags |= IFF_MULTI_QUEUE;\n+\t\tRTE_LOG(DEBUG, PMD, \"Multi-queue support for %d queues\\n\",\n+\t\t\tRTE_PMD_TAP_MAX_QUEUES);\n+\t}\n+\n+\t/* Set the TUN/TAP configuration and get the name if needed */\n+\tif (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) {\n+\t\tRTE_LOG(ERR, PMD, \"Unable to set TUNSETIFF for %s\\n\",\n+\t\t\tifr.ifr_name);\n+\t\tperror(\"TUNSETIFF\");\n+\t\tgoto error;\n+\t}\n+\n+\t/* Always set the file descriptor to non-blocking */\n+\tif (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {\n+\t\tRTE_LOG(ERR, PMD, \"Unable to set to nonblocking\\n\");\n+\t\tperror(\"F_SETFL, NONBLOCK\");\n+\t\tgoto error;\n+\t}\n+\n+\t/* If the name is different that new name as default */\n+\tif (name && strcmp(name, ifr.ifr_name))\n+\t\tsnprintf(name, RTE_ETH_NAME_MAX_LEN - 1, \"%s\", ifr.ifr_name);\n+\n+\treturn fd;\n+\n+error:\n+\tif (fd > 0)\n+\t\tclose(fd);\n+\treturn -1;\n+}\n+\n+/* Callback to handle the rx burst of packets to the correct interface and\n+ * file descriptor(s) in a multi-queue setup.\n+ */\n+static uint16_t\n+pmd_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)\n+{\n+\tint len;\n+\tstruct rte_mbuf *mbuf;\n+\tstruct rx_queue *rxq = queue;\n+\tuint16_t num_rx;\n+\tunsigned long num_rx_bytes = 0;\n+\n+\tfor (num_rx = 0; num_rx < nb_pkts; ) {\n+\t\t/* allocate the next mbuf */\n+\t\tmbuf = rte_pktmbuf_alloc(rxq->mp);\n+\t\tif (unlikely(!mbuf)) {\n+\t\t\tRTE_LOG(WARNING, PMD, \"Unable to allocate mbuf\\n\");\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tlen = read(rxq->fd, rte_pktmbuf_mtod(mbuf, char *),\n+\t\t\t   rte_pktmbuf_tailroom(mbuf));\n+\t\tif (len <= 0) {\n+\t\t\trte_pktmbuf_free(mbuf);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tmbuf->data_len = len;\n+\t\tmbuf->pkt_len = len;\n+\t\tmbuf->port = rxq->in_port;\n+\n+\t\t/* account for the receive frame */\n+\t\tbufs[num_rx++] = mbuf;\n+\t\tnum_rx_bytes += mbuf->pkt_len;\n+\t}\n+\trxq->stats.ipackets += num_rx;\n+\trxq->stats.ibytes += num_rx_bytes;\n+\n+\treturn num_rx;\n+}\n+\n+/* Callback to handle sending packets from the tap interface\n+ */\n+static uint16_t\n+pmd_tx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)\n+{\n+\tstruct rte_mbuf *mbuf;\n+\tstruct tx_queue *txq = queue;\n+\tstruct pollfd pfd;\n+\tuint16_t num_tx = 0;\n+\tunsigned long num_tx_bytes = 0;\n+\tint i, n;\n+\n+\tif (unlikely(nb_pkts == 0))\n+\t\treturn 0;\n+\n+\tpfd.events = POLLOUT;\n+\tpfd.fd = txq->fd;\n+\tfor (i = 0; i < nb_pkts; i++) {\n+\t\tn = poll(&pfd, 1, 0);\n+\n+\t\tif (n <= 0)\n+\t\t\tbreak;\n+\n+\t\tif (pfd.revents & POLLOUT) {\n+\t\t\t/* copy the tx frame data */\n+\t\t\tmbuf = bufs[num_tx];\n+\t\t\tn = write(pfd.fd, rte_pktmbuf_mtod(mbuf, void*),\n+\t\t\t\t  rte_pktmbuf_pkt_len(mbuf));\n+\t\t\tif (n <= 0)\n+\t\t\t\tbreak;\n+\n+\t\t\tnum_tx++;\n+\t\t\tnum_tx_bytes += mbuf->pkt_len;\n+\t\t\trte_pktmbuf_free(mbuf);\n+\t\t}\n+\t}\n+\n+\ttxq->stats.opackets += num_tx;\n+\ttxq->stats.errs += nb_pkts - num_tx;\n+\ttxq->stats.obytes += num_tx_bytes;\n+\n+\treturn num_tx;\n+}\n+\n+static int\n+tap_dev_start(struct rte_eth_dev *dev)\n+{\n+\t/* Force the Link up */\n+\tdev->data->dev_link.link_status = ETH_LINK_UP;\n+\n+\treturn 0;\n+}\n+\n+/* This function gets called when the current port gets stopped.\n+ */\n+static void\n+tap_dev_stop(struct rte_eth_dev *dev)\n+{\n+\tint i;\n+\tstruct pmd_internals *internals = dev->data->dev_private;\n+\n+\tfor (i = 0; i < internals->nb_queues; i++)\n+\t\tif (internals->fds[i] != -1)\n+\t\t\tclose(internals->fds[i]);\n+\n+\tdev->data->dev_link.link_status = ETH_LINK_DOWN;\n+}\n+\n+static int\n+tap_dev_configure(struct rte_eth_dev *dev __rte_unused)\n+{\n+\treturn 0;\n+}\n+\n+static void\n+tap_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)\n+{\n+\tstruct pmd_internals *internals = dev->data->dev_private;\n+\n+\tdev_info->driver_name = drivername;\n+\tdev_info->if_index = internals->if_index;\n+\tdev_info->max_mac_addrs = 1;\n+\tdev_info->max_rx_pktlen = (uint32_t)ETHER_MAX_VLAN_FRAME_LEN;\n+\tdev_info->max_rx_queues = internals->nb_queues;\n+\tdev_info->max_tx_queues = internals->nb_queues;\n+\tdev_info->min_rx_bufsize = 0;\n+\tdev_info->pci_dev = NULL;\n+}\n+\n+static void\n+tap_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *tap_stats)\n+{\n+\tunsigned int i, imax;\n+\tunsigned long rx_total = 0, tx_total = 0, tx_err_total = 0;\n+\tunsigned long rx_bytes_total = 0, tx_bytes_total = 0;\n+\tconst struct pmd_internals *pmd = dev->data->dev_private;\n+\n+\timax = (pmd->nb_queues < RTE_ETHDEV_QUEUE_STAT_CNTRS) ?\n+\t\tpmd->nb_queues : RTE_ETHDEV_QUEUE_STAT_CNTRS;\n+\n+\tfor (i = 0; i < imax; i++) {\n+\t\ttap_stats->q_ipackets[i] = pmd->rxq[i].stats.ipackets;\n+\t\ttap_stats->q_ibytes[i] = pmd->rxq[i].stats.ibytes;\n+\t\trx_total += tap_stats->q_ipackets[i];\n+\t\trx_bytes_total += tap_stats->q_ibytes[i];\n+\t}\n+\n+\tfor (i = 0; i < imax; i++) {\n+\t\ttap_stats->q_opackets[i] = pmd->txq[i].stats.opackets;\n+\t\ttap_stats->q_errors[i] = pmd->txq[i].stats.errs;\n+\t\ttap_stats->q_obytes[i] = pmd->txq[i].stats.obytes;\n+\t\ttx_total += tap_stats->q_opackets[i];\n+\t\ttx_err_total += tap_stats->q_errors[i];\n+\t\ttx_bytes_total += tap_stats->q_obytes[i];\n+\t}\n+\n+\ttap_stats->ipackets = rx_total;\n+\ttap_stats->ibytes = rx_bytes_total;\n+\ttap_stats->opackets = tx_total;\n+\ttap_stats->oerrors = tx_err_total;\n+\ttap_stats->obytes = tx_bytes_total;\n+}\n+\n+static void\n+tap_stats_reset(struct rte_eth_dev *dev)\n+{\n+\tint i;\n+\tstruct pmd_internals *pmd = dev->data->dev_private;\n+\n+\tfor (i = 0; i < pmd->nb_queues; i++) {\n+\t\tpmd->rxq[i].stats.ipackets = 0;\n+\t\tpmd->rxq[i].stats.ibytes = 0;\n+\t}\n+\n+\tfor (i = 0; i < pmd->nb_queues; i++) {\n+\t\tpmd->txq[i].stats.opackets = 0;\n+\t\tpmd->txq[i].stats.errs = 0;\n+\t\tpmd->txq[i].stats.obytes = 0;\n+\t}\n+}\n+\n+static void\n+tap_dev_close(struct rte_eth_dev *dev __rte_unused)\n+{\n+}\n+\n+static void\n+tap_rx_queue_release(void *queue)\n+{\n+\tstruct rx_queue *rxq = queue;\n+\n+\tif (rxq && (rxq->fd > 0)) {\n+\t\tclose(rxq->fd);\n+\t\trxq->fd = -1;\n+\t}\n+}\n+\n+static void\n+tap_tx_queue_release(void *queue)\n+{\n+\tstruct tx_queue *txq = queue;\n+\n+\tif (txq && (txq->fd > 0)) {\n+\t\tclose(txq->fd);\n+\t\ttxq->fd = -1;\n+\t}\n+}\n+\n+static int\n+tap_link_update(struct rte_eth_dev *dev __rte_unused,\n+\t\tint wait_to_complete __rte_unused)\n+{\n+\treturn 0;\n+}\n+\n+static int\n+tap_setup_queue(struct rte_eth_dev *dev,\n+\t\tstruct pmd_internals *internals,\n+\t\tuint16_t qid)\n+{\n+\tstruct rx_queue *rx = &internals->rxq[qid];\n+\tstruct tx_queue *tx = &internals->txq[qid];\n+\tint fd;\n+\n+\tfd = rx->fd;\n+\tif (fd < 0) {\n+\t\tfd = tx->fd;\n+\t\tif (fd < 0) {\n+\t\t\tRTE_LOG(INFO, PMD, \"Add queue to TAP %s for qid %d\\n\",\n+\t\t\t\tdev->data->name, qid);\n+\t\t\tfd = tun_alloc(dev->data->name);\n+\t\t\tif (fd < 0) {\n+\t\t\t\tRTE_LOG(ERR, PMD, \"tun_alloc(%s) failed\\n\",\n+\t\t\t\t\tdev->data->name);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\t}\n+\tdev->data->rx_queues[qid] = rx;\n+\tdev->data->tx_queues[qid] = tx;\n+\n+\trx->fd = fd;\n+\ttx->fd = fd;\n+\n+\treturn fd;\n+}\n+\n+static int\n+tap_rx_queue_setup(struct rte_eth_dev *dev,\n+\t\t   uint16_t rx_queue_id,\n+\t\t   uint16_t nb_rx_desc __rte_unused,\n+\t\t   unsigned int socket_id __rte_unused,\n+\t\t   const struct rte_eth_rxconf *rx_conf __rte_unused,\n+\t\t   struct rte_mempool *mp)\n+{\n+\tstruct pmd_internals *internals = dev->data->dev_private;\n+\tuint16_t buf_size;\n+\tint fd;\n+\n+\tif ((rx_queue_id >= internals->nb_queues) || !mp) {\n+\t\tRTE_LOG(ERR, PMD, \"nb_queues %d mp %p\\n\",\n+\t\t\tinternals->nb_queues, mp);\n+\t\treturn -1;\n+\t}\n+\n+\tinternals->rxq[rx_queue_id].mp = mp;\n+\tinternals->rxq[rx_queue_id].in_port = dev->data->port_id;\n+\n+\t/* Now get the space available for data in the mbuf */\n+\tbuf_size = (uint16_t)(rte_pktmbuf_data_room_size(mp) -\n+\t\t\t\tRTE_PKTMBUF_HEADROOM);\n+\n+\tif (buf_size < ETH_FRAME_LEN) {\n+\t\tRTE_LOG(ERR, PMD,\n+\t\t\t\"%s: %d bytes will not fit in mbuf (%d bytes)\\n\",\n+\t\t\tdev->data->name, ETH_FRAME_LEN, buf_size);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tfd = tap_setup_queue(dev, internals, rx_queue_id);\n+\tif (fd == -1)\n+\t\treturn -1;\n+\n+\tinternals->fds[rx_queue_id] = fd;\n+\tRTE_LOG(INFO, PMD, \"RX TAP device name %s, qid %d on fd %d\\n\",\n+\t\tdev->data->name, rx_queue_id, internals->rxq[rx_queue_id].fd);\n+\n+\treturn 0;\n+}\n+\n+static int\n+tap_tx_queue_setup(struct rte_eth_dev *dev,\n+\t\t   uint16_t tx_queue_id,\n+\t\t   uint16_t nb_tx_desc __rte_unused,\n+\t\t   unsigned int socket_id __rte_unused,\n+\t\t   const struct rte_eth_txconf *tx_conf __rte_unused)\n+{\n+\tstruct pmd_internals *internals = dev->data->dev_private;\n+\tint ret;\n+\n+\tif (tx_queue_id >= internals->nb_queues)\n+\t\treturn -1;\n+\n+\tret = tap_setup_queue(dev, internals, tx_queue_id);\n+\tif (ret == -1)\n+\t\treturn -1;\n+\n+\tRTE_LOG(INFO, PMD, \"TX TAP device name %s, qid %d on fd %d\\n\",\n+\t\tdev->data->name, tx_queue_id, internals->txq[tx_queue_id].fd);\n+\n+\treturn 0;\n+}\n+\n+static const struct eth_dev_ops ops = {\n+\t.dev_start              = tap_dev_start,\n+\t.dev_stop               = tap_dev_stop,\n+\t.dev_close              = tap_dev_close,\n+\t.dev_configure          = tap_dev_configure,\n+\t.dev_infos_get          = tap_dev_info,\n+\t.rx_queue_setup         = tap_rx_queue_setup,\n+\t.tx_queue_setup         = tap_tx_queue_setup,\n+\t.rx_queue_release       = tap_rx_queue_release,\n+\t.tx_queue_release       = tap_tx_queue_release,\n+\t.link_update            = tap_link_update,\n+\t.stats_get              = tap_stats_get,\n+\t.stats_reset            = tap_stats_reset,\n+};\n+\n+static int\n+pmd_mac_address(int fd, struct rte_eth_dev *dev, struct ether_addr *addr)\n+{\n+\tstruct ifreq ifr;\n+\n+\tif ((fd <= 0) || !dev || !addr)\n+\t\treturn -1;\n+\n+\tmemset(&ifr, 0, sizeof(ifr));\n+\n+\tif (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) {\n+\t\tRTE_LOG(ERR, PMD, \"ioctl failed (SIOCGIFHWADDR) (%s)\\n\",\n+\t\t\tifr.ifr_name);\n+\t\treturn -1;\n+\t}\n+\n+\t/* Set the host based MAC address to this special MAC format */\n+\tifr.ifr_hwaddr.sa_data[0] = 'T';\n+\tifr.ifr_hwaddr.sa_data[1] = 'a';\n+\tifr.ifr_hwaddr.sa_data[2] = 'p';\n+\tifr.ifr_hwaddr.sa_data[3] = '-';\n+\tifr.ifr_hwaddr.sa_data[4] = dev->data->port_id;\n+\tifr.ifr_hwaddr.sa_data[5] = dev->data->numa_node;\n+\tif (ioctl(fd, SIOCSIFHWADDR, &ifr) == -1) {\n+\t\tRTE_LOG(ERR, PMD, \"%s: ioctl failed (SIOCSIFHWADDR) (%s)\\n\",\n+\t\t\tdev->data->name, ifr.ifr_name);\n+\t\treturn -1;\n+\t}\n+\n+\t/* Set the local application MAC address, needs to be different then\n+\t * the host based MAC address.\n+\t */\n+\tifr.ifr_hwaddr.sa_data[0] = 'd';\n+\tifr.ifr_hwaddr.sa_data[1] = 'n';\n+\tifr.ifr_hwaddr.sa_data[2] = 'e';\n+\tifr.ifr_hwaddr.sa_data[3] = 't';\n+\tifr.ifr_hwaddr.sa_data[4] = dev->data->port_id;\n+\tifr.ifr_hwaddr.sa_data[5] = dev->data->numa_node;\n+\trte_memcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);\n+\n+\treturn 0;\n+}\n+\n+static int\n+eth_dev_tap_create(const char *name, char *tap_name)\n+{\n+\tint numa_node = rte_socket_id();\n+\tstruct rte_eth_dev *dev = NULL;\n+\tstruct pmd_internals *pmd = NULL;\n+\tstruct rte_eth_dev_data *data = NULL;\n+\tint i, fd = -1;\n+\n+\tRTE_LOG(INFO, PMD,\n+\t\t\"%s: Create TAP Ethernet device with %d queues on numa %u\\n\",\n+\t\t name, RTE_PMD_TAP_MAX_QUEUES, rte_socket_id());\n+\n+\tdata = rte_zmalloc_socket(tap_name, sizeof(*data), 0, numa_node);\n+\tif (!data)\n+\t\tgoto error_exit;\n+\n+\tpmd = rte_zmalloc_socket(tap_name, sizeof(*pmd), 0, numa_node);\n+\tif (!pmd)\n+\t\tgoto error_exit;\n+\n+\t/* Use the name and not the tap_name */\n+\tdev = rte_eth_dev_allocate(name);\n+\tif (!dev)\n+\t\tgoto error_exit;\n+\n+\tsnprintf(pmd->name, sizeof(pmd->name), \"%s\", tap_name);\n+\n+\tpmd->nb_queues = RTE_PMD_TAP_MAX_QUEUES;\n+\n+\t/* Setup some default values */\n+\tdata->dev_private = pmd;\n+\tdata->port_id = dev->data->port_id;\n+\tdata->dev_flags = RTE_ETH_DEV_DETACHABLE;\n+\tdata->kdrv = RTE_KDRV_NONE;\n+\tdata->drv_name = drivername;\n+\tdata->numa_node = numa_node;\n+\n+\tdata->dev_link = pmd_link;\n+\tdata->mac_addrs = &pmd->eth_addr;\n+\tdata->nb_rx_queues = pmd->nb_queues;\n+\tdata->nb_tx_queues = pmd->nb_queues;\n+\tdata->drv_name = drivername;\n+\n+\tdev->data = data;\n+\tdev->dev_ops = &ops;\n+\tdev->driver = NULL;\n+\tdev->rx_pkt_burst = pmd_rx_burst;\n+\tdev->tx_pkt_burst = pmd_tx_burst;\n+\tsnprintf(dev->data->name, sizeof(dev->data->name), \"%s\", name);\n+\n+\t/* Create the first Tap device */\n+\tfd = tun_alloc(tap_name);\n+\tif (fd < 0)\n+\t\tgoto error_exit;\n+\n+\t/* Presetup the fds to -1 as being not working */\n+\tfor (i = 0; i < RTE_PMD_TAP_MAX_QUEUES; i++) {\n+\t\tpmd->fds[i] = -1;\n+\t\tpmd->rxq[i].fd = -1;\n+\t\tpmd->txq[i].fd = -1;\n+\t}\n+\n+\t/* Take the TUN/TAP fd and place in the first location */\n+\tpmd->rxq[0].fd = fd;\n+\tpmd->txq[0].fd = fd;\n+\tpmd->fds[0] = fd;\n+\n+\tif (pmd_mac_address(fd, dev, &pmd->eth_addr) < 0)\n+\t\tgoto error_exit;\n+\n+\treturn 0;\n+\n+error_exit:\n+\tRTE_PMD_DEBUG_TRACE(\"Unable to initialize %s\\n\", name);\n+\n+\trte_free(data);\n+\trte_free(pmd);\n+\n+\trte_eth_dev_release_port(dev);\n+\n+\treturn -EINVAL;\n+}\n+\n+static int\n+set_interface_name(const char *key __rte_unused,\n+\t\t   const char *value,\n+\t\t   void *extra_args)\n+{\n+\tchar *name = (char *)extra_args;\n+\n+\tif (value)\n+\t\tsnprintf(name, RTE_ETH_NAME_MAX_LEN - 1, \"%s\", value);\n+\telse\n+\t\tsnprintf(name, RTE_ETH_NAME_MAX_LEN - 1, \"%s%d\",\n+\t\t\t DEFAULT_TAP_NAME, (tap_unit - 1));\n+\n+\treturn 0;\n+}\n+\n+static int\n+set_interface_speed(const char *key __rte_unused,\n+\t\t    const char *value,\n+\t\t    void *extra_args)\n+{\n+\t*(int *)extra_args = (value) ? atoi(value) : ETH_SPEED_NUM_10G;\n+\n+\treturn 0;\n+}\n+\n+/* Open a TAP interface device.\n+ */\n+static int\n+rte_pmd_tap_probe(const char *name, const char *params)\n+{\n+\tint ret;\n+\tstruct rte_kvargs *kvlist = NULL;\n+\tint speed;\n+\tchar tap_name[RTE_ETH_NAME_MAX_LEN];\n+\n+\tspeed = ETH_SPEED_NUM_10G;\n+\tsnprintf(tap_name, sizeof(tap_name), \"%s%d\",\n+\t\t DEFAULT_TAP_NAME, tap_unit++);\n+\n+\tRTE_LOG(INFO, PMD, \"Initializing pmd_tap for %s as %s\\n\",\n+\t\tname, tap_name);\n+\n+\tif (params && (params[0] != '\\0')) {\n+\t\tRTE_LOG(INFO, PMD, \"paramaters (%s)\\n\", params);\n+\n+\t\tkvlist = rte_kvargs_parse(params, valid_arguments);\n+\t\tif (kvlist) {\n+\t\t\tif (rte_kvargs_count(kvlist, ETH_TAP_SPEED_ARG) == 1) {\n+\t\t\t\tret = rte_kvargs_process(kvlist,\n+\t\t\t\t\t\t\t ETH_TAP_SPEED_ARG,\n+\t\t\t\t\t\t\t &set_interface_speed,\n+\t\t\t\t\t\t\t &speed);\n+\t\t\t\tif (ret == -1)\n+\t\t\t\t\tgoto leave;\n+\t\t\t}\n+\n+\t\t\tif (rte_kvargs_count(kvlist, ETH_TAP_IFACE_ARG) == 1) {\n+\t\t\t\tret = rte_kvargs_process(kvlist,\n+\t\t\t\t\t\t\t ETH_TAP_IFACE_ARG,\n+\t\t\t\t\t\t\t &set_interface_name,\n+\t\t\t\t\t\t\t tap_name);\n+\t\t\t\tif (ret == -1)\n+\t\t\t\t\tgoto leave;\n+\t\t\t}\n+\t\t}\n+\t}\n+\tpmd_link.link_speed = speed;\n+\n+\tret = eth_dev_tap_create(name, tap_name);\n+\n+leave:\n+\tif (ret == -1) {\n+\t\tRTE_LOG(INFO, PMD, \"Failed to create pmd_tap for %s as %s\\n\",\n+\t\t\tname, tap_name);\n+\t\ttap_unit--;\t\t/* Restore the unit number */\n+\t}\n+\trte_kvargs_free(kvlist);\n+\n+\treturn ret;\n+}\n+\n+/* detach a TAP device.\n+ */\n+static int\n+rte_pmd_tap_remove(const char *name)\n+{\n+\tstruct rte_eth_dev *eth_dev = NULL;\n+\tstruct pmd_internals *internals;\n+\tint i;\n+\n+\tRTE_LOG(INFO, PMD, \"Closing TUN/TAP Ethernet device on numa %u\\n\",\n+\t\trte_socket_id());\n+\n+\t/* find the ethdev entry */\n+\teth_dev = rte_eth_dev_allocated(name);\n+\tif (!eth_dev)\n+\t\treturn 0;\n+\n+\tinternals = eth_dev->data->dev_private;\n+\tfor (i = 0; i < internals->nb_queues; i++)\n+\t\tif (internals->fds[i] != -1)\n+\t\t\tclose(internals->fds[i]);\n+\n+\trte_free(eth_dev->data->dev_private);\n+\trte_free(eth_dev->data);\n+\n+\trte_eth_dev_release_port(eth_dev);\n+\n+\treturn 0;\n+}\n+\n+static struct rte_vdev_driver pmd_tap_drv = {\n+\t.probe = rte_pmd_tap_probe,\n+\t.remove = rte_pmd_tap_remove,\n+};\n+\n+DRIVER_REGISTER_VDEV(net_tap, pmd_tap_drv);\n+DRIVER_REGISTER_PARAM_STRING(net_tap, \"iface=<string>,speed=N\");\ndiff --git a/drivers/net/tap/rte_pmd_tap_version.map b/drivers/net/tap/rte_pmd_tap_version.map\nnew file mode 100644\nindex 0000000..61463bf\n--- /dev/null\n+++ b/drivers/net/tap/rte_pmd_tap_version.map\n@@ -0,0 +1,4 @@\n+DPDK_16.11 {\n+\n+\tlocal: *;\n+};\ndiff --git a/mk/rte.app.mk b/mk/rte.app.mk\nindex ac50a21..40d16f7 100644\n--- a/mk/rte.app.mk\n+++ b/mk/rte.app.mk\n@@ -123,6 +123,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_PCAP)       += -lrte_pmd_pcap -lpcap\n _LDLIBS-$(CONFIG_RTE_LIBRTE_QEDE_PMD)       += -lrte_pmd_qede -lz\n _LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_RING)       += -lrte_pmd_ring\n _LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_SZEDATA2)   += -lrte_pmd_szedata2 -lsze2\n+_LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_TAP)        += -lrte_pmd_tap\n _LDLIBS-$(CONFIG_RTE_LIBRTE_THUNDERX_NICVF_PMD) += -lrte_pmd_thunderx_nicvf -lm\n _LDLIBS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD)     += -lrte_pmd_virtio\n ifeq ($(CONFIG_RTE_LIBRTE_VHOST),y)\n",
    "prefixes": [
        "dpdk-dev",
        "v8"
    ]
}