get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 15993,
    "url": "http://patches.dpdk.org/api/patches/15993/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/1474423220-10207-1-git-send-email-keith.wiles@intel.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": "<1474423220-10207-1-git-send-email-keith.wiles@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/1474423220-10207-1-git-send-email-keith.wiles@intel.com",
    "date": "2016-09-21T02:00:20",
    "name": "[dpdk-dev,v3] drivers/net:new PMD using tun/tap host interface",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "063441daeda9965dbae7fc2d42d3302e29651b96",
    "submitter": {
        "id": 166,
        "url": "http://patches.dpdk.org/api/people/166/?format=api",
        "name": "Wiles, Keith",
        "email": "keith.wiles@intel.com"
    },
    "delegate": {
        "id": 10,
        "url": "http://patches.dpdk.org/api/users/10/?format=api",
        "username": "bruce",
        "first_name": "Bruce",
        "last_name": "Richardson",
        "email": "bruce.richardson@intel.com"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/1474423220-10207-1-git-send-email-keith.wiles@intel.com/mbox/",
    "series": [],
    "comments": "http://patches.dpdk.org/api/patches/15993/comments/",
    "check": "pending",
    "checks": "http://patches.dpdk.org/api/patches/15993/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 15AD937B8;\n\tWed, 21 Sep 2016 04:00:31 +0200 (CEST)",
            "from mga05.intel.com (mga05.intel.com [192.55.52.43])\n\tby dpdk.org (Postfix) with ESMTP id 4978A37B3\n\tfor <dev@dpdk.org>; Wed, 21 Sep 2016 04:00:28 +0200 (CEST)",
            "from orsmga005.jf.intel.com ([10.7.209.41])\n\tby fmsmga105.fm.intel.com with ESMTP; 20 Sep 2016 19:00:27 -0700",
            "from snehaban-mobl.amr.corp.intel.com ([10.252.140.51])\n\tby orsmga005.jf.intel.com with ESMTP; 20 Sep 2016 19:00:24 -0700"
        ],
        "X-ExtLoop1": "1",
        "X-IronPort-AV": "E=Sophos;i=\"5.30,371,1470726000\"; d=\"scan'208\";a=\"11631075\"",
        "From": "Keith Wiles <keith.wiles@intel.com>",
        "To": "dev@dpdk.org",
        "Cc": "pmatilai@redhat.com,\n\tyuanhan.liu@linux.intel.com",
        "Date": "Tue, 20 Sep 2016 22:00:20 -0400",
        "Message-Id": "<1474423220-10207-1-git-send-email-keith.wiles@intel.com>",
        "X-Mailer": "git-send-email 2.8.1",
        "In-Reply-To": "<1473948649-14169-2-git-send-email-keith.wiles@intel.com>",
        "References": "<1473948649-14169-2-git-send-email-keith.wiles@intel.com>",
        "Subject": "[dpdk-dev] [PATCH v3] 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\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_linuxapp                  |   2 +\n doc/guides/nics/tap.rst                 |  84 ++++\n drivers/net/Makefile                    |   1 +\n drivers/net/tap/Makefile                |  57 +++\n drivers/net/tap/rte_eth_tap.c           | 867 ++++++++++++++++++++++++++++++++\n drivers/net/tap/rte_pmd_tap_version.map |   4 +\n mk/rte.app.mk                           |   1 +\n 8 files changed, 1021 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 7c33ad4..fad74e4 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -392,6 +392,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_linuxapp b/config/common_linuxapp\nindex 2483dfa..59a2053 100644\n--- a/config/common_linuxapp\n+++ b/config/common_linuxapp\n@@ -44,3 +44,5 @@ 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\n+CONFIG_RTE_PMD_TAP_MAX_QUEUES=32\ndiff --git a/doc/guides/nics/tap.rst b/doc/guides/nics/tap.rst\nnew file mode 100644\nindex 0000000..072def8\n--- /dev/null\n+++ b/doc/guides/nics/tap.rst\n@@ -0,0 +1,84 @@\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=eth_tap option on the\n+DPDK application  command line. Each --vdev=eth_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. --vedv=eth_tap,iface=foo0 --vdev=eth_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=eth_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.\ndiff --git a/drivers/net/Makefile b/drivers/net/Makefile\nindex bc93230..b4afa98 100644\n--- a/drivers/net/Makefile\n+++ b/drivers/net/Makefile\n@@ -55,6 +55,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_THUNDERX_NICVF_PMD) += thunderx\n DIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio\n DIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += vmxnet3\n DIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += xenvirt\n+DIRS-$(CONFIG_RTE_LIBRTE_PMD_TAP) += tap\n \n ifeq ($(CONFIG_RTE_LIBRTE_VHOST),y)\n DIRS-$(CONFIG_RTE_LIBRTE_PMD_VHOST) += vhost\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..e60844b\n--- /dev/null\n+++ b/drivers/net/tap/rte_eth_tap.c\n@@ -0,0 +1,867 @@\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_kvargs.h>\n+#include <rte_dev.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+#include <poll.h>\n+\n+/* Linux based path to the TUN device */\n+#define TUN_TAP_DEV_PATH        \"/dev/net/tun\"\n+\n+#define ETH_TAP_IFACE_ARG       \"iface\"\n+#define ETH_TAP_SPEED_ARG       \"speed\"\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 = 0;\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 tap_info {\n+\tchar name[RTE_ETH_NAME_MAX_LEN]; /* Interface name supplied/given */\n+\tint speed;\t\t\t /* Speed of interface */\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\t/* Number of queues supported */\n+\tuint16_t pad0;\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+/*\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) && (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\", ifr.ifr_name);\n+\t\tperror(\"TUNSETIFF\");\n+\t\tgoto error;\n+\t}\n+\n+\t/* Always set the fiile 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\tstrcpy(name, ifr.ifr_name);\n+\n+\treturn fd;\n+\n+error:\n+\tif (fd > 0)\n+\t\tclose(fd);\n+\treturn -1;\n+}\n+\n+/*\n+ * Callback to handle the rx burst of packets to the correct interface and file\n+ * 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;\n+\tstruct rte_mbuf *mbuf;\n+\tstruct rx_queue *rxq = queue;\n+\tstruct pollfd pfd;\n+\tuint16_t num_rx;\n+\tunsigned long num_rx_bytes = 0;\n+\n+\tpfd.events = POLLIN;\n+\tpfd.fd = rxq->fd;\n+\tfor (num_rx = 0; num_rx < nb_pkts; ) {\n+\t\tn = poll(&pfd, 1, 0);\n+\n+\t\tif (n <= 0)\n+\t\t\tbreak;\n+\n+\t\tif (pfd.revents == 0)\n+\t\t\tcontinue;\n+\n+\t\tif (pfd.revents & POLLERR) {\n+\t\t\trxq->stats.errs++;\n+\t\t\tRTE_LOG(ERR, PMD, \"Packet Error\\n\");\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (pfd.revents & POLLHUP)\n+\t\t\tRTE_LOG(ERR, PMD, \"Peer closed connection\\n\");\n+\n+\t\t/* allocate the next mbuf */\n+\t\tmbuf = rte_pktmbuf_alloc(rxq->mp);\n+\t\tif (unlikely(mbuf == NULL)) {\n+\t\t\tRTE_LOG(ERR, PMD, \"Unable to allocate mbuf\\n\");\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tlen = read(pfd.fd, rte_pktmbuf_mtod(mbuf, char *),\n+\t\t\t   rte_pktmbuf_tailroom(mbuf));\n+\t\tif (len <= 0) {\n+\t\t\tRTE_LOG(ERR, PMD, \"len %d\\n\", len);\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+/*\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+/*\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 = (uint16_t)internals->nb_queues;\n+\tdev_info->max_tx_queues = (uint16_t)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 *igb_stats)\n+{\n+\tunsigned 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 *internal = dev->data->dev_private;\n+\n+\timax = (internal->nb_queues < RTE_ETHDEV_QUEUE_STAT_CNTRS) ?\n+\t\tinternal->nb_queues : RTE_ETHDEV_QUEUE_STAT_CNTRS;\n+\n+\tfor (i = 0; i < imax; i++) {\n+\t\tigb_stats->q_ipackets[i] = internal->rxq[i].stats.ipackets;\n+\t\tigb_stats->q_ibytes[i] = internal->rxq[i].stats.ibytes;\n+\t\trx_total += igb_stats->q_ipackets[i];\n+\t\trx_bytes_total += igb_stats->q_ibytes[i];\n+\t}\n+\n+\timax = (internal->nb_queues < RTE_ETHDEV_QUEUE_STAT_CNTRS) ?\n+\t\tinternal->nb_queues : RTE_ETHDEV_QUEUE_STAT_CNTRS;\n+\n+\tfor (i = 0; i < imax; i++) {\n+\t\tigb_stats->q_opackets[i] = internal->txq[i].stats.opackets;\n+\t\tigb_stats->q_errors[i] = internal->txq[i].stats.errs;\n+\t\tigb_stats->q_obytes[i] = internal->txq[i].stats.obytes;\n+\t\ttx_total += igb_stats->q_opackets[i];\n+\t\ttx_err_total += igb_stats->q_errors[i];\n+\t\ttx_bytes_total += igb_stats->q_obytes[i];\n+\t}\n+\n+\tigb_stats->ipackets = rx_total;\n+\tigb_stats->ibytes = rx_bytes_total;\n+\tigb_stats->opackets = tx_total;\n+\tigb_stats->oerrors = tx_err_total;\n+\tigb_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 *internal = dev->data->dev_private;\n+\n+\tfor (i = 0; i < internal->nb_queues; i++) {\n+\t\tinternal->rxq[i].stats.ipackets = 0;\n+\t\tinternal->rxq[i].stats.ibytes = 0;\n+\t}\n+\n+\tfor (i = 0; i < internal->nb_queues; i++) {\n+\t\tinternal->txq[i].stats.opackets = 0;\n+\t\tinternal->txq[i].stats.errs = 0;\n+\t\tinternal->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+\tif ((fd = rx->fd) < 0)\n+\t\tif ((fd = tx->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\tif ((fd = tun_alloc(dev->data->name)) < 0) {\n+\t\t\t\tRTE_LOG(ERR, PMD, \"tun_alloc(%s) failed\\n\", dev->data->name);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\n+\tdev->data->rx_queues[qid] = rx;\n+\tdev->data->tx_queues[qid] = tx;\n+\n+\trx->fd = tx->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 == NULL)) {\n+\t\tRTE_LOG(ERR, PMD, \"nb_queues %d mp %p\\n\", internals->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       RTE_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 = -1;\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+\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 ret;\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+#define RTE_USE_GLOBAL_DATA\t0x0000\n+#define RTE_USE_PRIVATE_DATA\t0x0001\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 == NULL) || (addr == NULL))\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/*\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+\tmemcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);\n+\n+\treturn 0;\n+}\n+\n+static int\n+rte_eth_dev_create(const char *name, int dev_type,\n+\t\t   struct rte_eth_dev **eth_dev,\n+\t\t   const struct eth_dev_ops *dev_ops,\n+\t\t   void **internals, size_t internal_size,\n+\t\t   uint16_t flag)\n+{\n+\tchar buff[RTE_ETH_NAME_MAX_LEN];\n+\tint numa_node = rte_socket_id();\n+\tstruct rte_eth_dev *dev = NULL;\n+\tstruct rte_eth_dev_data *data = NULL;\n+\tvoid *priv = NULL;\n+\n+\tif ((name == NULL) || (eth_dev == NULL) || (dev_ops == NULL) ||\n+\t    (internals == NULL) || (internal_size == 0)) {\n+\t\tRTE_PMD_DEBUG_TRACE(\"Paramters are invalid\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\tdev = rte_eth_dev_allocate(name, dev_type);\n+\tif (dev == NULL) {\n+\t\tRTE_PMD_DEBUG_TRACE(\"%s: rte_eth_dev_allocate failed for %s\\n\",\n+\t\t\t\t    name, buff);\n+\t\tgoto error;\n+\t}\n+\n+\tif (flag & RTE_USE_PRIVATE_DATA) {\n+\t\t/*\n+\t\t * now do all data allocation - for eth_dev structure, dummy\n+\t\t * pci driver and internal (private) data\n+\t\t */\n+\t\tsnprintf(buff, sizeof(buff), \"D-%s-%d\", name, numa_node);\n+\t\tdata = rte_zmalloc_socket(buff, sizeof(struct rte_eth_dev_data),\n+\t\t\t\t\t  0, numa_node);\n+\t\tif (data == NULL) {\n+\t\t\tRTE_PMD_DEBUG_TRACE(\"%s: Unable to allocate memory\\n\",\n+\t\t\t\t\t    name);\n+\t\t\tgoto error;\n+\t\t}\n+\t\t/* move the current state of the structure to the new one */\n+\t\trte_memcpy(data, dev->data, sizeof(struct rte_eth_dev_data));\n+\t\tdev->data = data;\t/* Override the current data pointer */\n+\t} else\n+\t\tdata = dev->data;\n+\n+\tsnprintf(buff, sizeof(buff), \"I-%s-%d\", name, numa_node);\n+\tpriv = rte_zmalloc_socket(buff, internal_size, 0, numa_node);\n+\tif (priv == NULL) {\n+\t\tRTE_PMD_DEBUG_TRACE(\"Unable to allocate internal memory %lu\\n\",\n+\t\t\t\t    internal_size);\n+\t\tgoto error;\n+\t}\n+\n+\t/* Setup some default values */\n+\tdev->dev_ops = dev_ops;\n+\tdata->dev_private = priv;\n+\tdata->port_id = dev->data->port_id;\n+\tmemmove(data->name, dev->data->name, strlen(dev->data->name));\n+\n+\tdev->driver = NULL;\n+\tdata->dev_flags = RTE_ETH_DEV_DETACHABLE;\n+\tdata->kdrv = RTE_KDRV_NONE;\n+\tdata->numa_node = numa_node;\n+\n+\t*eth_dev = dev;\n+\t*internals = priv;\n+\n+\treturn 0;\n+error:\n+\trte_free(priv);\n+\n+\tif (flag & RTE_USE_PRIVATE_DATA)\n+\t\trte_free(data);\n+\n+\trte_eth_dev_release_port(dev);\n+\n+\treturn -1;\n+}\n+\n+static int\n+pmd_init_internals(const char *name, struct tap_info *tap,\n+\t\t   struct pmd_internals **internals,\n+\t\t   struct rte_eth_dev **eth_dev)\n+{\n+\tstruct rte_eth_dev *dev = NULL;\n+\tstruct pmd_internals *internal = NULL;\n+\tstruct rte_eth_dev_data *data = NULL;\n+\tint ret, i, fd = -1;\n+\n+\tRTE_LOG(INFO, PMD,\n+\t\t\"%s: Create TUN/TAP Ethernet device with %d queues on numa %u\\n\",\n+\t\tname, RTE_PMD_TAP_MAX_QUEUES, rte_socket_id());\n+\n+\tpmd_link.link_speed = tap->speed;\n+\n+\tret = rte_eth_dev_create(tap->name, RTE_ETH_DEV_VIRTUAL, &dev, &ops,\n+\t\t\t\t (void **)&internal, sizeof(struct pmd_internals),\n+\t\t\t\t RTE_USE_PRIVATE_DATA);\n+\tif (ret < 0)\n+\t\treturn -1;\n+\n+\tstrncpy(internal->name, tap->name, sizeof(internal->name));\n+\n+\tinternal->nb_queues = RTE_PMD_TAP_MAX_QUEUES;\n+\n+\t/* Create the first Tap device */\n+\tif ((fd = tun_alloc(dev->data->name)) < 0) {\n+\t\tRTE_LOG(ERR, PMD, \"tun_alloc(%s) failed\\n\", dev->data->name);\n+\t\trte_free(internal);\n+\t\trte_eth_dev_release_port(dev);\n+\t\treturn -1;\n+\t}\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\tinternal->fds[i] = -1;\n+\t\tinternal->rxq[i].fd = -1;\n+\t\tinternal->txq[i].fd = -1;\n+\t}\n+\n+\t/* Take the TUN/TAP fd and place in the first location */\n+\tinternal->rxq[0].fd = fd;\n+\tinternal->txq[0].fd = fd;\n+\tinternal->fds[0] = fd;\n+\n+\tif (pmd_mac_address(fd, dev, &internal->eth_addr) < 0) {\n+\t\trte_free(internal);\n+\t\trte_eth_dev_release_port(dev);\n+\t\treturn -1;\n+\t}\n+\n+\tdata = dev->data;\n+\n+\tdata->dev_link = pmd_link;\n+\tdata->mac_addrs = &internal->eth_addr;\n+\n+\tdata->nb_rx_queues = (uint16_t)internal->nb_queues;\n+\tdata->nb_tx_queues = (uint16_t)internal->nb_queues;\n+\tdata->drv_name = drivername;\n+\n+\t*eth_dev = dev;\n+\t*internals = internal;\n+\n+\treturn 0;\n+}\n+\n+static int\n+eth_dev_tap_create(const char *name, struct tap_info *tap)\n+{\n+\tstruct pmd_internals *internals = NULL;\n+\tstruct rte_eth_dev *eth_dev = NULL;\n+\n+\tif (pmd_init_internals(name, tap, &internals, &eth_dev) < 0)\n+\t\treturn -1;\n+\n+\teth_dev->rx_pkt_burst = pmd_rx_burst;\n+\teth_dev->tx_pkt_burst = pmd_tx_burst;\n+\n+\treturn 0;\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+\tstruct tap_info *tap = (struct tap_info *)extra_args;\n+\n+\tif (value)\n+\t\tsnprintf(tap->name, sizeof(tap->name), \"%s\", value);\n+\telse\n+\t\tsnprintf(tap->name, sizeof(tap->name), \"dtap%d\", (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 __rte_unused)\n+{\n+\tstruct tap_info *tap = (struct tap_info *)extra_args;\n+\n+\tpmd_link.link_speed = (value) ? atoi(value) : ETH_SPEED_NUM_10G;\n+\ttap->speed = pmd_link.link_speed;\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Open a TAP interface device.\n+ */\n+static int\n+pmd_tap_devinit(const char *name, const char *params)\n+{\n+\tint ret = 0;\n+\tstruct rte_kvargs *kvlist;\n+\tstruct tap_info tap_info;\n+\n+\t/* Setup default values */\n+\tmemset(&tap_info, 0, sizeof(tap_info));\n+\n+\ttap_info.speed = ETH_SPEED_NUM_10G;\n+\tsnprintf(tap_info.name, sizeof(tap_info.name), \"dtap%d\", tap_unit++);\n+\n+\tif ((params == NULL) || (params[0] == '\\0')) {\n+\t\tRTE_LOG(INFO, PMD, \"Initializing pmd_tap for %s\\n\", name);\n+\n+\t\tret = eth_dev_tap_create(name, &tap_info);\n+\t\tgoto leave;\n+\t}\n+\n+\tRTE_LOG(INFO, PMD, \"Initialize %s with params (%s)\\n\", name, params);\n+\n+\tkvlist = rte_kvargs_parse(params, valid_arguments);\n+\tif (!kvlist) {\n+\t\tret = eth_dev_tap_create(name, &tap_info);\n+\t\tgoto leave;\n+\t}\n+\n+\tif (rte_kvargs_count(kvlist, ETH_TAP_SPEED_ARG) == 1) {\n+\t\tret = rte_kvargs_process(kvlist, ETH_TAP_SPEED_ARG,\n+\t\t\t\t\t &set_interface_speed, &tap_info);\n+\t\tif (ret < 0)\n+\t\t\tgoto leave;\n+\t} else\n+\t\tset_interface_speed(NULL, NULL, &tap_info);\n+\n+\tif (rte_kvargs_count(kvlist, ETH_TAP_IFACE_ARG) == 1) {\n+\t\tret = rte_kvargs_process(kvlist, ETH_TAP_IFACE_ARG,\n+\t\t\t\t\t &set_interface_name, &tap_info);\n+\t\tif (ret < 0)\n+\t\t\tgoto leave;\n+\t} else\n+\t\tset_interface_name(NULL, NULL, (void *)&tap_info);\n+\n+\trte_kvargs_free(kvlist);\n+\n+leave:\n+\tif (ret == -1)\n+\t\tRTE_LOG(INFO, PMD, \"Failed to create pmd_tap for %s\\n\", name);\n+\n+\treturn ret;\n+}\n+\n+/*\n+ * detach a TAP device.\n+ */\n+static int\n+pmd_tap_devuninit(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+\tif (name == NULL)\n+\t\treturn 0;\n+\n+\t/* find the ethdev entry */\n+\teth_dev = rte_eth_dev_allocated(name);\n+\tif (eth_dev == NULL)\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_driver pmd_tap_drv = {\n+\t.type = PMD_VDEV,\n+\t.init = pmd_tap_devinit,\n+\t.uninit = pmd_tap_devuninit,\n+};\n+\n+PMD_REGISTER_DRIVER(pmd_tap_drv, eth_tap);\n+DRIVER_REGISTER_PARAM_STRING(eth_tap,\n+\t\t\t     \"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 1a0095b..bd1d10f 100644\n--- a/mk/rte.app.mk\n+++ b/mk/rte.app.mk\n@@ -129,6 +129,7 @@ ifeq ($(CONFIG_RTE_LIBRTE_VHOST),y)\n _LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_VHOST)      += -lrte_pmd_vhost\n endif # $(CONFIG_RTE_LIBRTE_VHOST)\n _LDLIBS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD)    += -lrte_pmd_vmxnet3_uio\n+_LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_TAP)        += -lrte_pmd_tap\n \n ifeq ($(CONFIG_RTE_LIBRTE_CRYPTODEV),y)\n _LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB)   += -lrte_pmd_aesni_mb\n",
    "prefixes": [
        "dpdk-dev",
        "v3"
    ]
}