diff mbox

[dpdk-dev,6/8] examples: dynamic rss configuration for bonding

Message ID 1433329147-2840-7-git-send-email-tomaszx.kulasek@intel.com (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Tomasz Kulasek June 3, 2015, 10:59 a.m. UTC
This application allows you to test RSS configuration for bonded devices
changing configuration dynamically, during receiving packets on slaves and
observe the changes in its distribution over queues.

After initialization process, all accessible ports are attached to one bonding
as slaves.

Monitor screen is divided into five main parts:
 - Port selection (on the very top)
 - RSS Configuration for selected port including hash function, key and RETA
 - Incoming packets statistics
 - Incoming packets list for selected port
 - Status bar with contextual information about selected part

Signed-off-by: Tomasz Kulasek <tomaszx.kulasek@intel.com>
---
 examples/bond_rss/Makefile  |   59 +++
 examples/bond_rss/bondrss.c |  293 ++++++++++++++
 examples/bond_rss/bondrss.h |  163 ++++++++
 examples/bond_rss/config.c  |  251 ++++++++++++
 examples/bond_rss/ui.c      |  915 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 1681 insertions(+)
 create mode 100644 examples/bond_rss/Makefile
 create mode 100644 examples/bond_rss/bondrss.c
 create mode 100644 examples/bond_rss/bondrss.h
 create mode 100644 examples/bond_rss/config.c
 create mode 100644 examples/bond_rss/ui.c
diff mbox

Patch

diff --git a/examples/bond_rss/Makefile b/examples/bond_rss/Makefile
new file mode 100644
index 0000000..db457a3
--- /dev/null
+++ b/examples/bond_rss/Makefile
@@ -0,0 +1,59 @@ 
+#   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.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overridden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# binary name
+APP = bondrss
+
+# all source are stored in SRCS-y
+SRCS-y := bondrss.c
+SRCS-y += ui.c
+SRCS-y += config.c
+
+CFLAGS += $(WERROR_FLAGS)
+
+# workaround for a gcc bug with noreturn attribute
+# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603
+ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y)
+CFLAGS_main.o += -Wno-return-type
+endif
+
+LDLIBS += -lncurses
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/bond_rss/bondrss.c b/examples/bond_rss/bondrss.c
new file mode 100644
index 0000000..2dc9979
--- /dev/null
+++ b/examples/bond_rss/bondrss.c
@@ -0,0 +1,293 @@ 
+/*-
+ *   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 "bondrss.h"
+
+#define RSS_HASH_KEY_LENGTH 40
+
+static const struct rte_eth_conf port_conf_default = {
+		.rxmode = {
+				.mq_mode		= ETH_MQ_RX_RSS,
+				.max_rx_pkt_len	= ETHER_MAX_LEN,
+				.split_hdr_size	= 0,
+				.header_split	= 0, /**< Header Split disabled. */
+				.hw_ip_checksum	= 0, /**< IP checksum offload disabled. */
+				.hw_vlan_filter	= 1, /**< VLAN filtering enabled. */
+				.hw_vlan_strip	= 1, /**< VLAN strip enabled. */
+				.hw_vlan_extend	= 0, /**< Extended VLAN disabled. */
+				.jumbo_frame	= 0, /**< Jumbo Frame Support disabled. */
+				.hw_strip_crc	= 0, /**< CRC stripping by hardware disabled. */
+				.enable_scatter	= 0, /**< scatter rx disabled */
+		},
+		.rx_adv_conf = {
+				.rss_conf = {
+						.rss_key	= NULL,
+						.rss_hf		= ETH_RSS_IPV4,
+				},
+		},
+};
+
+static const struct rte_eth_conf port_conf_bonding = {
+		.rxmode = {
+				.mq_mode		= ETH_MQ_RX_RSS,
+				.max_rx_pkt_len	= ETHER_MAX_LEN,
+				.split_hdr_size	= 0,
+				.header_split	= 0, /**< Header Split disabled. */
+				.hw_ip_checksum	= 0, /**< IP checksum offload disabled. */
+				.hw_vlan_filter	= 1, /**< VLAN filtering enabled. */
+				.hw_vlan_strip	= 1, /**< VLAN strip enabled. */
+				.hw_vlan_extend	= 0, /**< Extended VLAN disabled. */
+				.jumbo_frame	= 0, /**< Jumbo Frame Support disabled. */
+				.hw_strip_crc	= 0, /**< CRC stripping by hardware disabled. */
+				.enable_scatter	= 0, /**< scatter rx disabled */
+		},
+		.rx_adv_conf = {
+				.rss_conf = {
+						.rss_key	= NULL,
+						.rss_hf		= ETH_RSS_IPV6,
+				},
+		},
+};
+
+/*
+ * Configurable number of RX/TX queues.
+ */
+int nb_rxq = 4;			/**< Number of RX queues per port. */
+int nb_txq = 1;			/**< Number of TX queues per port. */
+
+int bond_port_id = -1;	/**< Bonded device port id */
+
+int nb_ports;
+struct rte_port *ports;
+rte_atomic64_t counter;	/**< Received packet's counter */
+
+/*
+ * Initializes a given port using global settings and with the rx buffers
+ * coming from the mbuf_pool passed as parameter
+ */
+static inline
+int init_port(uint8_t port_id, struct rte_mempool *mbuf_pool,
+		struct rte_eth_conf port_conf)
+{
+
+	int retval;
+	uint16_t q;
+	struct rte_eth_rxconf rxconf;
+
+	struct rte_port *port;
+
+	if (port_id >= rte_eth_dev_count())
+		return -1;
+
+	port = &ports[port_id];
+	port->nb_rx_queues = nb_rxq;
+	port->nb_tx_queues = nb_txq;
+
+	memcpy(&(port->dev_conf), &port_conf, sizeof(port_conf));
+	retval = rte_eth_dev_configure(port_id, port->nb_rx_queues,
+	                               port->nb_tx_queues, &port_conf);
+
+	if (retval != 0)
+		return retval;
+
+	rte_eth_dev_info_get(port_id, &(port->dev_info));
+	rxconf = (port->dev_info).default_rxconf;
+	rxconf.rx_free_thresh = 32;
+
+	for (q = 0; q < port->nb_rx_queues; q++) {
+		retval = rte_eth_rx_queue_setup(port_id, q, RX_RING_SIZE,
+		                                rte_eth_dev_socket_id(port_id), &rxconf,
+		                                mbuf_pool);
+		if (retval < 0)
+			return retval;
+	}
+
+	for (q = 0; q < port->nb_tx_queues; q++) {
+		retval = rte_eth_tx_queue_setup(port_id, q, TX_RING_SIZE,
+		                                rte_eth_dev_socket_id(port_id),
+		                                &((port->dev_info).default_txconf));
+		if (retval < 0)
+			return retval;
+	}
+
+	port->bond_mode = -1;
+	port->promiscuous = 0;
+	port->enabled = 1;
+
+	return 0;
+}
+
+static int
+rx_loop(__attribute__((unused)) void *dummy)
+{
+	uint8_t port_id;
+	int nq;
+	int n;
+
+	for (;;)
+		for (port_id = 0; port_id < nb_ports; port_id++) {
+			/* Pool only bonding ports */
+			if (ports[port_id].bond_mode >= 0)
+				for (nq = 0; nq < ports[port_id].nb_rx_queues; nq++) {
+					struct rte_mbuf *bufs[BURST_SIZE];
+
+					const uint16_t nb_rx = rte_eth_rx_burst(port_id, nq, bufs,
+					BURST_SIZE);
+					if (unlikely(nb_rx == 0))
+						continue;
+
+					ports[port_id].rxq_ipackets[nq] += nb_rx;
+					for (n = 0; n < nb_rx; n++) {
+						record_pkt(port_id, nq, bufs[n]);
+						rte_pktmbuf_free(bufs[n]);
+					}
+				}
+		}
+
+	return 0;
+}
+
+static uint16_t
+rx_callback(uint8_t port_id, uint16_t qidx, struct rte_mbuf **pkts,
+		uint16_t nb_pkts, uint16_t max_pkts __rte_unused, void *_ __rte_unused)
+{
+	int n;
+
+	ports[port_id].rxq_ipackets[qidx] += nb_pkts;
+	for (n = 0; n < nb_pkts; n++) {
+		rte_atomic64_inc(&counter);
+		pkts[n]->udata64 = counter.cnt;
+		record_pkt(port_id, qidx, pkts[n]);
+	}
+
+	return nb_pkts;
+}
+
+int
+main(int argc, char *argv[])
+{
+	struct rte_mempool *mbuf_pool;
+	unsigned lcore_id = 0;
+	uint8_t port_id, queue_id;
+
+	/* init EAL */
+	int ret = rte_eal_init(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+	argc -= ret;
+	argv += ret;
+
+	nb_ports = rte_eth_dev_count();
+	if (nb_ports < 1)
+		rte_exit(EXIT_FAILURE,
+			"At least one port must be available to create a bonding\n");
+
+	mbuf_pool = rte_mempool_create("MBUF_POOL", NUM_MBUFS * nb_ports, MBUF_SIZE,
+	                               MBUF_CACHE_SIZE,
+	                               sizeof(struct rte_pktmbuf_pool_private),
+	                               rte_pktmbuf_pool_init, NULL,
+	                               rte_pktmbuf_init, NULL, rte_socket_id(), 0);
+	if (mbuf_pool == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
+
+	/* Configuration of Ethernet ports. */
+	ports = rte_zmalloc("bondrss: ports",
+	                    sizeof(struct rte_port) * RTE_MAX_ETHPORTS,
+	                    RTE_CACHE_LINE_SIZE);
+	if (ports == NULL)
+		rte_exit(EXIT_FAILURE, "rte_zmalloc(%d struct rte_port) failed\n",
+		         RTE_MAX_ETHPORTS);
+
+	/* enabled allocated ports */
+	for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
+		ports[port_id].enabled = 0;
+
+	/* initialize all ports */
+	for (port_id = 0; port_id < nb_ports; port_id++) {
+		if (init_port(port_id, mbuf_pool, port_conf_default) != 0)
+			rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8"\n", port_id);
+
+		/* Add rx callbacks to the slave's port */
+		for (queue_id = 0; queue_id < ports[port_id].nb_rx_queues; queue_id++)
+			rte_eth_add_rx_callback(port_id, queue_id, rx_callback, NULL);
+
+	}
+
+	/* create bonding port */
+	bond_port_id = rte_eth_bond_create("eth_bond", 0, 0);
+	if (bond_port_id < 0)
+		rte_exit(EXIT_FAILURE, "Cannot create bonding port\n");
+
+	for (port_id = 0; port_id < nb_ports; port_id++)
+		rte_eth_bond_slave_add(bond_port_id, port_id);
+
+	/* count again */
+	nb_ports = rte_eth_dev_count();
+
+	init_port(bond_port_id, mbuf_pool, port_conf_bonding);
+
+	/* start bonding port*/
+	ret = rte_eth_dev_start(bond_port_id);
+
+	/* enable promiscuous by default */
+	rte_eth_promiscuous_enable(bond_port_id);
+
+	for (port_id = 0; port_id < nb_ports; port_id++) {
+		ports[port_id].bond_mode = rte_eth_bond_mode_get(port_id);
+		ports[port_id].promiscuous = rte_eth_promiscuous_get(port_id);
+
+		/* map queues to stats */
+		if (ports[port_id].bond_mode >= 0) {
+			for (queue_id = 0; queue_id < ports[port_id].nb_rx_queues;
+			        queue_id++) {
+				rte_eth_dev_set_rx_queue_stats_mapping(port_id, queue_id,
+						queue_id);
+			}
+		}
+
+	}
+
+	/* Initialize received packet's counter */
+	rte_atomic64_init(&counter);
+
+	if (rte_lcore_count() <= 1)
+		rte_exit(EXIT_FAILURE, "More than one free lcore is needed\n");
+
+	lcore_id = rte_get_next_lcore(rte_lcore_id(), 1, 0);
+	rte_eal_remote_launch(rx_loop, NULL, lcore_id);
+
+	/* call lcore_main on master core only */
+	ui_loop();
+
+	return 0;
+}
diff --git a/examples/bond_rss/bondrss.h b/examples/bond_rss/bondrss.h
new file mode 100644
index 0000000..3e2ef73
--- /dev/null
+++ b/examples/bond_rss/bondrss.h
@@ -0,0 +1,163 @@ 
+/*-
+ *   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 BONDRSS_H_
+#define BONDRSS_H_
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_cycles.h>
+#include <rte_lcore.h>
+#include <rte_mbuf.h>
+#include <rte_malloc.h>
+
+#include <rte_eth_bond.h>
+
+#define RX_RING_SIZE 128
+#define TX_RING_SIZE 512
+
+#define NUM_MBUFS 8191
+#define MBUF_SIZE (1600 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
+#define MBUF_CACHE_SIZE 250
+#define BURST_SIZE 32
+
+/*
+ * Configurable number of RX/TX queues.
+ */
+extern int nb_rxq; /**< Number of RX queues per port. */
+extern int nb_txq; /**< Number of TX queues per port. */
+
+extern int bond_port_id;	/**< Bonded device port id */
+extern int bond_mode;
+
+struct rss_type_info {
+	char str[32];
+	uint64_t rss_type;
+};
+
+static struct rss_type_info rss_type_table[] = {
+	{"ipv4", ETH_RSS_IPV4},
+	{"ipv4-frag", ETH_RSS_FRAG_IPV4},
+	{"ipv4-tcp", ETH_RSS_NONFRAG_IPV4_TCP},
+	{"ipv4-udp", ETH_RSS_NONFRAG_IPV4_UDP},
+	{"ipv4-sctp", ETH_RSS_NONFRAG_IPV4_SCTP},
+	{"ipv4-other", ETH_RSS_NONFRAG_IPV4_OTHER},
+	{"ipv6", ETH_RSS_IPV6},
+	{"ipv6-frag", ETH_RSS_FRAG_IPV6},
+	{"ipv6-tcp", ETH_RSS_NONFRAG_IPV6_TCP},
+	{"ipv6-udp", ETH_RSS_NONFRAG_IPV6_UDP},
+	{"ipv6-sctp", ETH_RSS_NONFRAG_IPV6_SCTP},
+	{"ipv6-other", ETH_RSS_NONFRAG_IPV6_OTHER},
+	{"l2-payload", ETH_RSS_L2_PAYLOAD},
+	{"ipv6-ex", ETH_RSS_IPV6_EX},
+	{"ipv6-tcp-ex", ETH_RSS_IPV6_TCP_EX},
+	{"ipv6-udp-ex", ETH_RSS_IPV6_UDP_EX},
+};
+
+struct rss_type_fn {
+	uint8_t enabled;
+	struct rss_type_info *info;
+};
+
+/**
+ * Buffer  for last received packets
+ */
+#define PKT_RECORD_SIZE		256
+
+struct pkt_info {
+	uint64_t	n;			/**< Packet number */
+	uint8_t		port_id;
+	uint8_t		queue_id;
+	uint64_t	ol_flags;	/**< Offload features. */
+	uint32_t	rss;		/**< RSS hash result if RSS enabled */
+};
+
+struct pkt_record {
+	uint64_t		count;			/**< Total number of received packets */
+	int				top;
+	struct pkt_info pkt_info[PKT_RECORD_SIZE];
+};
+
+/**
+ * The data structure associated with each port.
+ */
+struct rte_port {
+	struct rte_eth_dev_info dev_info;   /**< PCI info + driver name */
+	struct rte_eth_conf     dev_conf;   /**< Port configuration. */
+
+	uint16_t nb_rx_queues;			/**< Total number of rx queues */
+	uint16_t nb_tx_queues;			/**< Total number of tx queues*/
+
+	int bond_mode;
+	struct ether_addr addr;
+
+	int rss_type_count;
+	struct rss_type_fn rss_type_fn[RTE_DIM(rss_type_table)];
+	struct rte_eth_rss_reta_entry64 reta_conf[512 / RTE_RETA_GROUP_SIZE];
+	uint8_t rss_key[40];
+
+
+	struct rte_eth_stats stats;
+	uint64_t rxq_ipackets[128];
+
+	struct pkt_record record;
+
+	uint8_t rss;				/**< RSS enabled for port */
+	uint8_t promiscuous;		/**< promiscuous mode enabled for port */
+	uint8_t enabled;			/**< port is enabled */
+};
+
+extern int nb_ports;
+extern struct rte_port *ports;
+extern rte_atomic64_t counter;	/**< Received packet's counter */
+
+void update_port_info(uint8_t port_id);
+
+int rss_enable(uint8_t port_id, int enabled);
+
+int key_set(uint8_t port_id, uint8_t *key, uint8_t len);
+int key_set_random(uint8_t port_id, uint8_t len);
+
+int reta_set_default(uint8_t port_id);
+int reta_set_random(uint8_t port_id);
+int reta_set_all(uint8_t port_id, uint8_t value);
+int reta_set_weights(uint8_t port_id, uint64_t *weights);
+
+struct pkt_info *record_pkt(uint8_t port_id, int queue_id, struct rte_mbuf *mb);
+
+void ui_loop(void);
+
+
+#endif /* BONDRSS_H_ */
diff --git a/examples/bond_rss/config.c b/examples/bond_rss/config.c
new file mode 100644
index 0000000..39c00e5
--- /dev/null
+++ b/examples/bond_rss/config.c
@@ -0,0 +1,251 @@ 
+/*-
+ *   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 <time.h>
+#include <stdlib.h>
+#include "bondrss.h"
+
+static const struct rte_eth_conf port_conf_default = {
+		.rxmode = {
+				.max_rx_pkt_len	= ETHER_MAX_LEN,
+				.split_hdr_size	= 0,
+				.header_split	= 0, /**< Header Split disabled. */
+				.hw_ip_checksum	= 0, /**< IP checksum offload disabled. */
+				.hw_vlan_filter	= 1, /**< VLAN filtering enabled. */
+				.hw_vlan_strip	= 1, /**< VLAN strip enabled. */
+				.hw_vlan_extend	= 0, /**< Extended VLAN disabled. */
+				.jumbo_frame	= 0, /**< Jumbo Frame Support disabled. */
+				.hw_strip_crc	= 0, /**< CRC stripping by hardware disabled. */
+				.enable_scatter	= 0, /**< scatter rx disabled */
+		},
+		.rx_adv_conf = {
+				.rss_conf = {
+						.rss_key	= NULL,
+						.rss_hf		= ETH_RSS_IPV4,
+				},
+		},
+};
+
+
+void
+update_port_info(uint8_t port_id)
+{
+	int i;
+	struct rte_port *port;
+
+	port = &ports[port_id];
+
+	rte_eth_dev_info_get(port_id, &(port->dev_info));
+
+	port->promiscuous = rte_eth_promiscuous_get(port_id);
+
+	/* Update device information */
+	rte_eth_dev_info_get(port_id, &port->dev_info);
+	rte_eth_macaddr_get(port_id, &port->addr);
+
+	port->dev_conf.rx_adv_conf.rss_conf.rss_key = port->rss_key;
+	port->dev_conf.rx_adv_conf.rss_conf.rss_key_len = 40;
+	rte_eth_dev_rss_hash_conf_get(port_id,
+			&(port->dev_conf.rx_adv_conf.rss_conf));
+
+	/* select all fields to be fetched */
+	for (i = 0; i < port->dev_info.reta_size / RTE_RETA_GROUP_SIZE; i++)
+		port->reta_conf[i].mask = ~0LL;
+
+	rte_eth_dev_rss_reta_query(port_id, port->reta_conf,
+			port->dev_info.reta_size);
+
+}
+
+/**
+ * Try to enable RSS for port_id.
+ */
+int
+rss_enable(uint8_t port_id, int enabled)
+{
+	struct rte_port *port;
+	int retval;
+
+	port = &ports[port_id];
+
+	rte_eth_dev_stop(port_id);
+
+	if (enabled) {
+		port->dev_conf.rxmode.mq_mode = ETH_MQ_RX_RSS;
+		port->dev_conf.rx_adv_conf.rss_conf.rss_hf = ETH_RSS_IPV4;
+	} else {
+		port->dev_conf.rxmode.mq_mode = 0;
+		port->dev_conf.rx_adv_conf.rss_conf.rss_hf = 0;
+	}
+
+	retval = rte_eth_dev_configure(port_id, port->nb_rx_queues,
+	                               port->nb_tx_queues, &port->dev_conf);
+	if (retval != 0)
+		return retval;
+
+	retval = rte_eth_dev_start(port_id);
+
+	return 0;
+}
+
+int
+key_set(uint8_t port_id, uint8_t *key, uint8_t len)
+{
+	struct rte_eth_rss_conf rss_conf;
+
+	int i;
+
+	rss_conf.rss_key = NULL;
+	rss_conf.rss_key_len = 0;
+	i = rte_eth_dev_rss_hash_conf_get(port_id, &rss_conf);
+
+	rss_conf.rss_key = key;
+	rss_conf.rss_key_len = len;
+
+	i = rte_eth_dev_rss_hash_update(port_id,
+			&rss_conf);
+
+	return i;
+}
+
+/**
+ * Set random RSS key value
+ */
+int
+key_set_random(uint8_t port_id, uint8_t len)
+{
+	int i;
+	uint8_t key[40];
+
+	for (i = 0; i < len; i++)
+		key[i] = rand() % 256;
+
+	return key_set(port_id, key, len);
+}
+
+/**
+ * Set random RETA values
+ */
+int
+reta_set_random(uint8_t port_id)
+{
+	struct rte_eth_rss_reta_entry64 reta_conf[512/RTE_RETA_GROUP_SIZE];
+	int i, j;
+
+	int reta_size = ports[port_id].dev_info.reta_size;
+
+	for (i = 0; i < reta_size / RTE_RETA_GROUP_SIZE; i++) {
+		/* select all fields to set */
+		reta_conf[i].mask = ~0LL;
+		for (j = 0; j < RTE_RETA_GROUP_SIZE; j++)
+			reta_conf[i].reta[j] = rand() % ports[port_id].nb_rx_queues;
+	}
+
+	return rte_eth_dev_rss_reta_update(port_id, reta_conf, reta_size);
+}
+
+/**
+ * Set default RETA values
+ */
+int
+reta_set_default(uint8_t port_id)
+{
+	struct rte_eth_rss_reta_entry64 reta_conf[512/RTE_RETA_GROUP_SIZE];
+	int i, j;
+
+	int reta_size = ports[port_id].dev_info.reta_size;
+
+	for (i = 0; i < reta_size / RTE_RETA_GROUP_SIZE; i++) {
+		/* select all fields to set */
+		reta_conf[i].mask = ~0LL;
+		for (j = 0; j < RTE_RETA_GROUP_SIZE; j++)
+			reta_conf[i].reta[j] = j % nb_rxq;
+	}
+
+	return rte_eth_dev_rss_reta_update(port_id, reta_conf, reta_size);
+}
+
+/**
+ * Fill the RETA with values
+ */
+int
+reta_set_all(uint8_t port_id, uint8_t value)
+{
+	struct rte_eth_rss_reta_entry64 reta_conf[512/RTE_RETA_GROUP_SIZE];
+	int i, j;
+
+	int reta_size = ports[port_id].dev_info.reta_size;
+
+	for (i = 0; i < reta_size / RTE_RETA_GROUP_SIZE; i++) {
+		/* select all fields to set */
+		reta_conf[i].mask = ~0LL;
+		for (j = 0; j < RTE_RETA_GROUP_SIZE; j++)
+			reta_conf[i].reta[j] = value;
+	}
+
+	return rte_eth_dev_rss_reta_update(port_id, reta_conf, reta_size);
+}
+
+/**
+ * Set the RETA basing on weights table
+ */
+int
+reta_set_weights(uint8_t port_id, uint64_t *weights)
+{
+	struct rte_eth_rss_reta_entry64 reta_conf[512/RTE_RETA_GROUP_SIZE];
+	unsigned i, j, k;
+
+	unsigned reta_size = ports[port_id].dev_info.reta_size;
+
+	uint64_t sum = 0, sum2;
+
+	for (i = 0; i < ports[port_id].nb_rx_queues; i++)
+		sum += weights[i];
+	sum2 = sum * (ports[port_id].nb_rx_queues-1);
+
+	if (sum2 == 0)
+		return 0;
+
+	for (i = 0; i < reta_size / RTE_RETA_GROUP_SIZE; i++)
+		reta_conf[i].mask = ~0LL;
+	k = 0;
+	for (i = 0; i < ports[port_id].nb_rx_queues; i++) {
+		for (j = 0; k < reta_size &&
+				j < ((sum - weights[i]) * reta_size + 1) / sum2; j++) {
+			reta_conf[k/RTE_RETA_GROUP_SIZE].reta[k%RTE_RETA_GROUP_SIZE] = i;
+			k++;
+		}
+	}
+
+	return rte_eth_dev_rss_reta_update(port_id, reta_conf, reta_size);
+}
diff --git a/examples/bond_rss/ui.c b/examples/bond_rss/ui.c
new file mode 100644
index 0000000..be3767c
--- /dev/null
+++ b/examples/bond_rss/ui.c
@@ -0,0 +1,915 @@ 
+/*-
+ *   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 <rte_atomic.h>
+
+#include <ncurses.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <menu.h>
+#include "bondrss.h"
+
+WINDOW *win_footer	= NULL;
+WINDOW *win_header	= NULL;
+WINDOW *win_list	= NULL;
+WINDOW *win_stats	= NULL;
+WINDOW *win_reta	= NULL;
+WINDOW *win_err		= NULL;
+
+int scr_lines		= 0;
+int scr_cols		= 0;
+
+unsigned win_header_lines;
+unsigned win_list_lines;
+unsigned win_stats_lines;
+unsigned win_reta_lines;
+unsigned win_err_lines = 0;
+
+const char  *ui_status_text;
+static char *ui_err_text[5];
+
+struct rte_port *selected_port;
+static uint8_t  selected_port_id;
+
+struct rte_port *focused_port;
+static uint8_t focused_port_id;
+
+#define UI_MODE_PORT		0
+#define UI_MODE_RSS_FN		1
+#define UI_MODE_RSS_KEY		2
+#define UI_MODE_RETA		3
+#define UI_MODE_HELP		4
+
+int ui_mode = UI_MODE_PORT;
+
+static uint16_t _is_lock;
+static uint16_t _is_ready;
+
+static uint16_t _do_configure;
+static uint16_t _do_repaint;
+static uint16_t _do_repaint_stats;
+static uint16_t _do_repaint_list;
+static uint16_t _do_repaint_info;
+static uint16_t _do_repaint_err;
+
+
+struct help_page {
+	const char *title;
+	const char *body;
+};
+
+static struct help_page help_pages[3] = {
+		{
+				.title = "About",
+
+				.body =
+				"\n\n"
+				" ______   ______   ______   _____    ______   ______   ______\n"
+				"| |  | \\ / |  | \\ | |  \\ \\ | | \\ \\  | |  | \\ / |      / |\n"
+				"| |--| < | |  | | | |  | | | |  | | | |__| | '------. '------.\n"
+				"|_|__|_/ \\_|__|_/ |_|  |_| |_|_/_/  |_|  \\_\\  ____|_/  ____|_/\n"
+				"\n\n"
+				"This application allows you to test RSS configuration for "
+				"bonded devices\nchanging configuration dynamically, during "
+				"receiving packets on slaves and\nobserve the changes in its "
+				"distribution over queues.\n"
+				"\n"
+
+		},
+		{
+				.title = "Introduction",
+				.body = "\n\n"
+				"After initialization process, all accessible ports are "
+				"attached to one\nbonding as slaves.\n"
+				"\n"
+
+				"Monitor screen is divided into five main parts:\n"
+				" - Port selection (on the very top)\n"
+				" - RSS Configuration for selected port including hash "
+				"function, key and\n   RETA\n"
+				" - Incoming packets statistics\n"
+				" - Incoming packets list for selected port\n"
+				" - Status bar with contextual information about selected "
+				"part\n"
+				"\n\n"
+
+				"[up], [down] arrows key lets you highlight a part "
+				"and parameter to change.\n\n"
+		},
+		{
+				.title = "Key bindings",
+				.body =
+				"_Port_selection_\n\n"
+				"  [left], [right] - select port,\n"
+				"  [space]         - force turning on/off RSS mode,\n"
+				"  [p]             - turn on/off promiscuous mode,\n"
+				"  [c]             - clear statistics and incoming packet list,\n"
+				"\n"
+				"_RSS_hash_function_\n\n"
+				"  [1]-[9]         - toggle selected hash function,\n"
+				"  [a]             - select all,\n"
+				"\n"
+				"_RSS_key_\n\n"
+				"  [1]-[4]         - set one of predefined key value,\n"
+				"  [r]             - randomize value,\n"
+				"\n"
+				"_RSS_RETA_\n\n"
+				"  [number]        - fill RETA with number,\n"
+				"  [r]             - randomize value,\n"
+				"  [d]             - set default value,"
+
+		}
+};
+
+static int
+ui_lock(int wait)
+{
+	int result;
+
+	while (!(result = rte_atomic16_cmpset(&_is_lock, 0, 1)) && wait)
+		rte_pause();
+
+	return result;
+}
+
+static int
+ui_unlock(void)
+{
+	return rte_atomic16_cmpset(&_is_lock, 1, 0);
+}
+
+/**
+ * Predefined key values
+ */
+static uint8_t RSS_KEY[4][40] = {
+	{	0x6D, 0x5A, 0x56, 0xDA, 0x25, 0x5B, 0x0E, 0xC2, 0x41, 0x67, 0x25, 0x3D,
+		0x43, 0xA3, 0x8F, 0xB0, 0xD0, 0xCA, 0x2B, 0xCB, 0xAE, 0x7B, 0x30, 0xB4,
+		0x77, 0xCB, 0x2D, 0xA3, 0x80, 0x30, 0xF2, 0x0C, 0x6A, 0x42, 0xB7, 0x3B,
+		0xBE, 0xAC, 0x01, 0xFA
+	},
+	{	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00,
+	},
+	{	0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
+		0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
+		0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
+		0x6D, 0x5A, 0x6D, 0x5A,
+	},
+	{	0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
+		0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+		0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
+		0x25, 0x26, 0x27, 0x28,
+	}
+};
+
+static void
+win_err_addline(const char *text)
+{
+	int i;
+
+	if (win_err_lines < 5) {
+		win_err_lines++;
+		_do_configure = 1;
+	} else {
+		free(ui_err_text[0]);
+		for (i = 1; i < 5; i++)
+			ui_err_text[i - 1] = ui_err_text[i];
+	}
+
+	ui_err_text[win_err_lines - 1] = strdup(text);
+	_do_repaint_err = 1;
+}
+
+static void
+win_err_fini(void)
+{
+	int i;
+	for (i = 0; i < (int)win_err_lines; i++)
+		free(ui_err_text[i]);
+}
+
+/**
+ * Draw full information for port
+ */
+static void
+win_header_draw(uint8_t port_id)
+{
+	int i, j;
+	attr_t a;
+
+	update_port_info(selected_port_id);
+
+	/* Draw title bar */
+	if (ui_mode == UI_MODE_PORT)
+		wattron(win_header, COLOR_PAIR(2));
+	else
+		wattron(win_header, COLOR_PAIR(4));
+
+	mvwprintw(win_header, 0, 0, "::");
+
+	for (i = 0; i < nb_ports; i++) {
+		if (ports[i].enabled) {
+			const char *name = (ports[i].bond_mode >= 0 ? "BOND" : "SLAVE");
+
+			if (i == port_id) {
+				wattron(win_header, A_REVERSE);
+				wprintw(win_header, " %s-%d ", name, (unsigned) i);
+				wattroff(win_header, A_REVERSE);
+			} else
+				wprintw(win_header, " %s-%d ", name, (unsigned) i);
+		}
+	}
+
+	for (i = scr_cols - getcurx(win_header) - 3; i > 0; i--)
+		waddch(win_header, ':');
+
+	waddch(win_header,
+			(ports[port_id].rss ? 'R' : '-'));
+	waddch(win_header, ports[port_id].bond_mode >= 0 ?
+			ports[port_id].bond_mode + '0' : '-');
+	waddch(win_header, ports[port_id].promiscuous == 0 ? '-' : 'P');
+
+	if (ui_mode == UI_MODE_PORT)
+		wattroff(win_header, COLOR_PAIR(2));
+	else
+		wattroff(win_header, COLOR_PAIR(4));
+
+	/* Redraw RSS-Fn */
+	selected_port->rss_type_count = 0;
+	for (i = 0; i < (int) RTE_DIM(rss_type_table); i++) {
+		if (selected_port->dev_info.flow_type_rss_offloads
+				& rss_type_table[i].rss_type) {
+			selected_port->rss_type_fn[selected_port->rss_type_count].info =
+				&rss_type_table[i];
+			selected_port->rss_type_fn[selected_port->rss_type_count].enabled =
+				((selected_port->dev_conf.rx_adv_conf.rss_conf.rss_hf
+						& rss_type_table[i].rss_type) ? 1 : 0);
+			selected_port->rss_type_count++;
+		}
+	}
+
+	a = (ui_mode == UI_MODE_RSS_FN ? COLOR_PAIR(2) : COLOR_PAIR(1));
+
+	wattron(win_header, a);
+	mvwprintw(win_header, 1, 0, "FN:  ");
+	for (i = 0; i < selected_port->rss_type_count; i++) {
+		if (selected_port->rss_type_fn[i].enabled)
+			wattron(win_header, COLOR_PAIR(3));
+		waddstr(win_header, selected_port->rss_type_fn[i].info->str);
+		if (selected_port->rss_type_fn[i].enabled)
+			wattron(win_header, a);
+		waddch(win_header, ' ');
+	}
+	wattroff(win_header, a);
+
+	/* Redraw RSS-Key */
+	if (ui_mode == UI_MODE_RSS_KEY)
+		wattron(win_header, COLOR_PAIR(2));
+	mvwprintw(win_header, 2, 0, "KEY: ");
+	for (i = 0; i < 40; i++)
+		wprintw(win_header, "%02X",
+		        selected_port->dev_conf.rx_adv_conf.rss_conf.rss_key[i]);
+	if (ui_mode == UI_MODE_RSS_KEY)
+		wattroff(win_header, COLOR_PAIR(2));
+
+
+	/* Redraw RETA window */
+	int idx, shift;
+
+	if (ui_mode == UI_MODE_RETA)
+		wattron(win_reta, COLOR_PAIR(2));
+
+	for (j = 0; j < selected_port->dev_info.reta_size / 16; j++) {
+		wmove(win_reta, j, 0);
+		for (i = 0; i < 16; i++) {
+			idx = (j*16 + i) / RTE_RETA_GROUP_SIZE;
+			shift = (j*16 + i) % RTE_RETA_GROUP_SIZE;
+			waddch(win_reta, ACS_VLINE);
+			wprintw(win_reta, "%d", selected_port->reta_conf[idx].reta[shift]);
+		}
+		waddch(win_reta, ACS_VLINE);
+	}
+
+	if (ui_mode == UI_MODE_RETA)
+		wattroff(win_reta, COLOR_PAIR(2));
+	wnoutrefresh(win_reta);
+
+
+	/* Stats repaint */
+	if (_do_repaint_stats) {
+		uint64_t total;
+
+		rte_eth_stats_get(selected_port_id, &(selected_port->stats));
+
+		wmove(win_stats, 0, 0);
+		total = 0;
+		for (i = 0; i < selected_port->nb_rx_queues; i++) {
+			wprintw(win_stats, "Queue %d: %10"PRIu64
+		        " (%10" PRIu64 ")\n", i, selected_port->rxq_ipackets[i],
+				selected_port->stats.q_ipackets[i]);
+			total += selected_port->rxq_ipackets[i];
+		}
+
+		wprintw(win_stats, "  Total: %10"PRIu64
+	        " (%10" PRIu64 ")\n", total, selected_port->stats.ipackets);
+
+		_do_repaint_stats = 0;
+		wnoutrefresh(win_stats);
+	}
+
+	if (_do_repaint_err && win_err_lines > 0) {
+		for (i = 0; i < (int)win_err_lines; i++) {
+			mvwprintw(win_err, i, 0, ui_err_text[i]);
+			wclrtoeol(win_err);
+		}
+		_do_repaint_err = 0;
+		wnoutrefresh(win_err);
+	}
+
+	mvwhline(win_header, win_header_lines - 1, 0, 0, scr_cols);
+}
+
+static void
+win_footer_draw(const char *text)
+{
+	if (text != NULL)
+		ui_status_text = text;
+
+	wclear(win_footer);
+	wmove(win_footer, 0, 0);
+	wprintw(win_footer, ui_status_text);
+
+	wprintw(win_footer, "; [arrows]navigate; [q]quit");
+	wnoutrefresh(win_footer);
+}
+
+static void
+win_list_draw(void)
+{
+	unsigned n, i;
+
+	struct pkt_info *pkt_info;
+
+	struct rte_port *port = &ports[focused_port_id];
+	struct pkt_record *record = &(port->record);
+
+	n = (win_list_lines > record->count) ? record->count : win_list_lines;
+
+	wmove(win_list, 0, 0);
+
+	for (i = 0; i < n; i++) {
+
+		pkt_info = &record->pkt_info[(record->top - n + i + PKT_RECORD_SIZE)
+				% PKT_RECORD_SIZE];
+
+		wmove(win_list, i, 0);
+
+		wprintw(win_list, "%5"PRIu64, (unsigned) pkt_info->n);
+		waddch(win_list, ACS_VLINE);
+
+		wprintw(win_list, "%2d: ", (unsigned) pkt_info->queue_id);
+		wprintw(win_list, "%08x  ", (unsigned) pkt_info->rss);
+
+		if (pkt_info->ol_flags != 0) {
+			unsigned rxf;
+			const char *name;
+
+			for (rxf = 0; rxf < sizeof(pkt_info->ol_flags) * 8; rxf++) {
+				if ((pkt_info->ol_flags & (1ULL << rxf)) == 0)
+					continue;
+				name = rte_get_rx_ol_flag_name(1ULL << rxf);
+				if (name == NULL)
+					continue;
+				wprintw(win_list, "%s ", name);
+			}
+		}
+		wclrtoeol(win_list);
+	}
+
+	wclrtobot(win_list);
+	wnoutrefresh(win_list);
+}
+
+static void
+ui_repaint(void)
+{
+	switch (ui_mode) {
+	default:
+		win_header_draw(selected_port_id);
+		wnoutrefresh(win_header);
+
+		if (_do_repaint_list) {
+			win_list_draw();
+			_do_repaint_list = 0;
+		}
+
+		break;
+	}
+
+	win_footer_draw(NULL);
+	_do_repaint = 0;
+}
+
+static void
+ui_mode_set(int mode)
+{
+	ui_mode = (mode + 4) % 4;
+	switch (ui_mode) {
+	case UI_MODE_PORT:
+		ui_status_text = "Port: [p]promiscuous; [c]clear stats";
+		break;
+	case UI_MODE_RSS_FN:
+		ui_status_text = "RSS Fn: [1-9]toggle fn";
+		break;
+	case UI_MODE_RETA:
+		ui_status_text =
+			"RETA: [d]set default; [r]randomize; [0-9]fill table with value";
+		break;
+	case UI_MODE_RSS_KEY:
+		ui_status_text = "RSS Key: [r]randomize; [1-4]select predefined key";
+		break;
+	}
+	_do_repaint = 1;
+}
+
+static void
+ui_configure(void)
+{
+	int win_reta_cols = (16 * 2 + 1);
+	win_reta_lines = selected_port->dev_info.reta_size / 16;
+	win_stats_lines = selected_port->nb_rx_queues + 1;
+	win_header_lines = win_reta_lines;
+	if (win_header_lines < win_stats_lines)
+		win_header_lines = win_stats_lines;
+	win_header_lines += 5;
+	win_list_lines = scr_lines - win_header_lines - win_err_lines - 1;
+
+	if (win_footer == NULL) {
+		win_footer = newwin(1, scr_cols, scr_lines - 1, 0);
+		ui_status_text = "";
+		wbkgdset(win_footer, COLOR_PAIR(1));
+	} else {
+		wresize(win_footer, 1, scr_cols);
+		mvwin(win_footer, scr_lines - 1, 0);
+	}
+
+	if (win_err == NULL) {
+		win_err = newwin(1, scr_cols, scr_lines - 1 - win_err_lines, 0);
+		ui_status_text = "";
+		wbkgdset(win_err, COLOR_PAIR(5));
+	} else {
+		wresize(win_err, win_err_lines, scr_cols);
+		mvwin(win_err, scr_lines - win_err_lines - 1, 0);
+	}
+
+
+	if (win_header == NULL) {
+		win_header = newwin(win_header_lines, scr_cols, 0, 0);
+		wbkgdset(win_header, COLOR_PAIR(1));
+	} else
+		wresize(win_header, win_header_lines, scr_cols);
+
+	if (win_stats == NULL)
+		win_stats = subwin(win_header, win_stats_lines,
+				scr_cols - win_reta_cols - 7, 4, win_reta_cols + 7);
+	else
+		wresize(win_stats, win_stats_lines, scr_cols - win_reta_cols - 7);
+
+	if (win_reta == NULL)
+		win_reta = subwin(win_header, win_reta_lines, win_reta_cols,
+				4, 5);
+	else
+		wresize(win_reta, win_reta_lines, win_reta_cols);
+
+	if (win_list == NULL)
+		win_list = newwin(win_list_lines, scr_cols, win_header_lines, 0);
+	else {
+		wresize(win_list, win_list_lines, scr_cols);
+		mvwin(win_list, win_header_lines, 0);
+	}
+
+	wclear(win_header);
+	wclear(win_reta);
+
+	_do_configure = 0;
+	_do_repaint = 1;
+	_do_repaint_list = 1;
+	_do_repaint_stats = 1;
+	_do_repaint_info = 1;
+	_do_repaint_err = 1;
+
+}
+
+static void
+ui_resize(void)
+{
+	if (COLS != scr_cols || LINES != scr_lines) {
+		scr_cols = COLS;
+		scr_lines = LINES;
+		_do_configure = 1;
+	}
+}
+
+static void
+ui_update(void)
+{
+	if (ui_lock(1)) {
+		if (_is_ready) {
+			if (_do_configure)
+				ui_configure();
+			if (_do_repaint) {
+				ui_repaint();
+				doupdate();
+			}
+		}
+		ui_unlock();
+	}
+}
+
+static void
+ui_fini(void)
+{
+	win_err_fini();
+	endwin();
+}
+
+static void
+ui_init(void)
+{
+	initscr();
+	start_color();
+	init_pair(1, COLOR_YELLOW, COLOR_BLUE);
+	init_pair(2, COLOR_BLACK, COLOR_CYAN);
+	init_pair(3, COLOR_BLACK, COLOR_YELLOW);
+	init_pair(4, COLOR_BLACK, COLOR_WHITE);
+	init_pair(5, COLOR_YELLOW, COLOR_RED);
+
+	cbreak();
+	noecho();
+	curs_set(FALSE);
+	keypad(stdscr, TRUE);
+	timeout(100);
+
+	scr_cols = COLS;
+	scr_lines = LINES;
+	_do_configure = 1;
+	_is_ready = 1;
+}
+
+static int
+port_select(uint8_t port_id)
+{
+	if (ports[port_id].enabled == 1) {
+		focused_port_id = selected_port_id = port_id;
+
+		selected_port = &ports[selected_port_id];
+		focused_port = &ports[focused_port_id];
+
+		update_port_info(selected_port_id);
+
+		_do_configure = 1;
+		_do_repaint = 1;
+		_do_repaint_info = 1;
+		_do_repaint_list = 1;
+		_do_repaint_stats = 1;
+		return selected_port_id;
+	}
+	return -1;
+}
+
+/**
+ * Record packet from mbuf
+ */
+struct pkt_info *
+record_pkt(uint8_t port_id, int queue_id, struct rte_mbuf *mb)
+{
+	struct rte_port *port = &ports[port_id];
+	struct pkt_record *record = &(port->record);
+	struct pkt_info *pkt_info = &(record->pkt_info[record->top]);
+
+	ui_lock(1);
+
+	record->count++;
+	pkt_info->n = mb->udata64;
+	pkt_info->ol_flags = mb->ol_flags;
+	pkt_info->queue_id = queue_id;
+	pkt_info->rss = mb->hash.rss;
+
+	record->top = (record->top + 1) % PKT_RECORD_SIZE;
+
+	_do_repaint = 1;
+	_do_repaint_list = 1;
+	_do_repaint_stats = 1;
+	ui_unlock();
+
+	return pkt_info;
+}
+
+static void
+ui_help(void) {
+	timeout(-1);
+
+	WINDOW *win_help_border = NULL;
+	WINDOW *win_help = NULL;
+	char title[256];
+	int page = 0;
+	int page_prev;
+	int page_next;
+	int c;
+	int cols;
+	int lines;
+	int top;
+	int left;
+
+	endwin();
+	initscr();
+	ui_resize();
+	ui_update();
+
+	cols = scr_cols > 80 ? 80 : scr_cols;
+	lines = scr_lines > 25 ? 25 : scr_lines;
+	top = (scr_lines - lines) / 2;
+	left = (scr_cols - cols) / 2;
+
+	win_help_border = newwin(lines, cols, top, left);
+	win_help = derwin(win_help_border, lines - 2, cols - 4, 1, 2);
+	wbkgdset(win_help_border, COLOR_PAIR(2));
+	wbkgdset(win_help, COLOR_PAIR(2));
+
+	do {
+		page_prev = (page + RTE_DIM(help_pages) - 1) % RTE_DIM(help_pages);
+		page_next = (page + 1) % RTE_DIM(help_pages);
+
+		wclear(win_help_border);
+		box(win_help_border, 0 , 0);
+
+		sprintf(title, "[ Help: %s ]", help_pages[page].title);
+		mvwprintw(win_help_border, 0, (cols - strlen(title)) / 2 - 2, "%s",
+		          title);
+
+		sprintf(title, "< %s ]", help_pages[page_prev].title);
+		mvwprintw(win_help_border, lines - 1, 1, "%s", title);
+
+		sprintf(title, "[ Page %d of %d ]", page + 1, (int)RTE_DIM(help_pages));
+		mvwprintw(win_help_border, lines - 1, (cols - strlen(title)) / 2 - 2,
+		          "%s", title);
+
+		sprintf(title, "[ %s >", help_pages[page_next].title);
+		mvwprintw(win_help_border, lines - 1, cols - strlen(title) - 1, "%s",
+		          title);
+
+		wrefresh(win_help_border);
+
+		wclear(win_help);
+
+		wmove(win_help, 0, 0);
+
+		wprintw(win_help,
+				help_pages[page].body
+			);
+
+		wrefresh(win_help);
+
+		switch (c = getch()) {
+
+		case KEY_RESIZE:
+			endwin();
+			initscr();
+			ui_resize();
+			ui_update();
+
+			cols = scr_cols > 80 ? 80 : scr_cols;
+			lines = scr_lines > 25 ? 25 : scr_cols;
+			top = (scr_lines - lines) / 2;
+			left = (scr_cols - cols) / 2;
+
+			wresize(win_help_border, lines, cols);
+			wresize(win_help, lines - 2, cols - 4);
+			break;
+
+		case KEY_LEFT:
+			page = page_prev;
+			break;
+
+		case KEY_RIGHT:
+			page = page_next;
+			break;
+
+		default:
+			c = -1; /* Exit */
+			break;
+		}
+
+
+	} while (c != -1);
+
+	timeout(100);
+	delwin(win_help);
+	delwin(win_help_border);
+	_do_configure = 1;
+	_do_repaint = 1;
+}
+
+/* main processing loop */
+void
+ui_loop(void)
+{
+	int c;
+	int i;
+	int port_id;
+
+	ui_init();
+	port_select(bond_port_id);
+
+	ui_mode_set(UI_MODE_PORT);
+
+	while ((c = getch()) != 'q') {
+		switch (c) {
+		case -1:
+			ui_update();
+			break;
+
+		case 'c':
+			/* clear stats */
+			ui_lock(1);
+
+			rte_atomic64_clear(&counter);
+			for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++) {
+				if (ports[port_id].enabled) {
+					ports[port_id].record.count = 0;
+					ports[port_id].record.top = 0;
+					for (i = 0; i < ports[port_id].nb_rx_queues; i++)
+						ports[port_id].rxq_ipackets[i] = 0;
+					rte_eth_stats_reset(port_id);
+				}
+			}
+
+			_do_repaint = 1;
+			ui_unlock();
+			break;
+
+		case ' ':
+			focused_port->rss ^= 1;
+			rss_enable(focused_port_id, focused_port->rss);
+			_do_configure = 1;
+			_do_repaint = 1;
+			break;
+
+		case 'p':
+			if (focused_port->promiscuous)
+				rte_eth_promiscuous_disable(focused_port_id);
+			else
+				rte_eth_promiscuous_enable(focused_port_id);
+			focused_port->promiscuous ^= 1;
+			_do_configure = 1;
+			_do_repaint = 1;
+			break;
+
+		case KEY_RESIZE:
+			ui_resize();
+			break;
+
+		case KEY_UP:
+			ui_mode_set(ui_mode - 1);
+			break;
+		case KEY_DOWN:
+			ui_mode_set(ui_mode + 1);
+			break;
+		case 'h':
+		case '?':
+			ui_help();
+			break;
+
+		default:
+			switch (ui_mode) {
+			case UI_MODE_PORT:
+				switch (c) {
+				case KEY_LEFT:
+					for (i = 1; i < nb_ports; i++)
+						if (port_select(
+								(selected_port_id - i + nb_ports) % nb_ports)
+								!= -1)
+							break;
+					break;
+
+				case KEY_RIGHT:
+					for (i = 1; i < nb_ports; i++)
+						if (port_select(
+								(selected_port_id + i + nb_ports) % nb_ports)
+								!= -1)
+							break;
+					break;
+
+				default:
+					i = (c - '1');
+					port_select(i);
+					break;
+				}
+				break;
+
+			case UI_MODE_RSS_KEY:
+				switch (c) {
+				case 'r':
+					i = key_set_random(focused_port_id, 40);
+					if (i < 0)
+						win_err_addline("Cannot update RSS key");
+					break;
+
+				default:
+					c = c - '1';
+					if (c >= 0 && c < (int) RTE_DIM(RSS_KEY)) {
+						i = key_set(focused_port_id, RSS_KEY[c], 40);
+						if (i < 0)
+							win_err_addline("Cannot update RSS key");
+					}
+					break;
+				}
+				update_port_info(focused_port_id);
+				break;
+
+			case UI_MODE_RETA:
+				switch (c) {
+				case 'r':
+					reta_set_random(focused_port_id);
+					break;
+				case 'd':
+					reta_set_default(focused_port_id);
+					break;
+				default:
+					c = c - '0';
+					reta_set_all(focused_port_id, c);
+					break;
+				}
+				break;
+
+			case UI_MODE_RSS_FN:
+				switch (c) {
+				case 'a':
+					/* Set all */
+					focused_port->dev_conf.rx_adv_conf.rss_conf.rss_hf =
+						ports[focused_port_id].dev_info.flow_type_rss_offloads;
+					rte_eth_dev_rss_hash_update(focused_port_id,
+						&focused_port->dev_conf.rx_adv_conf.rss_conf);
+					break;
+
+				default:
+					c -= '1';
+					if (c >= 0 && c < focused_port->rss_type_count) {
+						uint64_t rss_type = 0;
+
+						focused_port->rss_type_fn[c].enabled ^= 1;	/* toggle */
+
+						for (i = 0; i < focused_port->rss_type_count; i++)
+							if (focused_port->rss_type_fn[i].enabled)
+								rss_type |=
+									focused_port->rss_type_fn[i].info->rss_type;
+						focused_port->dev_conf.rx_adv_conf.rss_conf.rss_hf =
+								rss_type;
+
+						rte_eth_dev_rss_hash_update(focused_port_id,
+								&focused_port->dev_conf.rx_adv_conf.rss_conf);
+
+					}
+				}
+				break;
+
+			}
+			_do_repaint = 1;
+			break;
+		}
+	}
+	ui_fini();
+}