All of lore.kernel.org
 help / color / mirror / Atom feed
From: Liang-Min Larry Wang <liang-min.wang@intel.com>
To: dev@dpdk.org
Cc: Liang-Min Larry Wang <liang-min.wang@intel.com>, shemming@brocade.com
Subject: [PATCH v12 4/4] examples: new example: l2fwd-ethtool
Date: Tue,  7 Jul 2015 13:39:11 -0400	[thread overview]
Message-ID: <1436290751-14840-5-git-send-email-liang-min.wang@intel.com> (raw)
In-Reply-To: <1436290751-14840-1-git-send-email-liang-min.wang@intel.com>

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

Signed-off-by: Liang-Min Larry Wang <liang-min.wang@intel.com>
---
 examples/Makefile                                |    3 +
 examples/l2fwd-ethtool/Makefile                  |   55 ++
 examples/l2fwd-ethtool/l2fwd-app/Makefile        |   59 ++
 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 ++++++++++
 11 files changed, 3383 insertions(+)
 create mode 100644 examples/l2fwd-ethtool/Makefile
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/Makefile
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/main.c
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h
 create mode 100644 examples/l2fwd-ethtool/lib/Makefile
 create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.c
 create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.h
 create mode 100644 examples/l2fwd-ethtool/nic-control/Makefile
 create mode 100644 examples/l2fwd-ethtool/nic-control/nic_control.c

diff --git a/examples/Makefile b/examples/Makefile
index b4eddbd..cd1c4b0 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -50,6 +50,9 @@ DIRS-y += ip_reassembly
 DIRS-$(CONFIG_RTE_IP_FRAG) += ip_fragmentation
 DIRS-y += ipv4_multicast
 DIRS-$(CONFIG_RTE_LIBRTE_KNI) += kni
+DIRS-y += l2fwd-ethtool/lib
+DIRS-y += l2fwd-ethtool/nic-control
+DIRS-y += l2fwd-ethtool/l2fwd-app
 DIRS-y += l2fwd
 DIRS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += l2fwd-ivshmem
 DIRS-$(CONFIG_RTE_LIBRTE_JOBSTATS) += l2fwd-jobstats
diff --git a/examples/l2fwd-ethtool/Makefile b/examples/l2fwd-ethtool/Makefile
new file mode 100644
index 0000000..80d257e
--- /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 overwritten 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..e64d561
--- /dev/null
+++ b/examples/l2fwd-ethtool/l2fwd-app/Makefile
@@ -0,0 +1,59 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overwritten by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+# binary name
+APP = l2fwd-app
+
+# all source are stored in SRCS-y
+SRCS-y := main.c
+
+CFLAGS += -O3 -D_GNU_SOURCE -pthread -I$(SRCDIR)/../lib
+CFLAGS += $(WERROR_FLAGS)
+
+LDLIBS += -lrte_ethtool
+LDFLAGS += -L$(SRCDIR)/../build/lib
+LDFLAGS += -L$(SRCDIR)/../lib/l2fwd-ethtool/lib/$(RTE_TARGET)/lib
+
+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..be33a81
--- /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 overwritten by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+# library name
+LIB = librte_ethtool.a
+
+# 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..17ab4a3
--- /dev/null
+++ b/examples/l2fwd-ethtool/nic-control/Makefile
@@ -0,0 +1,55 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overwritten by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+# binary name
+APP = nic-control
+
+# all source are stored in SRCS-y
+SRCS-y := nic_control.c
+
+CFLAGS += -O3 -I$(SRCDIR)/../l2fwd-app -I$(SRCDIR)/../lib
+CFLAGS += $(WERROR_FLAGS)
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/l2fwd-ethtool/nic-control/nic_control.c b/examples/l2fwd-ethtool/nic-control/nic_control.c
new file mode 100644
index 0000000..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;
+}
-- 
2.1.4

  parent reply	other threads:[~2015-07-07 17:39 UTC|newest]

