DPDK patches and discussions
 help / color / mirror / Atom feed
* [dpdk-dev] [PATCH] User-space Ethool example
@ 2015-07-20 14:12 Liang-Min Larry Wang
  2015-07-20 14:12 ` [dpdk-dev] [PATCH] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
  0 siblings, 1 reply; 10+ messages in thread
From: Liang-Min Larry Wang @ 2015-07-20 14:12 UTC (permalink / raw)
  To: dev; +Cc: Liang-Min Larry Wang

This implementation is designed to provide an example illlustrating how to create a
user-space ethtool library from existing ethdev APIs. This example only implements
19 popular used Ethtool and Netdevice ops as described in examples/l2fwd-ethtool/lib/rte_ethtool.h

Liang-Min Larry Wang (1):
  examples: new example: l2fwd-ethtool

 examples/Makefile                                |    1 +
 examples/l2fwd-ethtool/Makefile                  |   48 +
 examples/l2fwd-ethtool/l2fwd-app/Makefile        |   58 ++
 examples/l2fwd-ethtool/l2fwd-app/main.c          | 1025 ++++++++++++++++++++++
 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h    |  770 ++++++++++++++++
 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h   |  159 ++++
 examples/l2fwd-ethtool/lib/Makefile              |   57 ++
 examples/l2fwd-ethtool/lib/rte_ethtool.c         |  336 +++++++
 examples/l2fwd-ethtool/lib/rte_ethtool.h         |  385 ++++++++
 examples/l2fwd-ethtool/nic-control/Makefile      |   55 ++
 examples/l2fwd-ethtool/nic-control/nic_control.c |  614 +++++++++++++
 mk/rte.lib.mk                                    |    2 +
 12 files changed, 3510 insertions(+)
 create mode 100644 examples/l2fwd-ethtool/Makefile
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/Makefile
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/main.c
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h
 create mode 100644 examples/l2fwd-ethtool/lib/Makefile
 create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.c
 create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.h
 create mode 100644 examples/l2fwd-ethtool/nic-control/Makefile
 create mode 100644 examples/l2fwd-ethtool/nic-control/nic_control.c

-- 
2.1.4

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [dpdk-dev] [PATCH] examples: new example: l2fwd-ethtool
  2015-07-20 14:12 [dpdk-dev] [PATCH] User-space Ethool example Liang-Min Larry Wang
@ 2015-07-20 14:12 ` Liang-Min Larry Wang
  2015-07-23 15:00   ` [dpdk-dev] [PATCH v2 0/2] Example: l2fwd-ethtool Liang-Min Larry Wang
  0 siblings, 1 reply; 10+ messages in thread
From: Liang-Min Larry Wang @ 2015-07-20 14:12 UTC (permalink / raw)
  To: dev; +Cc: Liang-Min Larry Wang

The example includes an ethtool library and two applications:
one application is a non- DPDK process (nic-control)
and the other is a DPDK l2fwd applicaiton (l2fwd-app).
The nic-control process sends ethtool alike device management
requests to l2fwd-app through a named pipe IPC. This example
is designed to show how to build a ethtool shim library and
how to use ethtool apis to manage device parameters.

Signed-off-by: Liang-Min Larry Wang <liang-min.wang@intel.com>
---
 examples/Makefile                                |    1 +
 examples/l2fwd-ethtool/Makefile                  |   48 +
 examples/l2fwd-ethtool/l2fwd-app/Makefile        |   58 ++
 examples/l2fwd-ethtool/l2fwd-app/main.c          | 1025 ++++++++++++++++++++++
 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h    |  770 ++++++++++++++++
 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h   |  159 ++++
 examples/l2fwd-ethtool/lib/Makefile              |   57 ++
 examples/l2fwd-ethtool/lib/rte_ethtool.c         |  336 +++++++
 examples/l2fwd-ethtool/lib/rte_ethtool.h         |  385 ++++++++
 examples/l2fwd-ethtool/nic-control/Makefile      |   55 ++
 examples/l2fwd-ethtool/nic-control/nic_control.c |  614 +++++++++++++
 mk/rte.lib.mk                                    |    2 +
 12 files changed, 3510 insertions(+)
 create mode 100644 examples/l2fwd-ethtool/Makefile
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/Makefile
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/main.c
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h
 create mode 100644 examples/l2fwd-ethtool/lib/Makefile
 create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.c
 create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.h
 create mode 100644 examples/l2fwd-ethtool/nic-control/Makefile
 create mode 100644 examples/l2fwd-ethtool/nic-control/nic_control.c

diff --git a/examples/Makefile b/examples/Makefile
index b4eddbd..3dc049c 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -53,6 +53,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_KNI) += kni
 DIRS-y += l2fwd
 DIRS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += l2fwd-ivshmem
 DIRS-$(CONFIG_RTE_LIBRTE_JOBSTATS) += l2fwd-jobstats
+DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += l2fwd-ethtool
 DIRS-y += l3fwd
 DIRS-$(CONFIG_RTE_LIBRTE_ACL) += l3fwd-acl
 DIRS-$(CONFIG_RTE_LIBRTE_POWER) += l3fwd-power
diff --git a/examples/l2fwd-ethtool/Makefile b/examples/l2fwd-ethtool/Makefile
new file mode 100644
index 0000000..d9ad439
--- /dev/null
+++ b/examples/l2fwd-ethtool/Makefile
@@ -0,0 +1,48 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overwritten by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+DIRS-y += lib nic-control l2fwd-app
+
+include $(RTE_SDK)/mk/rte.extsubdir.mk
diff --git a/examples/l2fwd-ethtool/l2fwd-app/Makefile b/examples/l2fwd-ethtool/l2fwd-app/Makefile
new file mode 100644
index 0000000..69405f2
--- /dev/null
+++ b/examples/l2fwd-ethtool/l2fwd-app/Makefile
@@ -0,0 +1,58 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overwritten by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+# binary name
+APP = l2fwd-app
+
+# all source are stored in SRCS-y
+SRCS-y := main.c
+
+CFLAGS += -O3 -D_GNU_SOURCE -pthread -I$(SRCDIR)/../lib
+CFLAGS += $(WERROR_FLAGS)
+
+LDLIBS += -L$(subst l2fwd-app,lib,$(RTE_OUTPUT))/lib
+LDLIBS += -lrte_ethtool
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/l2fwd-ethtool/l2fwd-app/main.c b/examples/l2fwd-ethtool/l2fwd-app/main.c
new file mode 100644
index 0000000..73c29e3
--- /dev/null
+++ b/examples/l2fwd-ethtool/l2fwd-app/main.c
@@ -0,0 +1,1025 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include <rte_common.h>
+#include <rte_log.h>
+#include <rte_memory.h>
+#include <rte_memcpy.h>
+#include <rte_memzone.h>
+#include <rte_tailq.h>
+#include <rte_eal.h>
+#include <rte_per_lcore.h>
+#include <rte_launch.h>
+#include <rte_atomic.h>
+#include <rte_cycles.h>
+#include <rte_prefetch.h>
+#include <rte_lcore.h>
+#include <rte_per_lcore.h>
+#include <rte_branch_prediction.h>
+#include <rte_interrupts.h>
+#include <rte_pci.h>
+#include <rte_random.h>
+#include <rte_debug.h>
+#include <rte_ether.h>
+#include <rte_ethdev.h>
+#include <rte_ring.h>
+#include <rte_mempool.h>
+#include <rte_mbuf.h>
+#include "rte_ethtool.h"
+#define NETDEV_OP_REPLY 1
+#include "netdev_api.h"
+
+#define RTE_LOGTYPE_L2FWD RTE_LOGTYPE_USER1
+
+#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
+#define NB_MBUF   8192
+
+#define MAX_PKT_BURST 32
+#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */
+
+#define is_vf_port(vf_mask, port_id) ((vf_mask & (1 << port_id)) > 0)
+#define is_port_enabled(port_mask, port_id) ((port_mask & (1 << port_id)) > 0)
+#define TX_PTHRESH 32
+#define TX_HTHRESH 0
+#define TX_WTHRESH 0
+/*
+ * Configurable number of RX/TX ring descriptors
+ */
+#define RTE_TEST_RX_DESC_DEFAULT 128
+#define RTE_TEST_TX_DESC_DEFAULT 512
+static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
+static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
+
+/* ethernet addresses of ports */
+static struct ether_addr l2fwd_ports_eth_addr[RTE_MAX_ETHPORTS];
+
+/* mask of enabled ports */
+static uint32_t l2fwd_enabled_port_mask;
+
+/* virtio setup enable */
+static int virtio_setup;
+
+/* list of enabled ports */
+static uint32_t l2fwd_dst_ports[RTE_MAX_ETHPORTS];
+
+static unsigned int l2fwd_rx_queue_per_lcore = 1;
+
+struct mbuf_table {
+	unsigned len;
+	struct rte_mbuf *m_table[MAX_PKT_BURST];
+};
+
+#define MAX_RX_QUEUE_PER_LCORE 16
+#define MAX_TX_QUEUE_PER_PORT 16
+struct lcore_queue_conf {
+	unsigned n_rx_port;
+	unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE];
+	struct mbuf_table tx_mbufs[RTE_MAX_ETHPORTS];
+
+} __rte_cache_aligned;
+struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE];
+
+static struct rte_eth_conf port_conf = {
+	.rxmode = {
+		.split_hdr_size = 0,
+		.header_split   = 0, /**< Header Split disabled */
+		.hw_ip_checksum = 0, /**< IP checksum offload disabled */
+		.hw_vlan_filter = 0, /**< VLAN filtering disabled */
+		.jumbo_frame    = 0, /**< Jumbo Frame Support disabled */
+		.hw_strip_crc   = 0, /**< CRC stripped by hardware */
+	},
+	.txmode = {
+		.mq_mode = ETH_MQ_TX_NONE,
+	},
+};
+
+static struct rte_eth_txconf tx_conf = {
+	.tx_thresh = {
+		.pthresh = TX_PTHRESH,
+		.hthresh = TX_HTHRESH,
+		.wthresh = TX_WTHRESH,
+	},
+	.tx_free_thresh = 32,
+	.tx_rs_thresh = 32,
+	.txq_flags = 0xf00,
+};
+
+struct rte_mempool *l2fwd_pktmbuf_pool;
+
+/* Per-port statistics struct */
+struct l2fwd_port_statistics {
+	uint64_t tx;
+	uint64_t rx;
+	uint64_t dropped;
+} __rte_cache_aligned;
+struct l2fwd_port_statistics port_statistics[RTE_MAX_ETHPORTS];
+
+/* A tsc-based timer responsible for triggering statistics printout */
+#define TIMER_MILLISECOND 2000000ULL /* around 1ms at 2 Ghz */
+#define MAX_TIMER_PERIOD 86400 /* 1 day max */
+/* default period is 10 seconds */
+static int64_t timer_period = 10 * TIMER_MILLISECOND * 1000;
+
+/* IPC done checking utility function */
+/* status of ipc completed */
+static rte_atomic64_t ipc_done;
+
+static inline void init_ipc_done(void)
+{
+	rte_atomic64_init(&ipc_done);
+}
+
+static inline int is_ipc_done(void)
+{
+	return rte_atomic64_read(&ipc_done) > 0;
+}
+
+static inline void set_ipc_done(void)
+{
+	rte_atomic64_inc(&ipc_done);
+}
+
+/* Print out statistics on packets dropped */
+static void
+print_stats(void)
+{
+	uint64_t total_packets_dropped, total_packets_tx, total_packets_rx;
+	unsigned portid;
+
+	total_packets_dropped = 0;
+	total_packets_tx = 0;
+	total_packets_rx = 0;
+
+	const char clr[] = { 27, '[', '2', 'J', '\0' };
+	const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' };
+
+		/* Clear screen and move to top left */
+	printf("%s%s", clr, topLeft);
+
+	printf("\nPort statistics ====================================");
+
+	for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
+		/* skip disabled ports */
+		if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
+			continue;
+		printf("\nStatistics for port %u ----------------------------",
+			portid);
+		printf("\nPackets sent: %24"PRIu64, port_statistics[portid].tx);
+		printf("\nPackets received: %20"PRIu64,
+			port_statistics[portid].rx);
+		printf("\nPackets dropped: %21"PRIu64,
+			port_statistics[portid].dropped);
+
+		total_packets_dropped += port_statistics[portid].dropped;
+		total_packets_tx += port_statistics[portid].tx;
+		total_packets_rx += port_statistics[portid].rx;
+	}
+	printf("\nAggregate statistics ===============================");
+	printf("\nTotal packets sent: %18"PRIu64, total_packets_tx);
+	printf("\nTotal packets received: %14"PRIu64, total_packets_rx);
+	printf("\nTotal packets dropped: %15"PRIu64, total_packets_dropped);
+	printf("\n====================================================\n");
+}
+
+/* Send the burst of packets on an output interface */
+static int
+l2fwd_send_burst(struct lcore_queue_conf *qconf, unsigned n, uint8_t port)
+{
+	struct rte_mbuf **m_table;
+	unsigned ret;
+	unsigned queueid = 0;
+
+	m_table = (struct rte_mbuf **)qconf->tx_mbufs[port].m_table;
+
+	ret = rte_eth_tx_burst(port, (uint16_t) queueid, m_table, (uint16_t) n);
+	port_statistics[port].tx += ret;
+	if (unlikely(ret < n)) {
+		port_statistics[port].dropped += (n - ret);
+		do {
+			rte_pktmbuf_free(m_table[ret]);
+		} while (++ret < n);
+	}
+
+	return 0;
+}
+
+/* Enqueue packets for TX and prepare them to be sent */
+static int
+l2fwd_send_packet(struct rte_mbuf *m, uint8_t port)
+{
+	unsigned lcore_id, len;
+	struct lcore_queue_conf *qconf;
+
+	lcore_id = rte_lcore_id();
+
+	qconf = &lcore_queue_conf[lcore_id];
+	len = qconf->tx_mbufs[port].len;
+	qconf->tx_mbufs[port].m_table[len] = m;
+	len++;
+
+	/* enough pkts to be sent */
+	if (unlikely(len == MAX_PKT_BURST)) {
+		l2fwd_send_burst(qconf, MAX_PKT_BURST, port);
+		len = 0;
+	}
+
+	qconf->tx_mbufs[port].len = len;
+	return 0;
+}
+
+static void
+l2fwd_simple_forward(struct rte_mbuf *m, unsigned portid)
+{
+	struct ether_hdr *eth;
+	void *tmp;
+	unsigned dst_port;
+
+	dst_port = l2fwd_dst_ports[portid];
+	eth = rte_pktmbuf_mtod(m, struct ether_hdr *);
+
+	/* 02:00:00:00:00:xx */
+	tmp = &eth->d_addr.addr_bytes[0];
+	*((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40);
+
+	/* src addr */
+	ether_addr_copy(&l2fwd_ports_eth_addr[dst_port], &eth->s_addr);
+
+	l2fwd_send_packet(m, (uint8_t) dst_port);
+}
+
+/* main processing loop */
+static void
+l2fwd_main_loop(void)
+{
+	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
+	struct rte_mbuf *m;
+	unsigned lcore_id;
+	uint64_t prev_tsc, diff_tsc, cur_tsc, timer_tsc;
+	unsigned i, j, portid, nb_rx;
+	struct lcore_queue_conf *qconf;
+	const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) /
+					US_PER_S * BURST_TX_DRAIN_US;
+
+	prev_tsc = 0;
+	timer_tsc = 0;
+
+	lcore_id = rte_lcore_id();
+	qconf = &lcore_queue_conf[lcore_id];
+
+	if (qconf->n_rx_port == 0) {
+		RTE_LOG(INFO, L2FWD, "lcore %u has nothing to do\n", lcore_id);
+		return;
+	}
+
+	RTE_LOG(INFO, L2FWD, "entering main loop on lcore %u\n", lcore_id);
+
+	for (i = 0; i < qconf->n_rx_port; i++) {
+
+		portid = qconf->rx_port_list[i];
+		RTE_LOG(INFO, L2FWD, " -- lcoreid=%u portid=%u\n", lcore_id,
+			portid);
+	}
+
+	if (virtio_setup) {
+		while (is_ipc_done() == 0)
+			usleep(50);
+	}
+
+	while (1) {
+		cur_tsc = rte_rdtsc();
+
+		/* TX burst queue drain */
+		diff_tsc = cur_tsc - prev_tsc;
+		if (unlikely(diff_tsc > drain_tsc)) {
+
+			for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
+				if (qconf->tx_mbufs[portid].len == 0)
+					continue;
+				l2fwd_send_burst(&lcore_queue_conf[lcore_id],
+						 qconf->tx_mbufs[portid].len,
+						 (uint8_t) portid);
+				qconf->tx_mbufs[portid].len = 0;
+			}
+
+			/* if timer is enabled */
+			if (timer_period > 0) {
+
+				/* advance the timer */
+				timer_tsc += diff_tsc;
+
+				/* if timer has reached its timeout */
+				if (unlikely(timer_tsc >=
+				    (uint64_t) timer_period)) {
+
+					/* do this only on master core */
+					if (lcore_id ==
+					    rte_get_master_lcore()) {
+						print_stats();
+						/* reset the timer */
+						timer_tsc = 0;
+					}
+				}
+			}
+
+			prev_tsc = cur_tsc;
+		}
+
+		/*
+		 * Read packet from RX queues
+		 */
+		for (i = 0; i < qconf->n_rx_port; i++) {
+
+			portid = qconf->rx_port_list[i];
+			nb_rx = rte_eth_rx_burst((uint8_t) portid, 0,
+						 pkts_burst, MAX_PKT_BURST);
+
+			port_statistics[portid].rx += nb_rx;
+
+			for (j = 0; j < nb_rx; j++) {
+				m = pkts_burst[j];
+				rte_prefetch0(rte_pktmbuf_mtod(m, void *));
+				l2fwd_simple_forward(m, portid);
+			}
+		}
+	}
+}
+
+static int
+l2fwd_launch_one_lcore(__attribute__((unused)) void *dummy)
+{
+	l2fwd_main_loop();
+	return 0;
+}
+
+/* display usage */
+static void
+l2fwd_usage(const char *prgname)
+{
+	printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n"
+		"  -p PORTMASK: hexadecimal bitmask of ports to configure\n"
+		"  -q NQ: number of queue (=ports) per lcore (default is 1)\n"
+		"  -V : setting rx/tx mode to enable virtio\n"
+		"  -T PERIOD: statistics will be refreshed each PERIOD seconds",
+		prgname);
+	printf("(0 to disable, 10 default, 86400 maximum)\n");
+}
+
+static int
+l2fwd_parse_portmask(const char *portmask)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* parse hexadecimal string */
+	pm = strtoul(portmask, &end, 16);
+	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (pm == 0)
+		return -1;
+
+	return pm;
+}
+
+static unsigned int
+l2fwd_parse_nqueue(const char *q_arg)
+{
+	char *end = NULL;
+	unsigned long n;
+
+	/* parse hexadecimal string */
+	n = strtoul(q_arg, &end, 10);
+	if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return 0;
+	if (n == 0)
+		return 0;
+	if (n >= MAX_RX_QUEUE_PER_LCORE)
+		return 0;
+
+	return n;
+}
+
+static int
+l2fwd_parse_timer_period(const char *q_arg)
+{
+	char *end = NULL;
+	int n;
+
+	/* parse number string */
+	n = strtol(q_arg, &end, 10);
+	if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+	if (n >= MAX_TIMER_PERIOD)
+		return -1;
+
+	return n;
+}
+
+static int
+l2fwd_parse_virtio_setup(const char *q_arg)
+{
+	char *end = NULL;
+	int n;
+
+	/* parse number string */
+	n = strtol(q_arg, &end, 10);
+	if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+	if (n >= MAX_TIMER_PERIOD)
+		return -1;
+
+	return n;
+}
+
+/* Parse the argument given in the command line of the application */
+static int
+l2fwd_parse_args(int argc, char **argv)
+{
+	int opt, ret;
+	char **argvopt;
+	int option_index;
+	char *prgname = argv[0];
+	static struct option lgopts[] = {
+		{NULL, 0, 0, 0}
+	};
+
+	argvopt = argv;
+
+	while ((opt = getopt_long(argc, argvopt, "p:q:T:V:",
+				  lgopts, &option_index)) != EOF) {
+
+		switch (opt) {
+		/* portmask */
+		case 'p':
+			l2fwd_enabled_port_mask = l2fwd_parse_portmask(optarg);
+			if (l2fwd_enabled_port_mask == 0) {
+				printf("invalid portmask\n");
+				l2fwd_usage(prgname);
+				return -1;
+			}
+			break;
+
+		/* nqueue */
+		case 'q':
+			l2fwd_rx_queue_per_lcore = l2fwd_parse_nqueue(optarg);
+			if (l2fwd_rx_queue_per_lcore == 0) {
+				printf("invalid queue number\n");
+				l2fwd_usage(prgname);
+				return -1;
+			}
+			break;
+
+		/* timer period */
+		case 'T':
+			timer_period = l2fwd_parse_timer_period(optarg) *
+				1000 * TIMER_MILLISECOND;
+			if (timer_period < 0) {
+				printf("invalid timer period\n");
+				l2fwd_usage(prgname);
+				return -1;
+			}
+			break;
+
+		/* virtio setup */
+		case 'V':
+			/* get option as the pf mac addr */
+			virtio_setup = l2fwd_parse_virtio_setup(optarg);
+			if (virtio_setup) {
+				port_conf.rxmode.hw_vlan_strip = 0;
+				port_conf.rxmode.hw_vlan_extend = 0;
+			}
+			break;
+
+		/* long options */
+		case 0:
+			l2fwd_usage(prgname);
+			return -1;
+
+		default:
+			l2fwd_usage(prgname);
+			return -1;
+		}
+	}
+
+	if (optind >= 0)
+		argv[optind-1] = prgname;
+
+	ret = optind-1;
+	optind = 0; /* reset getopt lib */
+	return ret;
+}
+
+/* Check the link status of all ports in up to 9s, and print them finally */
+static void
+check_all_ports_link_status(uint8_t port_num, uint32_t port_mask)
+{
+#define CHECK_INTERVAL 100 /* 100ms */
+#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
+	uint8_t portid, count, all_ports_up, print_flag = 0;
+	struct rte_eth_link link;
+
+	printf("\nChecking link status!!!");
+	fflush(stdout);
+	for (count = 0; count <= MAX_CHECK_TIME; count++) {
+		all_ports_up = 1;
+		for (portid = 0; portid < port_num; portid++) {
+			if ((port_mask & (1 << portid)) == 0)
+				continue;
+			memset(&link, 0, sizeof(link));
+			rte_eth_link_get_nowait(portid, &link);
+			/* print link status if flag set */
+			if (print_flag == 1) {
+				if (link.link_status) {
+					printf("Port %d Link Up - speed %u "
+						, (uint8_t)portid,
+						(unsigned)link.link_speed);
+					printf("Mbps - %s\n", (link.link_duplex
+						== ETH_LINK_FULL_DUPLEX) ?
+						("full-duplex") :
+						("half-duplex\n"));
+				} else
+					printf("Port %d Link Down\n",
+						(uint8_t)portid);
+				continue;
+			}
+			/* clear all_ports_up flag if any link down */
+			if (link.link_status == 0) {
+				all_ports_up = 0;
+				break;
+			}
+		}
+		/* after finally printing all link status, get out */
+		if (print_flag == 1)
+			break;
+
+		if (all_ports_up == 0) {
+			printf(".");
+			fflush(stdout);
+			rte_delay_ms(CHECK_INTERVAL);
+		}
+
+		/* set the print_flag if all ports up or timeout */
+		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
+			print_flag = 1;
+			printf("done\n");
+		}
+	}
+}
+
+static inline char*
+mac_addr_str(unsigned char *mac_addr)
+{
+#define MAC_STR_SIZE (3*MAC_ADDR_SIZE+1)
+	static char addr_string[MAC_STR_SIZE];
+
+	snprintf(addr_string, MAC_STR_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
+		mac_addr[0], mac_addr[1], mac_addr[2],
+		mac_addr[3], mac_addr[4], mac_addr[5]);
+	return addr_string;
+}
+
+static int
+proc_ipc_begin(struct nic_info *info, uint16_t req_id)
+{
+	struct ethtool_drvinfo drvinfo;
+	uint8_t mac_addr[MAC_ADDR_SIZE];
+	uint8_t param[4], port_id, num_of_ports = info->num_of_ports;
+	uint32_t param2[2];
+	int status;
+
+	param[0] = num_of_ports;
+	info->vf_port_mask = 0;
+	for (port_id = 0; port_id < num_of_ports; port_id++) {
+		status = rte_ethtool_get_drvinfo(port_id, &drvinfo);
+		if (status) {
+			printf("get_drvinfo from port #%d fails\n", port_id);
+			return -1;
+		}
+		info->vf_port_mask |= (drvinfo.eedump_len == 0?1:0) << port_id;
+		rte_ethtool_net_stop(port_id);
+	}
+	param2[0] = info->port_mask;
+	param2[1] = info->vf_port_mask;
+
+	for (port_id = 0; port_id < num_of_ports; port_id++) {
+		rte_ethtool_net_open(port_id);
+		if (!is_vf_port(info->vf_port_mask, port_id)) {
+			struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+			struct rte_eth_dev_data *dev_data =
+				(struct rte_eth_dev_data *)dev->data;
+
+			dev_data->promiscuous = 1;
+			rte_ethtool_net_set_rx_mode(port_id);
+		}
+		rte_ethtool_net_get_mac_addr(port_id, (void *)mac_addr);
+		printf("Port #%d init mac address is", port_id);
+		printf(" %s", mac_addr_str(mac_addr));
+
+	}
+
+	send_reply2(req_id, 1, param, (uint16_t)(sizeof(uint32_t)*2), param2);
+	return 0;
+}
+
+static inline void
+proc_no_action(uint16_t req_id)
+{
+	send_reply(req_id, 0, NULL);
+}
+
+static inline void
+proc_invalid(uint16_t req_id)
+{
+	send_reply(req_id, BAD_RETURN(0), NULL);
+}
+
+static void*
+ethtool(void *ctx)
+{
+	struct nic_info *info = ctx;
+	int keep_req = 1;
+	int reg_count, eeprom_size;
+	uint16_t req_id, param1_size, param2_size;
+	uint8_t req_type, port_id;
+	int status;
+	uint8_t param1[MAXI_PARA];
+	uint8_t param2[MAXI_PARA];
+	uint8_t reply1[MAXI_DATA];
+	void *first_param	= FIRST_PARAM(param1);
+
+	init_rep_pipe();
+	while (1) {
+		read_request(&req_id, &req_type, &param1_size, param1,
+			&param2_size, param2);
+		if (req_type != (enum req_t)ipc_begin)
+			proc_invalid(req_id);
+		else
+			break;
+	}
+	proc_ipc_begin(info, req_id);
+
+	set_ipc_done();
+	reg_count = eeprom_size = 0;
+
+	while (keep_req) {
+		status = NETDEV_INVALID;
+		read_request(&req_id, &req_type, &param1_size, param1,
+			&param2_size, param2);
+		port_id = param1[0];
+
+		switch ((enum req_t)req_type) {
+		case get_drvinfo:
+			status = proc_ethtool_get_drvinfo(port_id, req_id,
+				first_param);
+			break;
+
+		case get_regs_len:
+			status = reg_count = proc_ethtool_get_regs_len(
+				port_id, req_id);
+			break;
+
+		case get_regs:
+			if (reg_count == 0)
+				reg_count = rte_ethtool_get_regs_len(port_id);
+			if (reg_count)
+				status = proc_ethtool_get_regs(port_id, req_id,
+				first_param, reply1);
+			break;
+
+		case get_link:
+			status = proc_ethtool_get_link(port_id, req_id);
+			break;
+
+		case get_eeprom_len:
+			if (eeprom_size == 0)
+				eeprom_size = rte_ethtool_get_eeprom_len(
+				port_id);
+			status = proc_ethtool_get_eeprom_len(port_id, req_id);
+			break;
+
+		case get_eeprom:
+			status = proc_ethtool_get_eeprom(port_id, req_id,
+				first_param, reply1);
+			break;
+
+		case set_eeprom:
+			status = proc_ethtool_set_eeprom(port_id, req_id,
+				first_param, param2);
+			break;
+
+		case get_pauseparam:
+			status = proc_ethtool_get_pauseparam(port_id,
+				req_id,
+				cast_ptr(reply1, struct ethtool_pauseparam *));
+			break;
+
+		case set_pauseparam:
+			status = proc_ethtool_set_pauseparam(port_id,
+				req_id,
+				cast_ptr(reply1, struct ethtool_pauseparam *));
+			break;
+
+		case dev_open:
+			status = proc_net_open(port_id, req_id);
+			break;
+
+		case dev_stop:
+			status = proc_net_stop(port_id, req_id);
+			break;
+
+		case set_rx_mode:
+			status = proc_net_set_rx_mode(port_id, req_id);
+			break;
+
+		case get_mac_addr:
+			status = proc_net_get_mac_addr(port_id,
+				req_id, first_param);
+			break;
+
+		case set_mac_addr:
+			status = proc_net_set_mac_addr(port_id,
+				req_id, first_param);
+			break;
+
+		case validate_addr:
+			status = proc_net_validate_addr(port_id,
+				req_id, first_param);
+			break;
+
+		case set_config:
+			status = proc_net_set_config(port_id,
+				req_id, first_param);
+			break;
+
+		case change_mtu:
+			status = proc_net_change_mtu(port_id,
+				req_id, first_param);
+			break;
+
+		case get_stats64:
+			status = proc_net_get_stats64(port_id,
+				req_id, reply1);
+			break;
+
+		case vlan_rx_add_vid:
+			status = proc_net_vlan_rx_add_vid(port_id,
+				req_id, first_param);
+			break;
+
+		case vlan_rx_kill_vid:
+			status = proc_net_vlan_rx_kill_vid(port_id,
+				req_id, first_param);
+			break;
+
+		case ipc_end:
+			keep_req = 0;
+			proc_no_action(req_id);
+			status = 0;
+			break;
+
+		default:
+			proc_invalid(req_id);
+			printf("unsupported service request type:");
+			printf(" %d\n", req_type);
+			break;
+		}
+		if (status < 0)
+			printf("Request type (=%d) failed\n", (int)req_type);
+		/* check if termination flag is set */
+	}
+	printf("IPC session is over\n");
+	return NULL;
+}
+
+int
+main(int argc, char **argv)
+{
+	struct lcore_queue_conf *qconf;
+	struct rte_eth_dev_info dev_info;
+	int ret;
+	uint8_t nb_ports;
+	uint8_t nb_ports_available;
+	uint8_t portid, last_port;
+	unsigned lcore_id, rx_lcore_id;
+	unsigned nb_ports_in_mask = 0;
+
+	init_ipc_done();
+	/* init EAL */
+	ret = rte_eal_init(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n");
+	argc -= ret;
+	argv += ret;
+
+	/* parse application arguments (after the EAL ones) */
+	ret = l2fwd_parse_args(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid L2FWD arguments\n");
+
+	/* create the mbuf pool */
+	l2fwd_pktmbuf_pool =
+		rte_mempool_create("mbuf_pool", NB_MBUF,
+				   MBUF_SIZE, 32,
+				   sizeof(struct rte_pktmbuf_pool_private),
+				   rte_pktmbuf_pool_init, NULL,
+				   rte_pktmbuf_init, NULL,
+				   rte_socket_id(), 0);
+	if (l2fwd_pktmbuf_pool == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot init mbuf pool\n");
+
+	nb_ports = rte_eth_dev_count();
+	if (nb_ports == 0)
+		rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
+
+	if (nb_ports > RTE_MAX_ETHPORTS)
+		nb_ports = RTE_MAX_ETHPORTS;
+
+	/* reset l2fwd_dst_ports */
+	for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++)
+		l2fwd_dst_ports[portid] = 0;
+	last_port = 0;
+
+	/*
+	 * Each logical core is assigned a dedicated TX queue on each port.
+	 */
+	for (portid = 0; portid < nb_ports; portid++) {
+		/* skip ports that are not enabled */
+		if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
+			continue;
+
+		if (nb_ports_in_mask % 2) {
+			l2fwd_dst_ports[portid] = last_port;
+			l2fwd_dst_ports[last_port] = portid;
+		} else
+			last_port = portid;
+
+		nb_ports_in_mask++;
+
+		rte_eth_dev_info_get(portid, &dev_info);
+	}
+	if (nb_ports_in_mask % 2) {
+		printf("Notice: odd number of ports in portmask.\n");
+		l2fwd_dst_ports[last_port] = last_port;
+	}
+
+	rx_lcore_id = 0;
+	qconf = NULL;
+
+	/* Initialize the port/queue configuration of each logical core */
+	for (portid = 0; portid < nb_ports; portid++) {
+		/* skip ports that are not enabled */
+		if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
+			continue;
+
+		/* get the lcore_id for this port */
+		while (rte_lcore_is_enabled(rx_lcore_id) == 0 ||
+			lcore_queue_conf[rx_lcore_id].n_rx_port ==
+			l2fwd_rx_queue_per_lcore) {
+			rx_lcore_id++;
+			if (rx_lcore_id >= RTE_MAX_LCORE)
+				rte_exit(EXIT_FAILURE, "Not enough cores\n");
+		}
+
+		if (qconf != &lcore_queue_conf[rx_lcore_id])
+			/* Assigned a new logical core in the loop above. */
+			qconf = &lcore_queue_conf[rx_lcore_id];
+
+		qconf->rx_port_list[qconf->n_rx_port] = portid;
+		qconf->n_rx_port++;
+		printf("Lcore %u: RX port %u\n", rx_lcore_id,
+			(unsigned) portid);
+	}
+
+	nb_ports_available = nb_ports;
+
+	/* Initialise each port */
+	for (portid = 0; portid < nb_ports; portid++) {
+		/* skip ports that are not enabled */
+		if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) {
+			printf("Skipping disabled port %u\n",
+				(unsigned) portid);
+			nb_ports_available--;
+			continue;
+		}
+		/* init port */
+		printf("Initializing port %u... ", (unsigned) portid);
+		fflush(stdout);
+		ret = rte_eth_dev_configure(portid, 1, 1, &port_conf);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+			"Cannot configure device: err=%d, port=%u\n",
+				ret, (unsigned) portid);
+
+		rte_eth_macaddr_get(portid, &l2fwd_ports_eth_addr[portid]);
+
+		/* init one RX queue */
+		fflush(stdout);
+		ret = rte_eth_rx_queue_setup(portid, 0, nb_rxd,
+					rte_eth_dev_socket_id(portid),
+					NULL,
+					l2fwd_pktmbuf_pool);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+			"rte_eth_rx_queue_setup:err=%d, port=%u\n",
+				  ret, (unsigned) portid);
+
+		/* init one TX queue on each port */
+		fflush(stdout);
+		if (virtio_setup) {
+			ret = rte_eth_tx_queue_setup(portid, 0, nb_txd,
+				rte_eth_dev_socket_id(portid), &tx_conf);
+		} else {
+			ret = rte_eth_tx_queue_setup(portid, 0, nb_txd,
+				rte_eth_dev_socket_id(portid),
+				NULL);
+		}
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+			"rte_eth_tx_queue_setup:err=%d, port=%u\n",
+				ret, (unsigned) portid);
+	}
+
+	/* create a ethtool proxy thread */
+	pthread_attr_t attr;
+	cpu_set_t cpus;
+	pthread_t ethtool_thread;
+	struct nic_info info;
+
+	/* set core affinity to core 1 */
+	CPU_ZERO(&cpus);
+	CPU_SET(2, &cpus);
+	pthread_attr_init(&attr);
+	pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpus);
+	/* Since the register size is more than 4K (1147*4) */
+	pthread_attr_setstacksize(&attr, 4*PAGE_SIZE);
+
+	info.num_of_ports = nb_ports;
+	info.port_mask = l2fwd_enabled_port_mask;
+	if (pthread_create(&ethtool_thread, NULL, &ethtool, &info)) {
+		rte_exit(EXIT_FAILURE,
+			"Fail to create a pthread for ethtool task!!!\n");
+	}
+	memset(&port_statistics, 0, sizeof(port_statistics));
+
+	if (!nb_ports_available) {
+		rte_exit(EXIT_FAILURE,
+		"All available ports are disabled. Please set portmask.\n");
+	}
+
+	check_all_ports_link_status(nb_ports, l2fwd_enabled_port_mask);
+
+	/* launch per-lcore init on every lcore */
+	rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, NULL, CALL_MASTER);
+	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
+		if (rte_eal_wait_lcore(lcore_id) < 0)
+			return -1;
+	}
+
+	return 0;
+}
diff --git a/examples/l2fwd-ethtool/l2fwd-app/netdev_api.h b/examples/l2fwd-ethtool/l2fwd-app/netdev_api.h
new file mode 100644
index 0000000..2e93e9a
--- /dev/null
+++ b/examples/l2fwd-ethtool/l2fwd-app/netdev_api.h
@@ -0,0 +1,770 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _NETDEV_API_H_
+#define _NETDEV_API_H_
+
+#include <linux/ethtool.h>
+#include <string.h>
+#include "shared_fifo.h"
+
+#define MAC_ADDR_SIZE 6
+#define quad_aligned_size(x) ((x & 0x7) ? ((x+7)&0x7) : x)
+
+#define size16(data_type) (uint16_t)(sizeof(data_type))
+
+/* NETDEV_STATUS = 0 if successful */
+#define NETDEV_UNSUPPORTED -1
+#define NETDEV_INVALID -1
+#define NETDEV_STATUS(data_size) (GOOD_RETURN(data_size) \
+				? 0 : NETDEV_INVALID)
+#define UNUSED(x) (void)(x)
+
+#ifdef NETDEV_OP_REQUEST
+static uint16_t
+next_reqid(void) {
+	static uint16_t request_id;
+
+	return request_id++;
+}
+
+/*
+ * send request (with one or two variables) to request-pipe
+ * (invoked by non- DPDK process)
+ */
+static int
+send_request(uint16_t req_id, uint8_t req_type, uint16_t param_size,
+	void *param_data)
+{
+	int fd;
+	uint32_t req[2];
+
+	req[0] = REQ_DWORD_LO(req_id, 0, req_type);
+	req[1] = REQ_DWORD_HI(param_size, 0);
+
+	fd = open(REQ_PIPE, O_WRONLY);
+	write(fd, req, PIPE_CTL_BYTE_COUNT);
+	if (param_size)
+		write(fd, param_data, param_size);
+	close(fd);
+
+	return 0;
+}
+
+/*
+ * send request (with more than two variables) to request-pipe
+ * (invoked by non- DPDK process)
+ */
+static int
+send_request2(uint16_t req_id, uint8_t req_type, uint16_t param1_size,
+	void *param1_data, int param2_size, void *param2_data)
+{
+	int fd;
+	uint32_t req[2];
+
+	req[0] = REQ_DWORD_LO(req_id, 1, req_type);
+	req[1] = REQ_DWORD_HI(param1_size, param2_size);
+
+	fd = open(REQ_PIPE, O_WRONLY);
+	write(fd, req, PIPE_CTL_BYTE_COUNT);
+
+	if (param1_size)
+		write(fd, param1_data, param1_size);
+	if (param2_size)
+		write(fd, param2_data, param2_size);
+	close(fd);
+
+	return 0;
+}
+
+/* read return variables from the reply-pipe (invoked by non- DPDK process) */
+static int
+read_reply(uint16_t expected_id, uint16_t *byte_count, void *reply_data1,
+	void *reply_data2)
+{
+	int fd;
+	uint32_t req[2];
+	uint16_t rx_id, data1_size;
+
+	/* block on read if reply is not available */
+	fd = open(REP_PIPE, O_RDONLY);
+	read(fd, req, PIPE_CTL_BYTE_COUNT);
+
+	*byte_count = REP_DATA1_COUNT(req);
+	rx_id = REP_ID(req);
+
+	if (!GOOD_RETURN(*byte_count)) {
+		close(fd);
+		return -1;
+	}
+	data1_size = BYTE_COUNT((*byte_count));
+	read(fd, reply_data1, data1_size);
+	if (MULTIPLE_DATA(*byte_count)) {
+		assert(reply_data2);
+		read(fd, reply_data2, REP_DATA2_COUNT(req));
+	}
+	close(fd);
+
+	if (expected_id != rx_id)
+		return -1;
+	return 0;
+}
+
+/* definition of netdev op request */
+
+static int
+netdev_ethtool_get_drvinfo(uint8_t port_id, struct ethtool_drvinfo *drvinfo)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, get_drvinfo, 1, &port_id);
+	read_reply(req_id, &data_size, drvinfo, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_ethtool_get_regs_len(uint8_t port_id)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	int length;
+
+	send_request(req_id, get_regs_len, 1, &port_id);
+	read_reply(req_id, &data_size, &length, NULL);
+
+	if (GOOD_RETURN(data_size))
+		return length;
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_ethtool_get_regs(uint8_t port_id, struct ethtool_regs *regs, void *buf)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint8_t param_data[PARAM_SIZE(struct ethtool_regs)];
+
+	param_data[0] = port_id;
+	memcpy(FIRST_PARAM(param_data), regs, sizeof(struct ethtool_regs));
+
+	send_request(req_id, get_regs, PARAM_SIZE(struct ethtool_regs),
+		param_data);
+	read_reply(req_id, &data_size, regs, buf);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_ethtool_get_link(uint8_t port_id)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	int link_status;
+
+	send_request(req_id, get_link, 1, &port_id);
+	read_reply(req_id, &data_size, &link_status, NULL);
+	if (GOOD_RETURN(data_size))
+		return link_status;
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_ethtool_get_eeprom_len(uint8_t port_id)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	int length;
+
+	send_request(req_id, get_eeprom_len, 1, &port_id);
+	read_reply(req_id, &data_size, &length, NULL);
+
+	if (GOOD_RETURN(data_size))
+		return length;
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_ethtool_get_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom,
+	void *words)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint8_t param_data[PARAM_SIZE(struct ethtool_eeprom)];
+
+	param_data[0] = port_id;
+	memcpy(FIRST_PARAM(param_data), eeprom, sizeof(struct ethtool_eeprom));
+
+	send_request(req_id, get_eeprom, PARAM_SIZE(struct ethtool_eeprom),
+		param_data);
+	read_reply(req_id, &data_size, eeprom, words);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_ethtool_set_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom,
+	void *words)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint8_t param_data[PARAM_SIZE(struct ethtool_eeprom)];
+
+	param_data[0] = port_id;
+	memcpy(FIRST_PARAM(param_data), eeprom, sizeof(struct ethtool_eeprom));
+
+	send_request2(req_id, set_eeprom, PARAM_SIZE(struct ethtool_eeprom),
+		param_data, eeprom->len, words);
+	read_reply(req_id, &data_size, eeprom, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_ethtool_get_pauseparam(uint8_t port_id, struct ethtool_pauseparam *param)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, get_pauseparam, 1, &port_id);
+	read_reply(req_id, &data_size, param, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_ethtool_set_pauseparam(uint8_t port_id, struct ethtool_pauseparam *param)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, set_pauseparam, 1, &port_id);
+	read_reply(req_id, &data_size, param, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_open(uint8_t port_id) {
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, dev_open, 1, &port_id);
+	read_reply(req_id, &data_size, NULL, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_stop(uint8_t port_id) {
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, dev_stop, 1, &port_id);
+	read_reply(req_id, &data_size, NULL, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_set_rx_mode(uint8_t port_id)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, set_rx_mode, 1, &port_id);
+	read_reply(req_id, &data_size, NULL, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_get_mac_addr(uint8_t port_id, void *addr)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, get_mac_addr, 1, &port_id);
+	read_reply(req_id, &data_size, addr, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_set_mac_addr(uint8_t port_id, void *addr)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint8_t param_data[FIRST_DATA_OFFSET+MAC_ADDR_SIZE];
+
+	param_data[0] = port_id;
+	memcpy(FIRST_PARAM(param_data), addr, MAC_ADDR_SIZE);
+	send_request(req_id, set_mac_addr,
+		(FIRST_DATA_OFFSET+MAC_ADDR_SIZE), param_data);
+	read_reply(req_id, &data_size, NULL, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_validate_addr(uint8_t port_id, void *addr)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint8_t param_data[FIRST_DATA_OFFSET+MAC_ADDR_SIZE];
+	int valid;
+
+	param_data[0] = port_id;
+	memcpy(FIRST_PARAM(param_data), addr, MAC_ADDR_SIZE);
+	send_request(req_id, validate_addr,
+		(FIRST_DATA_OFFSET+MAC_ADDR_SIZE), param_data);
+	read_reply(req_id, &data_size, &valid, NULL);
+
+	if (GOOD_RETURN(data_size))
+		return valid;
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_change_mtu(uint8_t port_id, int mtu)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint8_t param_data[PARAM_SIZE(int)];
+
+	param_data[0] = port_id;
+	memcpy(FIRST_PARAM(param_data), &mtu, sizeof(int));
+	send_request(req_id, change_mtu, PARAM_SIZE(int), param_data);
+	read_reply(req_id, &data_size, NULL, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_get_stats64(uint8_t port_id, void *stats)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, get_stats64, 1, &port_id);
+	read_reply(req_id, &data_size, stats, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_vlan_rx_add_vid(uint8_t port_id, uint16_t vid)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint8_t param_data[PARAM_SIZE(int)];
+
+	param_data[0] = port_id;
+	memcpy(FIRST_PARAM(param_data), &vid, sizeof(uint16_t));
+	send_request(req_id, vlan_rx_add_vid, FIRST_DATA_OFFSET+sizeof(int),
+		param_data);
+	read_reply(req_id, &data_size, NULL, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_vlan_rx_kill_vid(uint8_t port_id, uint16_t vid)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint8_t param_data[PARAM_SIZE(int)];
+
+	param_data[0] = port_id;
+	memcpy(FIRST_PARAM(param_data), &vid, sizeof(uint16_t));
+	send_request(req_id, vlan_rx_kill_vid, FIRST_DATA_OFFSET+sizeof(int),
+		param_data);
+	read_reply(req_id, &data_size, NULL, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+#endif /* NETDEV_OP_REQUEST */
+
+#ifdef NETDEV_OP_REPLY
+/* read request from request-pipe (invoked by rte-api server thread) */
+static int
+read_request(uint16_t *req_id, uint8_t *req_type, uint16_t *param1_size,
+	uint8_t *param1_data, uint16_t *param2_size, void *param2_data)
+{
+	int fd;
+	uint32_t req[2];
+
+	/* block on read if request is not sent ... */
+	fd = open(REQ_PIPE, O_RDONLY);
+	read(fd, req, PIPE_CTL_BYTE_COUNT);
+
+	*req_id			= REQ_ID(req);
+	*req_type		= REQ_TYPE(req);
+	*param1_size	= REQ_PARAM1_SIZE(req);
+
+	if (*param1_size > 0) {
+		read(fd, param1_data, *param1_size);
+		if (REQ_IDTYPE(req)) {
+			*param2_size = REQ_PARAM2_SIZE(req);
+			read(fd, param2_data, *param2_size);
+		} else
+			*param2_size = 0;
+	}
+	close(fd);
+
+	return 0;
+}
+
+/* definition of netdev op service */
+/*
+ * rep[1:0]: request id
+ * rep[3:2]: data byte count; bit[15]: error status bit[14]: multiple return
+ *           variables are requested
+ *
+ * send reply with one return variable to reply-pipe
+ * (invoked by rte-api server thread)
+ */
+static int
+send_reply(uint16_t rx_id, uint16_t byte_count, void *reply_data)
+{
+	int fd;
+	uint32_t req[2];
+
+	req[0] = REP_DWORD_LO(rx_id, byte_count);
+	req[1] = REP_DWORD_HI(0);
+
+	fd = open(REP_PIPE, O_WRONLY);
+	write(fd, req, PIPE_CTL_BYTE_COUNT);
+
+	if (GOOD_RETURN(byte_count) && (byte_count > 0))
+		write(fd, reply_data, byte_count);
+	close(fd);
+
+	return 0;
+}
+
+/*
+ * send reply with two or more variables to reply-pipe
+ * (invoked by rte-api server thread)
+ */
+static int
+send_reply2(uint16_t rx_id, uint16_t byte_count1, void *reply_data1,
+	uint16_t byte_count2, void *reply_data2)
+{
+	int fd;
+	uint32_t req[2];
+
+	req[0] = REP_DWORD_LO(rx_id, REP_MUTILPLE_DATA(byte_count1));
+	req[1] = REP_DWORD_HI(byte_count2);
+
+	fd = open(REP_PIPE, O_WRONLY);
+	write(fd, req, PIPE_CTL_BYTE_COUNT);
+
+	if (GOOD_RETURN(byte_count1)  && (byte_count2 > 0)) {
+		write(fd, reply_data1, byte_count1);
+		write(fd, reply_data2, byte_count2);
+	}
+	close(fd);
+
+	return 0;
+}
+
+/* Functions for netdev service thread */
+static int
+proc_ethtool_get_drvinfo(uint8_t port_id, uint16_t req_id, void *param_data)
+{
+	struct ethtool_drvinfo *drvinfo = param_data;
+	uint16_t data_size;
+
+	if (rte_ethtool_get_drvinfo(port_id, drvinfo))
+		data_size = STATUS_MASK;
+	else
+		data_size = size16(struct ethtool_drvinfo);
+	return send_reply(req_id, data_size, param_data);
+};
+
+static int
+proc_ethtool_get_regs_len(uint8_t port_id, uint16_t req_id)
+{
+	int reg_len;
+	uint16_t data_size;
+
+	reg_len = rte_ethtool_get_regs_len(port_id);
+	if (reg_len == 0)
+		data_size = STATUS_MASK;
+	else
+		data_size = size16(int);
+	return send_reply(req_id, data_size, &reg_len);
+};
+
+static int
+proc_ethtool_get_regs(uint8_t port_id, uint16_t req_id, void *param_data,
+	void *reply_data2)
+{
+	struct ethtool_regs *reg_info = param_data;
+	void *buf = reply_data2;
+	uint16_t data_size;
+
+	if (rte_ethtool_get_regs(port_id, reg_info, buf))
+		data_size = STATUS_MASK;
+	else
+		data_size = sizeof(struct ethtool_regs);
+	return send_reply2(req_id, data_size, reg_info,
+		rte_ethtool_get_regs_len(port_id)*sizeof(int), reply_data2);
+};
+
+static int
+proc_ethtool_get_link(uint8_t port_id, uint16_t req_id)
+{
+	int link_status;
+
+	link_status = rte_ethtool_get_link(port_id);
+	return  send_reply(req_id, (uint16_t)sizeof(int), &link_status);
+};
+
+static int
+proc_ethtool_get_eeprom_len(uint8_t port_id, uint16_t req_id)
+{
+	int eeprom_length;
+	uint16_t data_size;
+
+	eeprom_length = rte_ethtool_get_eeprom_len(port_id);
+	if (eeprom_length == 0)
+		data_size = STATUS_MASK;
+	else
+		data_size = size16(int);
+	return send_reply(req_id, data_size, &eeprom_length);
+};
+
+static int
+proc_ethtool_get_eeprom(uint8_t port_id, uint16_t req_id, void *param_data,
+	void *reply_data2)
+{
+	struct ethtool_eeprom *eeprom_ptr = param_data;
+	uint16_t data_size;
+
+	if (rte_ethtool_get_eeprom(port_id, eeprom_ptr, reply_data2))
+		data_size = STATUS_MASK;
+	else
+		data_size = sizeof(struct ethtool_eeprom);
+	return send_reply2(req_id, data_size, eeprom_ptr,
+		eeprom_ptr->len & ~1, reply_data2);
+};
+
+static int
+proc_ethtool_set_eeprom(uint8_t port_id, uint16_t req_id, void *param_data,
+	void *param2_data)
+{
+	struct ethtool_eeprom *eeprom_ptr = param_data;
+	uint16_t data_size;
+
+	if (rte_ethtool_set_eeprom(port_id, eeprom_ptr, param2_data))
+		data_size = STATUS_MASK;
+	else
+		data_size = sizeof(struct ethtool_eeprom);
+	return send_reply(req_id, data_size, eeprom_ptr);
+};
+
+static int
+proc_ethtool_get_pauseparam(uint8_t port_id, uint16_t req_id, void *reply_data)
+{
+	uint16_t data_size;
+
+	if (rte_ethtool_get_pauseparam(port_id,
+		(struct ethtool_pauseparam *)reply_data))
+		data_size = STATUS_MASK;
+	else
+		data_size = (uint16_t)(sizeof(struct ethtool_pauseparam));
+	return send_reply(req_id, data_size, reply_data);
+};
+
+static int
+proc_ethtool_set_pauseparam(uint8_t port_id, uint16_t req_id, void *set_data)
+{
+	uint16_t data_size;
+
+	if (rte_ethtool_set_pauseparam(port_id,
+		(struct ethtool_pauseparam *)set_data))
+		data_size = STATUS_MASK;
+	else
+		data_size = (uint16_t)(sizeof(struct ethtool_pauseparam));
+	return send_reply(req_id, data_size, set_data);
+};
+
+static int
+proc_net_open(uint8_t port_id, uint16_t req_id)
+{
+	uint16_t data_size;
+
+	if (rte_ethtool_net_open(port_id))
+		data_size = STATUS_MASK;
+	else
+		data_size = 0;
+
+	return send_reply(req_id, data_size, &data_size);
+};
+
+static int
+proc_net_stop(uint8_t port_id, uint16_t req_id)
+{
+	uint16_t data_size;
+
+	rte_ethtool_net_stop(port_id);
+	data_size = 0;
+
+	return send_reply(req_id, data_size, &data_size);
+};
+
+static int
+proc_net_set_rx_mode(uint8_t port_id, uint16_t req_id)
+{
+	uint16_t data_size;
+
+	if (rte_ethtool_net_set_rx_mode(port_id))
+		data_size = STATUS_MASK;
+	else
+		data_size = 0;
+
+	return send_reply(req_id, data_size, &data_size);
+};
+
+static int
+proc_net_get_mac_addr(uint8_t port_id, uint16_t req_id, void *param_data)
+{
+	uint16_t data_size;
+
+	if (rte_ethtool_net_get_mac_addr(port_id, param_data))
+		data_size = STATUS_MASK;
+	else
+		data_size = MAC_ADDR_SIZE;
+
+	return send_reply(req_id, data_size, param_data);
+};
+
+static int
+proc_net_set_mac_addr(uint8_t port_id, uint16_t req_id, void *param_data)
+{
+	uint16_t data_size;
+
+	if (rte_ethtool_net_set_mac_addr(port_id, param_data))
+		data_size = STATUS_MASK;
+	else
+		data_size = 0;
+
+	return send_reply(req_id, data_size, &data_size);
+};
+
+static int
+proc_net_validate_addr(uint8_t port_id, uint16_t req_id, void *param_data)
+{
+	int status;
+
+	status = rte_ethtool_net_validate_addr(port_id, param_data);
+
+	return send_reply(req_id, (uint16_t)sizeof(int), &status);
+};
+
+static int
+proc_net_set_config(uint8_t port_id, uint16_t req_id, void *param_data)
+{
+	uint16_t data_size;
+
+	if (rte_ethtool_net_set_config(port_id, param_data))
+		data_size = STATUS_MASK;
+	else
+		data_size = 0;
+
+	return send_reply(req_id, data_size, &data_size);
+};
+
+static int
+proc_net_change_mtu(uint8_t port_id, uint16_t req_id, void *param_data)
+{
+	uint16_t data_size;
+	int mtu = *(int *)(param_data);
+
+	if (rte_ethtool_net_change_mtu(port_id, mtu))
+		data_size = STATUS_MASK;
+	else
+		data_size = 0;
+
+	return send_reply(req_id, data_size, &data_size);
+};
+
+static int
+proc_net_get_stats64(uint8_t port_id, uint16_t req_id, void *reply_data)
+{
+	uint16_t data_size;
+
+	if (rte_ethtool_net_get_stats64(port_id, reply_data))
+		data_size = STATUS_MASK;
+	else
+		data_size = size16(struct rte_eth_stats);
+
+	return send_reply(req_id, data_size, reply_data);
+};
+
+static int
+proc_net_vlan_rx_add_vid(uint8_t port_id, uint16_t req_id,
+	void *param_data)
+{
+	uint16_t data_size;
+	int *vid_ptr = (int *)param_data;
+
+	if (rte_ethtool_net_vlan_rx_add_vid(port_id, *vid_ptr))
+		data_size = STATUS_MASK;
+	else
+		data_size = 0;
+
+	return send_reply(req_id, data_size, &data_size);
+};
+
+static int
+proc_net_vlan_rx_kill_vid(uint8_t port_id, uint16_t req_id,
+	void *param_data)
+{
+	uint16_t data_size;
+	int *vid_ptr = (int *)param_data;
+
+	if (rte_ethtool_net_vlan_rx_kill_vid(port_id, *vid_ptr))
+		data_size = STATUS_MASK;
+	else
+		data_size = 0;
+
+	return send_reply(req_id, data_size, &data_size);
+};
+
+#endif /* NETDEV_OP_REPLY */
+#endif /* _NETDEV_API_H_ */
diff --git a/examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h b/examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h
new file mode 100644
index 0000000..be528b8
--- /dev/null
+++ b/examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h
@@ -0,0 +1,159 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SHARED_FIFO_H_
+#define _SHARED_FIFO_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#define REQ_PIPE "/tmp/nic_request"
+#define REP_PIPE "/tmp/nic_reply"
+#define PAGE_SIZE (4*1024)
+#define STACK_SIZE (4*PAGE_SIZE)
+#define MAXI_DATA (1024*6)
+#define MAXI_PARA 1024
+#define STATUS_MASK 0x8000
+#define MULTIPLE_DATA_MASK 0x4000
+#define MAXI_REQ_TYPE 16
+#define FIRST_DATA_OFFSET 8
+#define cast_ptr(x, new_type) (new_type)(void *)(x)
+#define to_ptr(new_ptr_type, data, offset) \
+	(new_ptr_type)(&((unsigned char *)(void *)data)[offset])
+#define u8ptr(x) cast_ptr(x, uint8_t *)
+#define to_mac_type(x) cast_ptr(x, struct ether_addr *)
+
+/*
+ * req[1:0]:	request-id
+ * req[2]:		request-id type
+ * req[3]:		request type
+ * req[4:5]:	param1-size
+ * req[7:6]:	param2-size
+ *
+ * rep[1:0]		reply-id
+ * rep[3:2]:	data1-size	// bit[15]: status bit[14]: two return data
+ * rep[7:4]:	data2-size
+ */
+#define PIPE_CTL_BYTE_COUNT (sizeof(uint32_t)*2)
+#define REQ_DWORD_LO(req_id, id_type, req_tye) \
+	(((uint32_t)req_type << 24) | ((uint32_t)id_type << 16) | req_id)
+#define REQ_DWORD_HI(param1_size, param2_size) \
+	(((uint32_t)param2_size << 16) | param1_size)
+
+#define REP_DWORD_LO(rep_id, data_bytes) \
+	(((uint32_t)data_bytes << 16) | (uint32_t)rep_id)
+#define REP_DWORD_HI(data2_bytes) (data2_bytes)
+
+#define REP_MUTILPLE_DATA(data1_size) (data1_size | MULTIPLE_DATA_MASK)
+#define REQ_ID(dword_ptr)		(dword_ptr[0] & 0xFFFF)
+#define REQ_IDTYPE(dword_ptr)	((dword_ptr[0] >> 16) & 0xFF)
+#define REQ_TYPE(dword_ptr)		((dword_ptr[0] >> 24) & 0xFF)
+#define REQ_PARAM1_SIZE(dword_ptr)	(dword_ptr[1] & 0xFFFF)
+#define REQ_PARAM2_SIZE(dword_ptr)	((dword_ptr[1]>>16) & 0xFFFF)
+#define REP_ID(dword_ptr)		(dword_ptr[0] & 0xFFFF)
+#define REP_DATA1_COUNT(dword_ptr)	((dword_ptr[0] >> 16) & 0xFFFF)
+#define REP_DATA2_COUNT(dword_ptr)	(dword_ptr[1])
+
+#define BAD_RETURN(data_size)	(data_size | STATUS_MASK)
+#define GOOD_RETURN(data_size)	((data_size & STATUS_MASK) == 0)
+#define MULTIPLE_DATA(data_size)	(data_size & MULTIPLE_DATA_MASK)
+#define BYTE_COUNT(data_size)	\
+	(data_size & ~(STATUS_MASK|MULTIPLE_DATA_MASK))
+
+#define PARAM_SIZE(type)		\
+	((uint16_t)(FIRST_DATA_OFFSET+sizeof(type)))
+#define FIRST_PARAM(param_data)	(void *)(&(param_data[FIRST_DATA_OFFSET]))
+#define FIRST_PARAM_TYPE(param_data, ptr_type)	\
+	(ptr_type)(FIRST_PARAM(param_data))
+
+void init_req_pipe(void);
+void init_rep_pipe(void);
+
+struct nic_info {
+	uint8_t num_of_ports;
+	uint32_t port_mask;
+	uint32_t vf_port_mask;
+	uint32_t flag;
+} nic_info;
+
+enum req_t {
+	get_drvinfo = 0,
+	get_setting,
+	set_setting,
+	get_regs_len,
+	get_regs,
+	get_link,
+	get_eeprom_len,
+	get_eeprom,
+	set_eeprom,
+	get_coalesce,
+	set_coalesce,
+	get_pauseparam,
+	set_pauseparam,
+	dump_data,
+
+	dev_open,
+	dev_stop,
+	set_rx_mode,
+	get_mac_addr,
+	set_mac_addr,
+	validate_addr,
+	set_config,
+	change_mtu,
+	get_stats64,
+	get_stats,
+	vlan_rx_add_vid,
+	vlan_rx_kill_vid,
+	ipc_begin,	/* request to start ipc, and get nic info ... */
+	ipc_end,	/* request to stop ipc ... */
+	invalid_req,
+};
+
+void
+init_req_pipe(void)
+{
+	mkfifo(REQ_PIPE, 0666);
+}
+
+void
+init_rep_pipe(void)
+{
+	mkfifo(REP_PIPE, 0666);
+}
+
+#endif /* _SHARED_FIFO_H_ */
diff --git a/examples/l2fwd-ethtool/lib/Makefile b/examples/l2fwd-ethtool/lib/Makefile
new file mode 100644
index 0000000..d7ee955
--- /dev/null
+++ b/examples/l2fwd-ethtool/lib/Makefile
@@ -0,0 +1,57 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overwritten by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+# library name
+LIB = librte_ethtool.a
+
+LIBABIVER := 1
+
+# all source are stored in SRC-Y
+SRCS-y := rte_ethtool.c
+
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS)
+
+include $(RTE_SDK)/mk/rte.extlib.mk
diff --git a/examples/l2fwd-ethtool/lib/rte_ethtool.c b/examples/l2fwd-ethtool/lib/rte_ethtool.c
new file mode 100644
index 0000000..2ef78f1
--- /dev/null
+++ b/examples/l2fwd-ethtool/lib/rte_ethtool.c
@@ -0,0 +1,336 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <rte_version.h>
+#include <rte_ethdev.h>
+#include "rte_ethtool.h"
+
+int
+rte_ethtool_get_drvinfo(uint8_t port_id, struct ethtool_drvinfo *drvinfo)
+{
+	struct rte_eth_dev_info dev_info;
+	int n;
+
+	memset(&dev_info, 0, sizeof(dev_info));
+	rte_eth_dev_info_get(port_id, &dev_info);
+
+	snprintf(drvinfo->driver, sizeof(drvinfo->driver), "%s",
+		dev_info.driver_name);
+	snprintf(drvinfo->version, sizeof(drvinfo->version), "%s",
+		rte_version());
+	snprintf(drvinfo->bus_info, sizeof(drvinfo->bus_info),
+		"%04x:%02x:%02x.%x",
+		dev_info.pci_dev->addr.domain, dev_info.pci_dev->addr.bus,
+		dev_info.pci_dev->addr.devid, dev_info.pci_dev->addr.function);
+
+	n = rte_eth_dev_get_reg_length(port_id);
+	if (n > 0)
+		drvinfo->regdump_len = n;
+	else
+		drvinfo->regdump_len = 0;
+
+	n = rte_eth_dev_get_eeprom_length(port_id);
+	if (n > 0)
+		drvinfo->eedump_len = n;
+	else
+		drvinfo->eedump_len = 0;
+
+	drvinfo->n_stats = sizeof(struct rte_eth_stats) / sizeof(uint64_t);
+	drvinfo->testinfo_len = 0;
+
+	return 0;
+}
+
+int
+rte_ethtool_get_regs_len(uint8_t port_id)
+{
+	return rte_eth_dev_get_reg_length(port_id);
+}
+
+int
+rte_ethtool_get_regs(uint8_t port_id, struct ethtool_regs *regs, void *data)
+{
+	struct rte_dev_reg_info reg_info;
+	int status;
+
+	reg_info.data = data;
+	reg_info.length = 0;
+
+	status = rte_eth_dev_get_reg_info(port_id, &reg_info);
+	if (status)
+		return status;
+	regs->version = reg_info.version;
+
+	return 0;
+}
+
+int
+rte_ethtool_get_link(uint8_t port_id)
+{
+	struct rte_eth_link link;
+
+	rte_eth_link_get(port_id, &link);
+	return link.link_status;
+}
+
+int
+rte_ethtool_get_eeprom_len(uint8_t port_id)
+{
+	return rte_eth_dev_get_eeprom_length(port_id);
+}
+
+int
+rte_ethtool_get_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom,
+	void *words)
+{
+	struct rte_dev_eeprom_info eeprom_info;
+	int status;
+
+	eeprom_info.offset = eeprom->offset;
+	eeprom_info.length = eeprom->len;
+	eeprom_info.data = words;
+
+	status = rte_eth_dev_get_eeprom(port_id, &eeprom_info);
+	if (status)
+		return status;
+
+	eeprom->magic = eeprom_info.magic;
+
+	return 0;
+}
+
+int
+rte_ethtool_set_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom,
+	void *words)
+{
+	struct rte_dev_eeprom_info eeprom_info;
+	int status;
+
+	eeprom_info.offset = eeprom->offset;
+	eeprom_info.length = eeprom->len;
+	eeprom_info.data = words;
+
+	status = rte_eth_dev_set_eeprom(port_id, &eeprom_info);
+	if (status)
+		return status;
+
+	eeprom->magic = eeprom_info.magic;
+
+	return 0;
+}
+
+int
+rte_ethtool_get_pauseparam(uint8_t port_id,
+	struct ethtool_pauseparam *pause_param)
+{
+	struct rte_eth_fc_conf fc_conf;
+	int status;
+
+	status = rte_eth_dev_flow_ctrl_get(port_id, &fc_conf);
+	if (status)
+		return status;
+
+	pause_param->tx_pause = 0;
+	pause_param->rx_pause = 0;
+	switch (fc_conf.mode) {
+	case RTE_FC_NONE:
+		/* dummy block to avoid compiler warning */
+		break;
+	case RTE_FC_RX_PAUSE:
+		pause_param->rx_pause = 1;
+		break;
+	case RTE_FC_TX_PAUSE:
+		pause_param->tx_pause = 1;
+		break;
+	case RTE_FC_FULL:
+		pause_param->rx_pause = 1;
+		pause_param->tx_pause = 1;
+	}
+	pause_param->autoneg = (uint32_t)fc_conf.autoneg;
+
+	return 0;
+}
+
+int
+rte_ethtool_set_pauseparam(uint8_t port_id,
+	struct ethtool_pauseparam *pause_param)
+{
+	struct rte_eth_fc_conf fc_conf;
+	int status;
+	/*
+	 * Read device flow control parameter first since
+	 * ethtool set_pauseparam op doesn't have all the information.
+	 * as defined in struct rte_eth_fc_conf.
+	 * This API requires the device to support both
+	 * rte_eth_dev_flow_ctrl_get and rte_eth_dev_flow_ctrl_set, otherwise
+	 * return -ENOTSUP
+	 */
+	status = rte_eth_dev_flow_ctrl_get(port_id, &fc_conf);
+	if (status)
+		return status;
+
+	fc_conf.autoneg = (uint8_t)pause_param->autoneg;
+
+	if (pause_param->tx_pause) {
+		if (pause_param->rx_pause)
+			fc_conf.mode = RTE_FC_FULL;
+		else
+			fc_conf.mode = RTE_FC_TX_PAUSE;
+	} else {
+		if (pause_param->rx_pause)
+			fc_conf.mode = RTE_FC_RX_PAUSE;
+		else
+			fc_conf.mode = RTE_FC_NONE;
+	}
+
+	status = rte_eth_dev_flow_ctrl_set(port_id, &fc_conf);
+	if (status)
+		return status;
+
+	return 0;
+}
+
+int
+rte_ethtool_net_open(uint8_t port_id)
+{
+	rte_eth_dev_stop(port_id);
+
+	return rte_eth_dev_start(port_id);
+}
+
+int
+rte_ethtool_net_stop(uint8_t port_id)
+{
+	rte_eth_dev_stop(port_id);
+
+	return 0;
+}
+
+int
+rte_ethtool_net_get_mac_addr(uint8_t port_id, struct ether_addr *addr)
+{
+	rte_eth_macaddr_get(port_id, addr);
+
+	return 0;
+}
+
+int
+rte_ethtool_net_set_mac_addr(uint8_t port_id, struct ether_addr *addr)
+{
+	return rte_eth_dev_default_mac_addr_set(port_id, addr);
+}
+
+int
+rte_ethtool_net_validate_addr(uint8_t port_id __rte_unused,
+	struct ether_addr *addr)
+{
+	return is_valid_assigned_ether_addr(addr);
+}
+
+int
+rte_ethtool_net_set_config(uint8_t port_id, void *config __rte_unused)
+{
+	struct rte_eth_link link;
+
+	memset(&link, 0, sizeof(link));
+	rte_eth_link_get(port_id, &link);
+	if (link.link_status == 1)
+		return -EINVAL;
+	return 0;
+}
+
+int
+rte_ethtool_net_change_mtu(uint8_t port_id, int mtu)
+{
+	return rte_eth_dev_set_mtu(port_id, (uint16_t)mtu);
+}
+
+int
+rte_ethtool_net_get_stats64(uint8_t port_id, struct rte_eth_stats *stats)
+{
+	return rte_eth_stats_get(port_id, stats);
+}
+
+int
+rte_ethtool_net_vlan_rx_add_vid(uint8_t port_id, uint16_t vid)
+{
+	return rte_eth_dev_vlan_filter(port_id, vid, 1);
+}
+
+int
+rte_ethtool_net_vlan_rx_kill_vid(uint8_t port_id, uint16_t vid)
+{
+	return rte_eth_dev_vlan_filter(port_id, vid, 0);
+}
+
+/*
+ * The set_rx_mode provides driver-specific rx mode setting.
+ * This implementation implements rx mode setting based upon
+ * ixgbe/igb drivers. Further improvement is to provide a
+ * callback op field over struct rte_eth_dev::dev_ops so each
+ * driver can register device-specific implementation
+ */
+int
+rte_ethtool_net_set_rx_mode(uint8_t port_id)
+{
+	uint16_t num_vfs;
+	struct rte_eth_dev_info dev_info;
+	uint16_t vf;
+
+	memset(&dev_info, 0, sizeof(dev_info));
+	rte_eth_dev_info_get(port_id, &dev_info);
+	num_vfs = dev_info.max_vfs;
+
+	/* Setting single cast promiscuous mode */
+	if (rte_eth_promiscuous_get(port_id) == 1)
+		rte_eth_promiscuous_enable(port_id);
+	else if (rte_eth_promiscuous_get(port_id) == 0)
+		rte_eth_promiscuous_disable(port_id);
+
+	/* Setting multi-cast promiscuous mode */
+	if (rte_eth_allmulticast_get(port_id) == 1)
+		rte_eth_allmulticast_enable(port_id);
+	else if (rte_eth_allmulticast_get(port_id) == 0)
+		rte_eth_allmulticast_disable(port_id);
+
+	/* Set VF vf_rx_mode, VF unsupport status is discard */
+	for (vf = 0; vf < num_vfs; vf++)
+		rte_eth_dev_set_vf_rxmode(port_id, vf, 1, 1);
+
+	/* Enable Rx vlan filter, VF unspport status is discard */
+	rte_eth_dev_set_vlan_offload(port_id, ETH_VLAN_FILTER_MASK);
+
+	return 0;
+}
diff --git a/examples/l2fwd-ethtool/lib/rte_ethtool.h b/examples/l2fwd-ethtool/lib/rte_ethtool.h
new file mode 100644
index 0000000..5d48b0d
--- /dev/null
+++ b/examples/l2fwd-ethtool/lib/rte_ethtool.h
@@ -0,0 +1,385 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTE_ETHTOOL_H_
+#define _RTE_ETHTOOL_H_
+
+/*
+ * This new interface is designed to provide a user-space shim layer for
+ * Ethtool and Netdevice op API.
+ *
+ * rte_ethtool_get_driver:          ethtool_ops::get_driverinfo
+ * rte_ethtool_get_link:            ethtool_ops::get_link
+ * rte_ethtool_get_regs_len:        ethtool_ops::get_regs_len
+ * rte_ethtool_get_regs:            ethtool_ops::get_regs
+ * rte_ethtool_get_eeprom_len:      ethtool_ops::get_eeprom_len
+ * rte_ethtool_get_eeprom:          ethtool_ops::get_eeprom
+ * rte_ethtool_set_eeprom:          ethtool_ops::set_eeprom
+ * rte_ethtool_get_pauseparam:      ethtool_ops::get_pauseparam
+ * rte_ethtool_set_pauseparam:      ethtool_ops::set_pauseparam
+ *
+ * rte_ethtool_net_open:            net_device_ops::ndo_open
+ * rte_ethtool_net_stop:            net_device_ops::ndo_stop
+ * rte_ethtool_net_set_mac_addr:    net_device_ops::ndo_set_mac_address
+ * rte_ethtool_net_validate_addr:   net_device_ops::ndo_validate_addr
+ * rte_ethtool_net_set_config:      net_device_ops::ndo_set_config
+ * rte_ethtool_net_change_mtu:      net_device_ops::rte_net_change_mtu
+ * rte_ethtool_net_get_stats64:     net_device_ops::ndo_get_stats64
+ * rte_ethtool_net_vlan_rx_add_vid  net_device_ops::ndo_vlan_rx_add_vid
+ * rte_ethtool_net_vlan_rx_kill_vid net_device_ops::ndo_vlan_rx_kill_vid
+ * rte_ethtool_net_set_rx_mode      net_device_ops::ndo_set_rx_mode
+ *
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <rte_ethdev.h>
+#include <linux/ethtool.h>
+
+/**
+ * Retrieve the Ethernet device driver information according to
+ * attributes described by ethtool data structure, ethtool_drvinfo.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param drvinfo
+ *   A pointer to get driver information
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_drvinfo(uint8_t port_id, struct ethtool_drvinfo *drvinfo);
+
+/**
+ * Retrieve the Ethernet device register length in bytes.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *   - (> 0) # of device registers (in bytes) available for dump
+ *   - (0) no registers available for dump.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_regs_len(uint8_t port_id);
+
+/**
+ * Retrieve the Ethernet device register information according to
+ * attributes described by ethtool data structure, ethtool_regs
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param reg
+ *   A pointer to ethtool_regs that has register information
+ * @param data
+ *   A pointer to a buffer that is used to retrieve device register content
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_regs(uint8_t port_id, struct ethtool_regs *regs,
+			    void *data);
+
+/**
+ * Retrieve the Ethernet device link status
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *   - (1) if link up.
+ *   - (0) if link down.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_link(uint8_t port_id);
+
+/**
+ * Retrieve the Ethernet device EEPROM size
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *	 - (> 0) device EEPROM size in bytes
+ *   - (0) device has NO EEPROM
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_eeprom_len(uint8_t port_id);
+
+/**
+ * Retrieve EEPROM content based upon eeprom range described in ethtool
+ * data structure, ethtool_eeprom
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param eeprom
+ *	 The pointer of ethtool_eeprom that provides eeprom range
+ * @param words
+ *	 A buffer that holds data read from eeprom
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom,
+			      void *words);
+
+/**
+ * Setting EEPROM content based upon eeprom range described in ethtool
+ * data structure, ethtool_eeprom
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param eeprom
+ *	 The pointer of ethtool_eeprom that provides eeprom range
+ * @param words
+ *	 A buffer that holds data to be written into eeprom
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_set_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom,
+			      void *words);
+
+/**
+ * Retrieve the Ethernet device pause frame configuration according to
+ * parameter attributes desribed by ethtool data structure,
+ * ethtool_pauseparam.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param pause_param
+ *	 The pointer of ethtool_coalesce that gets pause frame
+ *	 configuration parameters
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_pauseparam(uint8_t port_id,
+				   struct ethtool_pauseparam *pause_param);
+
+/**
+ * Setting the Ethernet device pause frame configuration according to
+ * parameter attributes desribed by ethtool data structure, ethtool_pauseparam.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param pause_param
+ *	 The pointer of ethtool_coalesce that gets ring configuration parameters
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_set_pauseparam(uint8_t port_id,
+				   struct ethtool_pauseparam *param);
+
+/**
+ * Start the Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_open(uint8_t port_id);
+
+/**
+ * Stop the Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_stop(uint8_t port_id);
+
+/**
+ * Get the Ethernet device MAC address.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param addr
+ *	 MAC address of the Ethernet device.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_get_mac_addr(uint8_t port_id, struct ether_addr *addr);
+
+/**
+ * Setting the Ethernet device MAC address.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param addr
+ *	 The new MAC addr.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_set_mac_addr(uint8_t port_id, struct ether_addr *addr);
+
+/**
+ * Validate if the provided MAC address is valid unicast address
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param addr
+ *	 A pointer to a buffer (6-byte, 48bit) for the target MAC address
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_validate_addr(uint8_t port_id, struct ether_addr *addr);
+
+/**
+ * Setting the Ethernet device configuration.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param config
+ *	 A opintr to a configuration parameter.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_set_config(uint8_t port_id, void *config);
+
+/**
+ * Setting the Ethernet device maximum Tx unit.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param mtu
+ *	 New MTU
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_change_mtu(uint8_t port_id, int mtu);
+
+/**
+ * Retrieve the Ethernet device traffic statistics
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param stats
+ *	 A pointer to struct rte_eth_stats for statistics parameters
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_get_stats64(uint8_t port_id, struct rte_eth_stats *stats);
+
+/**
+ * Update the Ethernet device VLAN filter with new vid
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param vid
+ *	 A new VLAN id
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_vlan_rx_add_vid(uint8_t port_id, uint16_t vid);
+
+/**
+ * Remove VLAN id from Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param vid
+ *	 A new VLAN id
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_vlan_rx_kill_vid(uint8_t port_id, uint16_t vid);
+
+/**
+ * Setting the Ethernet device rx mode.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_set_rx_mode(uint8_t port_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_ETHTOOL_H_ */
diff --git a/examples/l2fwd-ethtool/nic-control/Makefile b/examples/l2fwd-ethtool/nic-control/Makefile
new file mode 100644
index 0000000..17ab4a3
--- /dev/null
+++ b/examples/l2fwd-ethtool/nic-control/Makefile
@@ -0,0 +1,55 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overwritten by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+# binary name
+APP = nic-control
+
+# all source are stored in SRCS-y
+SRCS-y := nic_control.c
+
+CFLAGS += -O3 -I$(SRCDIR)/../l2fwd-app -I$(SRCDIR)/../lib
+CFLAGS += $(WERROR_FLAGS)
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/l2fwd-ethtool/nic-control/nic_control.c b/examples/l2fwd-ethtool/nic-control/nic_control.c
new file mode 100644
index 0000000..32769cb
--- /dev/null
+++ b/examples/l2fwd-ethtool/nic-control/nic_control.c
@@ -0,0 +1,614 @@
+/*-
+*   BSD LICENSE
+*
+*   Copyright(c) 2015 Intel Corporation. All rights reserved.
+*   All rights reserved.
+*
+*   Redistribution and use in source and binary forms, with or without
+*   modification, are permitted provided that the following conditions
+*   are met:
+*
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in
+*       the documentation and/or other materials provided with the
+*       distribution.
+*     * Neither the name of Intel Corporation nor the names of its
+*       contributors may be used to endorse or promote products derived
+*       from this software without specific prior written permission.
+*
+*   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+*   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+*   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+*   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+*   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+*   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+*   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+*   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+*   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+*   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+*   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * This is a non- DPDK application that sends NIC device management request
+ * through named pipe to a DPDK data plan process.
+ *
+ */
+#define USE_NEW_TYPE
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/resource.h>
+
+#include "rte_ethtool.h"
+#define NETDEV_OP_REQUEST 1
+#include "netdev_api.h"
+
+#define ITER_LIMIT	30
+#define CPU_CYCLES	(double)(2400.0*1000000)
+#define TEST_FAIL	-1
+#define TEST_PASS	0
+#define TEST_READ_SIZE	16
+#define STATS_PERIOD	10
+
+#define PACKET_RATE(before_value, after_value, before_ts, after_ts) \
+	((double)(after_value - before_value) * \
+	CPU_CYCLES/(after_ts - before_ts))
+
+#define BYTE2BIT_RATE(before_value, after_value, before_ts, after_ts) \
+	((double)(after_value - before_value) * \
+	CPU_CYCLES*8/(after_ts - before_ts))
+
+#define check(cond) (cond?"match":"miss-match")
+#define MAC_STR_SIZE (3*MAC_ADDR_SIZE+1)
+
+struct time_stamp {
+	uint32_t hi;
+	uint32_t lo;
+};
+
+enum test_type {
+	test_eeprom = 0,
+	test_regs,
+	test_mtu,
+	test_vlan_rx,
+	test_pauseparam,
+	test_counts,
+};
+
+struct api_test {
+	const char *test_name;
+	int mask_bit;
+	int (*test_task)(int port_id);
+};
+
+static inline unsigned long long
+rdtsc(void)
+{
+	unsigned hi, lo;
+
+	__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
+	return ((unsigned long long)lo) | (((unsigned long long)hi) << 32);
+}
+
+static uint32_t port_mask;
+static uint32_t vf_port_mask;
+static uint8_t num_of_ports;
+static char addr_string[MAC_STR_SIZE];
+
+static inline int
+is_port_enabled(uint8_t port_id)
+{
+	return (port_mask & (1 << port_id)) > 0;
+}
+
+static inline int
+is_vf_port(uint8_t port_id)
+{
+	return (vf_port_mask & (1 << port_id)) > 0;
+}
+
+static int
+netdev_ipc_begin(void)
+{
+	uint8_t reply_data[sizeof(double)];
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint32_t reply_data2[2];
+	uint8_t param_data[FIRST_DATA_OFFSET];
+
+	param_data[0] = 0;
+	send_request(req_id, ipc_begin,
+		FIRST_DATA_OFFSET, param_data);
+	read_reply(req_id, &data_size, reply_data, reply_data2);
+	num_of_ports = reply_data[0];
+	port_mask = reply_data2[0];
+	vf_port_mask = reply_data2[1];
+
+	return reply_data[0];
+}
+
+static int
+netdev_ipc_end(void)
+{
+	uint8_t reply_data[sizeof(double)];
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, ipc_end, 0, NULL);
+	read_reply(req_id, &data_size, reply_data, NULL);
+
+	return NETDEV_STATUS(data_size);
+}
+
+static uint8_t
+get_port(void)
+{
+	uint8_t port_id;
+	/* assume maximum of 32 ports */
+	port_id = rand() & 0x1F;
+	while (!is_port_enabled(port_id))
+		port_id = rand() & 0x1F;
+
+	return port_id;
+}
+
+static inline char*
+mac_addr_str(void *mac_addr_in)
+{
+	unsigned char *mac_addr = mac_addr_in;
+
+	snprintf(addr_string, MAC_STR_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
+		mac_addr[0], mac_addr[1], mac_addr[2],
+		mac_addr[3], mac_addr[4], mac_addr[5]);
+	return addr_string;
+}
+
+static int
+test_eeprom_get(int port_id)
+{
+	int count, i;
+	void *data;
+	uint16_t *word;
+	struct ethtool_eeprom eeprom;
+
+	count = netdev_ethtool_get_eeprom_len(port_id);
+	if (count <= 0) {
+		printf("fail to retrieve eeprom");
+		printf("count from port #%d\n", port_id);
+		return TEST_FAIL;
+	}
+
+	printf("eeprom size is %d bytes\n", count);
+	eeprom.offset = 0;
+	eeprom.len = TEST_READ_SIZE;
+	data = malloc(TEST_READ_SIZE);
+	if (data == NULL) {
+		printf("Fail to allocate memory, bailed out !!!\n");
+		return TEST_FAIL;
+	}
+
+	if (netdev_ethtool_get_eeprom(port_id, &eeprom, data)) {
+		printf("Fail to read eeprom from port #%d\n", port_id);
+		free(data);
+		return TEST_FAIL;
+	}
+
+	word = data;
+	printf("eeprom-magic: %x;", eeprom.magic);
+	printf("eeprom data[0:%d]:\n", TEST_READ_SIZE-1);
+	for (i = 0; i < (int)(eeprom.len >> 1); i++)
+		printf("%4x ", word[i]);
+	printf("\n");
+	free(data);
+	return TEST_PASS;
+}
+
+/*
+ * Testing eeprom get/set by getting eeprom data and write-back
+ * the same data
+ */
+static int
+test_eeprom_t(int port_id)
+{
+	int i, ind;
+	void *data_in, *data_out;
+	struct ethtool_eeprom eeprom;
+
+	eeprom.offset = 0;
+	eeprom.len = TEST_READ_SIZE;
+	data_in = malloc(eeprom.len);
+	if (data_in == NULL) {
+		printf("Fail to allocate memory, bailed out !!!\n");
+		return TEST_FAIL;
+	}
+
+	if (netdev_ethtool_get_eeprom(port_id, &eeprom, data_in)) {
+		printf("failed to read eeprom break from post-run");
+		free(data_in);
+		return TEST_FAIL;
+	}
+
+	if (netdev_ethtool_set_eeprom(port_id, &eeprom, data_in)) {
+		printf("Fail to write read-back data to eeprom!!!\n");
+		free(data_in);
+		return TEST_FAIL;
+	}
+
+	data_out = malloc(eeprom.len);
+	if (data_out == NULL) {
+		printf("Fail to allocate memory, bailed out !!!\n");
+		free(data_in);
+		return TEST_FAIL;
+	}
+	/* read-back for comparison */
+	if (netdev_ethtool_get_eeprom(port_id, &eeprom, data_out)) {
+		printf("failed to read eeprom break from post-run");
+		free(data_in);
+		free(data_out);
+		return TEST_FAIL;
+	}
+
+	for (i = eeprom.offset; i < (int)(eeprom.offset+eeprom.len); i++) {
+		unsigned char *in = (unsigned char *)data_in;
+		unsigned char *out = (unsigned char *)data_out;
+
+		ind = i - eeprom.offset;
+		if (in[ind] != out[ind])
+			printf("%d write-data:%x read-back-data:%x\n",
+				ind, in[ind], out[ind]);
+	}
+	free(data_in);
+	free(data_out);
+	return TEST_PASS;
+}
+
+static int
+test_regs_t(int port_id)
+{
+	int count, i;
+	void *data;
+	uint32_t *regs;
+	struct ethtool_regs reg_info;
+
+	count = netdev_ethtool_get_regs_len(port_id);
+	if (count <= 0)
+		printf("There are no registers available from port #%d",
+		port_id);
+	else
+		printf("Target has %d registers for dump", count);
+
+	data = malloc(count * sizeof(int));
+	if (data == NULL) {
+		printf("Fail to allocate memory, bailed out !!!\n");
+		return TEST_FAIL;
+	}
+	memset(&reg_info, 0xFF, sizeof(struct ethtool_regs));
+
+	if (netdev_ethtool_get_regs(port_id, &reg_info, data)) {
+		printf("Fail to read register\n");
+		free(data);
+		return TEST_FAIL;
+	}
+
+	regs = data;
+	printf("version: %x;", reg_info.version);
+	printf("register[0:%d]:\n", TEST_READ_SIZE-1);
+	for (i = 0; i < TEST_READ_SIZE; i++)
+		printf("%x ", regs[i]);
+	printf("\n");
+	free(data);
+	return TEST_PASS;
+}
+
+static int
+test_mtu_t(int port_id)
+{
+	const int mtu = 1024;
+
+	if (netdev_net_change_mtu(port_id, mtu)) {
+		printf("failed to set mtu to %d\n", mtu);
+		return TEST_FAIL;
+	}
+	return TEST_PASS;
+}
+
+static int
+test_vlan_rx_t(int port_id)
+{
+	/* add/remove vlan to vid */
+	netdev_net_vlan_rx_add_vid(port_id, 0);
+	if (netdev_net_vlan_rx_add_vid(port_id, 0)) {
+		if (netdev_net_vlan_rx_kill_vid(port_id, 0)) {
+			printf("netdev_net_vlan_rx_kill_vid() fails\n");
+			return TEST_FAIL;
+		}
+		return TEST_PASS;
+	}
+	printf("netdev_net_vlan_rx_add_vid() fails\n");
+	return TEST_FAIL;
+}
+
+static int
+test_pauseparam_t(int port_id)
+{
+	struct ethtool_pauseparam pause_param_in;
+	struct ethtool_pauseparam pause_param_out;
+
+	if (netdev_ethtool_get_pauseparam(port_id, &pause_param_in)) {
+		printf("netdev_ethtool_get_pauseparam() fails\n");
+		return TEST_FAIL;
+	}
+	printf("pause setup: autoneg: %d ",
+		pause_param_in.autoneg);
+	printf("tx_pause: %d ",
+		pause_param_in.tx_pause);
+	printf("rx_pause: %d\n",
+		pause_param_in.rx_pause);
+	if (netdev_ethtool_set_pauseparam(port_id, &pause_param_in)) {
+		printf("netdev_ethtool_set_pauseparam() fails\n");
+		return TEST_FAIL;
+	}
+	/* read-back pause frame setting for comparison */
+	if (netdev_ethtool_get_pauseparam(port_id, &pause_param_out)) {
+		printf("netdev_ethtool_get_pauseparam() fails\n");
+		return TEST_FAIL;
+	}
+	printf("pause frame checking auto:%s tx_pause:%s rx_pause:%s\n",
+		check(pause_param_in.autoneg == pause_param_out.autoneg),
+		check(pause_param_in.tx_pause == pause_param_out.tx_pause),
+		check(pause_param_in.rx_pause == pause_param_out.tx_pause));
+
+	return TEST_PASS;
+}
+
+static int
+test_get_drvinfo(int port_id)
+{
+	struct ethtool_drvinfo drvinfo;
+
+	if (netdev_ethtool_get_drvinfo(port_id, &drvinfo)) {
+		printf("fail to get drvinfo ...\n");
+		return TEST_FAIL;
+	}
+	printf("driver: %s version: %s fw_version: %s bus_info=%s\n",
+		drvinfo.driver, drvinfo.version,
+		drvinfo.fw_version, drvinfo.bus_info);
+	printf("reg-size(bytes)=%d eeprom-size=%d\n",
+		drvinfo.regdump_len,
+		drvinfo.eedump_len);
+	return TEST_PASS;
+}
+
+static int
+test_mac_addr(uint8_t port_id)
+{
+	unsigned char mac_addr[MAC_ADDR_SIZE];
+	unsigned char mac_addr_base[MAC_ADDR_SIZE] = {0x52, 0x54, 0, 0, 0, 1};
+	struct ether_addr *mac_addr_t;
+	struct ethter_add *mac_addr2;
+	int result;
+
+	mac_addr_t = to_mac_type(mac_addr);
+	result = netdev_net_get_mac_addr(port_id, mac_addr_t);
+	if (netdev_net_get_mac_addr(port_id, mac_addr_t)) {
+		printf("Fail to get mac addr from port #%d!!!\n", port_id);
+		return TEST_FAIL;
+	}
+	printf("Port #%d, device mac addr is %s\n", port_id,
+		mac_addr_str(mac_addr));
+
+	result = netdev_net_validate_addr(port_id, mac_addr_t);
+	if (!result) {
+		printf("Default mac addr, %s, is not valid\n",
+			mac_addr_str(mac_addr));
+		strncpy((char *)mac_addr, (char *)mac_addr_base, MAC_ADDR_SIZE);
+		mac_addr[MAC_ADDR_SIZE-1] += port_id;
+		printf("New mac address:%s is used.\n", mac_addr_str(mac_addr));
+
+		result = netdev_net_set_mac_addr(port_id, mac_addr_t);
+		if (result == TEST_FAIL)
+			return TEST_FAIL;
+
+		result = netdev_net_get_mac_addr(port_id, &mac_addr2);
+		if (strncmp(cast_ptr(mac_addr, char*),
+			    cast_ptr(mac_addr2, char*),
+			    MAC_ADDR_SIZE))
+			printf("Expected mac_addr %s return addr %s\n",
+				mac_addr_str(mac_addr),
+				mac_addr_str(cast_ptr(mac_addr2, unsigned char*)
+				));
+	}
+	return TEST_PASS;
+}
+
+struct api_test test_table[test_counts] = {
+	{"-test_eeprom", test_eeprom, test_eeprom_t},
+	{"-test_regs", test_regs, test_regs_t},
+	{"-test_mtu", test_mtu, test_mtu_t},
+	{"-test_vlan_rx", test_vlan_rx, test_vlan_rx_t},
+	{"-test_pauseparam", test_pauseparam, test_pauseparam_t},
+};
+
+static inline unsigned int
+get_test_mask(char *name)
+{
+	int ind = 0;
+	struct api_test *test = &test_table[ind];
+
+	while (ind++ < test_counts) {
+		if (!strncmp(name, test->test_name, strlen(name)))
+			return (1 << test->mask_bit);
+		test++;
+	}
+	return 0;
+}
+
+static unsigned int
+parse_args(int argc, char **argv)
+{
+	int i;
+	unsigned int test_mask = 0;
+
+
+	if (argc <= 1)
+		return 0;
+	for (i = 1; i < argc; i++)
+		test_mask |= get_test_mask(argv[i]);
+	return test_mask;
+}
+
+static inline void
+test_update(int result, int *fails, int *passes)
+{
+	if (result == TEST_PASS)
+		(*passes)++;
+	else
+		(*fails)++;
+}
+
+static void
+run_test(uint8_t port_id, unsigned int test_mask, int *fails, int *passes)
+{
+	int i;
+	int result;
+
+	for (i = 0; i < test_counts; i++) {
+		if (test_mask & (1<<i)) {
+			result = (*test_table[i].test_task)(port_id);
+			test_update(result, fails, passes);
+		}
+	}
+}
+
+static void
+wait_for_linkdown(uint8_t port_id)
+{
+	int link_up;
+
+	link_up = netdev_ethtool_get_link(port_id);
+	while (link_up) {
+		sleep(10);
+		link_up = netdev_ethtool_get_link(port_id);
+	}
+}
+
+int
+main(int argc, char **argv)
+{
+	uint8_t port_id;
+	unsigned int test_mask;
+	int passes = 0;
+	int fails = 0;
+	int iter_count = 0;
+	int link_up;
+	int result;
+
+
+	/* get command parameter */
+	test_mask = parse_args(argc, argv);
+
+	/* initialize request pipe */
+	init_req_pipe();
+
+	printf("issue ipc begin\n");
+	/* send a request to start the NIC device */
+	num_of_ports = netdev_ipc_begin();
+	while (num_of_ports == 0)
+		num_of_ports = netdev_ipc_begin() & 0xFF;
+
+	for (port_id = 0; port_id < num_of_ports; port_id++) {
+		link_up = netdev_ethtool_get_link(port_id);
+		printf("port #%d is %s\n", port_id, link_up?"up":"down");
+		if (!link_up) {
+			if (netdev_net_open(port_id) == 0)
+				netdev_net_set_rx_mode(port_id);
+			else
+				printf("failed to start port #%d\n", port_id);
+		}
+	}
+
+	/* Testing ethtool register/eeprom get */
+	for (port_id = 0; port_id < num_of_ports; port_id++) {
+		if (!is_port_enabled(port_id))
+			continue;
+
+		result = test_mac_addr(port_id);
+		test_update(result, &fails, &passes);
+
+		result = test_get_drvinfo(port_id);
+		test_update(result, &fails, &passes);
+
+		result = test_regs_t(port_id);
+		test_update(result, &fails, &passes);
+
+		/* Only testing eeprom access over a PF */
+		if (!is_vf_port(port_id)) {
+			result = test_eeprom_get(port_id);
+			test_update(result, &fails, &passes);
+		}
+	}
+
+	printf("start nic statistics collection ...\n");
+	port_id = get_port();
+	while (iter_count++ < ITER_LIMIT) {
+		uint64_t last_ts, ts;
+		struct rte_eth_stats last_stats, stats;
+
+		if (netdev_net_get_stats64(port_id, &last_stats)) {
+			printf("Fail to query statistics from port %d\n",
+				port_id);
+			break;
+		}
+		last_ts = rdtsc();
+
+		sleep(STATS_PERIOD);
+
+		if (netdev_net_get_stats64(port_id, &stats)) {
+			printf("Fail to query statistics from port %d\n",
+				port_id);
+			break;
+		}
+		ts = rdtsc();
+
+		printf("rx packet rate = %lf, tx packet rate = %lf\n",
+			PACKET_RATE(last_stats.ipackets, stats.ipackets,
+			last_ts, ts),
+			PACKET_RATE(last_stats.opackets, stats.opackets,
+			last_ts, ts));
+
+
+		printf("rx bit rate = %lf, tx bit rate = %lf\n",
+			BYTE2BIT_RATE(last_stats.ibytes, stats.ibytes,
+			last_ts, ts),
+			BYTE2BIT_RATE(last_stats.obytes, stats.obytes,
+			last_ts, ts));
+
+		sleep(STATS_PERIOD);
+	}
+
+	/* Stop link, testing APIs specified in command arguments */
+	if (test_mask) {
+		for (port_id = 0; port_id < num_of_ports; port_id++) {
+			link_up = netdev_ethtool_get_link(port_id);
+			if (link_up) {
+				netdev_net_stop(port_id);
+				wait_for_linkdown(port_id);
+			}
+		}
+
+		for (port_id = 0; port_id < num_of_ports; port_id++) {
+			link_up = netdev_ethtool_get_link(port_id);
+			if (!is_vf_port(port_id) && !link_up)
+				run_test(port_id, test_mask, &fails, &passes);
+		}
+	}
+
+	while (netdev_ipc_end() < 0)
+		;
+
+	printf("Pass count: %d Fail count: %d\n", passes, fails);
+	return 0;
+}
diff --git a/mk/rte.lib.mk b/mk/rte.lib.mk
index 9ff5cce..77689bf 100644
--- a/mk/rte.lib.mk
+++ b/mk/rte.lib.mk
@@ -43,8 +43,10 @@ LIB := $(patsubst %.a,%.so.$(LIBABIVER),$(LIB))
 ifeq ($(CONFIG_RTE_NEXT_ABI),y)
 LIB := $(LIB).1
 endif
+ifdef EXPORT_MAP
 CPU_LDFLAGS += --version-script=$(SRCDIR)/$(EXPORT_MAP)
 endif
+endif
 
 
 _BUILD = $(LIB)
-- 
2.1.4

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [dpdk-dev] [PATCH v2 0/2] Example: l2fwd-ethtool
  2015-07-20 14:12 ` [dpdk-dev] [PATCH] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
