diff mbox

[dpdk-dev,v9,4/5] examples: new example: l2fwd-ethtool

Message ID 1435367948-20240-5-git-send-email-liang-min.wang@intel.com (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Liang-Min Larry Wang June 27, 2015, 1:19 a.m. UTC
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/l2fwd-ethtool/Makefile                  |   55 ++
 examples/l2fwd-ethtool/l2fwd-app/Makefile        |   58 ++
 examples/l2fwd-ethtool/l2fwd-app/main.c          | 1066 ++++++++++++++++++++++
 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h    |  769 ++++++++++++++++
 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h   |  158 ++++
 examples/l2fwd-ethtool/lib/Makefile              |   55 ++
 examples/l2fwd-ethtool/lib/rte_ethtool.c         |  308 +++++++
 examples/l2fwd-ethtool/lib/rte_ethtool.h         |  384 ++++++++
 examples/l2fwd-ethtool/nic-control/Makefile      |   55 ++
 examples/l2fwd-ethtool/nic-control/nic_control.c |  471 ++++++++++
 10 files changed, 3379 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 mbox

Patch

diff --git a/examples/l2fwd-ethtool/Makefile b/examples/l2fwd-ethtool/Makefile
new file mode 100644
index 0000000..a753c33
--- /dev/null
+++ b/examples/l2fwd-ethtool/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 overriden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+unexport RTE_SRCDIR RTE_OUTPUT RTE_EXTMK
+
+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
+
+.PHONY: all clean $(DIRS-y)
+
+all: $(DIRS-y)
+clean: $(DIRS-y)
+
+$(DIRS-y):
+	$(MAKE) -C $@ $(MAKECMDGOALS) O=$(RTE_OUTPUT)
diff --git a/examples/l2fwd-ethtool/l2fwd-app/Makefile b/examples/l2fwd-ethtool/l2fwd-app/Makefile
new file mode 100644
index 0000000..3d31cb1
--- /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 overriden 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 += -lrte_ethtool
+LDFLAGS += -L$(SRCDIR)/../lib/build
+
+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..19dd58c
--- /dev/null
+++ b/examples/l2fwd-ethtool/l2fwd-app/main.c
@@ -0,0 +1,1066 @@ 
+/*-
+ *   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 to_mac_type(x) (struct ether_addr *)(void *)(x)
+#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, void *mac_ptr)
+{
+	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];
+	uint8_t *new_mac_addr = mac_ptr;
+	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);
+		/* Using rte_ethtool_net_set_rx_mode instead of */
+		/* rte_eth_promiscuous_enable to test */
+		/* rte_ethtool_net_set_rx_mode */
+		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));
+
+		if (is_vf_port(info->vf_port_mask, port_id)) {
+			/* use new mac addr if the default addr is not valid */
+			if (!is_valid_assigned_ether_addr(to_mac_type(mac_addr))
+				) {
+				if (rte_ethtool_net_set_mac_addr(port_id,
+					(void *)new_mac_addr) == 0) {
+					printf(", and re-assigned to ");
+					printf("%s\n",
+					mac_addr_str(new_mac_addr));
+					new_mac_addr[MAC_ADDR_SIZE-1]++;
+				} else {
+					printf("\n");
+				}
+			}
+		} else {
+			printf("\n");
+		}
+	}
+
+	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, first_param);
+
+	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:
+			{
+				struct ethtool_pauseparam *pause_param =
+					(void *)reply1;
+
+				status = proc_ethtool_get_pauseparam(port_id,
+					req_id, pause_param);
+
+				if (status != 0) {
+					printf("get_pauseparam return");
+					printf(" status %d\n", status);
+				}
+			}
+			break;
+
+		case set_pauseparam:
+			{
+				struct ethtool_pauseparam *pause_param =
+					(void *)reply1;
+
+				status = proc_ethtool_set_pauseparam(port_id,
+					req_id, pause_param);
+
+				if (status != 0) {
+					printf("set_pauseparam return");
+					printf(" status %d\n", status);
+				}
+			}
+			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..0964bea
--- /dev/null
+++ b/examples/l2fwd-ethtool/l2fwd-app/netdev_api.h
@@ -0,0 +1,769 @@ 
+/*-
+ *   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 leng;
+
+	send_request(req_id, get_regs_len, 1, &port_id);
+	read_reply(req_id, &data_size, &leng, NULL);
+
+	if (GOOD_RETURN(data_size))
+		return leng;
+	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 leng;
+
+	send_request(req_id, get_eeprom_len, 1, &port_id);
+	read_reply(req_id, &data_size, &leng, NULL);
+
+	if (GOOD_RETURN(data_size))
+		return leng;
+	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, words, NULL);
+
+	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, NULL, 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_open, 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 = rte_ethtool_get_regs_len(port_id)*sizeof(int);
+	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_leng;
+	uint16_t data_size;
+
+	eeprom_leng = rte_ethtool_get_eeprom_len(port_id);
+	if (eeprom_leng == 0)
+		data_size = STATUS_MASK;
+	else
+		data_size = size16(int);
+	return send_reply(req_id, data_size, &eeprom_leng);
+};
+
+static int
+proc_ethtool_get_eeprom(uint8_t port_id, uint16_t req_id, void *param_data,
+	void *reply_data)
+{
+	struct ethtool_eeprom *eeprom_ptr = param_data;
+	uint16_t data_size;
+
+	if (rte_ethtool_get_eeprom(port_id, eeprom_ptr, reply_data))
+		data_size = STATUS_MASK;
+	else
+		data_size = (uint16_t)(eeprom_ptr->len & ~1);
+	return send_reply(req_id, data_size, reply_data);
+};
+
+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 = 0;
+	return send_reply(req_id, data_size, &data_size);
+};
+
+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..82dd962
--- /dev/null
+++ b/examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h
@@ -0,0 +1,158 @@ 
+/*-
+ *   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 to_ptr(new_ptr_type, data, offset) \
+	(new_ptr_type)(&((unsigned char *)(void *)data)[offset])
+#define u8ptr(x) (uint8_t *)((void *)x)
+
+
+/*
+ * 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..b3b8c83
--- /dev/null
+++ b/examples/l2fwd-ethtool/lib/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 overriden 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
+
+# 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..744cb0c
--- /dev/null
+++ b/examples/l2fwd-ethtool/lib/rte_ethtool.c
@@ -0,0 +1,308 @@ 
+/*-
+ *   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_reg_length(port_id);
+	if (n > 0)
+		drvinfo->regdump_len = n;
+	else
+		drvinfo->regdump_len = 0;
+
+	n = rte_eth_dev_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_reg_length(port_id);
+}
+
+int
+rte_ethtool_get_regs(uint8_t port_id, struct ethtool_regs *regs, void *buf)
+{
+	struct rte_dev_reg_info reg_info;
+	int status;
+
+	reg_info.buf = buf;
+	reg_info.leng = 0;
+
+	status = rte_eth_dev_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_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.leng = eeprom->len;
+	eeprom_info.buf = 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.leng = eeprom->len;
+	eeprom_info.buf = 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);
+}
+
+int
+rte_ethtool_net_set_rx_mode(uint8_t port_id __rte_unused)
+{
+	/*
+	 * The set_rx_mode op is part of pmd driver start operation, and
+	 * the ethdev api maintains software configuration parameters and under-
+	 * line hardware states consistent, so no operation is needed for
+	 * rte_ethtool_net_set_rx_mode().
+	 */
+	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..dc234be
--- /dev/null
+++ b/examples/l2fwd-ethtool/lib/rte_ethtool.h
@@ -0,0 +1,384 @@ 
+/*-
+ *   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 buf
+ *   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 *buf);
+
+/**
+ * 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..d49ca98
--- /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 overriden 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..f99af58
--- /dev/null
+++ b/examples/l2fwd-ethtool/nic-control/nic_control.c
@@ -0,0 +1,471 @@ 
+/*-
+*   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 PACKET_THD	100000000
+#define ITER_LIMIT	30
+#define STOP_TIME	10 /* in seconds */
+#define CPU_CYCLES	(double)(2400.0*1000000)
+
+#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 PACKET2BIT_RATE(before_value, after_value, before_ts, after_ts) \
+	((double)(after_value - before_value) * \
+	CPU_CYCLES*64*8/(after_ts - before_ts))
+
+#define to_mac_type(x) (struct ether_addr *)(void *)(x)
+
+struct __time_stamp {
+	uint32_t hi;
+	uint32_t lo;
+} time_stamp;
+
+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 int keep_traffic = 1;
+
+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(unsigned char *mac_addr)
+{
+	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+MAC_ADDR_SIZE];
+
+	param_data[0] = 0;
+	memcpy(FIRST_PARAM(param_data), mac_addr, MAC_ADDR_SIZE);
+	send_request(req_id, ipc_begin,
+		(FIRST_DATA_OFFSET+MAC_ADDR_SIZE), 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 void
+set_stacksize(void)
+{
+	struct rlimit rl;
+	int result;
+
+	result = getrlimit(RLIMIT_STACK, &rl);
+	if (result == 0) {
+		if (rl.rlim_cur < (const rlim_t)STACK_SIZE) {
+			rl.rlim_cur = STACK_SIZE;
+			result = setrlimit(RLIMIT_STACK, &rl);
+			if (result != 0)
+				printf("setrlimit returned result = %d\n",
+					result);
+			else
+				printf("setrlimit succeed!!!\n");
+		} else
+			printf("default stack size is 0x%x\n",
+				(int)(rl.rlim_cur));
+	}
+}
+
+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(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;
+}
+
+int
+main(int argc, char **argv)
+{
+	struct ethtool_drvinfo drvinfo;
+	struct ethtool_regs regs;
+	struct ethtool_pauseparam pause_param;
+	struct ethtool_eeprom eeprom;
+
+	int8_t reply_data[MAXI_DATA] __attribute__((aligned(8)));
+	uint8_t mac_addr[MAC_ADDR_SIZE] = {0x52, 0x54, 0, 0, 0, 0};
+	uint8_t mac_base_addr[MAC_ADDR_SIZE] = {0x52, 0x54, 0, 0, 0, 1};
+	uint8_t port_id;
+	const int mtu = 1024;
+	int iter_count = 0;
+	int count, link_up;
+	int *int_ptr;
+
+	/* get command parameter */
+	if (argc > 1)
+		keep_traffic = atoi(argv[1]);
+	/* set stack size */
+	set_stacksize();
+
+	/* 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(mac_addr);
+	while (num_of_ports == 0)
+		num_of_ports = netdev_ipc_begin(mac_addr) & 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);
+		}
+	}
+
+	memset(reply_data, 0xFF, MAXI_DATA);
+	/* Testing ethtool API */
+	for (port_id = 0; port_id < num_of_ports; port_id++) {
+		if (!is_port_enabled(port_id))
+			continue;
+		else {
+			/* print out mac address */
+			if (netdev_net_get_mac_addr(port_id, mac_addr)) {
+				printf("Fail to get mac addr from port");
+				printf(" #%d!!!\n", port_id);
+			} else
+				printf("\nPort #%d mac addr is %s\n",
+					port_id, mac_addr_str(mac_addr));
+
+			if (netdev_ethtool_get_drvinfo(port_id, &drvinfo)) {
+				printf("fail to get drvinfo ...\n");
+			} else {
+				printf("driver: %s version: %s ",
+					drvinfo.driver, drvinfo.version);
+				printf("fw_version: %s bus_info=%s\n",
+					drvinfo.fw_version, drvinfo.bus_info);
+				printf("reg-size(bytes)=%d eeprom-size=%d\n",
+					drvinfo.regdump_len,
+					drvinfo.eedump_len);
+			}
+
+			count = netdev_ethtool_get_regs_len(port_id);
+			if (count <= 0) {
+				printf("There are no registers available from");
+				printf(" device/port #%d", port_id);
+			} else {
+				printf("Target device has %d registers ",
+					count);
+				printf("for dump\n");
+			}
+
+			if (count > 0) {
+				memset(&regs, 0xFF,
+					sizeof(struct ethtool_regs));
+				count = netdev_ethtool_get_regs(port_id,
+					&regs, reply_data);
+				if (count) {
+					printf("failed to run");
+					printf(" ethtool_get_regs ");
+					printf("from port #%d (err=%d)\n",
+						port_id, count);
+				} else {
+					int_ptr = (int *)((void *)reply_data);
+					printf("reg[0]=%x reg[10]=%x ",
+						int_ptr[0], int_ptr[10]);
+					printf("version=0x%x\n",
+						regs.version);
+				}
+			}
+
+			/* Only testing eeprom access over a PF */
+			count = 0;
+			if (!is_vf_port(port_id)) {
+				count = netdev_ethtool_get_eeprom_len(0);
+				if (count == 0) {
+					printf("fail to retrieve eeprom");
+					printf("count from port #%d\n",
+						port_id);
+				}
+			}
+
+			if (count) {
+				printf("eeprom size is %d bytes\n", count);
+				eeprom.offset = 20;
+				eeprom.len = 80;
+				eeprom.magic = 0;
+				if (netdev_ethtool_get_eeprom(port_id,
+					&eeprom, reply_data)) {
+					printf("Fail to read eeprom");
+					printf(" from port #%d\n",
+						port_id);
+				} else {
+					int i;
+					uint16_t *word = (uint16_t *)
+						((void *)reply_data);
+
+					printf("eeprom-magic: %x;",
+						eeprom.magic);
+					printf("eeprom data ...\n");
+					count = 80;
+					for (i = 0; i < (int)(eeprom.len
+						>> 1); i++) {
+						if (((i+1) % 16) == 0)
+							printf("\n");
+						printf("%4x ", word[i]);
+					}
+					printf("\n");
+				}
+			}
+		}
+	}
+
+	/* testing set/get mac address */
+	printf("MAC base address is %s\n", mac_addr_str(mac_base_addr));
+	for (port_id = 0; port_id < num_of_ports; port_id++) {
+		if (netdev_net_get_mac_addr(port_id,
+			to_mac_type(mac_addr)))
+			printf("Fail to get mac addr from port #%d!!!\n",
+				port_id);
+		else
+			printf("Port #%d, device mac addr is %s\n", port_id,
+				mac_addr_str(mac_addr));
+
+		if (!netdev_net_validate_addr(port_id,
+			to_mac_type(mac_addr))) {
+			printf("Default mac addr, %s, is not valid\n",
+				mac_addr_str(mac_addr));
+			strncpy((char *)mac_addr, (char *)mac_base_addr,
+				MAC_ADDR_SIZE);
+			mac_addr[MAC_ADDR_SIZE-1] = 1+port_id;
+			printf("New mac address:%s is used.\n",
+				mac_addr_str(mac_addr));
+
+			if (netdev_net_set_mac_addr(port_id,
+				to_mac_type(mac_addr)) ||
+				netdev_net_get_mac_addr(port_id,
+				to_mac_type(mac_addr))) {
+					printf("Fail to reset mac addr");
+					printf(" @ port #%d!!!\n", port_id);
+			} else {
+				printf("After mac address re-assign");
+				printf(" device mac addr is %s\n",
+					mac_addr_str(mac_addr));
+			}
+		}
+	}
+
+	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(10);
+
+		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(5);
+	}
+
+	/* stop link for testing */
+	if (!keep_traffic) {
+		int status;
+
+		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);
+		}
+
+		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) {
+				eeprom.offset = 20;
+				eeprom.len = 80;
+				if (netdev_ethtool_get_eeprom(port_id,
+					&eeprom, reply_data)) {
+					printf("failed to read eeprom");
+					printf(" break from post-run");
+					printf(" testing!!!\n");
+					break;
+				}
+				if (netdev_ethtool_set_eeprom(port_id,
+					&eeprom, reply_data)) {
+					printf("Fail to write read-back");
+					printf(" data to eeprom!!!\n");
+					break;
+				}
+				/* checking mtu setting */
+				if (netdev_net_change_mtu(port_id, mtu)) {
+					printf("failed to set mtu");
+					printf("to %d\n", mtu);
+				}
+
+				/* add/remove vlan to vid */
+				status = netdev_net_vlan_rx_add_vid(
+					port_id, 0);
+				if (status == 0) {
+					status = netdev_net_vlan_rx_kill_vid(
+						port_id, 0);
+
+					if (status) {
+						printf("fail kill vlan-vid\n");
+						break;
+					}
+				} else {
+					printf("fail adding vlan/vid 0\n");
+					break;
+				}
+
+				/* testing pause parameter get/set functions */
+				status = netdev_ethtool_get_pauseparam(
+					port_id, &pause_param);
+				if (status) {
+					printf("get pauseparam fail\n");
+					break;
+				}
+				printf("pause setup: autoneg: %d ",
+					pause_param.autoneg);
+				printf("tx_pause: %d ",
+					pause_param.tx_pause);
+				printf("rx_pause: %d\n",
+					pause_param.rx_pause);
+				status = netdev_ethtool_set_pauseparam(
+					port_id, &pause_param);
+				if (status) {
+					printf("set pause param fail\n");
+					break;
+				}
+
+			}
+		}
+	}
+
+	while (netdev_ipc_end() < 0)
+		;
+
+	printf("Done for ethtool service request!!!\n");
+	return 0;
+}