Thread overview: 148+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-05-30  0:37 [PATCH 0/2] User-space Ethtool Liang-Min Larry Wang
2015-05-30  0:37 ` [PATCH 1/2] ethdev: add api to set default mac address Liang-Min Larry Wang
2015-05-30  1:57   ` Andrew Harvey (agh)
2015-05-30  0:37 ` [PATCH 2/2] ethtool: add new library to provide ethtool-alike APIs Liang-Min Larry Wang
2015-05-30 15:48   ` Stephen Hemminger
2015-05-30 16:16     ` Wang, Liang-min
2015-05-30 19:26       ` Stephen Hemminger
2015-05-30 19:40         ` Wang, Liang-min
2015-05-31 16:48           ` Stephen Hemminger
2015-05-31 17:30             ` Wang, Liang-min
2015-05-31 18:31             ` Wang, Liang-min
2015-06-01 12:42   ` David Harton (dharton)
2015-06-10 15:09 ` [PATCH v4 0/4] User-space Ethtool Liang-Min Larry Wang
2015-06-10 15:09   ` [PATCH v4 1/4] ethdev: add apis to support access device info Liang-Min Larry Wang
2015-06-10 15:21     ` David Harton (dharton)
2015-06-11 12:26     ` Ananyev, Konstantin
2015-06-11 12:57       ` Wang, Liang-min
2015-06-11 13:07         ` Ananyev, Konstantin
2015-06-11 21:51           ` Wang, Liang-min
2015-06-12 12:30             ` Ananyev, Konstantin
2015-06-15 13:26               ` Wang, Liang-min
2015-06-15 13:45                 ` Ananyev, Konstantin
2015-06-15 14:47                   ` Wang, Liang-min
2015-06-15 18:10                     ` Ananyev, Konstantin
2015-06-17 17:25                       ` Ananyev, Konstantin
2015-06-15 16:05                   ` David Harton (dharton)
2015-06-15 18:23                     ` Ananyev, Konstantin
2015-06-16 18:15                       ` Ananyev, Konstantin
2015-06-11 13:14         ` Ananyev, Konstantin
2015-06-11 13:25           ` Wang, Liang-min
2015-06-10 15:09   ` [PATCH v4 2/4] ixgbe: add ops to support ethtool ops Liang-Min Larry Wang
2015-06-10 15:09   ` [PATCH v4 3/4] igb: " Liang-Min Larry Wang
2015-06-10 15:09   ` [PATCH v4 4/4] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
2015-06-11 21:43 ` [PATCH v5 0/4] User-space Ethtool Liang-Min Larry Wang
2015-06-11 21:43   ` [PATCH v5 1/4] ethdev: add apis to support access device info Liang-Min Larry Wang
2015-06-11 21:43   ` [PATCH v5 2/4] ixgbe: add ops to support ethtool ops Liang-Min Larry Wang
2015-06-11 21:43   ` [PATCH v5 3/4] igb: " Liang-Min Larry Wang
2015-06-11 21:43   ` [PATCH v5 4/4] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
2015-06-12 22:03 ` [PATCH v6 0/4] User-space Ethtool Liang-Min Larry Wang
2015-06-12 22:03   ` [PATCH v6 1/4] ethdev: add apis to support access device info Liang-Min Larry Wang
2015-06-13 23:25     ` David Harton (dharton)
2015-06-12 22:03   ` [PATCH v6 2/4] ixgbe: add ops to support ethtool ops Liang-Min Larry Wang
2015-06-12 22:03   ` [PATCH v6 3/4] igb: " Liang-Min Larry Wang
2015-06-12 22:03   ` [PATCH v6 4/4] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
2015-06-13  0:21   ` [PATCH v6 0/4] User-space Ethtool Andrew Harvey (agh)
2015-06-17 22:22 ` [PATCH v7 " Liang-Min Larry Wang
2015-06-17 22:22   ` [PATCH v7 1/4] ethdev: add apis to support access device info Liang-Min Larry Wang
2015-06-25 13:39     ` Stephen Hemminger
2015-06-25 20:58       ` Wang, Liang-min
2015-06-25 13:44     ` Stephen Hemminger
2015-06-25 21:05       ` Wang, Liang-min
2015-06-17 22:22   ` [PATCH v7 2/4] ixgbe: add ops to support ethtool ops Liang-Min Larry Wang
2015-06-25 13:45     ` Stephen Hemminger
2015-06-26  6:26       ` Andrew Harvey (agh)
2015-06-17 22:22   ` [PATCH v7 3/4] igb: " Liang-Min Larry Wang
2015-06-17 22:22   ` [PATCH v7 4/4] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
2015-06-18  2:04   ` [PATCH v7 0/4] User-space Ethtool Stephen Hemminger
2015-06-18 12:47     ` Wang, Liang-min
2015-06-23 15:19       ` Wang, Liang-min
2015-06-24 13:55   ` Andrew Harvey (agh)
2015-06-24 17:16   ` David Harton (dharton)
2015-06-26 14:26 ` [PATCH v8 0/5] " Liang-Min Larry Wang
2015-06-26 14:26   ` [PATCH v8 1/5] ethdev: add apis to support access device info Liang-Min Larry Wang
2015-06-26 16:51     ` Stephen Hemminger
2015-06-26 17:05       ` Wang, Liang-min
2015-06-27  1:21       ` Wang, Liang-min
2015-06-26 14:26   ` [PATCH v8 2/5] ixgbe: add ops to support ethtool ops Liang-Min Larry Wang
2015-06-26 14:26   ` [PATCH v8 3/5] igb: " Liang-Min Larry Wang
2015-06-26 14:26   ` [PATCH v8 4/5] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
2015-06-26 14:26   ` [PATCH v8 5/5] Changed register tables to const Liang-Min Larry Wang
2015-06-26 19:15   ` [PATCH v8 0/5] User-space Ethtool Ananyev, Konstantin
2015-06-27  1:19 ` [PATCH v9 " Liang-Min Larry Wang
2015-06-27  1:19   ` [PATCH v9 1/5] ethdev: add apis to support access device info Liang-Min Larry Wang
2015-06-27  1:19   ` [PATCH v9 2/5] ixgbe: add ops to support ethtool ops Liang-Min Larry Wang
2015-06-27  1:32     ` Stephen Hemminger
2015-06-27  2:37       ` Wang, Liang-min
2015-06-27  1:34     ` Stephen Hemminger
2015-06-27  2:39       ` Wang, Liang-min
2015-06-27  1:34     ` Stephen Hemminger
2015-06-27  1:19   ` [PATCH v9 3/5] igb: " Liang-Min Larry Wang
2015-06-27  1:35     ` Stephen Hemminger
2015-06-27  1:19   ` [PATCH v9 4/5] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
2015-06-27  1:19   ` [PATCH v9 5/5] ixgbe/igb: changed register tables to const Liang-Min Larry Wang
2015-06-27  1:36     ` Stephen Hemminger
2015-06-27  1:50       ` Wang, Liang-min
2015-06-27  2:40       ` Wang, Liang-min
2015-07-10 12:55       ` Wang, Liang-min
2015-06-27  2:36 ` [PATCH v10 0/4] User-space Ethtool Liang-Min Larry Wang
2015-06-27  2:36   ` [PATCH v10 1/4] ethdev: add apis to support access device info Liang-Min Larry Wang
2015-06-27  2:36   ` [PATCH v10 2/4] ixgbe: add ops to support ethtool ops Liang-Min Larry Wang
2015-06-27  2:36   ` [PATCH v10 3/4] igb: " Liang-Min Larry Wang
2015-06-27  2:36   ` [PATCH v10 4/4] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
2015-06-27 12:25 ` [PATCH v11 0/4] User-space Ethtool Liang-Min Larry Wang
2015-06-27 12:25   ` [PATCH v11 1/4] ethdev: add apis to support access device info Liang-Min Larry Wang
2015-06-27 12:25   ` [PATCH v11 2/4] ixgbe: add ops to support ethtool ops Liang-Min Larry Wang
2015-06-27 12:25   ` [PATCH v11 3/4] igb: " Liang-Min Larry Wang
2015-06-27 12:25   ` [PATCH v11 4/4] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
2015-07-07 17:39 ` [PATCH v12 0/4] User-space Ethtool Liang-Min Larry Wang
2015-07-07 17:39   ` [PATCH v12 1/4] ethdev: add apis to support access device info Liang-Min Larry Wang
2015-07-07 17:39   ` [PATCH v12 2/4] ixgbe: add ops to support ethtool ops Liang-Min Larry Wang
2015-07-07 17:39   ` [PATCH v12 3/4] igb: " Liang-Min Larry Wang
2015-07-07 17:39   ` Liang-Min Larry Wang [this message]
2015-07-10 12:48 ` [PATCH v13 0/4] User-space Ethtool Liang-Min Larry Wang
2015-07-10 12:48   ` [PATCH v13 1/4] ethdev: add apis to support access device info Liang-Min Larry Wang
2015-07-10 12:48   ` [PATCH v13 2/4] ixgbe: add ops to support ethtool ops Liang-Min Larry Wang
2015-07-10 12:48   ` [PATCH v13 3/4] igb: " Liang-Min Larry Wang
2015-07-10 12:48   ` [PATCH v13 4/4] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
2015-07-12 21:22 ` [PATCH v14 0/4] User-space Ethtool Liang-Min Larry Wang
2015-07-12 21:22   ` [PATCH v14 1/4] ethdev: add apis to support access device info Liang-Min Larry Wang
2015-07-13 22:26     ` Thomas Monjalon
2015-07-12 21:22   ` [PATCH v14 2/4] ixgbe: add ops to support ethtool ops Liang-Min Larry Wang
2015-07-12 21:22   ` [PATCH v14 3/4] igb: " Liang-Min Larry Wang
2015-07-12 21:22   ` [PATCH v14 4/4] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
2015-07-14  2:18 ` [PATCH v15 0/4] User-space Ethtool Liang-Min Larry Wang
2015-07-14  2:18   ` [PATCH v15 1/4] ethdev: add apis to support access device info Liang-Min Larry Wang
2015-07-15  6:16     ` Thomas Monjalon
2015-07-15 10:07       ` Wang, Liang-min
2015-07-15 10:27         ` Thomas Monjalon
2015-07-15 10:48           ` Wang, Liang-min
2015-07-15 11:20             ` Thomas Monjalon
2015-07-15 11:36               ` Wang, Liang-min
2015-07-15 12:06                 ` Thomas Monjalon
2015-07-14  2:18   ` [PATCH v15 2/4] ixgbe: add ops to support ethtool ops Liang-Min Larry Wang
2015-07-14  2:18   ` [PATCH v15 3/4] igb: " Liang-Min Larry Wang
2015-07-14  2:18   ` [PATCH v15 4/4] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
2015-07-14 13:11 ` [PATCH v16 0/6] User-space Ethtool Liang-Min Larry Wang
2015-07-14 13:11   ` [PATCH v16 1/6] ethdev: add apis to support access device info Liang-Min Larry Wang
2015-07-14 13:11   ` [PATCH v16 2/6] ixgbe: add ops to support ethtool ops Liang-Min Larry Wang
2015-07-14 13:11   ` [PATCH v16 3/6] igb: " Liang-Min Larry Wang
2015-07-14 13:11   ` [PATCH v16 4/6] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
2015-07-14 13:11   ` [PATCH v16 5/6] ethdev: change api name, version information and fix macro Liang-Min Larry Wang
2015-07-14 13:11   ` [PATCH v16 6/6] examples/l2fwd-ethtool: replace lib with new API name Liang-Min Larry Wang
2015-07-14 20:13   ` [PATCH v16 0/6] User-space Ethtool Thomas Monjalon
2015-07-14 20:56     ` Wang, Liang-min
2015-07-15  5:53       ` Thomas Monjalon
2015-07-15 10:15         ` Wang, Liang-min
2015-07-15 10:30           ` Thomas Monjalon
2015-07-16 13:25 ` [PATCH v17 0/5] " Liang-Min Larry Wang
2015-07-16 13:25   ` [PATCH v17 1/5] ethdev: add api to support setting default mac addr Liang-Min Larry Wang
2015-07-16 13:25   ` [PATCH v17 2/5] ethdev: add apis to support access device info Liang-Min Larry Wang
2015-07-16 13:25   ` [PATCH v17 3/5] ixgbe: add ops to support ethtool ops Liang-Min Larry Wang
2015-07-16 13:25   ` [PATCH v17 4/5] igb: " Liang-Min Larry Wang
2015-07-16 13:25   ` [PATCH v17 5/5] examples: new example: l2fwd-ethtool Liang-Min Larry Wang
2015-07-16 21:25     ` Thomas Monjalon
2015-07-16 21:48   ` [PATCH v17 0/5] User-space Ethtool Thomas Monjalon
2015-07-16 21:55     ` Wang, Liang-min
2015-07-16 22:09       ` Thomas Monjalon
2015-07-16 22:15         ` Wang, Liang-min

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1436290751-14840-5-git-send-email-liang-min.wang@intel.com \
    --to=liang-min.wang@intel.com \
    --cc=dev@dpdk.org \
    --cc=shemming@brocade.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.