@ 2015-07-23 15:00   ` Liang-Min Larry Wang
  2015-07-23 15:00     ` [dpdk-dev] [PATCH v2 1/2] Remove ABI requierment for external library builds Liang-Min Larry Wang
  2015-07-23 15:00     ` [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
  0 siblings, 2 replies; 10+ messages in thread
From: Liang-Min Larry Wang @ 2015-07-23 15:00 UTC (permalink / raw)
  To: dev; +Cc: Liang-Min Larry Wang

This implementation is designed to provide an example illlustrating how to create a
user-space ethtool library from existing ethdev APIs. In contrast to kernel version
of same API (such as ops defined in KNI), the user-space APIs enable a fast-path
(no kernel API calls) for device query and data return. This example implements 19 popular
used Ethtool and Netdevice ops as described in examples/l2fwd-ethtool/lib/rte_ethtool.h,
and commnity support of un-implemented Ethtool and Netdevice ops are very welcomed.

v2 change:
- Separate changes on .mk files into a separate patch file
- Remove requirement of ABI version for external library build
- Fix example/l2fwd-ethtool share object build

Andrew G. Harvey (1):
  Remove ABI requierment for external library builds.

Liang-Min Larry Wang (1):
  examples: new example: l2fwd-ethtool

 examples/Makefile                                |    1 +
 examples/l2fwd-ethtool/Makefile                  |   48 +
 examples/l2fwd-ethtool/l2fwd-app/Makefile        |   58 ++
 examples/l2fwd-ethtool/l2fwd-app/main.c          | 1025 ++++++++++++++++++++++
 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h    |  770 ++++++++++++++++
 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h   |  159 ++++
 examples/l2fwd-ethtool/lib/Makefile              |   57 ++
 examples/l2fwd-ethtool/lib/rte_ethtool.c         |  336 +++++++
 examples/l2fwd-ethtool/lib/rte_ethtool.h         |  385 ++++++++
 examples/l2fwd-ethtool/nic-control/Makefile      |   55 ++
 examples/l2fwd-ethtool/nic-control/nic_control.c |  614 +++++++++++++
 mk/rte.extlib.mk                                 |    2 +
 mk/rte.lib.mk                                    |    6 +
 13 files changed, 3516 insertions(+)
 create mode 100644 examples/l2fwd-ethtool/Makefile
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/Makefile
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/main.c
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h
 create mode 100644 examples/l2fwd-ethtool/lib/Makefile
 create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.c
 create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.h
 create mode 100644 examples/l2fwd-ethtool/nic-control/Makefile
 create mode 100644 examples/l2fwd-ethtool/nic-control/nic_control.c

-- 
2.1.4

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [dpdk-dev] [PATCH v2 1/2] Remove ABI requierment for external library builds.
  2015-07-23 15:00   ` [dpdk-dev] [PATCH v2 0/2] Example: l2fwd-ethtool Liang-Min Larry Wang
