* [dpdk-dev] [PATCH] User-space Ethool example @ 2015-07-20 14:12 Liang-Min Larry Wang 2015-07-20 14:12 ` [dpdk-dev] [PATCH] examples: new example: l2fwd-ethtool Liang-Min Larry Wang 0 siblings, 1 reply; 10+ messages in thread From: Liang-Min Larry Wang @ 2015-07-20 14:12 UTC (permalink / raw) To: dev; +Cc: Liang-Min Larry Wang This implementation is designed to provide an example illlustrating how to create a user-space ethtool library from existing ethdev APIs. This example only implements 19 popular used Ethtool and Netdevice ops as described in examples/l2fwd-ethtool/lib/rte_ethtool.h Liang-Min Larry Wang (1): examples: new example: l2fwd-ethtool examples/Makefile | 1 + examples/l2fwd-ethtool/Makefile | 48 + examples/l2fwd-ethtool/l2fwd-app/Makefile | 58 ++ examples/l2fwd-ethtool/l2fwd-app/main.c | 1025 ++++++++++++++++++++++ examples/l2fwd-ethtool/l2fwd-app/netdev_api.h | 770 ++++++++++++++++ examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h | 159 ++++ examples/l2fwd-ethtool/lib/Makefile | 57 ++ examples/l2fwd-ethtool/lib/rte_ethtool.c | 336 +++++++ examples/l2fwd-ethtool/lib/rte_ethtool.h | 385 ++++++++ examples/l2fwd-ethtool/nic-control/Makefile | 55 ++ examples/l2fwd-ethtool/nic-control/nic_control.c | 614 +++++++++++++ mk/rte.lib.mk | 2 + 12 files changed, 3510 insertions(+) create mode 100644 examples/l2fwd-ethtool/Makefile create mode 100644 examples/l2fwd-ethtool/l2fwd-app/Makefile create mode 100644 examples/l2fwd-ethtool/l2fwd-app/main.c create mode 100644 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h create mode 100644 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h create mode 100644 examples/l2fwd-ethtool/lib/Makefile create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.c create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.h create mode 100644 examples/l2fwd-ethtool/nic-control/Makefile create mode 100644 examples/l2fwd-ethtool/nic-control/nic_control.c -- 2.1.4 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [dpdk-dev] [PATCH] examples: new example: l2fwd-ethtool 2015-07-20 14:12 [dpdk-dev] [PATCH] User-space Ethool example Liang-Min Larry Wang @ 2015-07-20 14:12 ` Liang-Min Larry Wang 2015-07-23 15:00 ` [dpdk-dev] [PATCH v2 0/2] Example: l2fwd-ethtool Liang-Min Larry Wang 0 siblings, 1 reply; 10+ messages in thread From: Liang-Min Larry Wang @ 2015-07-20 14:12 UTC (permalink / raw) To: dev; +Cc: Liang-Min Larry Wang The example includes an ethtool library and two applications: one application is a non- DPDK process (nic-control) and the other is a DPDK l2fwd applicaiton (l2fwd-app). The nic-control process sends ethtool alike device management requests to l2fwd-app through a named pipe IPC. This example is designed to show how to build a ethtool shim library and how to use ethtool apis to manage device parameters. Signed-off-by: Liang-Min Larry Wang <liang-min.wang@intel.com> --- examples/Makefile | 1 + examples/l2fwd-ethtool/Makefile | 48 + examples/l2fwd-ethtool/l2fwd-app/Makefile | 58 ++ examples/l2fwd-ethtool/l2fwd-app/main.c | 1025 ++++++++++++++++++++++ examples/l2fwd-ethtool/l2fwd-app/netdev_api.h | 770 ++++++++++++++++ examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h | 159 ++++ examples/l2fwd-ethtool/lib/Makefile | 57 ++ examples/l2fwd-ethtool/lib/rte_ethtool.c | 336 +++++++ examples/l2fwd-ethtool/lib/rte_ethtool.h | 385 ++++++++ examples/l2fwd-ethtool/nic-control/Makefile | 55 ++ examples/l2fwd-ethtool/nic-control/nic_control.c | 614 +++++++++++++ mk/rte.lib.mk | 2 + 12 files changed, 3510 insertions(+) create mode 100644 examples/l2fwd-ethtool/Makefile create mode 100644 examples/l2fwd-ethtool/l2fwd-app/Makefile create mode 100644 examples/l2fwd-ethtool/l2fwd-app/main.c create mode 100644 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h create mode 100644 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h create mode 100644 examples/l2fwd-ethtool/lib/Makefile create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.c create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.h create mode 100644 examples/l2fwd-ethtool/nic-control/Makefile create mode 100644 examples/l2fwd-ethtool/nic-control/nic_control.c diff --git a/examples/Makefile b/examples/Makefile index b4eddbd..3dc049c 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -53,6 +53,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_KNI) += kni DIRS-y += l2fwd DIRS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += l2fwd-ivshmem DIRS-$(CONFIG_RTE_LIBRTE_JOBSTATS) += l2fwd-jobstats +DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += l2fwd-ethtool DIRS-y += l3fwd DIRS-$(CONFIG_RTE_LIBRTE_ACL) += l3fwd-acl DIRS-$(CONFIG_RTE_LIBRTE_POWER) += l3fwd-power diff --git a/examples/l2fwd-ethtool/Makefile b/examples/l2fwd-ethtool/Makefile new file mode 100644 index 0000000..d9ad439 --- /dev/null +++ b/examples/l2fwd-ethtool/Makefile @@ -0,0 +1,48 @@ +# BSD LICENSE +# +# Copyright(c) 2015 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overwritten by command line or environment +RTE_TARGET ?= x86_64-native-linuxapp-gcc + +include $(RTE_SDK)/mk/rte.vars.mk + +ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp") +$(error This application can only operate in a linuxapp environment, \ +please change the definition of the RTE_TARGET environment variable) +endif + +DIRS-y += lib nic-control l2fwd-app + +include $(RTE_SDK)/mk/rte.extsubdir.mk diff --git a/examples/l2fwd-ethtool/l2fwd-app/Makefile b/examples/l2fwd-ethtool/l2fwd-app/Makefile new file mode 100644 index 0000000..69405f2 --- /dev/null +++ b/examples/l2fwd-ethtool/l2fwd-app/Makefile @@ -0,0 +1,58 @@ +# BSD LICENSE +# +# Copyright(c) 2015 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overwritten by command line or environment +RTE_TARGET ?= x86_64-native-linuxapp-gcc + +include $(RTE_SDK)/mk/rte.vars.mk + +ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp") +$(error This application can only operate in a linuxapp environment, \ +please change the definition of the RTE_TARGET environment variable) +endif + +# binary name +APP = l2fwd-app + +# all source are stored in SRCS-y +SRCS-y := main.c + +CFLAGS += -O3 -D_GNU_SOURCE -pthread -I$(SRCDIR)/../lib +CFLAGS += $(WERROR_FLAGS) + +LDLIBS += -L$(subst l2fwd-app,lib,$(RTE_OUTPUT))/lib +LDLIBS += -lrte_ethtool + +include $(RTE_SDK)/mk/rte.extapp.mk diff --git a/examples/l2fwd-ethtool/l2fwd-app/main.c b/examples/l2fwd-ethtool/l2fwd-app/main.c new file mode 100644 index 0000000..73c29e3 --- /dev/null +++ b/examples/l2fwd-ethtool/l2fwd-app/main.c @@ -0,0 +1,1025 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2015 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/queue.h> +#include <netinet/in.h> +#include <setjmp.h> +#include <stdarg.h> +#include <ctype.h> +#include <errno.h> +#include <getopt.h> +#include <pthread.h> +#include <unistd.h> + +#include <rte_common.h> +#include <rte_log.h> +#include <rte_memory.h> +#include <rte_memcpy.h> +#include <rte_memzone.h> +#include <rte_tailq.h> +#include <rte_eal.h> +#include <rte_per_lcore.h> +#include <rte_launch.h> +#include <rte_atomic.h> +#include <rte_cycles.h> +#include <rte_prefetch.h> +#include <rte_lcore.h> +#include <rte_per_lcore.h> +#include <rte_branch_prediction.h> +#include <rte_interrupts.h> +#include <rte_pci.h> +#include <rte_random.h> +#include <rte_debug.h> +#include <rte_ether.h> +#include <rte_ethdev.h> +#include <rte_ring.h> +#include <rte_mempool.h> +#include <rte_mbuf.h> +#include "rte_ethtool.h" +#define NETDEV_OP_REPLY 1 +#include "netdev_api.h" + +#define RTE_LOGTYPE_L2FWD RTE_LOGTYPE_USER1 + +#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define NB_MBUF 8192 + +#define MAX_PKT_BURST 32 +#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */ + +#define is_vf_port(vf_mask, port_id) ((vf_mask & (1 << port_id)) > 0) +#define is_port_enabled(port_mask, port_id) ((port_mask & (1 << port_id)) > 0) +#define TX_PTHRESH 32 +#define TX_HTHRESH 0 +#define TX_WTHRESH 0 +/* + * Configurable number of RX/TX ring descriptors + */ +#define RTE_TEST_RX_DESC_DEFAULT 128 +#define RTE_TEST_TX_DESC_DEFAULT 512 +static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT; +static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT; + +/* ethernet addresses of ports */ +static struct ether_addr l2fwd_ports_eth_addr[RTE_MAX_ETHPORTS]; + +/* mask of enabled ports */ +static uint32_t l2fwd_enabled_port_mask; + +/* virtio setup enable */ +static int virtio_setup; + +/* list of enabled ports */ +static uint32_t l2fwd_dst_ports[RTE_MAX_ETHPORTS]; + +static unsigned int l2fwd_rx_queue_per_lcore = 1; + +struct mbuf_table { + unsigned len; + struct rte_mbuf *m_table[MAX_PKT_BURST]; +}; + +#define MAX_RX_QUEUE_PER_LCORE 16 +#define MAX_TX_QUEUE_PER_PORT 16 +struct lcore_queue_conf { + unsigned n_rx_port; + unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE]; + struct mbuf_table tx_mbufs[RTE_MAX_ETHPORTS]; + +} __rte_cache_aligned; +struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE]; + +static struct rte_eth_conf port_conf = { + .rxmode = { + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled */ + .hw_ip_checksum = 0, /**< IP checksum offload disabled */ + .hw_vlan_filter = 0, /**< VLAN filtering disabled */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ + .hw_strip_crc = 0, /**< CRC stripped by hardware */ + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, +}; + +static struct rte_eth_txconf tx_conf = { + .tx_thresh = { + .pthresh = TX_PTHRESH, + .hthresh = TX_HTHRESH, + .wthresh = TX_WTHRESH, + }, + .tx_free_thresh = 32, + .tx_rs_thresh = 32, + .txq_flags = 0xf00, +}; + +struct rte_mempool *l2fwd_pktmbuf_pool; + +/* Per-port statistics struct */ +struct l2fwd_port_statistics { + uint64_t tx; + uint64_t rx; + uint64_t dropped; +} __rte_cache_aligned; +struct l2fwd_port_statistics port_statistics[RTE_MAX_ETHPORTS]; + +/* A tsc-based timer responsible for triggering statistics printout */ +#define TIMER_MILLISECOND 2000000ULL /* around 1ms at 2 Ghz */ +#define MAX_TIMER_PERIOD 86400 /* 1 day max */ +/* default period is 10 seconds */ +static int64_t timer_period = 10 * TIMER_MILLISECOND * 1000; + +/* IPC done checking utility function */ +/* status of ipc completed */ +static rte_atomic64_t ipc_done; + +static inline void init_ipc_done(void) +{ + rte_atomic64_init(&ipc_done); +} + +static inline int is_ipc_done(void) +{ + return rte_atomic64_read(&ipc_done) > 0; +} + +static inline void set_ipc_done(void) +{ + rte_atomic64_inc(&ipc_done); +} + +/* Print out statistics on packets dropped */ +static void +print_stats(void) +{ + uint64_t total_packets_dropped, total_packets_tx, total_packets_rx; + unsigned portid; + + total_packets_dropped = 0; + total_packets_tx = 0; + total_packets_rx = 0; + + const char clr[] = { 27, '[', '2', 'J', '\0' }; + const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' }; + + /* Clear screen and move to top left */ + printf("%s%s", clr, topLeft); + + printf("\nPort statistics ===================================="); + + for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) { + /* skip disabled ports */ + if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) + continue; + printf("\nStatistics for port %u ----------------------------", + portid); + printf("\nPackets sent: %24"PRIu64, port_statistics[portid].tx); + printf("\nPackets received: %20"PRIu64, + port_statistics[portid].rx); + printf("\nPackets dropped: %21"PRIu64, + port_statistics[portid].dropped); + + total_packets_dropped += port_statistics[portid].dropped; + total_packets_tx += port_statistics[portid].tx; + total_packets_rx += port_statistics[portid].rx; + } + printf("\nAggregate statistics ==============================="); + printf("\nTotal packets sent: %18"PRIu64, total_packets_tx); + printf("\nTotal packets received: %14"PRIu64, total_packets_rx); + printf("\nTotal packets dropped: %15"PRIu64, total_packets_dropped); + printf("\n====================================================\n"); +} + +/* Send the burst of packets on an output interface */ +static int +l2fwd_send_burst(struct lcore_queue_conf *qconf, unsigned n, uint8_t port) +{ + struct rte_mbuf **m_table; + unsigned ret; + unsigned queueid = 0; + + m_table = (struct rte_mbuf **)qconf->tx_mbufs[port].m_table; + + ret = rte_eth_tx_burst(port, (uint16_t) queueid, m_table, (uint16_t) n); + port_statistics[port].tx += ret; + if (unlikely(ret < n)) { + port_statistics[port].dropped += (n - ret); + do { + rte_pktmbuf_free(m_table[ret]); + } while (++ret < n); + } + + return 0; +} + +/* Enqueue packets for TX and prepare them to be sent */ +static int +l2fwd_send_packet(struct rte_mbuf *m, uint8_t port) +{ + unsigned lcore_id, len; + struct lcore_queue_conf *qconf; + + lcore_id = rte_lcore_id(); + + qconf = &lcore_queue_conf[lcore_id]; + len = qconf->tx_mbufs[port].len; + qconf->tx_mbufs[port].m_table[len] = m; + len++; + + /* enough pkts to be sent */ + if (unlikely(len == MAX_PKT_BURST)) { + l2fwd_send_burst(qconf, MAX_PKT_BURST, port); + len = 0; + } + + qconf->tx_mbufs[port].len = len; + return 0; +} + +static void +l2fwd_simple_forward(struct rte_mbuf *m, unsigned portid) +{ + struct ether_hdr *eth; + void *tmp; + unsigned dst_port; + + dst_port = l2fwd_dst_ports[portid]; + eth = rte_pktmbuf_mtod(m, struct ether_hdr *); + + /* 02:00:00:00:00:xx */ + tmp = ð->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], ð->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, ¶m1_size, param1, + ¶m2_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, ¶m1_size, param1, + ¶m2_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(ðtool_thread, NULL, ðtool, &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, ®_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, ®_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(®_info, 0xFF, sizeof(struct ethtool_regs)); + + if (netdev_ethtool_get_regs(port_id, ®_info, data)) { + printf("Fail to read register\n"); + free(data); + return TEST_FAIL; + } + + regs = data; + printf("version: %x;", reg_info.version); + printf("register[0:%d]:\n", TEST_READ_SIZE-1); + for (i = 0; i < TEST_READ_SIZE; i++) + printf("%x ", regs[i]); + printf("\n"); + free(data); + return TEST_PASS; +} + +static int +test_mtu_t(int port_id) +{ + const int mtu = 1024; + + if (netdev_net_change_mtu(port_id, mtu)) { + printf("failed to set mtu to %d\n", mtu); + return TEST_FAIL; + } + return TEST_PASS; +} + +static int +test_vlan_rx_t(int port_id) +{ + /* add/remove vlan to vid */ + netdev_net_vlan_rx_add_vid(port_id, 0); + if (netdev_net_vlan_rx_add_vid(port_id, 0)) { + if (netdev_net_vlan_rx_kill_vid(port_id, 0)) { + printf("netdev_net_vlan_rx_kill_vid() fails\n"); + return TEST_FAIL; + } + return TEST_PASS; + } + printf("netdev_net_vlan_rx_add_vid() fails\n"); + return TEST_FAIL; +} + +static int +test_pauseparam_t(int port_id) +{ + struct ethtool_pauseparam pause_param_in; + struct ethtool_pauseparam pause_param_out; + + if (netdev_ethtool_get_pauseparam(port_id, &pause_param_in)) { + printf("netdev_ethtool_get_pauseparam() fails\n"); + return TEST_FAIL; + } + printf("pause setup: autoneg: %d ", + pause_param_in.autoneg); + printf("tx_pause: %d ", + pause_param_in.tx_pause); + printf("rx_pause: %d\n", + pause_param_in.rx_pause); + if (netdev_ethtool_set_pauseparam(port_id, &pause_param_in)) { + printf("netdev_ethtool_set_pauseparam() fails\n"); + return TEST_FAIL; + } + /* read-back pause frame setting for comparison */ + if (netdev_ethtool_get_pauseparam(port_id, &pause_param_out)) { + printf("netdev_ethtool_get_pauseparam() fails\n"); + return TEST_FAIL; + } + printf("pause frame checking auto:%s tx_pause:%s rx_pause:%s\n", + check(pause_param_in.autoneg == pause_param_out.autoneg), + check(pause_param_in.tx_pause == pause_param_out.tx_pause), + check(pause_param_in.rx_pause == pause_param_out.tx_pause)); + + return TEST_PASS; +} + +static int +test_get_drvinfo(int port_id) +{ + struct ethtool_drvinfo drvinfo; + + if (netdev_ethtool_get_drvinfo(port_id, &drvinfo)) { + printf("fail to get drvinfo ...\n"); + return TEST_FAIL; + } + printf("driver: %s version: %s fw_version: %s bus_info=%s\n", + drvinfo.driver, drvinfo.version, + drvinfo.fw_version, drvinfo.bus_info); + printf("reg-size(bytes)=%d eeprom-size=%d\n", + drvinfo.regdump_len, + drvinfo.eedump_len); + return TEST_PASS; +} + +static int +test_mac_addr(uint8_t port_id) +{ + unsigned char mac_addr[MAC_ADDR_SIZE]; + unsigned char mac_addr_base[MAC_ADDR_SIZE] = {0x52, 0x54, 0, 0, 0, 1}; + struct ether_addr *mac_addr_t; + struct ethter_add *mac_addr2; + int result; + + mac_addr_t = to_mac_type(mac_addr); + result = netdev_net_get_mac_addr(port_id, mac_addr_t); + if (netdev_net_get_mac_addr(port_id, mac_addr_t)) { + printf("Fail to get mac addr from port #%d!!!\n", port_id); + return TEST_FAIL; + } + printf("Port #%d, device mac addr is %s\n", port_id, + mac_addr_str(mac_addr)); + + result = netdev_net_validate_addr(port_id, mac_addr_t); + if (!result) { + printf("Default mac addr, %s, is not valid\n", + mac_addr_str(mac_addr)); + strncpy((char *)mac_addr, (char *)mac_addr_base, MAC_ADDR_SIZE); + mac_addr[MAC_ADDR_SIZE-1] += port_id; + printf("New mac address:%s is used.\n", mac_addr_str(mac_addr)); + + result = netdev_net_set_mac_addr(port_id, mac_addr_t); + if (result == TEST_FAIL) + return TEST_FAIL; + + result = netdev_net_get_mac_addr(port_id, &mac_addr2); + if (strncmp(cast_ptr(mac_addr, char*), + cast_ptr(mac_addr2, char*), + MAC_ADDR_SIZE)) + printf("Expected mac_addr %s return addr %s\n", + mac_addr_str(mac_addr), + mac_addr_str(cast_ptr(mac_addr2, unsigned char*) + )); + } + return TEST_PASS; +} + +struct api_test test_table[test_counts] = { + {"-test_eeprom", test_eeprom, test_eeprom_t}, + {"-test_regs", test_regs, test_regs_t}, + {"-test_mtu", test_mtu, test_mtu_t}, + {"-test_vlan_rx", test_vlan_rx, test_vlan_rx_t}, + {"-test_pauseparam", test_pauseparam, test_pauseparam_t}, +}; + +static inline unsigned int +get_test_mask(char *name) +{ + int ind = 0; + struct api_test *test = &test_table[ind]; + + while (ind++ < test_counts) { + if (!strncmp(name, test->test_name, strlen(name))) + return (1 << test->mask_bit); + test++; + } + return 0; +} + +static unsigned int +parse_args(int argc, char **argv) +{ + int i; + unsigned int test_mask = 0; + + + if (argc <= 1) + return 0; + for (i = 1; i < argc; i++) + test_mask |= get_test_mask(argv[i]); + return test_mask; +} + +static inline void +test_update(int result, int *fails, int *passes) +{ + if (result == TEST_PASS) + (*passes)++; + else + (*fails)++; +} + +static void +run_test(uint8_t port_id, unsigned int test_mask, int *fails, int *passes) +{ + int i; + int result; + + for (i = 0; i < test_counts; i++) { + if (test_mask & (1<<i)) { + result = (*test_table[i].test_task)(port_id); + test_update(result, fails, passes); + } + } +} + +static void +wait_for_linkdown(uint8_t port_id) +{ + int link_up; + + link_up = netdev_ethtool_get_link(port_id); + while (link_up) { + sleep(10); + link_up = netdev_ethtool_get_link(port_id); + } +} + +int +main(int argc, char **argv) +{ + uint8_t port_id; + unsigned int test_mask; + int passes = 0; + int fails = 0; + int iter_count = 0; + int link_up; + int result; + + + /* get command parameter */ + test_mask = parse_args(argc, argv); + + /* initialize request pipe */ + init_req_pipe(); + + printf("issue ipc begin\n"); + /* send a request to start the NIC device */ + num_of_ports = netdev_ipc_begin(); + while (num_of_ports == 0) + num_of_ports = netdev_ipc_begin() & 0xFF; + + for (port_id = 0; port_id < num_of_ports; port_id++) { + link_up = netdev_ethtool_get_link(port_id); + printf("port #%d is %s\n", port_id, link_up?"up":"down"); + if (!link_up) { + if (netdev_net_open(port_id) == 0) + netdev_net_set_rx_mode(port_id); + else + printf("failed to start port #%d\n", port_id); + } + } + + /* Testing ethtool register/eeprom get */ + for (port_id = 0; port_id < num_of_ports; port_id++) { + if (!is_port_enabled(port_id)) + continue; + + result = test_mac_addr(port_id); + test_update(result, &fails, &passes); + + result = test_get_drvinfo(port_id); + test_update(result, &fails, &passes); + + result = test_regs_t(port_id); + test_update(result, &fails, &passes); + + /* Only testing eeprom access over a PF */ + if (!is_vf_port(port_id)) { + result = test_eeprom_get(port_id); + test_update(result, &fails, &passes); + } + } + + printf("start nic statistics collection ...\n"); + port_id = get_port(); + while (iter_count++ < ITER_LIMIT) { + uint64_t last_ts, ts; + struct rte_eth_stats last_stats, stats; + + if (netdev_net_get_stats64(port_id, &last_stats)) { + printf("Fail to query statistics from port %d\n", + port_id); + break; + } + last_ts = rdtsc(); + + sleep(STATS_PERIOD); + + if (netdev_net_get_stats64(port_id, &stats)) { + printf("Fail to query statistics from port %d\n", + port_id); + break; + } + ts = rdtsc(); + + printf("rx packet rate = %lf, tx packet rate = %lf\n", + PACKET_RATE(last_stats.ipackets, stats.ipackets, + last_ts, ts), + PACKET_RATE(last_stats.opackets, stats.opackets, + last_ts, ts)); + + + printf("rx bit rate = %lf, tx bit rate = %lf\n", + BYTE2BIT_RATE(last_stats.ibytes, stats.ibytes, + last_ts, ts), + BYTE2BIT_RATE(last_stats.obytes, stats.obytes, + last_ts, ts)); + + sleep(STATS_PERIOD); + } + + /* Stop link, testing APIs specified in command arguments */ + if (test_mask) { + for (port_id = 0; port_id < num_of_ports; port_id++) { + link_up = netdev_ethtool_get_link(port_id); + if (link_up) { + netdev_net_stop(port_id); + wait_for_linkdown(port_id); + } + } + + for (port_id = 0; port_id < num_of_ports; port_id++) { + link_up = netdev_ethtool_get_link(port_id); + if (!is_vf_port(port_id) && !link_up) + run_test(port_id, test_mask, &fails, &passes); + } + } + + while (netdev_ipc_end() < 0) + ; + + printf("Pass count: %d Fail count: %d\n", passes, fails); + return 0; +} diff --git a/mk/rte.lib.mk b/mk/rte.lib.mk index 9ff5cce..77689bf 100644 --- a/mk/rte.lib.mk +++ b/mk/rte.lib.mk @@ -43,8 +43,10 @@ LIB := $(patsubst %.a,%.so.$(LIBABIVER),$(LIB)) ifeq ($(CONFIG_RTE_NEXT_ABI),y) LIB := $(LIB).1 endif +ifdef EXPORT_MAP CPU_LDFLAGS += --version-script=$(SRCDIR)/$(EXPORT_MAP) endif +endif _BUILD = $(LIB) -- 2.1.4 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [dpdk-dev] [PATCH v2 0/2] Example: l2fwd-ethtool 2015-07-20 14:12 ` [dpdk-dev] [PATCH] examples: new example: l2fwd-ethtool Liang-Min Larry Wang @ 2015-07-23 15:00 ` Liang-Min Larry Wang 2015-07-23 15:00 ` [dpdk-dev] [PATCH v2 1/2] Remove ABI requierment for external library builds Liang-Min Larry Wang 2015-07-23 15:00 ` [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool Liang-Min Larry Wang 0 siblings, 2 replies; 10+ messages in thread From: Liang-Min Larry Wang @ 2015-07-23 15:00 UTC (permalink / raw) To: dev; +Cc: Liang-Min Larry Wang This implementation is designed to provide an example illlustrating how to create a user-space ethtool library from existing ethdev APIs. In contrast to kernel version of same API (such as ops defined in KNI), the user-space APIs enable a fast-path (no kernel API calls) for device query and data return. This example implements 19 popular used Ethtool and Netdevice ops as described in examples/l2fwd-ethtool/lib/rte_ethtool.h, and commnity support of un-implemented Ethtool and Netdevice ops are very welcomed. v2 change: - Separate changes on .mk files into a separate patch file - Remove requirement of ABI version for external library build - Fix example/l2fwd-ethtool share object build Andrew G. Harvey (1): Remove ABI requierment for external library builds. Liang-Min Larry Wang (1): examples: new example: l2fwd-ethtool examples/Makefile | 1 + examples/l2fwd-ethtool/Makefile | 48 + examples/l2fwd-ethtool/l2fwd-app/Makefile | 58 ++ examples/l2fwd-ethtool/l2fwd-app/main.c | 1025 ++++++++++++++++++++++ examples/l2fwd-ethtool/l2fwd-app/netdev_api.h | 770 ++++++++++++++++ examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h | 159 ++++ examples/l2fwd-ethtool/lib/Makefile | 57 ++ examples/l2fwd-ethtool/lib/rte_ethtool.c | 336 +++++++ examples/l2fwd-ethtool/lib/rte_ethtool.h | 385 ++++++++ examples/l2fwd-ethtool/nic-control/Makefile | 55 ++ examples/l2fwd-ethtool/nic-control/nic_control.c | 614 +++++++++++++ mk/rte.extlib.mk | 2 + mk/rte.lib.mk | 6 + 13 files changed, 3516 insertions(+) create mode 100644 examples/l2fwd-ethtool/Makefile create mode 100644 examples/l2fwd-ethtool/l2fwd-app/Makefile create mode 100644 examples/l2fwd-ethtool/l2fwd-app/main.c create mode 100644 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h create mode 100644 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h create mode 100644 examples/l2fwd-ethtool/lib/Makefile create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.c create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.h create mode 100644 examples/l2fwd-ethtool/nic-control/Makefile create mode 100644 examples/l2fwd-ethtool/nic-control/nic_control.c -- 2.1.4 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [dpdk-dev] [PATCH v2 1/2] Remove ABI requierment for external library builds. 2015-07-23 15:00 ` [dpdk-dev] [PATCH v2 0/2] Example: l2fwd-ethtool Liang-Min Larry Wang @ 2015-07-23 15:00 ` Liang-Min Larry Wang 2015-10-21 16:31 ` Thomas Monjalon 2015-07-23 15:00 ` [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool Liang-Min Larry Wang 1 sibling, 1 reply; 10+ messages in thread From: Liang-Min Larry Wang @ 2015-07-23 15:00 UTC (permalink / raw) To: dev From: "Andrew G. Harvey" <agh@cisco.com> Signed-off-by: Andrew G. Harvey <agh@cisco.com> --- mk/rte.extlib.mk | 2 ++ mk/rte.lib.mk | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/mk/rte.extlib.mk b/mk/rte.extlib.mk index ba066bc..d2a9b6d 100644 --- a/mk/rte.extlib.mk +++ b/mk/rte.extlib.mk @@ -31,6 +31,8 @@ MAKEFLAGS += --no-print-directory +export EXTLIB_BUILD := 1 + # we must create the output dir first and recall the same Makefile # from this directory ifeq ($(NOT_FIRST_CALL),) diff --git a/mk/rte.lib.mk b/mk/rte.lib.mk index 9ff5cce..63ca640 100644 --- a/mk/rte.lib.mk +++ b/mk/rte.lib.mk @@ -40,11 +40,13 @@ VPATH += $(SRCDIR) ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),y) LIB := $(patsubst %.a,%.so.$(LIBABIVER),$(LIB)) +ifndef EXTLIB_BUILD ifeq ($(CONFIG_RTE_NEXT_ABI),y) LIB := $(LIB).1 endif CPU_LDFLAGS += --version-script=$(SRCDIR)/$(EXPORT_MAP) endif +endif _BUILD = $(LIB) @@ -173,12 +175,16 @@ $(RTE_OUTPUT)/lib/$(LIB): $(LIB) @[ -d $(RTE_OUTPUT)/lib ] || mkdir -p $(RTE_OUTPUT)/lib $(Q)cp -f $(LIB) $(RTE_OUTPUT)/lib ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),y) +ifdef EXTLIB_BUILD + $(Q)ln -s -f $< $(basename $@) +else ifeq ($(CONFIG_RTE_NEXT_ABI),y) $(Q)ln -s -f $< $(basename $(basename $@)) else $(Q)ln -s -f $< $(basename $@) endif endif +endif # # Clean all generated files -- 2.1.4 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [dpdk-dev] [PATCH v2 1/2] Remove ABI requierment for external library builds. 2015-07-23 15:00 ` [dpdk-dev] [PATCH v2 1/2] Remove ABI requierment for external library builds Liang-Min Larry Wang @ 2015-10-21 16:31 ` Thomas Monjalon 0 siblings, 0 replies; 10+ messages in thread From: Thomas Monjalon @ 2015-10-21 16:31 UTC (permalink / raw) To: Liang-Min Larry Wang; +Cc: dev 2015-07-23 11:00, Liang-Min Larry Wang: > From: "Andrew G. Harvey" <agh@cisco.com> > --- a/mk/rte.extlib.mk > +++ b/mk/rte.extlib.mk > @@ -31,6 +31,8 @@ > > MAKEFLAGS += --no-print-directory > > +export EXTLIB_BUILD := 1 Is export really needed? Except that, this patch looks OK. ^ permalink raw reply [flat|nested] 10+ messages in thread
* [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool 2015-07-23 15:00 ` [dpdk-dev] [PATCH v2 0/2] Example: l2fwd-ethtool Liang-Min Larry Wang 2015-07-23 15:00 ` [dpdk-dev] [PATCH v2 1/2] Remove ABI requierment for external library builds Liang-Min Larry Wang @ 2015-07-23 15:00 ` Liang-Min Larry Wang 2015-10-21 16:36 ` Thomas Monjalon 1 sibling, 1 reply; 10+ messages in thread From: Liang-Min Larry Wang @ 2015-07-23 15:00 UTC (permalink / raw) To: dev; +Cc: Liang-Min Larry Wang The example includes an ethtool library and two applications: one application is a non- DPDK process (nic-control) and the other is a DPDK l2fwd applicaiton (l2fwd-app). The nic-control process sends ethtool alike device management requests to l2fwd-app through a named pipe IPC. This example is designed to show how to build a ethtool shim library and how to use ethtool apis to manage device parameters. Signed-off-by: Liang-Min Larry Wang <liang-min.wang@intel.com> --- examples/Makefile | 1 + examples/l2fwd-ethtool/Makefile | 48 + examples/l2fwd-ethtool/l2fwd-app/Makefile | 58 ++ examples/l2fwd-ethtool/l2fwd-app/main.c | 1025 ++++++++++++++++++++++ examples/l2fwd-ethtool/l2fwd-app/netdev_api.h | 770 ++++++++++++++++ examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h | 159 ++++ examples/l2fwd-ethtool/lib/Makefile | 57 ++ examples/l2fwd-ethtool/lib/rte_ethtool.c | 336 +++++++ examples/l2fwd-ethtool/lib/rte_ethtool.h | 385 ++++++++ examples/l2fwd-ethtool/nic-control/Makefile | 55 ++ examples/l2fwd-ethtool/nic-control/nic_control.c | 614 +++++++++++++ 11 files changed, 3508 insertions(+) create mode 100644 examples/l2fwd-ethtool/Makefile create mode 100644 examples/l2fwd-ethtool/l2fwd-app/Makefile create mode 100644 examples/l2fwd-ethtool/l2fwd-app/main.c create mode 100644 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h create mode 100644 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h create mode 100644 examples/l2fwd-ethtool/lib/Makefile create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.c create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.h create mode 100644 examples/l2fwd-ethtool/nic-control/Makefile create mode 100644 examples/l2fwd-ethtool/nic-control/nic_control.c diff --git a/examples/Makefile b/examples/Makefile index b4eddbd..3dc049c 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -53,6 +53,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_KNI) += kni DIRS-y += l2fwd DIRS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += l2fwd-ivshmem DIRS-$(CONFIG_RTE_LIBRTE_JOBSTATS) += l2fwd-jobstats +DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += l2fwd-ethtool DIRS-y += l3fwd DIRS-$(CONFIG_RTE_LIBRTE_ACL) += l3fwd-acl DIRS-$(CONFIG_RTE_LIBRTE_POWER) += l3fwd-power diff --git a/examples/l2fwd-ethtool/Makefile b/examples/l2fwd-ethtool/Makefile new file mode 100644 index 0000000..d9ad439 --- /dev/null +++ b/examples/l2fwd-ethtool/Makefile @@ -0,0 +1,48 @@ +# BSD LICENSE +# +# Copyright(c) 2015 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overwritten by command line or environment +RTE_TARGET ?= x86_64-native-linuxapp-gcc + +include $(RTE_SDK)/mk/rte.vars.mk + +ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp") +$(error This application can only operate in a linuxapp environment, \ +please change the definition of the RTE_TARGET environment variable) +endif + +DIRS-y += lib nic-control l2fwd-app + +include $(RTE_SDK)/mk/rte.extsubdir.mk diff --git a/examples/l2fwd-ethtool/l2fwd-app/Makefile b/examples/l2fwd-ethtool/l2fwd-app/Makefile new file mode 100644 index 0000000..69405f2 --- /dev/null +++ b/examples/l2fwd-ethtool/l2fwd-app/Makefile @@ -0,0 +1,58 @@ +# BSD LICENSE +# +# Copyright(c) 2015 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overwritten by command line or environment +RTE_TARGET ?= x86_64-native-linuxapp-gcc + +include $(RTE_SDK)/mk/rte.vars.mk + +ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp") +$(error This application can only operate in a linuxapp environment, \ +please change the definition of the RTE_TARGET environment variable) +endif + +# binary name +APP = l2fwd-app + +# all source are stored in SRCS-y +SRCS-y := main.c + +CFLAGS += -O3 -D_GNU_SOURCE -pthread -I$(SRCDIR)/../lib +CFLAGS += $(WERROR_FLAGS) + +LDLIBS += -L$(subst l2fwd-app,lib,$(RTE_OUTPUT))/lib +LDLIBS += -lrte_ethtool + +include $(RTE_SDK)/mk/rte.extapp.mk diff --git a/examples/l2fwd-ethtool/l2fwd-app/main.c b/examples/l2fwd-ethtool/l2fwd-app/main.c new file mode 100644 index 0000000..73c29e3 --- /dev/null +++ b/examples/l2fwd-ethtool/l2fwd-app/main.c @@ -0,0 +1,1025 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2015 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/queue.h> +#include <netinet/in.h> +#include <setjmp.h> +#include <stdarg.h> +#include <ctype.h> +#include <errno.h> +#include <getopt.h> +#include <pthread.h> +#include <unistd.h> + +#include <rte_common.h> +#include <rte_log.h> +#include <rte_memory.h> +#include <rte_memcpy.h> +#include <rte_memzone.h> +#include <rte_tailq.h> +#include <rte_eal.h> +#include <rte_per_lcore.h> +#include <rte_launch.h> +#include <rte_atomic.h> +#include <rte_cycles.h> +#include <rte_prefetch.h> +#include <rte_lcore.h> +#include <rte_per_lcore.h> +#include <rte_branch_prediction.h> +#include <rte_interrupts.h> +#include <rte_pci.h> +#include <rte_random.h> +#include <rte_debug.h> +#include <rte_ether.h> +#include <rte_ethdev.h> +#include <rte_ring.h> +#include <rte_mempool.h> +#include <rte_mbuf.h> +#include "rte_ethtool.h" +#define NETDEV_OP_REPLY 1 +#include "netdev_api.h" + +#define RTE_LOGTYPE_L2FWD RTE_LOGTYPE_USER1 + +#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define NB_MBUF 8192 + +#define MAX_PKT_BURST 32 +#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */ + +#define is_vf_port(vf_mask, port_id) ((vf_mask & (1 << port_id)) > 0) +#define is_port_enabled(port_mask, port_id) ((port_mask & (1 << port_id)) > 0) +#define TX_PTHRESH 32 +#define TX_HTHRESH 0 +#define TX_WTHRESH 0 +/* + * Configurable number of RX/TX ring descriptors + */ +#define RTE_TEST_RX_DESC_DEFAULT 128 +#define RTE_TEST_TX_DESC_DEFAULT 512 +static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT; +static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT; + +/* ethernet addresses of ports */ +static struct ether_addr l2fwd_ports_eth_addr[RTE_MAX_ETHPORTS]; + +/* mask of enabled ports */ +static uint32_t l2fwd_enabled_port_mask; + +/* virtio setup enable */ +static int virtio_setup; + +/* list of enabled ports */ +static uint32_t l2fwd_dst_ports[RTE_MAX_ETHPORTS]; + +static unsigned int l2fwd_rx_queue_per_lcore = 1; + +struct mbuf_table { + unsigned len; + struct rte_mbuf *m_table[MAX_PKT_BURST]; +}; + +#define MAX_RX_QUEUE_PER_LCORE 16 +#define MAX_TX_QUEUE_PER_PORT 16 +struct lcore_queue_conf { + unsigned n_rx_port; + unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE]; + struct mbuf_table tx_mbufs[RTE_MAX_ETHPORTS]; + +} __rte_cache_aligned; +struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE]; + +static struct rte_eth_conf port_conf = { + .rxmode = { + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled */ + .hw_ip_checksum = 0, /**< IP checksum offload disabled */ + .hw_vlan_filter = 0, /**< VLAN filtering disabled */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ + .hw_strip_crc = 0, /**< CRC stripped by hardware */ + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, +}; + +static struct rte_eth_txconf tx_conf = { + .tx_thresh = { + .pthresh = TX_PTHRESH, + .hthresh = TX_HTHRESH, + .wthresh = TX_WTHRESH, + }, + .tx_free_thresh = 32, + .tx_rs_thresh = 32, + .txq_flags = 0xf00, +}; + +struct rte_mempool *l2fwd_pktmbuf_pool; + +/* Per-port statistics struct */ +struct l2fwd_port_statistics { + uint64_t tx; + uint64_t rx; + uint64_t dropped; +} __rte_cache_aligned; +struct l2fwd_port_statistics port_statistics[RTE_MAX_ETHPORTS]; + +/* A tsc-based timer responsible for triggering statistics printout */ +#define TIMER_MILLISECOND 2000000ULL /* around 1ms at 2 Ghz */ +#define MAX_TIMER_PERIOD 86400 /* 1 day max */ +/* default period is 10 seconds */ +static int64_t timer_period = 10 * TIMER_MILLISECOND * 1000; + +/* IPC done checking utility function */ +/* status of ipc completed */ +static rte_atomic64_t ipc_done; + +static inline void init_ipc_done(void) +{ + rte_atomic64_init(&ipc_done); +} + +static inline int is_ipc_done(void) +{ + return rte_atomic64_read(&ipc_done) > 0; +} + +static inline void set_ipc_done(void) +{ + rte_atomic64_inc(&ipc_done); +} + +/* Print out statistics on packets dropped */ +static void +print_stats(void) +{ + uint64_t total_packets_dropped, total_packets_tx, total_packets_rx; + unsigned portid; + + total_packets_dropped = 0; + total_packets_tx = 0; + total_packets_rx = 0; + + const char clr[] = { 27, '[', '2', 'J', '\0' }; + const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' }; + + /* Clear screen and move to top left */ + printf("%s%s", clr, topLeft); + + printf("\nPort statistics ===================================="); + + for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) { + /* skip disabled ports */ + if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) + continue; + printf("\nStatistics for port %u ----------------------------", + portid); + printf("\nPackets sent: %24"PRIu64, port_statistics[portid].tx); + printf("\nPackets received: %20"PRIu64, + port_statistics[portid].rx); + printf("\nPackets dropped: %21"PRIu64, + port_statistics[portid].dropped); + + total_packets_dropped += port_statistics[portid].dropped; + total_packets_tx += port_statistics[portid].tx; + total_packets_rx += port_statistics[portid].rx; + } + printf("\nAggregate statistics ==============================="); + printf("\nTotal packets sent: %18"PRIu64, total_packets_tx); + printf("\nTotal packets received: %14"PRIu64, total_packets_rx); + printf("\nTotal packets dropped: %15"PRIu64, total_packets_dropped); + printf("\n====================================================\n"); +} + +/* Send the burst of packets on an output interface */ +static int +l2fwd_send_burst(struct lcore_queue_conf *qconf, unsigned n, uint8_t port) +{ + struct rte_mbuf **m_table; + unsigned ret; + unsigned queueid = 0; + + m_table = (struct rte_mbuf **)qconf->tx_mbufs[port].m_table; + + ret = rte_eth_tx_burst(port, (uint16_t) queueid, m_table, (uint16_t) n); + port_statistics[port].tx += ret; + if (unlikely(ret < n)) { + port_statistics[port].dropped += (n - ret); + do { + rte_pktmbuf_free(m_table[ret]); + } while (++ret < n); + } + + return 0; +} + +/* Enqueue packets for TX and prepare them to be sent */ +static int +l2fwd_send_packet(struct rte_mbuf *m, uint8_t port) +{ + unsigned lcore_id, len; + struct lcore_queue_conf *qconf; + + lcore_id = rte_lcore_id(); + + qconf = &lcore_queue_conf[lcore_id]; + len = qconf->tx_mbufs[port].len; + qconf->tx_mbufs[port].m_table[len] = m; + len++; + + /* enough pkts to be sent */ + if (unlikely(len == MAX_PKT_BURST)) { + l2fwd_send_burst(qconf, MAX_PKT_BURST, port); + len = 0; + } + + qconf->tx_mbufs[port].len = len; + return 0; +} + +static void +l2fwd_simple_forward(struct rte_mbuf *m, unsigned portid) +{ + struct ether_hdr *eth; + void *tmp; + unsigned dst_port; + + dst_port = l2fwd_dst_ports[portid]; + eth = rte_pktmbuf_mtod(m, struct ether_hdr *); + + /* 02:00:00:00:00:xx */ + tmp = ð->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], ð->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, ¶m1_size, param1, + ¶m2_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, ¶m1_size, param1, + ¶m2_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(ðtool_thread, NULL, ðtool, &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, ®_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, ®_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(®_info, 0xFF, sizeof(struct ethtool_regs)); + + if (netdev_ethtool_get_regs(port_id, ®_info, data)) { + printf("Fail to read register\n"); + free(data); + return TEST_FAIL; + } + + regs = data; + printf("version: %x;", reg_info.version); + printf("register[0:%d]:\n", TEST_READ_SIZE-1); + for (i = 0; i < TEST_READ_SIZE; i++) + printf("%x ", regs[i]); + printf("\n"); + free(data); + return TEST_PASS; +} + +static int +test_mtu_t(int port_id) +{ + const int mtu = 1024; + + if (netdev_net_change_mtu(port_id, mtu)) { + printf("failed to set mtu to %d\n", mtu); + return TEST_FAIL; + } + return TEST_PASS; +} + +static int +test_vlan_rx_t(int port_id) +{ + /* add/remove vlan to vid */ + netdev_net_vlan_rx_add_vid(port_id, 0); + if (netdev_net_vlan_rx_add_vid(port_id, 0)) { + if (netdev_net_vlan_rx_kill_vid(port_id, 0)) { + printf("netdev_net_vlan_rx_kill_vid() fails\n"); + return TEST_FAIL; + } + return TEST_PASS; + } + printf("netdev_net_vlan_rx_add_vid() fails\n"); + return TEST_FAIL; +} + +static int +test_pauseparam_t(int port_id) +{ + struct ethtool_pauseparam pause_param_in; + struct ethtool_pauseparam pause_param_out; + + if (netdev_ethtool_get_pauseparam(port_id, &pause_param_in)) { + printf("netdev_ethtool_get_pauseparam() fails\n"); + return TEST_FAIL; + } + printf("pause setup: autoneg: %d ", + pause_param_in.autoneg); + printf("tx_pause: %d ", + pause_param_in.tx_pause); + printf("rx_pause: %d\n", + pause_param_in.rx_pause); + if (netdev_ethtool_set_pauseparam(port_id, &pause_param_in)) { + printf("netdev_ethtool_set_pauseparam() fails\n"); + return TEST_FAIL; + } + /* read-back pause frame setting for comparison */ + if (netdev_ethtool_get_pauseparam(port_id, &pause_param_out)) { + printf("netdev_ethtool_get_pauseparam() fails\n"); + return TEST_FAIL; + } + printf("pause frame checking auto:%s tx_pause:%s rx_pause:%s\n", + check(pause_param_in.autoneg == pause_param_out.autoneg), + check(pause_param_in.tx_pause == pause_param_out.tx_pause), + check(pause_param_in.rx_pause == pause_param_out.tx_pause)); + + return TEST_PASS; +} + +static int +test_get_drvinfo(int port_id) +{ + struct ethtool_drvinfo drvinfo; + + if (netdev_ethtool_get_drvinfo(port_id, &drvinfo)) { + printf("fail to get drvinfo ...\n"); + return TEST_FAIL; + } + printf("driver: %s version: %s fw_version: %s bus_info=%s\n", + drvinfo.driver, drvinfo.version, + drvinfo.fw_version, drvinfo.bus_info); + printf("reg-size(bytes)=%d eeprom-size=%d\n", + drvinfo.regdump_len, + drvinfo.eedump_len); + return TEST_PASS; +} + +static int +test_mac_addr(uint8_t port_id) +{ + unsigned char mac_addr[MAC_ADDR_SIZE]; + unsigned char mac_addr_base[MAC_ADDR_SIZE] = {0x52, 0x54, 0, 0, 0, 1}; + struct ether_addr *mac_addr_t; + struct ethter_add *mac_addr2; + int result; + + mac_addr_t = to_mac_type(mac_addr); + result = netdev_net_get_mac_addr(port_id, mac_addr_t); + if (netdev_net_get_mac_addr(port_id, mac_addr_t)) { + printf("Fail to get mac addr from port #%d!!!\n", port_id); + return TEST_FAIL; + } + printf("Port #%d, device mac addr is %s\n", port_id, + mac_addr_str(mac_addr)); + + result = netdev_net_validate_addr(port_id, mac_addr_t); + if (!result) { + printf("Default mac addr, %s, is not valid\n", + mac_addr_str(mac_addr)); + strncpy((char *)mac_addr, (char *)mac_addr_base, MAC_ADDR_SIZE); + mac_addr[MAC_ADDR_SIZE-1] += port_id; + printf("New mac address:%s is used.\n", mac_addr_str(mac_addr)); + + result = netdev_net_set_mac_addr(port_id, mac_addr_t); + if (result == TEST_FAIL) + return TEST_FAIL; + + result = netdev_net_get_mac_addr(port_id, &mac_addr2); + if (strncmp(cast_ptr(mac_addr, char*), + cast_ptr(mac_addr2, char*), + MAC_ADDR_SIZE)) + printf("Expected mac_addr %s return addr %s\n", + mac_addr_str(mac_addr), + mac_addr_str(cast_ptr(mac_addr2, unsigned char*) + )); + } + return TEST_PASS; +} + +struct api_test test_table[test_counts] = { + {"-test_eeprom", test_eeprom, test_eeprom_t}, + {"-test_regs", test_regs, test_regs_t}, + {"-test_mtu", test_mtu, test_mtu_t}, + {"-test_vlan_rx", test_vlan_rx, test_vlan_rx_t}, + {"-test_pauseparam", test_pauseparam, test_pauseparam_t}, +}; + +static inline unsigned int +get_test_mask(char *name) +{ + int ind = 0; + struct api_test *test = &test_table[ind]; + + while (ind++ < test_counts) { + if (!strncmp(name, test->test_name, strlen(name))) + return (1 << test->mask_bit); + test++; + } + return 0; +} + +static unsigned int +parse_args(int argc, char **argv) +{ + int i; + unsigned int test_mask = 0; + + + if (argc <= 1) + return 0; + for (i = 1; i < argc; i++) + test_mask |= get_test_mask(argv[i]); + return test_mask; +} + +static inline void +test_update(int result, int *fails, int *passes) +{ + if (result == TEST_PASS) + (*passes)++; + else + (*fails)++; +} + +static void +run_test(uint8_t port_id, unsigned int test_mask, int *fails, int *passes) +{ + int i; + int result; + + for (i = 0; i < test_counts; i++) { + if (test_mask & (1<<i)) { + result = (*test_table[i].test_task)(port_id); + test_update(result, fails, passes); + } + } +} + +static void +wait_for_linkdown(uint8_t port_id) +{ + int link_up; + + link_up = netdev_ethtool_get_link(port_id); + while (link_up) { + sleep(10); + link_up = netdev_ethtool_get_link(port_id); + } +} + +int +main(int argc, char **argv) +{ + uint8_t port_id; + unsigned int test_mask; + int passes = 0; + int fails = 0; + int iter_count = 0; + int link_up; + int result; + + + /* get command parameter */ + test_mask = parse_args(argc, argv); + + /* initialize request pipe */ + init_req_pipe(); + + printf("issue ipc begin\n"); + /* send a request to start the NIC device */ + num_of_ports = netdev_ipc_begin(); + while (num_of_ports == 0) + num_of_ports = netdev_ipc_begin() & 0xFF; + + for (port_id = 0; port_id < num_of_ports; port_id++) { + link_up = netdev_ethtool_get_link(port_id); + printf("port #%d is %s\n", port_id, link_up?"up":"down"); + if (!link_up) { + if (netdev_net_open(port_id) == 0) + netdev_net_set_rx_mode(port_id); + else + printf("failed to start port #%d\n", port_id); + } + } + + /* Testing ethtool register/eeprom get */ + for (port_id = 0; port_id < num_of_ports; port_id++) { + if (!is_port_enabled(port_id)) + continue; + + result = test_mac_addr(port_id); + test_update(result, &fails, &passes); + + result = test_get_drvinfo(port_id); + test_update(result, &fails, &passes); + + result = test_regs_t(port_id); + test_update(result, &fails, &passes); + + /* Only testing eeprom access over a PF */ + if (!is_vf_port(port_id)) { + result = test_eeprom_get(port_id); + test_update(result, &fails, &passes); + } + } + + printf("start nic statistics collection ...\n"); + port_id = get_port(); + while (iter_count++ < ITER_LIMIT) { + uint64_t last_ts, ts; + struct rte_eth_stats last_stats, stats; + + if (netdev_net_get_stats64(port_id, &last_stats)) { + printf("Fail to query statistics from port %d\n", + port_id); + break; + } + last_ts = rdtsc(); + + sleep(STATS_PERIOD); + + if (netdev_net_get_stats64(port_id, &stats)) { + printf("Fail to query statistics from port %d\n", + port_id); + break; + } + ts = rdtsc(); + + printf("rx packet rate = %lf, tx packet rate = %lf\n", + PACKET_RATE(last_stats.ipackets, stats.ipackets, + last_ts, ts), + PACKET_RATE(last_stats.opackets, stats.opackets, + last_ts, ts)); + + + printf("rx bit rate = %lf, tx bit rate = %lf\n", + BYTE2BIT_RATE(last_stats.ibytes, stats.ibytes, + last_ts, ts), + BYTE2BIT_RATE(last_stats.obytes, stats.obytes, + last_ts, ts)); + + sleep(STATS_PERIOD); + } + + /* Stop link, testing APIs specified in command arguments */ + if (test_mask) { + for (port_id = 0; port_id < num_of_ports; port_id++) { + link_up = netdev_ethtool_get_link(port_id); + if (link_up) { + netdev_net_stop(port_id); + wait_for_linkdown(port_id); + } + } + + for (port_id = 0; port_id < num_of_ports; port_id++) { + link_up = netdev_ethtool_get_link(port_id); + if (!is_vf_port(port_id) && !link_up) + run_test(port_id, test_mask, &fails, &passes); + } + } + + while (netdev_ipc_end() < 0) + ; + + printf("Pass count: %d Fail count: %d\n", passes, fails); + return 0; +} -- 2.1.4 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool 2015-07-23 15:00 ` [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool Liang-Min Larry Wang @ 2015-10-21 16:36 ` Thomas Monjalon 2015-10-21 16:46 ` Wang, Liang-min 2015-11-17 22:33 ` Wang, Liang-min 0 siblings, 2 replies; 10+ messages in thread From: Thomas Monjalon @ 2015-10-21 16:36 UTC (permalink / raw) To: Liang-Min Larry Wang; +Cc: dev 2015-07-23 11:00, Liang-Min Larry Wang: > examples/Makefile | 1 + > examples/l2fwd-ethtool/Makefile | 48 + > examples/l2fwd-ethtool/l2fwd-app/Makefile | 58 ++ > examples/l2fwd-ethtool/l2fwd-app/main.c | 1025 ++++++++++++++++++++++ > examples/l2fwd-ethtool/l2fwd-app/netdev_api.h | 770 ++++++++++++++++ > examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h | 159 ++++ > examples/l2fwd-ethtool/lib/Makefile | 57 ++ > examples/l2fwd-ethtool/lib/rte_ethtool.c | 336 +++++++ > examples/l2fwd-ethtool/lib/rte_ethtool.h | 385 ++++++++ > examples/l2fwd-ethtool/nic-control/Makefile | 55 ++ > examples/l2fwd-ethtool/nic-control/nic_control.c | 614 +++++++++++++ > 11 files changed, 3508 insertions(+) This patch is huge. Please split a bit. > --- a/examples/Makefile > +++ b/examples/Makefile > @@ -53,6 +53,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_KNI) += kni > DIRS-y += l2fwd > DIRS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += l2fwd-ivshmem > DIRS-$(CONFIG_RTE_LIBRTE_JOBSTATS) += l2fwd-jobstats > +DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += l2fwd-ethtool > DIRS-y += l3fwd Please keep the alphabetical order. I do not plan to review it more. If nobody complains, it means it's accepted. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool 2015-10-21 16:36 ` Thomas Monjalon @ 2015-10-21 16:46 ` Wang, Liang-min 2015-11-17 22:33 ` Wang, Liang-min 1 sibling, 0 replies; 10+ messages in thread From: Wang, Liang-min @ 2015-10-21 16:46 UTC (permalink / raw) To: Thomas Monjalon; +Cc: dev Thomas, Let's put this patch on defer list because there are related work might take a different approach. Let's only review the make file change (PATCH 1/2). I believe "export" is needed since the variable is shared by all the build but it might be already included due to the mk file inclusion. Since Andy is on vacation, I am not sure if he could make a comment on that. Larry > -----Original Message----- > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com] > Sent: Wednesday, October 21, 2015 12:36 PM > To: Wang, Liang-min > Cc: dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd- > ethtool > > 2015-07-23 11:00, Liang-Min Larry Wang: > > examples/Makefile | 1 + > > examples/l2fwd-ethtool/Makefile | 48 + > > examples/l2fwd-ethtool/l2fwd-app/Makefile | 58 ++ > > examples/l2fwd-ethtool/l2fwd-app/main.c | 1025 > ++++++++++++++++++++++ > > examples/l2fwd-ethtool/l2fwd-app/netdev_api.h | 770 > ++++++++++++++++ > > examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h | 159 ++++ > > examples/l2fwd-ethtool/lib/Makefile | 57 ++ > > examples/l2fwd-ethtool/lib/rte_ethtool.c | 336 +++++++ > > examples/l2fwd-ethtool/lib/rte_ethtool.h | 385 ++++++++ > > examples/l2fwd-ethtool/nic-control/Makefile | 55 ++ > > examples/l2fwd-ethtool/nic-control/nic_control.c | 614 +++++++++++++ > > 11 files changed, 3508 insertions(+) > > This patch is huge. > Please split a bit. > > > --- a/examples/Makefile > > +++ b/examples/Makefile > > @@ -53,6 +53,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_KNI) += kni > > DIRS-y += l2fwd > > DIRS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += l2fwd-ivshmem > > DIRS-$(CONFIG_RTE_LIBRTE_JOBSTATS) += l2fwd-jobstats > > +DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += l2fwd-ethtool > > DIRS-y += l3fwd > > Please keep the alphabetical order. > > I do not plan to review it more. > If nobody complains, it means it's accepted. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool 2015-10-21 16:36 ` Thomas Monjalon 2015-10-21 16:46 ` Wang, Liang-min @ 2015-11-17 22:33 ` Wang, Liang-min 2015-11-18 17:56 ` Thomas Monjalon 1 sibling, 1 reply; 10+ messages in thread From: Wang, Liang-min @ 2015-11-17 22:33 UTC (permalink / raw) To: Thomas Monjalon; +Cc: dev Thomas, Could you explain why this patch is put on RFC? Thanks, Larry > -----Original Message----- > From: Wang, Liang-min > Sent: Wednesday, October 21, 2015 12:47 PM > To: 'Thomas Monjalon' > Cc: dev@dpdk.org; Andrew Harvey (agh) (agh@cisco.com) > Subject: RE: [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool > > Thomas, > Let's put this patch on defer list because there are related work might > take a different approach. Let's only review the make file change (PATCH 1/2). > I believe "export" is needed since the variable is shared by all the build but it > might be already included due to the mk file inclusion. Since Andy is on > vacation, I am not sure if he could make a comment on that. > > Larry > > > -----Original Message----- > > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com] > > Sent: Wednesday, October 21, 2015 12:36 PM > > To: Wang, Liang-min > > Cc: dev@dpdk.org > > Subject: Re: [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd- > > ethtool > > > > 2015-07-23 11:00, Liang-Min Larry Wang: > > > examples/Makefile | 1 + > > > examples/l2fwd-ethtool/Makefile | 48 + > > > examples/l2fwd-ethtool/l2fwd-app/Makefile | 58 ++ > > > examples/l2fwd-ethtool/l2fwd-app/main.c | 1025 > > ++++++++++++++++++++++ > > > examples/l2fwd-ethtool/l2fwd-app/netdev_api.h | 770 > > ++++++++++++++++ > > > examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h | 159 ++++ > > > examples/l2fwd-ethtool/lib/Makefile | 57 ++ > > > examples/l2fwd-ethtool/lib/rte_ethtool.c | 336 +++++++ > > > examples/l2fwd-ethtool/lib/rte_ethtool.h | 385 ++++++++ > > > examples/l2fwd-ethtool/nic-control/Makefile | 55 ++ > > > examples/l2fwd-ethtool/nic-control/nic_control.c | 614 +++++++++++++ > > > 11 files changed, 3508 insertions(+) > > > > This patch is huge. > > Please split a bit. > > > > > --- a/examples/Makefile > > > +++ b/examples/Makefile > > > @@ -53,6 +53,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_KNI) += kni > > > DIRS-y += l2fwd > > > DIRS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += l2fwd-ivshmem > > > DIRS-$(CONFIG_RTE_LIBRTE_JOBSTATS) += l2fwd-jobstats > > > +DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += l2fwd-ethtool > > > DIRS-y += l3fwd > > > > Please keep the alphabetical order. > > > > I do not plan to review it more. > > If nobody complains, it means it's accepted. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool 2015-11-17 22:33 ` Wang, Liang-min @ 2015-11-18 17:56 ` Thomas Monjalon 0 siblings, 0 replies; 10+ messages in thread From: Thomas Monjalon @ 2015-11-18 17:56 UTC (permalink / raw) To: Wang, Liang-min; +Cc: dev 2015-11-17 22:33, Wang, Liang-min: > Thomas, > Could you explain why this patch is put on RFC? Hi, It is not RFC but Changes Requested. It means a new version is expected to adress comments. ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2015-11-18 17:58 UTC | newest] Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2015-07-20 14:12 [dpdk-dev] [PATCH] User-space Ethool example Liang-Min Larry Wang 2015-07-20 14:12 ` [dpdk-dev] [PATCH] examples: new example: l2fwd-ethtool Liang-Min Larry Wang 2015-07-23 15:00 ` [dpdk-dev] [PATCH v2 0/2] Example: l2fwd-ethtool Liang-Min Larry Wang 2015-07-23 15:00 ` [dpdk-dev] [PATCH v2 1/2] Remove ABI requierment for external library builds Liang-Min Larry Wang 2015-10-21 16:31 ` Thomas Monjalon 2015-07-23 15:00 ` [dpdk-dev] [PATCH v2 2/2] examples: new example: l2fwd-ethtool Liang-Min Larry Wang 2015-10-21 16:36 ` Thomas Monjalon 2015-10-21 16:46 ` Wang, Liang-min 2015-11-17 22:33 ` Wang, Liang-min 2015-11-18 17:56 ` Thomas Monjalon
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).