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 at intel.com>
---
 examples/Makefile                                |    1 +
 examples/l2fwd-ethtool/Makefile                  |   48 +
 examples/l2fwd-ethtool/l2fwd-app/Makefile        |   58 ++
 examples/l2fwd-ethtool/l2fwd-app/main.c          | 1025 ++++++++++++++++++++++
 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h    |  770 ++++++++++++++++
 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h   |  159 ++++
 examples/l2fwd-ethtool/lib/Makefile              |   57 ++
 examples/l2fwd-ethtool/lib/rte_ethtool.c         |  336 +++++++
 examples/l2fwd-ethtool/lib/rte_ethtool.h         |  385 ++++++++
 examples/l2fwd-ethtool/nic-control/Makefile      |   55 ++
 examples/l2fwd-ethtool/nic-control/nic_control.c |  614 +++++++++++++
 11 files changed, 3508 insertions(+)
 create mode 100644 examples/l2fwd-ethtool/Makefile
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/Makefile
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/main.c
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h
 create mode 100644 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h
 create mode 100644 examples/l2fwd-ethtool/lib/Makefile
 create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.c
 create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.h
 create mode 100644 examples/l2fwd-ethtool/nic-control/Makefile
 create mode 100644 examples/l2fwd-ethtool/nic-control/nic_control.c

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

Reply via email to