@ 2015-07-23 15:00     ` Liang-Min Larry Wang
  2015-10-21 16:31       ` Thomas Monjalon
  2015-07-23 15:00     ` [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
  1 sibling, 1 reply; 10+ messages in thread
From: Liang-Min Larry Wang @ 2015-07-23 15:00 UTC (permalink / raw)
  To: dev

From: "Andrew G. Harvey" <agh@cisco.com>

Signed-off-by: Andrew G. Harvey <agh@cisco.com>
---
 mk/rte.extlib.mk | 2 ++
 mk/rte.lib.mk    | 6 ++++++
 2 files changed, 8 insertions(+)

diff --git a/mk/rte.extlib.mk b/mk/rte.extlib.mk
index ba066bc..d2a9b6d 100644
--- a/mk/rte.extlib.mk
+++ b/mk/rte.extlib.mk
@@ -31,6 +31,8 @@
 
 MAKEFLAGS += --no-print-directory
 
+export EXTLIB_BUILD := 1
+
 # we must create the output dir first and recall the same Makefile
 # from this directory
 ifeq ($(NOT_FIRST_CALL),)
diff --git a/mk/rte.lib.mk b/mk/rte.lib.mk
index 9ff5cce..63ca640 100644
--- a/mk/rte.lib.mk
+++ b/mk/rte.lib.mk
@@ -40,11 +40,13 @@ VPATH += $(SRCDIR)
 
 ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),y)
 LIB := $(patsubst %.a,%.so.$(LIBABIVER),$(LIB))
+ifndef EXTLIB_BUILD
 ifeq ($(CONFIG_RTE_NEXT_ABI),y)
 LIB := $(LIB).1
 endif
 CPU_LDFLAGS += --version-script=$(SRCDIR)/$(EXPORT_MAP)
 endif
+endif
 
 
 _BUILD = $(LIB)
@@ -173,12 +175,16 @@ $(RTE_OUTPUT)/lib/$(LIB): $(LIB)
 	@[ -d $(RTE_OUTPUT)/lib ] || mkdir -p $(RTE_OUTPUT)/lib
 	$(Q)cp -f $(LIB) $(RTE_OUTPUT)/lib
 ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),y)
+ifdef EXTLIB_BUILD
+	$(Q)ln -s -f $< $(basename $@)
+else
 ifeq ($(CONFIG_RTE_NEXT_ABI),y)
 	$(Q)ln -s -f $< $(basename $(basename $@))
 else
 	$(Q)ln -s -f $< $(basename $@)
 endif
 endif
+endif
 
 #
 # Clean all generated files
-- 
2.1.4

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool
  2015-07-23 15:00   ` [dpdk-dev] [PATCH v2 0/2] Example: l2fwd-ethtool Liang-Min Larry Wang
  2015-07-23 15:00     ` [dpdk-dev] [PATCH v2 1/2] Remove ABI requierment for external library builds Liang-Min Larry Wang
@ 2015-07-23 15:00     ` Liang-Min Larry Wang
  2015-10-21 16:36       ` Thomas Monjalon
  1 sibling, 1 reply; 10+ messages in thread
From: Liang-Min Larry Wang @ 2015-07-23 15:00 UTC (permalink / raw)
  To: dev; +Cc: Liang-Min Larry Wang

The example includes an ethtool library and two applications:
one application is a non- DPDK process (nic-control)
and the other is a DPDK l2fwd applicaiton (l2fwd-app).
The nic-control process sends ethtool alike device management
requests to l2fwd-app through a named pipe IPC. This example
is designed to show how to build a ethtool shim library and
how to use ethtool apis to manage device parameters.

Signed-off-by: Liang-Min Larry Wang <liang-min.wang@intel.com>
---
 examples/Makefile                                |    1 +
 examples/l2fwd-ethtool/Makefile                  |   48 +
 examples/l2fwd-ethtool/l2fwd-app/Makefile        |   58 ++
 examples/l2fwd-ethtool/l2fwd-app/main.c          | 1025 ++++++++++++++++++++++
 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h    |  770 ++++++++++++++++
 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h   |  159 ++++
 examples/l2fwd-ethtool/lib/Makefile              |   57 ++
 examples/l2fwd-ethtool/lib/rte_ethtool.c         |  336 +++++++
 examples/l2fwd-ethtool/lib/rte_ethtool.h         |  385 ++++++++
 examples/l2fwd-ethtool/nic-control/Makefile      |   55 ++
 examples/l2fwd-ethtool/nic-control/nic_control.c |  614 +++++++++++++
 11 files changed, 3508 insertions(+)
 create mode 100644 examples/l2fwd-ethtool/Makefile
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/Makefile
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/main.c
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h
 create mode 100644 examples/l2fwd-ethtool/lib/Makefile
 create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.c
 create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.h
 create mode 100644 examples/l2fwd-ethtool/nic-control/Makefile
 create mode 100644 examples/l2fwd-ethtool/nic-control/nic_control.c

diff --git a/examples/Makefile b/examples/Makefile
index b4eddbd..3dc049c 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -53,6 +53,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_KNI) += kni
 DIRS-y += l2fwd
 DIRS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += l2fwd-ivshmem
 DIRS-$(CONFIG_RTE_LIBRTE_JOBSTATS) += l2fwd-jobstats
+DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += l2fwd-ethtool
 DIRS-y += l3fwd
 DIRS-$(CONFIG_RTE_LIBRTE_ACL) += l3fwd-acl
 DIRS-$(CONFIG_RTE_LIBRTE_POWER) += l3fwd-power
diff --git a/examples/l2fwd-ethtool/Makefile b/examples/l2fwd-ethtool/Makefile
new file mode 100644
index 0000000..d9ad439
--- /dev/null
+++ b/examples/l2fwd-ethtool/Makefile
@@ -0,0 +1,48 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overwritten by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+DIRS-y += lib nic-control l2fwd-app
+
+include $(RTE_SDK)/mk/rte.extsubdir.mk
diff --git a/examples/l2fwd-ethtool/l2fwd-app/Makefile b/examples/l2fwd-ethtool/l2fwd-app/Makefile
new file mode 100644
index 0000000..69405f2
--- /dev/null
+++ b/examples/l2fwd-ethtool/l2fwd-app/Makefile
@@ -0,0 +1,58 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overwritten by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+# binary name
+APP = l2fwd-app
+
+# all source are stored in SRCS-y
+SRCS-y := main.c
+
+CFLAGS += -O3 -D_GNU_SOURCE -pthread -I$(SRCDIR)/../lib
+CFLAGS += $(WERROR_FLAGS)
+
+LDLIBS += -L$(subst l2fwd-app,lib,$(RTE_OUTPUT))/lib
+LDLIBS += -lrte_ethtool
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/l2fwd-ethtool/l2fwd-app/main.c b/examples/l2fwd-ethtool/l2fwd-app/main.c
new file mode 100644
index 0000000..73c29e3
--- /dev/null
+++ b/examples/l2fwd-ethtool/l2fwd-app/main.c
@@ -0,0 +1,1025 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include <rte_common.h>
+#include <rte_log.h>
+#include <rte_memory.h>
+#include <rte_memcpy.h>
+#include <rte_memzone.h>
+#include <rte_tailq.h>
+#include <rte_eal.h>
+#include <rte_per_lcore.h>
+#include <rte_launch.h>
+#include <rte_atomic.h>
+#include <rte_cycles.h>
+#include <rte_prefetch.h>
+#include <rte_lcore.h>
+#include <rte_per_lcore.h>
+#include <rte_branch_prediction.h>
+#include <rte_interrupts.h>
+#include <rte_pci.h>
+#include <rte_random.h>
+#include <rte_debug.h>
+#include <rte_ether.h>
+#include <rte_ethdev.h>
+#include <rte_ring.h>
+#include <rte_mempool.h>
+#include <rte_mbuf.h>
+#include "rte_ethtool.h"
+#define NETDEV_OP_REPLY 1
+#include "netdev_api.h"
+
+#define RTE_LOGTYPE_L2FWD RTE_LOGTYPE_USER1
+
+#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
+#define NB_MBUF   8192
+
+#define MAX_PKT_BURST 32
+#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */
+
+#define is_vf_port(vf_mask, port_id) ((vf_mask & (1 << port_id)) > 0)
+#define is_port_enabled(port_mask, port_id) ((port_mask & (1 << port_id)) > 0)
+#define TX_PTHRESH 32
+#define TX_HTHRESH 0
+#define TX_WTHRESH 0
+/*
+ * Configurable number of RX/TX ring descriptors
+ */
+#define RTE_TEST_RX_DESC_DEFAULT 128
+#define RTE_TEST_TX_DESC_DEFAULT 512
+static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
+static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
+
+/* ethernet addresses of ports */
+static struct ether_addr l2fwd_ports_eth_addr[RTE_MAX_ETHPORTS];
+
+/* mask of enabled ports */
+static uint32_t l2fwd_enabled_port_mask;
+
+/* virtio setup enable */
+static int virtio_setup;
+
+/* list of enabled ports */
+static uint32_t l2fwd_dst_ports[RTE_MAX_ETHPORTS];
+
+static unsigned int l2fwd_rx_queue_per_lcore = 1;
+
+struct mbuf_table {
+	unsigned len;
+	struct rte_mbuf *m_table[MAX_PKT_BURST];
+};
+
+#define MAX_RX_QUEUE_PER_LCORE 16
+#define MAX_TX_QUEUE_PER_PORT 16
+struct lcore_queue_conf {
+	unsigned n_rx_port;
+	unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE];
+	struct mbuf_table tx_mbufs[RTE_MAX_ETHPORTS];
+
+} __rte_cache_aligned;
+struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE];
+
+static struct rte_eth_conf port_conf = {
+	.rxmode = {
+		.split_hdr_size = 0,
+		.header_split   = 0, /**< Header Split disabled */
+		.hw_ip_checksum = 0, /**< IP checksum offload disabled */
+		.hw_vlan_filter = 0, /**< VLAN filtering disabled */
+		.jumbo_frame    = 0, /**< Jumbo Frame Support disabled */
+		.hw_strip_crc   = 0, /**< CRC stripped by hardware */
+	},
+	.txmode = {
+		.mq_mode = ETH_MQ_TX_NONE,
+	},
+};
+
+static struct rte_eth_txconf tx_conf = {
+	.tx_thresh = {
+		.pthresh = TX_PTHRESH,
+		.hthresh = TX_HTHRESH,
+		.wthresh = TX_WTHRESH,
+	},
+	.tx_free_thresh = 32,
+	.tx_rs_thresh = 32,
+	.txq_flags = 0xf00,
+};
+
+struct rte_mempool *l2fwd_pktmbuf_pool;
+
+/* Per-port statistics struct */
+struct l2fwd_port_statistics {
+	uint64_t tx;
+	uint64_t rx;
+	uint64_t dropped;
+} __rte_cache_aligned;
+struct l2fwd_port_statistics port_statistics[RTE_MAX_ETHPORTS];
+
+/* A tsc-based timer responsible for triggering statistics printout */
+#define TIMER_MILLISECOND 2000000ULL /* around 1ms at 2 Ghz */
+#define MAX_TIMER_PERIOD 86400 /* 1 day max */
+/* default period is 10 seconds */
+static int64_t timer_period = 10 * TIMER_MILLISECOND * 1000;
+
+/* IPC done checking utility function */
+/* status of ipc completed */
+static rte_atomic64_t ipc_done;
+
+static inline void init_ipc_done(void)
+{
+	rte_atomic64_init(&ipc_done);
+}
+
+static inline int is_ipc_done(void)
+{
+	return rte_atomic64_read(&ipc_done) > 0;
+}
+
+static inline void set_ipc_done(void)
+{
+	rte_atomic64_inc(&ipc_done);
+}
+
+/* Print out statistics on packets dropped */
+static void
+print_stats(void)
+{
+	uint64_t total_packets_dropped, total_packets_tx, total_packets_rx;
+	unsigned portid;
+
+	total_packets_dropped = 0;
+	total_packets_tx = 0;
+	total_packets_rx = 0;
+
+	const char clr[] = { 27, '[', '2', 'J', '\0' };
+	const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' };
+
+		/* Clear screen and move to top left */
+	printf("%s%s", clr, topLeft);
+
+	printf("\nPort statistics ====================================");
+
+	for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
+		/* skip disabled ports */
+		if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
+			continue;
+		printf("\nStatistics for port %u ----------------------------",
+			portid);
+		printf("\nPackets sent: %24"PRIu64, port_statistics[portid].tx);
+		printf("\nPackets received: %20"PRIu64,
+			port_statistics[portid].rx);
+		printf("\nPackets dropped: %21"PRIu64,
+			port_statistics[portid].dropped);
+
+		total_packets_dropped += port_statistics[portid].dropped;
+		total_packets_tx += port_statistics[portid].tx;
+		total_packets_rx += port_statistics[portid].rx;
+	}
+	printf("\nAggregate statistics ===============================");
+	printf("\nTotal packets sent: %18"PRIu64, total_packets_tx);
+	printf("\nTotal packets received: %14"PRIu64, total_packets_rx);
+	printf("\nTotal packets dropped: %15"PRIu64, total_packets_dropped);
+	printf("\n====================================================\n");
+}
+
+/* Send the burst of packets on an output interface */
+static int
+l2fwd_send_burst(struct lcore_queue_conf *qconf, unsigned n, uint8_t port)
+{
+	struct rte_mbuf **m_table;
+	unsigned ret;
+	unsigned queueid = 0;
+
+	m_table = (struct rte_mbuf **)qconf->tx_mbufs[port].m_table;
+
+	ret = rte_eth_tx_burst(port, (uint16_t) queueid, m_table, (uint16_t) n);
+	port_statistics[port].tx += ret;
+	if (unlikely(ret < n)) {
+		port_statistics[port].dropped += (n - ret);
+		do {
+			rte_pktmbuf_free(m_table[ret]);
+		} while (++ret < n);
+	}
+
+	return 0;
+}
+
+/* Enqueue packets for TX and prepare them to be sent */
+static int
+l2fwd_send_packet(struct rte_mbuf *m, uint8_t port)
+{
+	unsigned lcore_id, len;
+	struct lcore_queue_conf *qconf;
+
+	lcore_id = rte_lcore_id();
+
+	qconf = &lcore_queue_conf[lcore_id];
+	len = qconf->tx_mbufs[port].len;
+	qconf->tx_mbufs[port].m_table[len] = m;
+	len++;
+
+	/* enough pkts to be sent */
+	if (unlikely(len == MAX_PKT_BURST)) {
+		l2fwd_send_burst(qconf, MAX_PKT_BURST, port);
+		len = 0;
+	}
+
+	qconf->tx_mbufs[port].len = len;
+	return 0;
+}
+
+static void
+l2fwd_simple_forward(struct rte_mbuf *m, unsigned portid)
+{
+	struct ether_hdr *eth;
+	void *tmp;
+	unsigned dst_port;
+
+	dst_port = l2fwd_dst_ports[portid];
+	eth = rte_pktmbuf_mtod(m, struct ether_hdr *);
+
+	/* 02:00:00:00:00:xx */
+	tmp = &eth->d_addr.addr_bytes[0];
+	*((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40);
+
+	/* src addr */
+	ether_addr_copy(&l2fwd_ports_eth_addr[dst_port], &eth->s_addr);
+
+	l2fwd_send_packet(m, (uint8_t) dst_port);
+}
+
+/* main processing loop */
+static void
+l2fwd_main_loop(void)
+{
+	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
+	struct rte_mbuf *m;
+	unsigned lcore_id;
+	uint64_t prev_tsc, diff_tsc, cur_tsc, timer_tsc;
+	unsigned i, j, portid, nb_rx;
+	struct lcore_queue_conf *qconf;
+	const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) /
+					US_PER_S * BURST_TX_DRAIN_US;
+
+	prev_tsc = 0;
+	timer_tsc = 0;
+
+	lcore_id = rte_lcore_id();
+	qconf = &lcore_queue_conf[lcore_id];
+
+	if (qconf->n_rx_port == 0) {
+		RTE_LOG(INFO, L2FWD, "lcore %u has nothing to do\n", lcore_id);
+		return;
+	}
+
+	RTE_LOG(INFO, L2FWD, "entering main loop on lcore %u\n", lcore_id);
+
+	for (i = 0; i < qconf->n_rx_port; i++) {
+
+		portid = qconf->rx_port_list[i];
+		RTE_LOG(INFO, L2FWD, " -- lcoreid=%u portid=%u\n", lcore_id,
+			portid);
+	}
+
+	if (virtio_setup) {
+		while (is_ipc_done() == 0)
+			usleep(50);
+	}
+
+	while (1) {
+		cur_tsc = rte_rdtsc();
+
+		/* TX burst queue drain */
+		diff_tsc = cur_tsc - prev_tsc;
+		if (unlikely(diff_tsc > drain_tsc)) {
+
+			for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
+				if (qconf->tx_mbufs[portid].len == 0)
+					continue;
+				l2fwd_send_burst(&lcore_queue_conf[lcore_id],
+						 qconf->tx_mbufs[portid].len,
+						 (uint8_t) portid);
+				qconf->tx_mbufs[portid].len = 0;
+			}
+
+			/* if timer is enabled */
+			if (timer_period > 0) {
+
+				/* advance the timer */
+				timer_tsc += diff_tsc;
+
+				/* if timer has reached its timeout */
+				if (unlikely(timer_tsc >=
+				    (uint64_t) timer_period)) {
+
+					/* do this only on master core */
+					if (lcore_id ==
+					    rte_get_master_lcore()) {
+						print_stats();
+						/* reset the timer */
+						timer_tsc = 0;
+					}
+				}
+			}
+
+			prev_tsc = cur_tsc;
+		}
+
+		/*
+		 * Read packet from RX queues
+		 */
+		for (i = 0; i < qconf->n_rx_port; i++) {
+
+			portid = qconf->rx_port_list[i];
+			nb_rx = rte_eth_rx_burst((uint8_t) portid, 0,
+						 pkts_burst, MAX_PKT_BURST);
+
+			port_statistics[portid].rx += nb_rx;
+
+			for (j = 0; j < nb_rx; j++) {
+				m = pkts_burst[j];
+				rte_prefetch0(rte_pktmbuf_mtod(m, void *));
+				l2fwd_simple_forward(m, portid);
+			}
+		}
+	}
+}
+
+static int
+l2fwd_launch_one_lcore(__attribute__((unused)) void *dummy)
+{
+	l2fwd_main_loop();
+	return 0;
+}
+
+/* display usage */
+static void
+l2fwd_usage(const char *prgname)
+{
+	printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n"
+		"  -p PORTMASK: hexadecimal bitmask of ports to configure\n"
+		"  -q NQ: number of queue (=ports) per lcore (default is 1)\n"
+		"  -V : setting rx/tx mode to enable virtio\n"
+		"  -T PERIOD: statistics will be refreshed each PERIOD seconds",
+		prgname);
+	printf("(0 to disable, 10 default, 86400 maximum)\n");
+}
+
+static int
+l2fwd_parse_portmask(const char *portmask)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* parse hexadecimal string */
+	pm = strtoul(portmask, &end, 16);
+	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (pm == 0)
+		return -1;
+
+	return pm;
+}
+
+static unsigned int
+l2fwd_parse_nqueue(const char *q_arg)
+{
+	char *end = NULL;
+	unsigned long n;
+
+	/* parse hexadecimal string */
+	n = strtoul(q_arg, &end, 10);
+	if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return 0;
+	if (n == 0)
+		return 0;
+	if (n >= MAX_RX_QUEUE_PER_LCORE)
+		return 0;
+
+	return n;
+}
+
+static int
+l2fwd_parse_timer_period(const char *q_arg)
+{
+	char *end = NULL;
+	int n;
+
+	/* parse number string */
+	n = strtol(q_arg, &end, 10);
+	if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+	if (n >= MAX_TIMER_PERIOD)
+		return -1;
+
+	return n;
+}
+
+static int
+l2fwd_parse_virtio_setup(const char *q_arg)
+{
+	char *end = NULL;
+	int n;
+
+	/* parse number string */
+	n = strtol(q_arg, &end, 10);
+	if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+	if (n >= MAX_TIMER_PERIOD)
+		return -1;
+
+	return n;
+}
+
+/* Parse the argument given in the command line of the application */
+static int
+l2fwd_parse_args(int argc, char **argv)
+{
+	int opt, ret;
+	char **argvopt;
+	int option_index;
+	char *prgname = argv[0];
+	static struct option lgopts[] = {
+		{NULL, 0, 0, 0}
+	};
+
+	argvopt = argv;
+
+	while ((opt = getopt_long(argc, argvopt, "p:q:T:V:",
+				  lgopts, &option_index)) != EOF) {
+
+		switch (opt) {
+		/* portmask */
+		case 'p':
+			l2fwd_enabled_port_mask = l2fwd_parse_portmask(optarg);
+			if (l2fwd_enabled_port_mask == 0) {
+				printf("invalid portmask\n");
+				l2fwd_usage(prgname);
+				return -1;
+			}
+			break;
+
+		/* nqueue */
+		case 'q':
+			l2fwd_rx_queue_per_lcore = l2fwd_parse_nqueue(optarg);
+			if (l2fwd_rx_queue_per_lcore == 0) {
+				printf("invalid queue number\n");
+				l2fwd_usage(prgname);
+				return -1;
+			}
+			break;
+
+		/* timer period */
+		case 'T':
+			timer_period = l2fwd_parse_timer_period(optarg) *
+				1000 * TIMER_MILLISECOND;
+			if (timer_period < 0) {
+				printf("invalid timer period\n");
+				l2fwd_usage(prgname);
+				return -1;
+			}
+			break;
+
+		/* virtio setup */
+		case 'V':
+			/* get option as the pf mac addr */
+			virtio_setup = l2fwd_parse_virtio_setup(optarg);
+			if (virtio_setup) {
+				port_conf.rxmode.hw_vlan_strip = 0;
+				port_conf.rxmode.hw_vlan_extend = 0;
+			}
+			break;
+
+		/* long options */
+		case 0:
+			l2fwd_usage(prgname);
+			return -1;
+
+		default:
+			l2fwd_usage(prgname);
+			return -1;
+		}
+	}
+
+	if (optind >= 0)
+		argv[optind-1] = prgname;
+
+	ret = optind-1;
+	optind = 0; /* reset getopt lib */
+	return ret;
+}
+
+/* Check the link status of all ports in up to 9s, and print them finally */
+static void
+check_all_ports_link_status(uint8_t port_num, uint32_t port_mask)
+{
+#define CHECK_INTERVAL 100 /* 100ms */
+#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
+	uint8_t portid, count, all_ports_up, print_flag = 0;
+	struct rte_eth_link link;
+
+	printf("\nChecking link status!!!");
+	fflush(stdout);
+	for (count = 0; count <= MAX_CHECK_TIME; count++) {
+		all_ports_up = 1;
+		for (portid = 0; portid < port_num; portid++) {
+			if ((port_mask & (1 << portid)) == 0)
+				continue;
+			memset(&link, 0, sizeof(link));
+			rte_eth_link_get_nowait(portid, &link);
+			/* print link status if flag set */
+			if (print_flag == 1) {
+				if (link.link_status) {
+					printf("Port %d Link Up - speed %u "
+						, (uint8_t)portid,
+						(unsigned)link.link_speed);
+					printf("Mbps - %s\n", (link.link_duplex
+						== ETH_LINK_FULL_DUPLEX) ?
+						("full-duplex") :
+						("half-duplex\n"));
+				} else
+					printf("Port %d Link Down\n",
+						(uint8_t)portid);
+				continue;
+			}
+			/* clear all_ports_up flag if any link down */
+			if (link.link_status == 0) {
+				all_ports_up = 0;
+				break;
+			}
+		}
+		/* after finally printing all link status, get out */
+		if (print_flag == 1)
+			break;
+
+		if (all_ports_up == 0) {
+			printf(".");
+			fflush(stdout);
+			rte_delay_ms(CHECK_INTERVAL);
+		}
+
+		/* set the print_flag if all ports up or timeout */
+		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
+			print_flag = 1;
+			printf("done\n");
+		}
+	}
+}
+
+static inline char*
+mac_addr_str(unsigned char *mac_addr)
+{
+#define MAC_STR_SIZE (3*MAC_ADDR_SIZE+1)
+	static char addr_string[MAC_STR_SIZE];
+
+	snprintf(addr_string, MAC_STR_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
+		mac_addr[0], mac_addr[1], mac_addr[2],
+		mac_addr[3], mac_addr[4], mac_addr[5]);
+	return addr_string;
+}
+
+static int
+proc_ipc_begin(struct nic_info *info, uint16_t req_id)
+{
+	struct ethtool_drvinfo drvinfo;
+	uint8_t mac_addr[MAC_ADDR_SIZE];
+	uint8_t param[4], port_id, num_of_ports = info->num_of_ports;
+	uint32_t param2[2];
+	int status;
+
+	param[0] = num_of_ports;
+	info->vf_port_mask = 0;
+	for (port_id = 0; port_id < num_of_ports; port_id++) {
+		status = rte_ethtool_get_drvinfo(port_id, &drvinfo);
+		if (status) {
+			printf("get_drvinfo from port #%d fails\n", port_id);
+			return -1;
+		}
+		info->vf_port_mask |= (drvinfo.eedump_len == 0?1:0) << port_id;
+		rte_ethtool_net_stop(port_id);
+	}
+	param2[0] = info->port_mask;
+	param2[1] = info->vf_port_mask;
+
+	for (port_id = 0; port_id < num_of_ports; port_id++) {
+		rte_ethtool_net_open(port_id);
+		if (!is_vf_port(info->vf_port_mask, port_id)) {
+			struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+			struct rte_eth_dev_data *dev_data =
+				(struct rte_eth_dev_data *)dev->data;
+
+			dev_data->promiscuous = 1;
+			rte_ethtool_net_set_rx_mode(port_id);
+		}
+		rte_ethtool_net_get_mac_addr(port_id, (void *)mac_addr);
+		printf("Port #%d init mac address is", port_id);
+		printf(" %s", mac_addr_str(mac_addr));
+
+	}
+
+	send_reply2(req_id, 1, param, (uint16_t)(sizeof(uint32_t)*2), param2);
+	return 0;
+}
+
+static inline void
+proc_no_action(uint16_t req_id)
+{
+	send_reply(req_id, 0, NULL);
+}
+
+static inline void
+proc_invalid(uint16_t req_id)
+{
+	send_reply(req_id, BAD_RETURN(0), NULL);
+}
+
+static void*
+ethtool(void *ctx)
+{
+	struct nic_info *info = ctx;
+	int keep_req = 1;
+	int reg_count, eeprom_size;
+	uint16_t req_id, param1_size, param2_size;
+	uint8_t req_type, port_id;
+	int status;
+	uint8_t param1[MAXI_PARA];
+	uint8_t param2[MAXI_PARA];
+	uint8_t reply1[MAXI_DATA];
+	void *first_param	= FIRST_PARAM(param1);
+
+	init_rep_pipe();
+	while (1) {
+		read_request(&req_id, &req_type, &param1_size, param1,
+			&param2_size, param2);
+		if (req_type != (enum req_t)ipc_begin)
+			proc_invalid(req_id);
+		else
+			break;
+	}
+	proc_ipc_begin(info, req_id);
+
+	set_ipc_done();
+	reg_count = eeprom_size = 0;
+
+	while (keep_req) {
+		status = NETDEV_INVALID;
+		read_request(&req_id, &req_type, &param1_size, param1,
+			&param2_size, param2);
+		port_id = param1[0];
+
+		switch ((enum req_t)req_type) {
+		case get_drvinfo:
+			status = proc_ethtool_get_drvinfo(port_id, req_id,
+				first_param);
+			break;
+
+		case get_regs_len:
+			status = reg_count = proc_ethtool_get_regs_len(
+				port_id, req_id);
+			break;
+
+		case get_regs:
+			if (reg_count == 0)
+				reg_count = rte_ethtool_get_regs_len(port_id);
+			if (reg_count)
+				status = proc_ethtool_get_regs(port_id, req_id,
+				first_param, reply1);
+			break;
+
+		case get_link:
+			status = proc_ethtool_get_link(port_id, req_id);
+			break;
+
+		case get_eeprom_len:
+			if (eeprom_size == 0)
+				eeprom_size = rte_ethtool_get_eeprom_len(
+				port_id);
+			status = proc_ethtool_get_eeprom_len(port_id, req_id);
+			break;
+
+		case get_eeprom:
+			status = proc_ethtool_get_eeprom(port_id, req_id,
+				first_param, reply1);
+			break;
+
+		case set_eeprom:
+			status = proc_ethtool_set_eeprom(port_id, req_id,
+				first_param, param2);
+			break;
+
+		case get_pauseparam:
+			status = proc_ethtool_get_pauseparam(port_id,
+				req_id,
+				cast_ptr(reply1, struct ethtool_pauseparam *));
+			break;
+
+		case set_pauseparam:
+			status = proc_ethtool_set_pauseparam(port_id,
+				req_id,
+				cast_ptr(reply1, struct ethtool_pauseparam *));
+			break;
+
+		case dev_open:
+			status = proc_net_open(port_id, req_id);
+			break;
+
+		case dev_stop:
+			status = proc_net_stop(port_id, req_id);
+			break;
+
+		case set_rx_mode:
+			status = proc_net_set_rx_mode(port_id, req_id);
+			break;
+
+		case get_mac_addr:
+			status = proc_net_get_mac_addr(port_id,
+				req_id, first_param);
+			break;
+
+		case set_mac_addr:
+			status = proc_net_set_mac_addr(port_id,
+				req_id, first_param);
+			break;
+
+		case validate_addr:
+			status = proc_net_validate_addr(port_id,
+				req_id, first_param);
+			break;
+
+		case set_config:
+			status = proc_net_set_config(port_id,
+				req_id, first_param);
+			break;
+
+		case change_mtu:
+			status = proc_net_change_mtu(port_id,
+				req_id, first_param);
+			break;
+
+		case get_stats64:
+			status = proc_net_get_stats64(port_id,
+				req_id, reply1);
+			break;
+
+		case vlan_rx_add_vid:
+			status = proc_net_vlan_rx_add_vid(port_id,
+				req_id, first_param);
+			break;
+
+		case vlan_rx_kill_vid:
+			status = proc_net_vlan_rx_kill_vid(port_id,
+				req_id, first_param);
+			break;
+
+		case ipc_end:
+			keep_req = 0;
+			proc_no_action(req_id);
+			status = 0;
+			break;
+
+		default:
+			proc_invalid(req_id);
+			printf("unsupported service request type:");
+			printf(" %d\n", req_type);
+			break;
+		}
+		if (status < 0)
+			printf("Request type (=%d) failed\n", (int)req_type);
+		/* check if termination flag is set */
+	}
+	printf("IPC session is over\n");
+	return NULL;
+}
+
+int
+main(int argc, char **argv)
+{
+	struct lcore_queue_conf *qconf;
+	struct rte_eth_dev_info dev_info;
+	int ret;
+	uint8_t nb_ports;
+	uint8_t nb_ports_available;
+	uint8_t portid, last_port;
+	unsigned lcore_id, rx_lcore_id;
+	unsigned nb_ports_in_mask = 0;
+
+	init_ipc_done();
+	/* init EAL */
+	ret = rte_eal_init(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n");
+	argc -= ret;
+	argv += ret;
+
+	/* parse application arguments (after the EAL ones) */
+	ret = l2fwd_parse_args(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid L2FWD arguments\n");
+
+	/* create the mbuf pool */
+	l2fwd_pktmbuf_pool =
+		rte_mempool_create("mbuf_pool", NB_MBUF,
+				   MBUF_SIZE, 32,
+				   sizeof(struct rte_pktmbuf_pool_private),
+				   rte_pktmbuf_pool_init, NULL,
+				   rte_pktmbuf_init, NULL,
+				   rte_socket_id(), 0);
+	if (l2fwd_pktmbuf_pool == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot init mbuf pool\n");
+
+	nb_ports = rte_eth_dev_count();
+	if (nb_ports == 0)
+		rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
+
+	if (nb_ports > RTE_MAX_ETHPORTS)
+		nb_ports = RTE_MAX_ETHPORTS;
+
+	/* reset l2fwd_dst_ports */
+	for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++)
+		l2fwd_dst_ports[portid] = 0;
+	last_port = 0;
+
+	/*
+	 * Each logical core is assigned a dedicated TX queue on each port.
+	 */
+	for (portid = 0; portid < nb_ports; portid++) {
+		/* skip ports that are not enabled */
+		if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
+			continue;
+
+		if (nb_ports_in_mask % 2) {
+			l2fwd_dst_ports[portid] = last_port;
+			l2fwd_dst_ports[last_port] = portid;
+		} else
+			last_port = portid;
+
+		nb_ports_in_mask++;
+
+		rte_eth_dev_info_get(portid, &dev_info);
+	}
+	if (nb_ports_in_mask % 2) {
+		printf("Notice: odd number of ports in portmask.\n");
+		l2fwd_dst_ports[last_port] = last_port;
+	}
+
+	rx_lcore_id = 0;
+	qconf = NULL;
+
+	/* Initialize the port/queue configuration of each logical core */
+	for (portid = 0; portid < nb_ports; portid++) {
+		/* skip ports that are not enabled */
+		if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
+			continue;
+
+		/* get the lcore_id for this port */
+		while (rte_lcore_is_enabled(rx_lcore_id) == 0 ||
+			lcore_queue_conf[rx_lcore_id].n_rx_port ==
+			l2fwd_rx_queue_per_lcore) {
+			rx_lcore_id++;
+			if (rx_lcore_id >= RTE_MAX_LCORE)
+				rte_exit(EXIT_FAILURE, "Not enough cores\n");
+		}
+
+		if (qconf != &lcore_queue_conf[rx_lcore_id])
+			/* Assigned a new logical core in the loop above. */
+			qconf = &lcore_queue_conf[rx_lcore_id];
+
+		qconf->rx_port_list[qconf->n_rx_port] = portid;
+		qconf->n_rx_port++;
+		printf("Lcore %u: RX port %u\n", rx_lcore_id,
+			(unsigned) portid);
+	}
+
+	nb_ports_available = nb_ports;
+
+	/* Initialise each port */
+	for (portid = 0; portid < nb_ports; portid++) {
+		/* skip ports that are not enabled */
+		if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) {
+			printf("Skipping disabled port %u\n",
+				(unsigned) portid);
+			nb_ports_available--;
+			continue;
+		}
+		/* init port */
+		printf("Initializing port %u... ", (unsigned) portid);
+		fflush(stdout);
+		ret = rte_eth_dev_configure(portid, 1, 1, &port_conf);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+			"Cannot configure device: err=%d, port=%u\n",
+				ret, (unsigned) portid);
+
+		rte_eth_macaddr_get(portid, &l2fwd_ports_eth_addr[portid]);
+
+		/* init one RX queue */
+		fflush(stdout);
+		ret = rte_eth_rx_queue_setup(portid, 0, nb_rxd,
+					rte_eth_dev_socket_id(portid),
+					NULL,
+					l2fwd_pktmbuf_pool);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+			"rte_eth_rx_queue_setup:err=%d, port=%u\n",
+				  ret, (unsigned) portid);
+
+		/* init one TX queue on each port */
+		fflush(stdout);
+		if (virtio_setup) {
+			ret = rte_eth_tx_queue_setup(portid, 0, nb_txd,
+				rte_eth_dev_socket_id(portid), &tx_conf);
+		} else {
+			ret = rte_eth_tx_queue_setup(portid, 0, nb_txd,
+				rte_eth_dev_socket_id(portid),
+				NULL);
+		}
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+			"rte_eth_tx_queue_setup:err=%d, port=%u\n",
+				ret, (unsigned) portid);
+	}
+
+	/* create a ethtool proxy thread */
+	pthread_attr_t attr;
+	cpu_set_t cpus;
+	pthread_t ethtool_thread;
+	struct nic_info info;
+
+	/* set core affinity to core 1 */
+	CPU_ZERO(&cpus);
+	CPU_SET(2, &cpus);
+	pthread_attr_init(&attr);
+	pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpus);
+	/* Since the register size is more than 4K (1147*4) */
+	pthread_attr_setstacksize(&attr, 4*PAGE_SIZE);
+
+	info.num_of_ports = nb_ports;
+	info.port_mask = l2fwd_enabled_port_mask;
+	if (pthread_create(&ethtool_thread, NULL, &ethtool, &info)) {
+		rte_exit(EXIT_FAILURE,
+			"Fail to create a pthread for ethtool task!!!\n");
+	}
+	memset(&port_statistics, 0, sizeof(port_statistics));
+
+	if (!nb_ports_available) {
+		rte_exit(EXIT_FAILURE,
+		"All available ports are disabled. Please set portmask.\n");
+	}
+
+	check_all_ports_link_status(nb_ports, l2fwd_enabled_port_mask);
+
+	/* launch per-lcore init on every lcore */
+	rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, NULL, CALL_MASTER);
+	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
+		if (rte_eal_wait_lcore(lcore_id) < 0)
+			return -1;
+	}
+
+	return 0;
+}
diff --git a/examples/l2fwd-ethtool/l2fwd-app/netdev_api.h b/examples/l2fwd-ethtool/l2fwd-app/netdev_api.h
new file mode 100644
index 0000000..2e93e9a
--- /dev/null
+++ b/examples/l2fwd-ethtool/l2fwd-app/netdev_api.h
@@ -0,0 +1,770 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _NETDEV_API_H_
+#define _NETDEV_API_H_
+
+#include <linux/ethtool.h>
+#include <string.h>
+#include "shared_fifo.h"
+
+#define MAC_ADDR_SIZE 6
+#define quad_aligned_size(x) ((x & 0x7) ? ((x+7)&0x7) : x)
+
+#define size16(data_type) (uint16_t)(sizeof(data_type))
+
+/* NETDEV_STATUS = 0 if successful */
+#define NETDEV_UNSUPPORTED -1
+#define NETDEV_INVALID -1
+#define NETDEV_STATUS(data_size) (GOOD_RETURN(data_size) \
+				? 0 : NETDEV_INVALID)
+#define UNUSED(x) (void)(x)
+
+#ifdef NETDEV_OP_REQUEST
+static uint16_t
+next_reqid(void) {
+	static uint16_t request_id;
+
+	return request_id++;
+}
+
+/*
+ * send request (with one or two variables) to request-pipe
+ * (invoked by non- DPDK process)
+ */
+static int
+send_request(uint16_t req_id, uint8_t req_type, uint16_t param_size,
+	void *param_data)
+{
+	int fd;
+	uint32_t req[2];
+
+	req[0] = REQ_DWORD_LO(req_id, 0, req_type);
+	req[1] = REQ_DWORD_HI(param_size, 0);
+
+	fd = open(REQ_PIPE, O_WRONLY);
+	write(fd, req, PIPE_CTL_BYTE_COUNT);
+	if (param_size)
+		write(fd, param_data, param_size);
+	close(fd);
+
+	return 0;
+}
+
+/*
+ * send request (with more than two variables) to request-pipe
+ * (invoked by non- DPDK process)
+ */
+static int
+send_request2(uint16_t req_id, uint8_t req_type, uint16_t param1_size,
+	void *param1_data, int param2_size, void *param2_data)
+{
+	int fd;
+	uint32_t req[2];
+
+	req[0] = REQ_DWORD_LO(req_id, 1, req_type);
+	req[1] = REQ_DWORD_HI(param1_size, param2_size);
+
+	fd = open(REQ_PIPE, O_WRONLY);
+	write(fd, req, PIPE_CTL_BYTE_COUNT);
+
+	if (param1_size)
+		write(fd, param1_data, param1_size);
+	if (param2_size)
+		write(fd, param2_data, param2_size);
+	close(fd);
+
+	return 0;
+}
+
+/* read return variables from the reply-pipe (invoked by non- DPDK process) */
+static int
+read_reply(uint16_t expected_id, uint16_t *byte_count, void *reply_data1,
+	void *reply_data2)
+{
+	int fd;
+	uint32_t req[2];
+	uint16_t rx_id, data1_size;
+
+	/* block on read if reply is not available */
+	fd = open(REP_PIPE, O_RDONLY);
+	read(fd, req, PIPE_CTL_BYTE_COUNT);
+
+	*byte_count = REP_DATA1_COUNT(req);
+	rx_id = REP_ID(req);
+
+	if (!GOOD_RETURN(*byte_count)) {
+		close(fd);
+		return -1;
+	}
+	data1_size = BYTE_COUNT((*byte_count));
+	read(fd, reply_data1, data1_size);
+	if (MULTIPLE_DATA(*byte_count)) {
+		assert(reply_data2);
+		read(fd, reply_data2, REP_DATA2_COUNT(req));
+	}
+	close(fd);
+
+	if (expected_id != rx_id)
+		return -1;
+	return 0;
+}
+
+/* definition of netdev op request */
+
+static int
+netdev_ethtool_get_drvinfo(uint8_t port_id, struct ethtool_drvinfo *drvinfo)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, get_drvinfo, 1, &port_id);
+	read_reply(req_id, &data_size, drvinfo, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_ethtool_get_regs_len(uint8_t port_id)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	int length;
+
+	send_request(req_id, get_regs_len, 1, &port_id);
+	read_reply(req_id, &data_size, &length, NULL);
+
+	if (GOOD_RETURN(data_size))
+		return length;
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_ethtool_get_regs(uint8_t port_id, struct ethtool_regs *regs, void *buf)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint8_t param_data[PARAM_SIZE(struct ethtool_regs)];
+
+	param_data[0] = port_id;
+	memcpy(FIRST_PARAM(param_data), regs, sizeof(struct ethtool_regs));
+
+	send_request(req_id, get_regs, PARAM_SIZE(struct ethtool_regs),
+		param_data);
+	read_reply(req_id, &data_size, regs, buf);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_ethtool_get_link(uint8_t port_id)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	int link_status;
+
+	send_request(req_id, get_link, 1, &port_id);
+	read_reply(req_id, &data_size, &link_status, NULL);
+	if (GOOD_RETURN(data_size))
+		return link_status;
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_ethtool_get_eeprom_len(uint8_t port_id)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	int length;
+
+	send_request(req_id, get_eeprom_len, 1, &port_id);
+	read_reply(req_id, &data_size, &length, NULL);
+
+	if (GOOD_RETURN(data_size))
+		return length;
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_ethtool_get_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom,
+	void *words)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint8_t param_data[PARAM_SIZE(struct ethtool_eeprom)];
+
+	param_data[0] = port_id;
+	memcpy(FIRST_PARAM(param_data), eeprom, sizeof(struct ethtool_eeprom));
+
+	send_request(req_id, get_eeprom, PARAM_SIZE(struct ethtool_eeprom),
+		param_data);
+	read_reply(req_id, &data_size, eeprom, words);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_ethtool_set_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom,
+	void *words)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint8_t param_data[PARAM_SIZE(struct ethtool_eeprom)];
+
+	param_data[0] = port_id;
+	memcpy(FIRST_PARAM(param_data), eeprom, sizeof(struct ethtool_eeprom));
+
+	send_request2(req_id, set_eeprom, PARAM_SIZE(struct ethtool_eeprom),
+		param_data, eeprom->len, words);
+	read_reply(req_id, &data_size, eeprom, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_ethtool_get_pauseparam(uint8_t port_id, struct ethtool_pauseparam *param)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, get_pauseparam, 1, &port_id);
+	read_reply(req_id, &data_size, param, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_ethtool_set_pauseparam(uint8_t port_id, struct ethtool_pauseparam *param)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, set_pauseparam, 1, &port_id);
+	read_reply(req_id, &data_size, param, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_open(uint8_t port_id) {
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, dev_open, 1, &port_id);
+	read_reply(req_id, &data_size, NULL, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_stop(uint8_t port_id) {
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, dev_stop, 1, &port_id);
+	read_reply(req_id, &data_size, NULL, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_set_rx_mode(uint8_t port_id)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, set_rx_mode, 1, &port_id);
+	read_reply(req_id, &data_size, NULL, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_get_mac_addr(uint8_t port_id, void *addr)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, get_mac_addr, 1, &port_id);
+	read_reply(req_id, &data_size, addr, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_set_mac_addr(uint8_t port_id, void *addr)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint8_t param_data[FIRST_DATA_OFFSET+MAC_ADDR_SIZE];
+
+	param_data[0] = port_id;
+	memcpy(FIRST_PARAM(param_data), addr, MAC_ADDR_SIZE);
+	send_request(req_id, set_mac_addr,
+		(FIRST_DATA_OFFSET+MAC_ADDR_SIZE), param_data);
+	read_reply(req_id, &data_size, NULL, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_validate_addr(uint8_t port_id, void *addr)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint8_t param_data[FIRST_DATA_OFFSET+MAC_ADDR_SIZE];
+	int valid;
+
+	param_data[0] = port_id;
+	memcpy(FIRST_PARAM(param_data), addr, MAC_ADDR_SIZE);
+	send_request(req_id, validate_addr,
+		(FIRST_DATA_OFFSET+MAC_ADDR_SIZE), param_data);
+	read_reply(req_id, &data_size, &valid, NULL);
+
+	if (GOOD_RETURN(data_size))
+		return valid;
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_change_mtu(uint8_t port_id, int mtu)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint8_t param_data[PARAM_SIZE(int)];
+
+	param_data[0] = port_id;
+	memcpy(FIRST_PARAM(param_data), &mtu, sizeof(int));
+	send_request(req_id, change_mtu, PARAM_SIZE(int), param_data);
+	read_reply(req_id, &data_size, NULL, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_get_stats64(uint8_t port_id, void *stats)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, get_stats64, 1, &port_id);
+	read_reply(req_id, &data_size, stats, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_vlan_rx_add_vid(uint8_t port_id, uint16_t vid)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint8_t param_data[PARAM_SIZE(int)];
+
+	param_data[0] = port_id;
+	memcpy(FIRST_PARAM(param_data), &vid, sizeof(uint16_t));
+	send_request(req_id, vlan_rx_add_vid, FIRST_DATA_OFFSET+sizeof(int),
+		param_data);
+	read_reply(req_id, &data_size, NULL, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+static int
+netdev_net_vlan_rx_kill_vid(uint8_t port_id, uint16_t vid)
+{
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint8_t param_data[PARAM_SIZE(int)];
+
+	param_data[0] = port_id;
+	memcpy(FIRST_PARAM(param_data), &vid, sizeof(uint16_t));
+	send_request(req_id, vlan_rx_kill_vid, FIRST_DATA_OFFSET+sizeof(int),
+		param_data);
+	read_reply(req_id, &data_size, NULL, NULL);
+
+	return NETDEV_STATUS(data_size);
+};
+
+#endif /* NETDEV_OP_REQUEST */
+
+#ifdef NETDEV_OP_REPLY
+/* read request from request-pipe (invoked by rte-api server thread) */
+static int
+read_request(uint16_t *req_id, uint8_t *req_type, uint16_t *param1_size,
+	uint8_t *param1_data, uint16_t *param2_size, void *param2_data)
+{
+	int fd;
+	uint32_t req[2];
+
+	/* block on read if request is not sent ... */
+	fd = open(REQ_PIPE, O_RDONLY);
+	read(fd, req, PIPE_CTL_BYTE_COUNT);
+
+	*req_id			= REQ_ID(req);
+	*req_type		= REQ_TYPE(req);
+	*param1_size	= REQ_PARAM1_SIZE(req);
+
+	if (*param1_size > 0) {
+		read(fd, param1_data, *param1_size);
+		if (REQ_IDTYPE(req)) {
+			*param2_size = REQ_PARAM2_SIZE(req);
+			read(fd, param2_data, *param2_size);
+		} else
+			*param2_size = 0;
+	}
+	close(fd);
+
+	return 0;
+}
+
+/* definition of netdev op service */
+/*
+ * rep[1:0]: request id
+ * rep[3:2]: data byte count; bit[15]: error status bit[14]: multiple return
+ *           variables are requested
+ *
+ * send reply with one return variable to reply-pipe
+ * (invoked by rte-api server thread)
+ */
+static int
+send_reply(uint16_t rx_id, uint16_t byte_count, void *reply_data)
+{
+	int fd;
+	uint32_t req[2];
+
+	req[0] = REP_DWORD_LO(rx_id, byte_count);
+	req[1] = REP_DWORD_HI(0);
+
+	fd = open(REP_PIPE, O_WRONLY);
+	write(fd, req, PIPE_CTL_BYTE_COUNT);
+
+	if (GOOD_RETURN(byte_count) && (byte_count > 0))
+		write(fd, reply_data, byte_count);
+	close(fd);
+
+	return 0;
+}
+
+/*
+ * send reply with two or more variables to reply-pipe
+ * (invoked by rte-api server thread)
+ */
+static int
+send_reply2(uint16_t rx_id, uint16_t byte_count1, void *reply_data1,
+	uint16_t byte_count2, void *reply_data2)
+{
+	int fd;
+	uint32_t req[2];
+
+	req[0] = REP_DWORD_LO(rx_id, REP_MUTILPLE_DATA(byte_count1));
+	req[1] = REP_DWORD_HI(byte_count2);
+
+	fd = open(REP_PIPE, O_WRONLY);
+	write(fd, req, PIPE_CTL_BYTE_COUNT);
+
+	if (GOOD_RETURN(byte_count1)  && (byte_count2 > 0)) {
+		write(fd, reply_data1, byte_count1);
+		write(fd, reply_data2, byte_count2);
+	}
+	close(fd);
+
+	return 0;
+}
+
+/* Functions for netdev service thread */
+static int
+proc_ethtool_get_drvinfo(uint8_t port_id, uint16_t req_id, void *param_data)
+{
+	struct ethtool_drvinfo *drvinfo = param_data;
+	uint16_t data_size;
+
+	if (rte_ethtool_get_drvinfo(port_id, drvinfo))
+		data_size = STATUS_MASK;
+	else
+		data_size = size16(struct ethtool_drvinfo);
+	return send_reply(req_id, data_size, param_data);
+};
+
+static int
+proc_ethtool_get_regs_len(uint8_t port_id, uint16_t req_id)
+{
+	int reg_len;
+	uint16_t data_size;
+
+	reg_len = rte_ethtool_get_regs_len(port_id);
+	if (reg_len == 0)
+		data_size = STATUS_MASK;
+	else
+		data_size = size16(int);
+	return send_reply(req_id, data_size, &reg_len);
+};
+
+static int
+proc_ethtool_get_regs(uint8_t port_id, uint16_t req_id, void *param_data,
+	void *reply_data2)
+{
+	struct ethtool_regs *reg_info = param_data;
+	void *buf = reply_data2;
+	uint16_t data_size;
+
+	if (rte_ethtool_get_regs(port_id, reg_info, buf))
+		data_size = STATUS_MASK;
+	else
+		data_size = sizeof(struct ethtool_regs);
+	return send_reply2(req_id, data_size, reg_info,
+		rte_ethtool_get_regs_len(port_id)*sizeof(int), reply_data2);
+};
+
+static int
+proc_ethtool_get_link(uint8_t port_id, uint16_t req_id)
+{
+	int link_status;
+
+	link_status = rte_ethtool_get_link(port_id);
+	return  send_reply(req_id, (uint16_t)sizeof(int), &link_status);
+};
+
+static int
+proc_ethtool_get_eeprom_len(uint8_t port_id, uint16_t req_id)
+{
+	int eeprom_length;
+	uint16_t data_size;
+
+	eeprom_length = rte_ethtool_get_eeprom_len(port_id);
+	if (eeprom_length == 0)
+		data_size = STATUS_MASK;
+	else
+		data_size = size16(int);
+	return send_reply(req_id, data_size, &eeprom_length);
+};
+
+static int
+proc_ethtool_get_eeprom(uint8_t port_id, uint16_t req_id, void *param_data,
+	void *reply_data2)
+{
+	struct ethtool_eeprom *eeprom_ptr = param_data;
+	uint16_t data_size;
+
+	if (rte_ethtool_get_eeprom(port_id, eeprom_ptr, reply_data2))
+		data_size = STATUS_MASK;
+	else
+		data_size = sizeof(struct ethtool_eeprom);
+	return send_reply2(req_id, data_size, eeprom_ptr,
+		eeprom_ptr->len & ~1, reply_data2);
+};
+
+static int
+proc_ethtool_set_eeprom(uint8_t port_id, uint16_t req_id, void *param_data,
+	void *param2_data)
+{
+	struct ethtool_eeprom *eeprom_ptr = param_data;
+	uint16_t data_size;
+
+	if (rte_ethtool_set_eeprom(port_id, eeprom_ptr, param2_data))
+		data_size = STATUS_MASK;
+	else
+		data_size = sizeof(struct ethtool_eeprom);
+	return send_reply(req_id, data_size, eeprom_ptr);
+};
+
+static int
+proc_ethtool_get_pauseparam(uint8_t port_id, uint16_t req_id, void *reply_data)
+{
+	uint16_t data_size;
+
+	if (rte_ethtool_get_pauseparam(port_id,
+		(struct ethtool_pauseparam *)reply_data))
+		data_size = STATUS_MASK;
+	else
+		data_size = (uint16_t)(sizeof(struct ethtool_pauseparam));
+	return send_reply(req_id, data_size, reply_data);
+};
+
+static int
+proc_ethtool_set_pauseparam(uint8_t port_id, uint16_t req_id, void *set_data)
+{
+	uint16_t data_size;
+
+	if (rte_ethtool_set_pauseparam(port_id,
+		(struct ethtool_pauseparam *)set_data))
+		data_size = STATUS_MASK;
+	else
+		data_size = (uint16_t)(sizeof(struct ethtool_pauseparam));
+	return send_reply(req_id, data_size, set_data);
+};
+
+static int
+proc_net_open(uint8_t port_id, uint16_t req_id)
+{
+	uint16_t data_size;
+
+	if (rte_ethtool_net_open(port_id))
+		data_size = STATUS_MASK;
+	else
+		data_size = 0;
+
+	return send_reply(req_id, data_size, &data_size);
+};
+
+static int
+proc_net_stop(uint8_t port_id, uint16_t req_id)
+{
+	uint16_t data_size;
+
+	rte_ethtool_net_stop(port_id);
+	data_size = 0;
+
+	return send_reply(req_id, data_size, &data_size);
+};
+
+static int
+proc_net_set_rx_mode(uint8_t port_id, uint16_t req_id)
+{
+	uint16_t data_size;
+
+	if (rte_ethtool_net_set_rx_mode(port_id))
+		data_size = STATUS_MASK;
+	else
+		data_size = 0;
+
+	return send_reply(req_id, data_size, &data_size);
+};
+
+static int
+proc_net_get_mac_addr(uint8_t port_id, uint16_t req_id, void *param_data)
+{
+	uint16_t data_size;
+
+	if (rte_ethtool_net_get_mac_addr(port_id, param_data))
+		data_size = STATUS_MASK;
+	else
+		data_size = MAC_ADDR_SIZE;
+
+	return send_reply(req_id, data_size, param_data);
+};
+
+static int
+proc_net_set_mac_addr(uint8_t port_id, uint16_t req_id, void *param_data)
+{
+	uint16_t data_size;
+
+	if (rte_ethtool_net_set_mac_addr(port_id, param_data))
+		data_size = STATUS_MASK;
+	else
+		data_size = 0;
+
+	return send_reply(req_id, data_size, &data_size);
+};
+
+static int
+proc_net_validate_addr(uint8_t port_id, uint16_t req_id, void *param_data)
+{
+	int status;
+
+	status = rte_ethtool_net_validate_addr(port_id, param_data);
+
+	return send_reply(req_id, (uint16_t)sizeof(int), &status);
+};
+
+static int
+proc_net_set_config(uint8_t port_id, uint16_t req_id, void *param_data)
+{
+	uint16_t data_size;
+
+	if (rte_ethtool_net_set_config(port_id, param_data))
+		data_size = STATUS_MASK;
+	else
+		data_size = 0;
+
+	return send_reply(req_id, data_size, &data_size);
+};
+
+static int
+proc_net_change_mtu(uint8_t port_id, uint16_t req_id, void *param_data)
+{
+	uint16_t data_size;
+	int mtu = *(int *)(param_data);
+
+	if (rte_ethtool_net_change_mtu(port_id, mtu))
+		data_size = STATUS_MASK;
+	else
+		data_size = 0;
+
+	return send_reply(req_id, data_size, &data_size);
+};
+
+static int
+proc_net_get_stats64(uint8_t port_id, uint16_t req_id, void *reply_data)
+{
+	uint16_t data_size;
+
+	if (rte_ethtool_net_get_stats64(port_id, reply_data))
+		data_size = STATUS_MASK;
+	else
+		data_size = size16(struct rte_eth_stats);
+
+	return send_reply(req_id, data_size, reply_data);
+};
+
+static int
+proc_net_vlan_rx_add_vid(uint8_t port_id, uint16_t req_id,
+	void *param_data)
+{
+	uint16_t data_size;
+	int *vid_ptr = (int *)param_data;
+
+	if (rte_ethtool_net_vlan_rx_add_vid(port_id, *vid_ptr))
+		data_size = STATUS_MASK;
+	else
+		data_size = 0;
+
+	return send_reply(req_id, data_size, &data_size);
+};
+
+static int
+proc_net_vlan_rx_kill_vid(uint8_t port_id, uint16_t req_id,
+	void *param_data)
+{
+	uint16_t data_size;
+	int *vid_ptr = (int *)param_data;
+
+	if (rte_ethtool_net_vlan_rx_kill_vid(port_id, *vid_ptr))
+		data_size = STATUS_MASK;
+	else
+		data_size = 0;
+
+	return send_reply(req_id, data_size, &data_size);
+};
+
+#endif /* NETDEV_OP_REPLY */
+#endif /* _NETDEV_API_H_ */
diff --git a/examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h b/examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h
new file mode 100644
index 0000000..be528b8
--- /dev/null
+++ b/examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h
@@ -0,0 +1,159 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SHARED_FIFO_H_
+#define _SHARED_FIFO_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#define REQ_PIPE "/tmp/nic_request"
+#define REP_PIPE "/tmp/nic_reply"
+#define PAGE_SIZE (4*1024)
+#define STACK_SIZE (4*PAGE_SIZE)
+#define MAXI_DATA (1024*6)
+#define MAXI_PARA 1024
+#define STATUS_MASK 0x8000
+#define MULTIPLE_DATA_MASK 0x4000
+#define MAXI_REQ_TYPE 16
+#define FIRST_DATA_OFFSET 8
+#define cast_ptr(x, new_type) (new_type)(void *)(x)
+#define to_ptr(new_ptr_type, data, offset) \
+	(new_ptr_type)(&((unsigned char *)(void *)data)[offset])
+#define u8ptr(x) cast_ptr(x, uint8_t *)
+#define to_mac_type(x) cast_ptr(x, struct ether_addr *)
+
+/*
+ * req[1:0]:	request-id
+ * req[2]:		request-id type
+ * req[3]:		request type
+ * req[4:5]:	param1-size
+ * req[7:6]:	param2-size
+ *
+ * rep[1:0]		reply-id
+ * rep[3:2]:	data1-size	// bit[15]: status bit[14]: two return data
+ * rep[7:4]:	data2-size
+ */
+#define PIPE_CTL_BYTE_COUNT (sizeof(uint32_t)*2)
+#define REQ_DWORD_LO(req_id, id_type, req_tye) \
+	(((uint32_t)req_type << 24) | ((uint32_t)id_type << 16) | req_id)
+#define REQ_DWORD_HI(param1_size, param2_size) \
+	(((uint32_t)param2_size << 16) | param1_size)
+
+#define REP_DWORD_LO(rep_id, data_bytes) \
+	(((uint32_t)data_bytes << 16) | (uint32_t)rep_id)
+#define REP_DWORD_HI(data2_bytes) (data2_bytes)
+
+#define REP_MUTILPLE_DATA(data1_size) (data1_size | MULTIPLE_DATA_MASK)
+#define REQ_ID(dword_ptr)		(dword_ptr[0] & 0xFFFF)
+#define REQ_IDTYPE(dword_ptr)	((dword_ptr[0] >> 16) & 0xFF)
+#define REQ_TYPE(dword_ptr)		((dword_ptr[0] >> 24) & 0xFF)
+#define REQ_PARAM1_SIZE(dword_ptr)	(dword_ptr[1] & 0xFFFF)
+#define REQ_PARAM2_SIZE(dword_ptr)	((dword_ptr[1]>>16) & 0xFFFF)
+#define REP_ID(dword_ptr)		(dword_ptr[0] & 0xFFFF)
+#define REP_DATA1_COUNT(dword_ptr)	((dword_ptr[0] >> 16) & 0xFFFF)
+#define REP_DATA2_COUNT(dword_ptr)	(dword_ptr[1])
+
+#define BAD_RETURN(data_size)	(data_size | STATUS_MASK)
+#define GOOD_RETURN(data_size)	((data_size & STATUS_MASK) == 0)
+#define MULTIPLE_DATA(data_size)	(data_size & MULTIPLE_DATA_MASK)
+#define BYTE_COUNT(data_size)	\
+	(data_size & ~(STATUS_MASK|MULTIPLE_DATA_MASK))
+
+#define PARAM_SIZE(type)		\
+	((uint16_t)(FIRST_DATA_OFFSET+sizeof(type)))
+#define FIRST_PARAM(param_data)	(void *)(&(param_data[FIRST_DATA_OFFSET]))
+#define FIRST_PARAM_TYPE(param_data, ptr_type)	\
+	(ptr_type)(FIRST_PARAM(param_data))
+
+void init_req_pipe(void);
+void init_rep_pipe(void);
+
+struct nic_info {
+	uint8_t num_of_ports;
+	uint32_t port_mask;
+	uint32_t vf_port_mask;
+	uint32_t flag;
+} nic_info;
+
+enum req_t {
+	get_drvinfo = 0,
+	get_setting,
+	set_setting,
+	get_regs_len,
+	get_regs,
+	get_link,
+	get_eeprom_len,
+	get_eeprom,
+	set_eeprom,
+	get_coalesce,
+	set_coalesce,
+	get_pauseparam,
+	set_pauseparam,
+	dump_data,
+
+	dev_open,
+	dev_stop,
+	set_rx_mode,
+	get_mac_addr,
+	set_mac_addr,
+	validate_addr,
+	set_config,
+	change_mtu,
+	get_stats64,
+	get_stats,
+	vlan_rx_add_vid,
+	vlan_rx_kill_vid,
+	ipc_begin,	/* request to start ipc, and get nic info ... */
+	ipc_end,	/* request to stop ipc ... */
+	invalid_req,
+};
+
+void
+init_req_pipe(void)
+{
+	mkfifo(REQ_PIPE, 0666);
+}
+
+void
+init_rep_pipe(void)
+{
+	mkfifo(REP_PIPE, 0666);
+}
+
+#endif /* _SHARED_FIFO_H_ */
diff --git a/examples/l2fwd-ethtool/lib/Makefile b/examples/l2fwd-ethtool/lib/Makefile
new file mode 100644
index 0000000..d7ee955
--- /dev/null
+++ b/examples/l2fwd-ethtool/lib/Makefile
@@ -0,0 +1,57 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overwritten by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+# library name
+LIB = librte_ethtool.a
+
+LIBABIVER := 1
+
+# all source are stored in SRC-Y
+SRCS-y := rte_ethtool.c
+
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS)
+
+include $(RTE_SDK)/mk/rte.extlib.mk
diff --git a/examples/l2fwd-ethtool/lib/rte_ethtool.c b/examples/l2fwd-ethtool/lib/rte_ethtool.c
new file mode 100644
index 0000000..2ef78f1
--- /dev/null
+++ b/examples/l2fwd-ethtool/lib/rte_ethtool.c
@@ -0,0 +1,336 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <rte_version.h>
+#include <rte_ethdev.h>
+#include "rte_ethtool.h"
+
+int
+rte_ethtool_get_drvinfo(uint8_t port_id, struct ethtool_drvinfo *drvinfo)
+{
+	struct rte_eth_dev_info dev_info;
+	int n;
+
+	memset(&dev_info, 0, sizeof(dev_info));
+	rte_eth_dev_info_get(port_id, &dev_info);
+
+	snprintf(drvinfo->driver, sizeof(drvinfo->driver), "%s",
+		dev_info.driver_name);
+	snprintf(drvinfo->version, sizeof(drvinfo->version), "%s",
+		rte_version());
+	snprintf(drvinfo->bus_info, sizeof(drvinfo->bus_info),
+		"%04x:%02x:%02x.%x",
+		dev_info.pci_dev->addr.domain, dev_info.pci_dev->addr.bus,
+		dev_info.pci_dev->addr.devid, dev_info.pci_dev->addr.function);
+
+	n = rte_eth_dev_get_reg_length(port_id);
+	if (n > 0)
+		drvinfo->regdump_len = n;
+	else
+		drvinfo->regdump_len = 0;
+
+	n = rte_eth_dev_get_eeprom_length(port_id);
+	if (n > 0)
+		drvinfo->eedump_len = n;
+	else
+		drvinfo->eedump_len = 0;
+
+	drvinfo->n_stats = sizeof(struct rte_eth_stats) / sizeof(uint64_t);
+	drvinfo->testinfo_len = 0;
+
+	return 0;
+}
+
+int
+rte_ethtool_get_regs_len(uint8_t port_id)
+{
+	return rte_eth_dev_get_reg_length(port_id);
+}
+
+int
+rte_ethtool_get_regs(uint8_t port_id, struct ethtool_regs *regs, void *data)
+{
+	struct rte_dev_reg_info reg_info;
+	int status;
+
+	reg_info.data = data;
+	reg_info.length = 0;
+
+	status = rte_eth_dev_get_reg_info(port_id, &reg_info);
+	if (status)
+		return status;
+	regs->version = reg_info.version;
+
+	return 0;
+}
+
+int
+rte_ethtool_get_link(uint8_t port_id)
+{
+	struct rte_eth_link link;
+
+	rte_eth_link_get(port_id, &link);
+	return link.link_status;
+}
+
+int
+rte_ethtool_get_eeprom_len(uint8_t port_id)
+{
+	return rte_eth_dev_get_eeprom_length(port_id);
+}
+
+int
+rte_ethtool_get_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom,
+	void *words)
+{
+	struct rte_dev_eeprom_info eeprom_info;
+	int status;
+
+	eeprom_info.offset = eeprom->offset;
+	eeprom_info.length = eeprom->len;
+	eeprom_info.data = words;
+
+	status = rte_eth_dev_get_eeprom(port_id, &eeprom_info);
+	if (status)
+		return status;
+
+	eeprom->magic = eeprom_info.magic;
+
+	return 0;
+}
+
+int
+rte_ethtool_set_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom,
+	void *words)
+{
+	struct rte_dev_eeprom_info eeprom_info;
+	int status;
+
+	eeprom_info.offset = eeprom->offset;
+	eeprom_info.length = eeprom->len;
+	eeprom_info.data = words;
+
+	status = rte_eth_dev_set_eeprom(port_id, &eeprom_info);
+	if (status)
+		return status;
+
+	eeprom->magic = eeprom_info.magic;
+
+	return 0;
+}
+
+int
+rte_ethtool_get_pauseparam(uint8_t port_id,
+	struct ethtool_pauseparam *pause_param)
+{
+	struct rte_eth_fc_conf fc_conf;
+	int status;
+
+	status = rte_eth_dev_flow_ctrl_get(port_id, &fc_conf);
+	if (status)
+		return status;
+
+	pause_param->tx_pause = 0;
+	pause_param->rx_pause = 0;
+	switch (fc_conf.mode) {
+	case RTE_FC_NONE:
+		/* dummy block to avoid compiler warning */
+		break;
+	case RTE_FC_RX_PAUSE:
+		pause_param->rx_pause = 1;
+		break;
+	case RTE_FC_TX_PAUSE:
+		pause_param->tx_pause = 1;
+		break;
+	case RTE_FC_FULL:
+		pause_param->rx_pause = 1;
+		pause_param->tx_pause = 1;
+	}
+	pause_param->autoneg = (uint32_t)fc_conf.autoneg;
+
+	return 0;
+}
+
+int
+rte_ethtool_set_pauseparam(uint8_t port_id,
+	struct ethtool_pauseparam *pause_param)
+{
+	struct rte_eth_fc_conf fc_conf;
+	int status;
+	/*
+	 * Read device flow control parameter first since
+	 * ethtool set_pauseparam op doesn't have all the information.
+	 * as defined in struct rte_eth_fc_conf.
+	 * This API requires the device to support both
+	 * rte_eth_dev_flow_ctrl_get and rte_eth_dev_flow_ctrl_set, otherwise
+	 * return -ENOTSUP
+	 */
+	status = rte_eth_dev_flow_ctrl_get(port_id, &fc_conf);
+	if (status)
+		return status;
+
+	fc_conf.autoneg = (uint8_t)pause_param->autoneg;
+
+	if (pause_param->tx_pause) {
+		if (pause_param->rx_pause)
+			fc_conf.mode = RTE_FC_FULL;
+		else
+			fc_conf.mode = RTE_FC_TX_PAUSE;
+	} else {
+		if (pause_param->rx_pause)
+			fc_conf.mode = RTE_FC_RX_PAUSE;
+		else
+			fc_conf.mode = RTE_FC_NONE;
+	}
+
+	status = rte_eth_dev_flow_ctrl_set(port_id, &fc_conf);
+	if (status)
+		return status;
+
+	return 0;
+}
+
+int
+rte_ethtool_net_open(uint8_t port_id)
+{
+	rte_eth_dev_stop(port_id);
+
+	return rte_eth_dev_start(port_id);
+}
+
+int
+rte_ethtool_net_stop(uint8_t port_id)
+{
+	rte_eth_dev_stop(port_id);
+
+	return 0;
+}
+
+int
+rte_ethtool_net_get_mac_addr(uint8_t port_id, struct ether_addr *addr)
+{
+	rte_eth_macaddr_get(port_id, addr);
+
+	return 0;
+}
+
+int
+rte_ethtool_net_set_mac_addr(uint8_t port_id, struct ether_addr *addr)
+{
+	return rte_eth_dev_default_mac_addr_set(port_id, addr);
+}
+
+int
+rte_ethtool_net_validate_addr(uint8_t port_id __rte_unused,
+	struct ether_addr *addr)
+{
+	return is_valid_assigned_ether_addr(addr);
+}
+
+int
+rte_ethtool_net_set_config(uint8_t port_id, void *config __rte_unused)
+{
+	struct rte_eth_link link;
+
+	memset(&link, 0, sizeof(link));
+	rte_eth_link_get(port_id, &link);
+	if (link.link_status == 1)
+		return -EINVAL;
+	return 0;
+}
+
+int
+rte_ethtool_net_change_mtu(uint8_t port_id, int mtu)
+{
+	return rte_eth_dev_set_mtu(port_id, (uint16_t)mtu);
+}
+
+int
+rte_ethtool_net_get_stats64(uint8_t port_id, struct rte_eth_stats *stats)
+{
+	return rte_eth_stats_get(port_id, stats);
+}
+
+int
+rte_ethtool_net_vlan_rx_add_vid(uint8_t port_id, uint16_t vid)
+{
+	return rte_eth_dev_vlan_filter(port_id, vid, 1);
+}
+
+int
+rte_ethtool_net_vlan_rx_kill_vid(uint8_t port_id, uint16_t vid)
+{
+	return rte_eth_dev_vlan_filter(port_id, vid, 0);
+}
+
+/*
+ * The set_rx_mode provides driver-specific rx mode setting.
+ * This implementation implements rx mode setting based upon
+ * ixgbe/igb drivers. Further improvement is to provide a
+ * callback op field over struct rte_eth_dev::dev_ops so each
+ * driver can register device-specific implementation
+ */
+int
+rte_ethtool_net_set_rx_mode(uint8_t port_id)
+{
+	uint16_t num_vfs;
+	struct rte_eth_dev_info dev_info;
+	uint16_t vf;
+
+	memset(&dev_info, 0, sizeof(dev_info));
+	rte_eth_dev_info_get(port_id, &dev_info);
+	num_vfs = dev_info.max_vfs;
+
+	/* Setting single cast promiscuous mode */
+	if (rte_eth_promiscuous_get(port_id) == 1)
+		rte_eth_promiscuous_enable(port_id);
+	else if (rte_eth_promiscuous_get(port_id) == 0)
+		rte_eth_promiscuous_disable(port_id);
+
+	/* Setting multi-cast promiscuous mode */
+	if (rte_eth_allmulticast_get(port_id) == 1)
+		rte_eth_allmulticast_enable(port_id);
+	else if (rte_eth_allmulticast_get(port_id) == 0)
+		rte_eth_allmulticast_disable(port_id);
+
+	/* Set VF vf_rx_mode, VF unsupport status is discard */
+	for (vf = 0; vf < num_vfs; vf++)
+		rte_eth_dev_set_vf_rxmode(port_id, vf, 1, 1);
+
+	/* Enable Rx vlan filter, VF unspport status is discard */
+	rte_eth_dev_set_vlan_offload(port_id, ETH_VLAN_FILTER_MASK);
+
+	return 0;
+}
diff --git a/examples/l2fwd-ethtool/lib/rte_ethtool.h b/examples/l2fwd-ethtool/lib/rte_ethtool.h
new file mode 100644
index 0000000..5d48b0d
--- /dev/null
+++ b/examples/l2fwd-ethtool/lib/rte_ethtool.h
@@ -0,0 +1,385 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTE_ETHTOOL_H_
+#define _RTE_ETHTOOL_H_
+
+/*
+ * This new interface is designed to provide a user-space shim layer for
+ * Ethtool and Netdevice op API.
+ *
+ * rte_ethtool_get_driver:          ethtool_ops::get_driverinfo
+ * rte_ethtool_get_link:            ethtool_ops::get_link
+ * rte_ethtool_get_regs_len:        ethtool_ops::get_regs_len
+ * rte_ethtool_get_regs:            ethtool_ops::get_regs
+ * rte_ethtool_get_eeprom_len:      ethtool_ops::get_eeprom_len
+ * rte_ethtool_get_eeprom:          ethtool_ops::get_eeprom
+ * rte_ethtool_set_eeprom:          ethtool_ops::set_eeprom
+ * rte_ethtool_get_pauseparam:      ethtool_ops::get_pauseparam
+ * rte_ethtool_set_pauseparam:      ethtool_ops::set_pauseparam
+ *
+ * rte_ethtool_net_open:            net_device_ops::ndo_open
+ * rte_ethtool_net_stop:            net_device_ops::ndo_stop
+ * rte_ethtool_net_set_mac_addr:    net_device_ops::ndo_set_mac_address
+ * rte_ethtool_net_validate_addr:   net_device_ops::ndo_validate_addr
+ * rte_ethtool_net_set_config:      net_device_ops::ndo_set_config
+ * rte_ethtool_net_change_mtu:      net_device_ops::rte_net_change_mtu
+ * rte_ethtool_net_get_stats64:     net_device_ops::ndo_get_stats64
+ * rte_ethtool_net_vlan_rx_add_vid  net_device_ops::ndo_vlan_rx_add_vid
+ * rte_ethtool_net_vlan_rx_kill_vid net_device_ops::ndo_vlan_rx_kill_vid
+ * rte_ethtool_net_set_rx_mode      net_device_ops::ndo_set_rx_mode
+ *
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <rte_ethdev.h>
+#include <linux/ethtool.h>
+
+/**
+ * Retrieve the Ethernet device driver information according to
+ * attributes described by ethtool data structure, ethtool_drvinfo.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param drvinfo
+ *   A pointer to get driver information
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_drvinfo(uint8_t port_id, struct ethtool_drvinfo *drvinfo);
+
+/**
+ * Retrieve the Ethernet device register length in bytes.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *   - (> 0) # of device registers (in bytes) available for dump
+ *   - (0) no registers available for dump.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_regs_len(uint8_t port_id);
+
+/**
+ * Retrieve the Ethernet device register information according to
+ * attributes described by ethtool data structure, ethtool_regs
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param reg
+ *   A pointer to ethtool_regs that has register information
+ * @param data
+ *   A pointer to a buffer that is used to retrieve device register content
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_regs(uint8_t port_id, struct ethtool_regs *regs,
+			    void *data);
+
+/**
+ * Retrieve the Ethernet device link status
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *   - (1) if link up.
+ *   - (0) if link down.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_link(uint8_t port_id);
+
+/**
+ * Retrieve the Ethernet device EEPROM size
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *	 - (> 0) device EEPROM size in bytes
+ *   - (0) device has NO EEPROM
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_eeprom_len(uint8_t port_id);
+
+/**
+ * Retrieve EEPROM content based upon eeprom range described in ethtool
+ * data structure, ethtool_eeprom
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param eeprom
+ *	 The pointer of ethtool_eeprom that provides eeprom range
+ * @param words
+ *	 A buffer that holds data read from eeprom
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom,
+			      void *words);
+
+/**
+ * Setting EEPROM content based upon eeprom range described in ethtool
+ * data structure, ethtool_eeprom
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param eeprom
+ *	 The pointer of ethtool_eeprom that provides eeprom range
+ * @param words
+ *	 A buffer that holds data to be written into eeprom
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_set_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom,
+			      void *words);
+
+/**
+ * Retrieve the Ethernet device pause frame configuration according to
+ * parameter attributes desribed by ethtool data structure,
+ * ethtool_pauseparam.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param pause_param
+ *	 The pointer of ethtool_coalesce that gets pause frame
+ *	 configuration parameters
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_pauseparam(uint8_t port_id,
+				   struct ethtool_pauseparam *pause_param);
+
+/**
+ * Setting the Ethernet device pause frame configuration according to
+ * parameter attributes desribed by ethtool data structure, ethtool_pauseparam.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param pause_param
+ *	 The pointer of ethtool_coalesce that gets ring configuration parameters
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_set_pauseparam(uint8_t port_id,
+				   struct ethtool_pauseparam *param);
+
+/**
+ * Start the Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_open(uint8_t port_id);
+
+/**
+ * Stop the Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_stop(uint8_t port_id);
+
+/**
+ * Get the Ethernet device MAC address.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param addr
+ *	 MAC address of the Ethernet device.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_get_mac_addr(uint8_t port_id, struct ether_addr *addr);
+
+/**
+ * Setting the Ethernet device MAC address.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param addr
+ *	 The new MAC addr.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_set_mac_addr(uint8_t port_id, struct ether_addr *addr);
+
+/**
+ * Validate if the provided MAC address is valid unicast address
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param addr
+ *	 A pointer to a buffer (6-byte, 48bit) for the target MAC address
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_validate_addr(uint8_t port_id, struct ether_addr *addr);
+
+/**
+ * Setting the Ethernet device configuration.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param config
+ *	 A opintr to a configuration parameter.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_set_config(uint8_t port_id, void *config);
+
+/**
+ * Setting the Ethernet device maximum Tx unit.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param mtu
+ *	 New MTU
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_change_mtu(uint8_t port_id, int mtu);
+
+/**
+ * Retrieve the Ethernet device traffic statistics
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param stats
+ *	 A pointer to struct rte_eth_stats for statistics parameters
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_get_stats64(uint8_t port_id, struct rte_eth_stats *stats);
+
+/**
+ * Update the Ethernet device VLAN filter with new vid
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param vid
+ *	 A new VLAN id
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_vlan_rx_add_vid(uint8_t port_id, uint16_t vid);
+
+/**
+ * Remove VLAN id from Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param vid
+ *	 A new VLAN id
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_vlan_rx_kill_vid(uint8_t port_id, uint16_t vid);
+
+/**
+ * Setting the Ethernet device rx mode.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_set_rx_mode(uint8_t port_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_ETHTOOL_H_ */
diff --git a/examples/l2fwd-ethtool/nic-control/Makefile b/examples/l2fwd-ethtool/nic-control/Makefile
new file mode 100644
index 0000000..17ab4a3
--- /dev/null
+++ b/examples/l2fwd-ethtool/nic-control/Makefile
@@ -0,0 +1,55 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overwritten by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+# binary name
+APP = nic-control
+
+# all source are stored in SRCS-y
+SRCS-y := nic_control.c
+
+CFLAGS += -O3 -I$(SRCDIR)/../l2fwd-app -I$(SRCDIR)/../lib
+CFLAGS += $(WERROR_FLAGS)
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/l2fwd-ethtool/nic-control/nic_control.c b/examples/l2fwd-ethtool/nic-control/nic_control.c
new file mode 100644
index 0000000..32769cb
--- /dev/null
+++ b/examples/l2fwd-ethtool/nic-control/nic_control.c
@@ -0,0 +1,614 @@
+/*-
+*   BSD LICENSE
+*
+*   Copyright(c) 2015 Intel Corporation. All rights reserved.
+*   All rights reserved.
+*
+*   Redistribution and use in source and binary forms, with or without
+*   modification, are permitted provided that the following conditions
+*   are met:
+*
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in
+*       the documentation and/or other materials provided with the
+*       distribution.
+*     * Neither the name of Intel Corporation nor the names of its
+*       contributors may be used to endorse or promote products derived
+*       from this software without specific prior written permission.
+*
+*   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+*   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+*   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+*   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+*   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+*   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+*   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+*   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+*   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+*   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+*   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * This is a non- DPDK application that sends NIC device management request
+ * through named pipe to a DPDK data plan process.
+ *
+ */
+#define USE_NEW_TYPE
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/resource.h>
+
+#include "rte_ethtool.h"
+#define NETDEV_OP_REQUEST 1
+#include "netdev_api.h"
+
+#define ITER_LIMIT	30
+#define CPU_CYCLES	(double)(2400.0*1000000)
+#define TEST_FAIL	-1
+#define TEST_PASS	0
+#define TEST_READ_SIZE	16
+#define STATS_PERIOD	10
+
+#define PACKET_RATE(before_value, after_value, before_ts, after_ts) \
+	((double)(after_value - before_value) * \
+	CPU_CYCLES/(after_ts - before_ts))
+
+#define BYTE2BIT_RATE(before_value, after_value, before_ts, after_ts) \
+	((double)(after_value - before_value) * \
+	CPU_CYCLES*8/(after_ts - before_ts))
+
+#define check(cond) (cond?"match":"miss-match")
+#define MAC_STR_SIZE (3*MAC_ADDR_SIZE+1)
+
+struct time_stamp {
+	uint32_t hi;
+	uint32_t lo;
+};
+
+enum test_type {
+	test_eeprom = 0,
+	test_regs,
+	test_mtu,
+	test_vlan_rx,
+	test_pauseparam,
+	test_counts,
+};
+
+struct api_test {
+	const char *test_name;
+	int mask_bit;
+	int (*test_task)(int port_id);
+};
+
+static inline unsigned long long
+rdtsc(void)
+{
+	unsigned hi, lo;
+
+	__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
+	return ((unsigned long long)lo) | (((unsigned long long)hi) << 32);
+}
+
+static uint32_t port_mask;
+static uint32_t vf_port_mask;
+static uint8_t num_of_ports;
+static char addr_string[MAC_STR_SIZE];
+
+static inline int
+is_port_enabled(uint8_t port_id)
+{
+	return (port_mask & (1 << port_id)) > 0;
+}
+
+static inline int
+is_vf_port(uint8_t port_id)
+{
+	return (vf_port_mask & (1 << port_id)) > 0;
+}
+
+static int
+netdev_ipc_begin(void)
+{
+	uint8_t reply_data[sizeof(double)];
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+	uint32_t reply_data2[2];
+	uint8_t param_data[FIRST_DATA_OFFSET];
+
+	param_data[0] = 0;
+	send_request(req_id, ipc_begin,
+		FIRST_DATA_OFFSET, param_data);
+	read_reply(req_id, &data_size, reply_data, reply_data2);
+	num_of_ports = reply_data[0];
+	port_mask = reply_data2[0];
+	vf_port_mask = reply_data2[1];
+
+	return reply_data[0];
+}
+
+static int
+netdev_ipc_end(void)
+{
+	uint8_t reply_data[sizeof(double)];
+	uint16_t req_id = next_reqid();
+	uint16_t data_size;
+
+	send_request(req_id, ipc_end, 0, NULL);
+	read_reply(req_id, &data_size, reply_data, NULL);
+
+	return NETDEV_STATUS(data_size);
+}
+
+static uint8_t
+get_port(void)
+{
+	uint8_t port_id;
+	/* assume maximum of 32 ports */
+	port_id = rand() & 0x1F;
+	while (!is_port_enabled(port_id))
+		port_id = rand() & 0x1F;
+
+	return port_id;
+}
+
+static inline char*
+mac_addr_str(void *mac_addr_in)
+{
+	unsigned char *mac_addr = mac_addr_in;
+
+	snprintf(addr_string, MAC_STR_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
+		mac_addr[0], mac_addr[1], mac_addr[2],
+		mac_addr[3], mac_addr[4], mac_addr[5]);
+	return addr_string;
+}
+
+static int
+test_eeprom_get(int port_id)
+{
+	int count, i;
+	void *data;
+	uint16_t *word;
+	struct ethtool_eeprom eeprom;
+
+	count = netdev_ethtool_get_eeprom_len(port_id);
+	if (count <= 0) {
+		printf("fail to retrieve eeprom");
+		printf("count from port #%d\n", port_id);
+		return TEST_FAIL;
+	}
+
+	printf("eeprom size is %d bytes\n", count);
+	eeprom.offset = 0;
+	eeprom.len = TEST_READ_SIZE;
+	data = malloc(TEST_READ_SIZE);
+	if (data == NULL) {
+		printf("Fail to allocate memory, bailed out !!!\n");
+		return TEST_FAIL;
+	}
+
+	if (netdev_ethtool_get_eeprom(port_id, &eeprom, data)) {
+		printf("Fail to read eeprom from port #%d\n", port_id);
+		free(data);
+		return TEST_FAIL;
+	}
+
+	word = data;
+	printf("eeprom-magic: %x;", eeprom.magic);
+	printf("eeprom data[0:%d]:\n", TEST_READ_SIZE-1);
+	for (i = 0; i < (int)(eeprom.len >> 1); i++)
+		printf("%4x ", word[i]);
+	printf("\n");
+	free(data);
+	return TEST_PASS;
+}
+
+/*
+ * Testing eeprom get/set by getting eeprom data and write-back
+ * the same data
+ */
+static int
+test_eeprom_t(int port_id)
+{
+	int i, ind;
+	void *data_in, *data_out;
+	struct ethtool_eeprom eeprom;
+
+	eeprom.offset = 0;
+	eeprom.len = TEST_READ_SIZE;
+	data_in = malloc(eeprom.len);
+	if (data_in == NULL) {
+		printf("Fail to allocate memory, bailed out !!!\n");
+		return TEST_FAIL;
+	}
+
+	if (netdev_ethtool_get_eeprom(port_id, &eeprom, data_in)) {
+		printf("failed to read eeprom break from post-run");
+		free(data_in);
+		return TEST_FAIL;
+	}
+
+	if (netdev_ethtool_set_eeprom(port_id, &eeprom, data_in)) {
+		printf("Fail to write read-back data to eeprom!!!\n");
+		free(data_in);
+		return TEST_FAIL;
+	}
+
+	data_out = malloc(eeprom.len);
+	if (data_out == NULL) {
+		printf("Fail to allocate memory, bailed out !!!\n");
+		free(data_in);
+		return TEST_FAIL;
+	}
+	/* read-back for comparison */
+	if (netdev_ethtool_get_eeprom(port_id, &eeprom, data_out)) {
+		printf("failed to read eeprom break from post-run");
+		free(data_in);
+		free(data_out);
+		return TEST_FAIL;
+	}
+
+	for (i = eeprom.offset; i < (int)(eeprom.offset+eeprom.len); i++) {
+		unsigned char *in = (unsigned char *)data_in;
+		unsigned char *out = (unsigned char *)data_out;
+
+		ind = i - eeprom.offset;
+		if (in[ind] != out[ind])
+			printf("%d write-data:%x read-back-data:%x\n",
+				ind, in[ind], out[ind]);
+	}
+	free(data_in);
+	free(data_out);
+	return TEST_PASS;
+}
+
+static int
+test_regs_t(int port_id)
+{
+	int count, i;
+	void *data;
+	uint32_t *regs;
+	struct ethtool_regs reg_info;
+
+	count = netdev_ethtool_get_regs_len(port_id);
+	if (count <= 0)
+		printf("There are no registers available from port #%d",
+		port_id);
+	else
+		printf("Target has %d registers for dump", count);
+
+	data = malloc(count * sizeof(int));
+	if (data == NULL) {
+		printf("Fail to allocate memory, bailed out !!!\n");
+		return TEST_FAIL;
+	}
+	memset(&reg_info, 0xFF, sizeof(struct ethtool_regs));
+
+	if (netdev_ethtool_get_regs(port_id, &reg_info, data)) {
+		printf("Fail to read register\n");
+		free(data);
+		return TEST_FAIL;
+	}
+
+	regs = data;
+	printf("version: %x;", reg_info.version);
+	printf("register[0:%d]:\n", TEST_READ_SIZE-1);
+	for (i = 0; i < TEST_READ_SIZE; i++)
+		printf("%x ", regs[i]);
+	printf("\n");
+	free(data);
+	return TEST_PASS;
+}
+
+static int
+test_mtu_t(int port_id)
+{
+	const int mtu = 1024;
+
+	if (netdev_net_change_mtu(port_id, mtu)) {
+		printf("failed to set mtu to %d\n", mtu);
+		return TEST_FAIL;
+	}
+	return TEST_PASS;
+}
+
+static int
+test_vlan_rx_t(int port_id)
+{
+	/* add/remove vlan to vid */
+	netdev_net_vlan_rx_add_vid(port_id, 0);
+	if (netdev_net_vlan_rx_add_vid(port_id, 0)) {
+		if (netdev_net_vlan_rx_kill_vid(port_id, 0)) {
+			printf("netdev_net_vlan_rx_kill_vid() fails\n");
+			return TEST_FAIL;
+		}
+		return TEST_PASS;
+	}
+	printf("netdev_net_vlan_rx_add_vid() fails\n");
+	return TEST_FAIL;
+}
+
+static int
+test_pauseparam_t(int port_id)
+{
+	struct ethtool_pauseparam pause_param_in;
+	struct ethtool_pauseparam pause_param_out;
+
+	if (netdev_ethtool_get_pauseparam(port_id, &pause_param_in)) {
+		printf("netdev_ethtool_get_pauseparam() fails\n");
+		return TEST_FAIL;
+	}
+	printf("pause setup: autoneg: %d ",
+		pause_param_in.autoneg);
+	printf("tx_pause: %d ",
+		pause_param_in.tx_pause);
+	printf("rx_pause: %d\n",
+		pause_param_in.rx_pause);
+	if (netdev_ethtool_set_pauseparam(port_id, &pause_param_in)) {
+		printf("netdev_ethtool_set_pauseparam() fails\n");
+		return TEST_FAIL;
+	}
+	/* read-back pause frame setting for comparison */
+	if (netdev_ethtool_get_pauseparam(port_id, &pause_param_out)) {
+		printf("netdev_ethtool_get_pauseparam() fails\n");
+		return TEST_FAIL;
+	}
+	printf("pause frame checking auto:%s tx_pause:%s rx_pause:%s\n",
+		check(pause_param_in.autoneg == pause_param_out.autoneg),
+		check(pause_param_in.tx_pause == pause_param_out.tx_pause),
+		check(pause_param_in.rx_pause == pause_param_out.tx_pause));
+
+	return TEST_PASS;
+}
+
+static int
+test_get_drvinfo(int port_id)
+{
+	struct ethtool_drvinfo drvinfo;
+
+	if (netdev_ethtool_get_drvinfo(port_id, &drvinfo)) {
+		printf("fail to get drvinfo ...\n");
+		return TEST_FAIL;
+	}
+	printf("driver: %s version: %s fw_version: %s bus_info=%s\n",
+		drvinfo.driver, drvinfo.version,
+		drvinfo.fw_version, drvinfo.bus_info);
+	printf("reg-size(bytes)=%d eeprom-size=%d\n",
+		drvinfo.regdump_len,
+		drvinfo.eedump_len);
+	return TEST_PASS;
+}
+
+static int
+test_mac_addr(uint8_t port_id)
+{
+	unsigned char mac_addr[MAC_ADDR_SIZE];
+	unsigned char mac_addr_base[MAC_ADDR_SIZE] = {0x52, 0x54, 0, 0, 0, 1};
+	struct ether_addr *mac_addr_t;
+	struct ethter_add *mac_addr2;
+	int result;
+
+	mac_addr_t = to_mac_type(mac_addr);
+	result = netdev_net_get_mac_addr(port_id, mac_addr_t);
+	if (netdev_net_get_mac_addr(port_id, mac_addr_t)) {
+		printf("Fail to get mac addr from port #%d!!!\n", port_id);
+		return TEST_FAIL;
+	}
+	printf("Port #%d, device mac addr is %s\n", port_id,
+		mac_addr_str(mac_addr));
+
+	result = netdev_net_validate_addr(port_id, mac_addr_t);
+	if (!result) {
+		printf("Default mac addr, %s, is not valid\n",
+			mac_addr_str(mac_addr));
+		strncpy((char *)mac_addr, (char *)mac_addr_base, MAC_ADDR_SIZE);
+		mac_addr[MAC_ADDR_SIZE-1] += port_id;
+		printf("New mac address:%s is used.\n", mac_addr_str(mac_addr));
+
+		result = netdev_net_set_mac_addr(port_id, mac_addr_t);
+		if (result == TEST_FAIL)
+			return TEST_FAIL;
+
+		result = netdev_net_get_mac_addr(port_id, &mac_addr2);
+		if (strncmp(cast_ptr(mac_addr, char*),
+			    cast_ptr(mac_addr2, char*),
+			    MAC_ADDR_SIZE))
+			printf("Expected mac_addr %s return addr %s\n",
+				mac_addr_str(mac_addr),
+				mac_addr_str(cast_ptr(mac_addr2, unsigned char*)
+				));
+	}
+	return TEST_PASS;
+}
+
+struct api_test test_table[test_counts] = {
+	{"-test_eeprom", test_eeprom, test_eeprom_t},
+	{"-test_regs", test_regs, test_regs_t},
+	{"-test_mtu", test_mtu, test_mtu_t},
+	{"-test_vlan_rx", test_vlan_rx, test_vlan_rx_t},
+	{"-test_pauseparam", test_pauseparam, test_pauseparam_t},
+};
+
+static inline unsigned int
+get_test_mask(char *name)
+{
+	int ind = 0;
+	struct api_test *test = &test_table[ind];
+
+	while (ind++ < test_counts) {
+		if (!strncmp(name, test->test_name, strlen(name)))
+			return (1 << test->mask_bit);
+		test++;
+	}
+	return 0;
+}
+
+static unsigned int
+parse_args(int argc, char **argv)
+{
+	int i;
+	unsigned int test_mask = 0;
+
+
+	if (argc <= 1)
+		return 0;
+	for (i = 1; i < argc; i++)
+		test_mask |= get_test_mask(argv[i]);
+	return test_mask;
+}
+
+static inline void
+test_update(int result, int *fails, int *passes)
+{
+	if (result == TEST_PASS)
+		(*passes)++;
+	else
+		(*fails)++;
+}
+
+static void
+run_test(uint8_t port_id, unsigned int test_mask, int *fails, int *passes)
+{
+	int i;
+	int result;
+
+	for (i = 0; i < test_counts; i++) {
+		if (test_mask & (1<<i)) {
+			result = (*test_table[i].test_task)(port_id);
+			test_update(result, fails, passes);
+		}
+	}
+}
+
+static void
+wait_for_linkdown(uint8_t port_id)
+{
+	int link_up;
+
+	link_up = netdev_ethtool_get_link(port_id);
+	while (link_up) {
+		sleep(10);
+		link_up = netdev_ethtool_get_link(port_id);
+	}
+}
+
+int
+main(int argc, char **argv)
+{
+	uint8_t port_id;
+	unsigned int test_mask;
+	int passes = 0;
+	int fails = 0;
+	int iter_count = 0;
+	int link_up;
+	int result;
+
+
+	/* get command parameter */
+	test_mask = parse_args(argc, argv);
+
+	/* initialize request pipe */
+	init_req_pipe();
+
+	printf("issue ipc begin\n");
+	/* send a request to start the NIC device */
+	num_of_ports = netdev_ipc_begin();
+	while (num_of_ports == 0)
+		num_of_ports = netdev_ipc_begin() & 0xFF;
+
+	for (port_id = 0; port_id < num_of_ports; port_id++) {
+		link_up = netdev_ethtool_get_link(port_id);
+		printf("port #%d is %s\n", port_id, link_up?"up":"down");
+		if (!link_up) {
+			if (netdev_net_open(port_id) == 0)
+				netdev_net_set_rx_mode(port_id);
+			else
+				printf("failed to start port #%d\n", port_id);
+		}
+	}
+
+	/* Testing ethtool register/eeprom get */
+	for (port_id = 0; port_id < num_of_ports; port_id++) {
+		if (!is_port_enabled(port_id))
+			continue;
+
+		result = test_mac_addr(port_id);
+		test_update(result, &fails, &passes);
+
+		result = test_get_drvinfo(port_id);
+		test_update(result, &fails, &passes);
+
+		result = test_regs_t(port_id);
+		test_update(result, &fails, &passes);
+
+		/* Only testing eeprom access over a PF */
+		if (!is_vf_port(port_id)) {
+			result = test_eeprom_get(port_id);
+			test_update(result, &fails, &passes);
+		}
+	}
+
+	printf("start nic statistics collection ...\n");
+	port_id = get_port();
+	while (iter_count++ < ITER_LIMIT) {
+		uint64_t last_ts, ts;
+		struct rte_eth_stats last_stats, stats;
+
+		if (netdev_net_get_stats64(port_id, &last_stats)) {
+			printf("Fail to query statistics from port %d\n",
+				port_id);
+			break;
+		}
+		last_ts = rdtsc();
+
+		sleep(STATS_PERIOD);
+
+		if (netdev_net_get_stats64(port_id, &stats)) {
+			printf("Fail to query statistics from port %d\n",
+				port_id);
+			break;
+		}
+		ts = rdtsc();
+
+		printf("rx packet rate = %lf, tx packet rate = %lf\n",
+			PACKET_RATE(last_stats.ipackets, stats.ipackets,
+			last_ts, ts),
+			PACKET_RATE(last_stats.opackets, stats.opackets,
+			last_ts, ts));
+
+
+		printf("rx bit rate = %lf, tx bit rate = %lf\n",
+			BYTE2BIT_RATE(last_stats.ibytes, stats.ibytes,
+			last_ts, ts),
+			BYTE2BIT_RATE(last_stats.obytes, stats.obytes,
+			last_ts, ts));
+
+		sleep(STATS_PERIOD);
+	}
+
+	/* Stop link, testing APIs specified in command arguments */
+	if (test_mask) {
+		for (port_id = 0; port_id < num_of_ports; port_id++) {
+			link_up = netdev_ethtool_get_link(port_id);
+			if (link_up) {
+				netdev_net_stop(port_id);
+				wait_for_linkdown(port_id);
+			}
+		}
+
+		for (port_id = 0; port_id < num_of_ports; port_id++) {
+			link_up = netdev_ethtool_get_link(port_id);
+			if (!is_vf_port(port_id) && !link_up)
+				run_test(port_id, test_mask, &fails, &passes);
+		}
+	}
+
+	while (netdev_ipc_end() < 0)
+		;
+
+	printf("Pass count: %d Fail count: %d\n", passes, fails);
+	return 0;
+}
-- 
2.1.4

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [dpdk-dev] [PATCH v2 1/2] Remove ABI requierment for external library builds.
  2015-07-23 15:00     ` [dpdk-dev] [PATCH v2 1/2] Remove ABI requierment for external library builds Liang-Min Larry Wang
@ 2015-10-21 16:31       ` Thomas Monjalon
  0 siblings, 0 replies; 10+ messages in thread
From: Thomas Monjalon @ 2015-10-21 16:31 UTC (permalink / raw)
  To: Liang-Min Larry Wang; +Cc: dev

2015-07-23 11:00, Liang-Min Larry Wang:
> From: "Andrew G. Harvey" <agh@cisco.com>
> --- a/mk/rte.extlib.mk
> +++ b/mk/rte.extlib.mk
> @@ -31,6 +31,8 @@
>  
>  MAKEFLAGS += --no-print-directory
>  
> +export EXTLIB_BUILD := 1

Is export really needed?

Except that, this patch looks OK.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool
  2015-07-23 15:00     ` [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
@ 2015-10-21 16:36       ` Thomas Monjalon
  2015-10-21 16:46         ` Wang, Liang-min
  2015-11-17 22:33         ` Wang, Liang-min
  0 siblings, 2 replies; 10+ messages in thread
From: Thomas Monjalon @ 2015-10-21 16:36 UTC (permalink / raw)
  To: Liang-Min Larry Wang; +Cc: dev

2015-07-23 11:00, Liang-Min Larry Wang:
>  examples/Makefile                                |    1 +
>  examples/l2fwd-ethtool/Makefile                  |   48 +
>  examples/l2fwd-ethtool/l2fwd-app/Makefile        |   58 ++
>  examples/l2fwd-ethtool/l2fwd-app/main.c          | 1025 ++++++++++++++++++++++
>  examples/l2fwd-ethtool/l2fwd-app/netdev_api.h    |  770 ++++++++++++++++
>  examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h   |  159 ++++
>  examples/l2fwd-ethtool/lib/Makefile              |   57 ++
>  examples/l2fwd-ethtool/lib/rte_ethtool.c         |  336 +++++++
>  examples/l2fwd-ethtool/lib/rte_ethtool.h         |  385 ++++++++
>  examples/l2fwd-ethtool/nic-control/Makefile      |   55 ++
>  examples/l2fwd-ethtool/nic-control/nic_control.c |  614 +++++++++++++
>  11 files changed, 3508 insertions(+)

This patch is huge.
Please split a bit.

> --- a/examples/Makefile
> +++ b/examples/Makefile
> @@ -53,6 +53,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_KNI) += kni
>  DIRS-y += l2fwd
>  DIRS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += l2fwd-ivshmem
>  DIRS-$(CONFIG_RTE_LIBRTE_JOBSTATS) += l2fwd-jobstats
> +DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += l2fwd-ethtool
>  DIRS-y += l3fwd

Please keep the alphabetical order.

I do not plan to review it more.
If nobody complains, it means it's accepted.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool
  2015-10-21 16:36       ` Thomas Monjalon
@ 2015-10-21 16:46         ` Wang, Liang-min
  2015-11-17 22:33         ` Wang, Liang-min
  1 sibling, 0 replies; 10+ messages in thread
From: Wang, Liang-min @ 2015-10-21 16:46 UTC (permalink / raw)
  To: Thomas Monjalon; +Cc: dev

Thomas,
	Let's put this patch on defer list because there are related work might take a different approach. Let's only review the make file change (PATCH 1/2). I believe "export" is needed since the variable is shared by all the build but it might be already included due to the mk file inclusion. Since Andy is on vacation, I am not sure if he could make a comment on that.

Larry

> -----Original Message-----
> From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com]
> Sent: Wednesday, October 21, 2015 12:36 PM
> To: Wang, Liang-min
> Cc: dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-
> ethtool
> 
> 2015-07-23 11:00, Liang-Min Larry Wang:
> >  examples/Makefile                                |    1 +
> >  examples/l2fwd-ethtool/Makefile                  |   48 +
> >  examples/l2fwd-ethtool/l2fwd-app/Makefile        |   58 ++
> >  examples/l2fwd-ethtool/l2fwd-app/main.c          | 1025
> ++++++++++++++++++++++
> >  examples/l2fwd-ethtool/l2fwd-app/netdev_api.h    |  770
> ++++++++++++++++
> >  examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h   |  159 ++++
> >  examples/l2fwd-ethtool/lib/Makefile              |   57 ++
> >  examples/l2fwd-ethtool/lib/rte_ethtool.c         |  336 +++++++
> >  examples/l2fwd-ethtool/lib/rte_ethtool.h         |  385 ++++++++
> >  examples/l2fwd-ethtool/nic-control/Makefile      |   55 ++
> >  examples/l2fwd-ethtool/nic-control/nic_control.c |  614 +++++++++++++
> >  11 files changed, 3508 insertions(+)
> 
> This patch is huge.
> Please split a bit.
> 
> > --- a/examples/Makefile
> > +++ b/examples/Makefile
> > @@ -53,6 +53,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_KNI) += kni
> >  DIRS-y += l2fwd
> >  DIRS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += l2fwd-ivshmem
> >  DIRS-$(CONFIG_RTE_LIBRTE_JOBSTATS) += l2fwd-jobstats
> > +DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += l2fwd-ethtool
> >  DIRS-y += l3fwd
> 
> Please keep the alphabetical order.
> 
> I do not plan to review it more.
> If nobody complains, it means it's accepted.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool
  2015-10-21 16:36       ` Thomas Monjalon
  2015-10-21 16:46         ` Wang, Liang-min
@ 2015-11-17 22:33         ` Wang, Liang-min
  2015-11-18 17:56           ` Thomas Monjalon
  1 sibling, 1 reply; 10+ messages in thread
From: Wang, Liang-min @ 2015-11-17 22:33 UTC (permalink / raw)
  To: Thomas Monjalon; +Cc: dev

Thomas,
	Could you explain why this patch is put on RFC?

Thanks,
Larry

> -----Original Message-----
> From: Wang, Liang-min
> Sent: Wednesday, October 21, 2015 12:47 PM
> To: 'Thomas Monjalon'
> Cc: dev@dpdk.org; Andrew Harvey (agh) (agh@cisco.com)
> Subject: RE: [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool
> 
> Thomas,
> 	Let's put this patch on defer list because there are related work might
> take a different approach. Let's only review the make file change (PATCH 1/2).
> I believe "export" is needed since the variable is shared by all the build but it
> might be already included due to the mk file inclusion. Since Andy is on
> vacation, I am not sure if he could make a comment on that.
> 
> Larry
> 
> > -----Original Message-----
> > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com]
> > Sent: Wednesday, October 21, 2015 12:36 PM
> > To: Wang, Liang-min
> > Cc: dev@dpdk.org
> > Subject: Re: [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-
> > ethtool
> >
> > 2015-07-23 11:00, Liang-Min Larry Wang:
> > >  examples/Makefile                                |    1 +
> > >  examples/l2fwd-ethtool/Makefile                  |   48 +
> > >  examples/l2fwd-ethtool/l2fwd-app/Makefile        |   58 ++
> > >  examples/l2fwd-ethtool/l2fwd-app/main.c          | 1025
> > ++++++++++++++++++++++
> > >  examples/l2fwd-ethtool/l2fwd-app/netdev_api.h    |  770
> > ++++++++++++++++
> > >  examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h   |  159 ++++
> > >  examples/l2fwd-ethtool/lib/Makefile              |   57 ++
> > >  examples/l2fwd-ethtool/lib/rte_ethtool.c         |  336 +++++++
> > >  examples/l2fwd-ethtool/lib/rte_ethtool.h         |  385 ++++++++
> > >  examples/l2fwd-ethtool/nic-control/Makefile      |   55 ++
> > >  examples/l2fwd-ethtool/nic-control/nic_control.c |  614 +++++++++++++
> > >  11 files changed, 3508 insertions(+)
> >
> > This patch is huge.
> > Please split a bit.
> >
> > > --- a/examples/Makefile
> > > +++ b/examples/Makefile
> > > @@ -53,6 +53,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_KNI) += kni
> > >  DIRS-y += l2fwd
> > >  DIRS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += l2fwd-ivshmem
> > >  DIRS-$(CONFIG_RTE_LIBRTE_JOBSTATS) += l2fwd-jobstats
> > > +DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += l2fwd-ethtool
> > >  DIRS-y += l3fwd
> >
> > Please keep the alphabetical order.
> >
> > I do not plan to review it more.
> > If nobody complains, it means it's accepted.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool
  2015-11-17 22:33         ` Wang, Liang-min
@ 2015-11-18 17:56           ` Thomas Monjalon
  0 siblings, 0 replies; 10+ messages in thread
From: Thomas Monjalon @ 2015-11-18 17:56 UTC (permalink / raw)
  To: Wang, Liang-min; +Cc: dev

2015-11-17 22:33, Wang, Liang-min:
> Thomas,
> 	Could you explain why this patch is put on RFC?

Hi,
It is not RFC but Changes Requested.
It means a new version is expected to adress comments.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2015-11-18 17:58 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-07-20 14:12 [dpdk-dev] [PATCH] User-space Ethool example Liang-Min Larry Wang
2015-07-20 14:12 ` [dpdk-dev] [PATCH] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
2015-07-23 15:00   ` [dpdk-dev] [PATCH v2 0/2] Example: l2fwd-ethtool Liang-Min Larry Wang
2015-07-23 15:00     ` [dpdk-dev] [PATCH v2 1/2] Remove ABI requierment for external library builds Liang-Min Larry Wang
2015-10-21 16:31       ` Thomas Monjalon
2015-07-23 15:00     ` [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
2015-10-21 16:36       ` Thomas Monjalon
2015-10-21 16:46         ` Wang, Liang-min
2015-11-17 22:33         ` Wang, Liang-min
2015-11-18 17:56           ` Thomas Monjalon

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).