The example includes an ethtool library and two applications: one application is a non- DPDK process (nic-control) and the other is a DPDK l2fwd applicaiton (l2fwd-app). The nic-control process sends ethtool alike device management requests to l2fwd-app through a named pipe IPC. This example is designed to show how to build a ethtool shim library and how to use ethtool apis to manage device parameters.
Signed-off-by: Liang-Min Larry Wang <liang-min.wang at intel.com> --- examples/Makefile | 3 + examples/l2fwd-ethtool/Makefile | 55 ++ examples/l2fwd-ethtool/l2fwd-app/Makefile | 60 ++ examples/l2fwd-ethtool/l2fwd-app/main.c | 1066 ++++++++++++++++++++++ examples/l2fwd-ethtool/l2fwd-app/netdev_api.h | 769 ++++++++++++++++ examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h | 158 ++++ examples/l2fwd-ethtool/lib/Makefile | 55 ++ examples/l2fwd-ethtool/lib/rte_ethtool.c | 308 +++++++ examples/l2fwd-ethtool/lib/rte_ethtool.h | 384 ++++++++ examples/l2fwd-ethtool/nic-control/Makefile | 55 ++ examples/l2fwd-ethtool/nic-control/nic_control.c | 471 ++++++++++ 11 files changed, 3384 insertions(+) create mode 100644 examples/l2fwd-ethtool/Makefile create mode 100644 examples/l2fwd-ethtool/l2fwd-app/Makefile create mode 100644 examples/l2fwd-ethtool/l2fwd-app/main.c create mode 100644 examples/l2fwd-ethtool/l2fwd-app/netdev_api.h create mode 100644 examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h create mode 100644 examples/l2fwd-ethtool/lib/Makefile create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.c create mode 100644 examples/l2fwd-ethtool/lib/rte_ethtool.h create mode 100644 examples/l2fwd-ethtool/nic-control/Makefile create mode 100644 examples/l2fwd-ethtool/nic-control/nic_control.c diff --git a/examples/Makefile b/examples/Makefile index b4eddbd..cd1c4b0 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -50,6 +50,9 @@ DIRS-y += ip_reassembly DIRS-$(CONFIG_RTE_IP_FRAG) += ip_fragmentation DIRS-y += ipv4_multicast DIRS-$(CONFIG_RTE_LIBRTE_KNI) += kni +DIRS-y += l2fwd-ethtool/lib +DIRS-y += l2fwd-ethtool/nic-control +DIRS-y += l2fwd-ethtool/l2fwd-app DIRS-y += l2fwd DIRS-$(CONFIG_RTE_LIBRTE_IVSHMEM) += l2fwd-ivshmem DIRS-$(CONFIG_RTE_LIBRTE_JOBSTATS) += l2fwd-jobstats diff --git a/examples/l2fwd-ethtool/Makefile b/examples/l2fwd-ethtool/Makefile new file mode 100644 index 0000000..80d257e --- /dev/null +++ b/examples/l2fwd-ethtool/Makefile @@ -0,0 +1,55 @@ +# BSD LICENSE +# +# Copyright(c) 2015 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overwritten by command line or environment +RTE_TARGET ?= x86_64-native-linuxapp-gcc + +include $(RTE_SDK)/mk/rte.vars.mk +unexport RTE_SRCDIR RTE_OUTPUT RTE_EXTMK + +ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp") +$(error This application can only operate in a linuxapp environment, \ +please change the definition of the RTE_TARGET environment variable) +endif + +DIRS-y += lib nic-control l2fwd-app + +.PHONY: all clean $(DIRS-y) + +all: $(DIRS-y) +clean: $(DIRS-y) + +$(DIRS-y): + $(MAKE) -C $@ $(MAKECMDGOALS) O=$(RTE_OUTPUT) diff --git a/examples/l2fwd-ethtool/l2fwd-app/Makefile b/examples/l2fwd-ethtool/l2fwd-app/Makefile new file mode 100644 index 0000000..fd0b9a7 --- /dev/null +++ b/examples/l2fwd-ethtool/l2fwd-app/Makefile @@ -0,0 +1,60 @@ +# 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) + +LIBDIRS += -L$(S)/../lib/l2fwd-ethtool/lib/$(RTE_TARGET)/lib +LIBDIRS += -L$(S)/../lib/$(RTE_TARGET)/lib +LIBDIRS += -L$(S)/../build/lib +LDLIBS += $(LIBDIRS) -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..19dd58c --- /dev/null +++ b/examples/l2fwd-ethtool/l2fwd-app/main.c @@ -0,0 +1,1066 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2015 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/queue.h> +#include <netinet/in.h> +#include <setjmp.h> +#include <stdarg.h> +#include <ctype.h> +#include <errno.h> +#include <getopt.h> +#include <pthread.h> +#include <unistd.h> + +#include <rte_common.h> +#include <rte_log.h> +#include <rte_memory.h> +#include <rte_memcpy.h> +#include <rte_memzone.h> +#include <rte_tailq.h> +#include <rte_eal.h> +#include <rte_per_lcore.h> +#include <rte_launch.h> +#include <rte_atomic.h> +#include <rte_cycles.h> +#include <rte_prefetch.h> +#include <rte_lcore.h> +#include <rte_per_lcore.h> +#include <rte_branch_prediction.h> +#include <rte_interrupts.h> +#include <rte_pci.h> +#include <rte_random.h> +#include <rte_debug.h> +#include <rte_ether.h> +#include <rte_ethdev.h> +#include <rte_ring.h> +#include <rte_mempool.h> +#include <rte_mbuf.h> +#include "rte_ethtool.h" +#define NETDEV_OP_REPLY 1 +#include "netdev_api.h" + +#define to_mac_type(x) (struct ether_addr *)(void *)(x) +#define RTE_LOGTYPE_L2FWD RTE_LOGTYPE_USER1 + +#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define NB_MBUF 8192 + +#define MAX_PKT_BURST 32 +#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */ + +#define is_vf_port(vf_mask, port_id) ((vf_mask & (1 << port_id)) > 0) +#define is_port_enabled(port_mask, port_id) ((port_mask & (1 << port_id)) > 0) +#define TX_PTHRESH 32 +#define TX_HTHRESH 0 +#define TX_WTHRESH 0 +/* + * Configurable number of RX/TX ring descriptors + */ +#define RTE_TEST_RX_DESC_DEFAULT 128 +#define RTE_TEST_TX_DESC_DEFAULT 512 +static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT; +static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT; + +/* ethernet addresses of ports */ +static struct ether_addr l2fwd_ports_eth_addr[RTE_MAX_ETHPORTS]; + +/* mask of enabled ports */ +static uint32_t l2fwd_enabled_port_mask; + +/* virtio setup enable */ +static int virtio_setup; + +/* list of enabled ports */ +static uint32_t l2fwd_dst_ports[RTE_MAX_ETHPORTS]; + +static unsigned int l2fwd_rx_queue_per_lcore = 1; + +struct mbuf_table { + unsigned len; + struct rte_mbuf *m_table[MAX_PKT_BURST]; +}; + +#define MAX_RX_QUEUE_PER_LCORE 16 +#define MAX_TX_QUEUE_PER_PORT 16 +struct lcore_queue_conf { + unsigned n_rx_port; + unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE]; + struct mbuf_table tx_mbufs[RTE_MAX_ETHPORTS]; + +} __rte_cache_aligned; +struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE]; + +static struct rte_eth_conf port_conf = { + .rxmode = { + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled */ + .hw_ip_checksum = 0, /**< IP checksum offload disabled */ + .hw_vlan_filter = 0, /**< VLAN filtering disabled */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ + .hw_strip_crc = 0, /**< CRC stripped by hardware */ + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, +}; + +static struct rte_eth_txconf tx_conf = { + .tx_thresh = { + .pthresh = TX_PTHRESH, + .hthresh = TX_HTHRESH, + .wthresh = TX_WTHRESH, + }, + .tx_free_thresh = 32, + .tx_rs_thresh = 32, + .txq_flags = 0xf00, +}; + +struct rte_mempool *l2fwd_pktmbuf_pool; + +/* Per-port statistics struct */ +struct l2fwd_port_statistics { + uint64_t tx; + uint64_t rx; + uint64_t dropped; +} __rte_cache_aligned; +struct l2fwd_port_statistics port_statistics[RTE_MAX_ETHPORTS]; + +/* A tsc-based timer responsible for triggering statistics printout */ +#define TIMER_MILLISECOND 2000000ULL /* around 1ms at 2 Ghz */ +#define MAX_TIMER_PERIOD 86400 /* 1 day max */ +/* default period is 10 seconds */ +static int64_t timer_period = 10 * TIMER_MILLISECOND * 1000; + +/* IPC done checking utility function */ +/* status of ipc completed */ +static rte_atomic64_t ipc_done; + +static inline void init_ipc_done(void) +{ + rte_atomic64_init(&ipc_done); +} + +static inline int is_ipc_done(void) +{ + return rte_atomic64_read(&ipc_done) > 0; +} + +static inline void set_ipc_done(void) +{ + rte_atomic64_inc(&ipc_done); +} + +/* Print out statistics on packets dropped */ +static void +print_stats(void) +{ + uint64_t total_packets_dropped, total_packets_tx, total_packets_rx; + unsigned portid; + + total_packets_dropped = 0; + total_packets_tx = 0; + total_packets_rx = 0; + + const char clr[] = { 27, '[', '2', 'J', '\0' }; + const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' }; + + /* Clear screen and move to top left */ + printf("%s%s", clr, topLeft); + + printf("\nPort statistics ===================================="); + + for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) { + /* skip disabled ports */ + if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) + continue; + printf("\nStatistics for port %u ----------------------------", + portid); + printf("\nPackets sent: %24"PRIu64, port_statistics[portid].tx); + printf("\nPackets received: %20"PRIu64, + port_statistics[portid].rx); + printf("\nPackets dropped: %21"PRIu64, + port_statistics[portid].dropped); + + total_packets_dropped += port_statistics[portid].dropped; + total_packets_tx += port_statistics[portid].tx; + total_packets_rx += port_statistics[portid].rx; + } + printf("\nAggregate statistics ==============================="); + printf("\nTotal packets sent: %18"PRIu64, total_packets_tx); + printf("\nTotal packets received: %14"PRIu64, total_packets_rx); + printf("\nTotal packets dropped: %15"PRIu64, total_packets_dropped); + printf("\n====================================================\n"); +} + +/* Send the burst of packets on an output interface */ +static int +l2fwd_send_burst(struct lcore_queue_conf *qconf, unsigned n, uint8_t port) +{ + struct rte_mbuf **m_table; + unsigned ret; + unsigned queueid = 0; + + m_table = (struct rte_mbuf **)qconf->tx_mbufs[port].m_table; + + ret = rte_eth_tx_burst(port, (uint16_t) queueid, m_table, (uint16_t) n); + port_statistics[port].tx += ret; + if (unlikely(ret < n)) { + port_statistics[port].dropped += (n - ret); + do { + rte_pktmbuf_free(m_table[ret]); + } while (++ret < n); + } + + return 0; +} + +/* Enqueue packets for TX and prepare them to be sent */ +static int +l2fwd_send_packet(struct rte_mbuf *m, uint8_t port) +{ + unsigned lcore_id, len; + struct lcore_queue_conf *qconf; + + lcore_id = rte_lcore_id(); + + qconf = &lcore_queue_conf[lcore_id]; + len = qconf->tx_mbufs[port].len; + qconf->tx_mbufs[port].m_table[len] = m; + len++; + + /* enough pkts to be sent */ + if (unlikely(len == MAX_PKT_BURST)) { + l2fwd_send_burst(qconf, MAX_PKT_BURST, port); + len = 0; + } + + qconf->tx_mbufs[port].len = len; + return 0; +} + +static void +l2fwd_simple_forward(struct rte_mbuf *m, unsigned portid) +{ + struct ether_hdr *eth; + void *tmp; + unsigned dst_port; + + dst_port = l2fwd_dst_ports[portid]; + eth = rte_pktmbuf_mtod(m, struct ether_hdr *); + + /* 02:00:00:00:00:xx */ + tmp = ð->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, void *mac_ptr) +{ + struct ethtool_drvinfo drvinfo; + uint8_t mac_addr[MAC_ADDR_SIZE]; + uint8_t param[4], port_id, num_of_ports = info->num_of_ports; + uint32_t param2[2]; + uint8_t *new_mac_addr = mac_ptr; + int status; + + param[0] = num_of_ports; + info->vf_port_mask = 0; + for (port_id = 0; port_id < num_of_ports; port_id++) { + status = rte_ethtool_get_drvinfo(port_id, &drvinfo); + if (status) { + printf("get_drvinfo from port #%d fails\n", port_id); + return -1; + } + info->vf_port_mask |= (drvinfo.eedump_len == 0?1:0) << port_id; + rte_ethtool_net_stop(port_id); + } + param2[0] = info->port_mask; + param2[1] = info->vf_port_mask; + + for (port_id = 0; port_id < num_of_ports; port_id++) { + rte_ethtool_net_open(port_id); + /* Using rte_ethtool_net_set_rx_mode instead of */ + /* rte_eth_promiscuous_enable to test */ + /* rte_ethtool_net_set_rx_mode */ + if (!is_vf_port(info->vf_port_mask, port_id)) { + struct rte_eth_dev *dev = &rte_eth_devices[port_id]; + struct rte_eth_dev_data *dev_data = + (struct rte_eth_dev_data *)dev->data; + + dev_data->promiscuous = 1; + + rte_ethtool_net_set_rx_mode(port_id); + } + rte_ethtool_net_get_mac_addr(port_id, (void *)mac_addr); + printf("Port #%d init mac address is", port_id); + printf(" %s", mac_addr_str(mac_addr)); + + if (is_vf_port(info->vf_port_mask, port_id)) { + /* use new mac addr if the default addr is not valid */ + if (!is_valid_assigned_ether_addr(to_mac_type(mac_addr)) + ) { + if (rte_ethtool_net_set_mac_addr(port_id, + (void *)new_mac_addr) == 0) { + printf(", and re-assigned to "); + printf("%s\n", + mac_addr_str(new_mac_addr)); + new_mac_addr[MAC_ADDR_SIZE-1]++; + } else { + printf("\n"); + } + } + } else { + printf("\n"); + } + } + + send_reply2(req_id, 1, param, (uint16_t)(sizeof(uint32_t)*2), param2); + return 0; +} + +static inline void +proc_no_action(uint16_t req_id) +{ + send_reply(req_id, 0, NULL); +} + +static inline void +proc_invalid(uint16_t req_id) +{ + send_reply(req_id, BAD_RETURN(0), NULL); +} + +static void* +ethtool(void *ctx) +{ + struct nic_info *info = ctx; + int keep_req = 1; + int reg_count, eeprom_size; + uint16_t req_id, param1_size, param2_size; + uint8_t req_type, port_id; + int status; + uint8_t param1[MAXI_PARA]; + uint8_t param2[MAXI_PARA]; + uint8_t reply1[MAXI_DATA]; + void *first_param = FIRST_PARAM(param1); + + init_rep_pipe(); + while (1) { + read_request(&req_id, &req_type, ¶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, first_param); + + 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: + { + struct ethtool_pauseparam *pause_param = + (void *)reply1; + + status = proc_ethtool_get_pauseparam(port_id, + req_id, pause_param); + + if (status != 0) { + printf("get_pauseparam return"); + printf(" status %d\n", status); + } + } + break; + + case set_pauseparam: + { + struct ethtool_pauseparam *pause_param = + (void *)reply1; + + status = proc_ethtool_set_pauseparam(port_id, + req_id, pause_param); + + if (status != 0) { + printf("set_pauseparam return"); + printf(" status %d\n", status); + } + } + break; + + case dev_open: + status = proc_net_open(port_id, req_id); + break; + + case dev_stop: + status = proc_net_stop(port_id, req_id); + break; + + case set_rx_mode: + status = proc_net_set_rx_mode(port_id, req_id); + break; + + case get_mac_addr: + status = proc_net_get_mac_addr(port_id, + req_id, first_param); + break; + + case set_mac_addr: + status = proc_net_set_mac_addr(port_id, + req_id, first_param); + break; + + case validate_addr: + status = proc_net_validate_addr(port_id, + req_id, first_param); + break; + + case set_config: + status = proc_net_set_config(port_id, + req_id, first_param); + break; + + case change_mtu: + status = proc_net_change_mtu(port_id, + req_id, first_param); + break; + + case get_stats64: + status = proc_net_get_stats64(port_id, + req_id, reply1); + break; + + case vlan_rx_add_vid: + status = proc_net_vlan_rx_add_vid(port_id, + req_id, first_param); + break; + + case vlan_rx_kill_vid: + status = proc_net_vlan_rx_kill_vid(port_id, + req_id, first_param); + break; + + case ipc_end: + keep_req = 0; + proc_no_action(req_id); + status = 0; + break; + + default: + proc_invalid(req_id); + printf("unsupported service request type:"); + printf(" %d\n", req_type); + break; + } + if (status < 0) + printf("Request type (=%d) failed\n", (int)req_type); + /* check if termination flag is set */ + } + printf("IPC session is over\n"); + return NULL; +} + +int +main(int argc, char **argv) +{ + struct lcore_queue_conf *qconf; + struct rte_eth_dev_info dev_info; + int ret; + uint8_t nb_ports; + uint8_t nb_ports_available; + uint8_t portid, last_port; + unsigned lcore_id, rx_lcore_id; + unsigned nb_ports_in_mask = 0; + + init_ipc_done(); + /* init EAL */ + ret = rte_eal_init(argc, argv); + if (ret < 0) + rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n"); + argc -= ret; + argv += ret; + + /* parse application arguments (after the EAL ones) */ + ret = l2fwd_parse_args(argc, argv); + if (ret < 0) + rte_exit(EXIT_FAILURE, "Invalid L2FWD arguments\n"); + + /* create the mbuf pool */ + l2fwd_pktmbuf_pool = + rte_mempool_create("mbuf_pool", NB_MBUF, + MBUF_SIZE, 32, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (l2fwd_pktmbuf_pool == NULL) + rte_exit(EXIT_FAILURE, "Cannot init mbuf pool\n"); + + nb_ports = rte_eth_dev_count(); + if (nb_ports == 0) + rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n"); + + if (nb_ports > RTE_MAX_ETHPORTS) + nb_ports = RTE_MAX_ETHPORTS; + + /* reset l2fwd_dst_ports */ + for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) + l2fwd_dst_ports[portid] = 0; + last_port = 0; + + /* + * Each logical core is assigned a dedicated TX queue on each port. + */ + for (portid = 0; portid < nb_ports; portid++) { + /* skip ports that are not enabled */ + if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) + continue; + + if (nb_ports_in_mask % 2) { + l2fwd_dst_ports[portid] = last_port; + l2fwd_dst_ports[last_port] = portid; + } else + last_port = portid; + + nb_ports_in_mask++; + + rte_eth_dev_info_get(portid, &dev_info); + } + if (nb_ports_in_mask % 2) { + printf("Notice: odd number of ports in portmask.\n"); + l2fwd_dst_ports[last_port] = last_port; + } + + rx_lcore_id = 0; + qconf = NULL; + + /* Initialize the port/queue configuration of each logical core */ + for (portid = 0; portid < nb_ports; portid++) { + /* skip ports that are not enabled */ + if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) + continue; + + /* get the lcore_id for this port */ + while (rte_lcore_is_enabled(rx_lcore_id) == 0 || + lcore_queue_conf[rx_lcore_id].n_rx_port == + l2fwd_rx_queue_per_lcore) { + rx_lcore_id++; + if (rx_lcore_id >= RTE_MAX_LCORE) + rte_exit(EXIT_FAILURE, "Not enough cores\n"); + } + + if (qconf != &lcore_queue_conf[rx_lcore_id]) + /* Assigned a new logical core in the loop above. */ + qconf = &lcore_queue_conf[rx_lcore_id]; + + qconf->rx_port_list[qconf->n_rx_port] = portid; + qconf->n_rx_port++; + printf("Lcore %u: RX port %u\n", rx_lcore_id, + (unsigned) portid); + } + + nb_ports_available = nb_ports; + + /* Initialise each port */ + for (portid = 0; portid < nb_ports; portid++) { + /* skip ports that are not enabled */ + if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) { + printf("Skipping disabled port %u\n", + (unsigned) portid); + nb_ports_available--; + continue; + } + /* init port */ + printf("Initializing port %u... ", (unsigned) portid); + fflush(stdout); + ret = rte_eth_dev_configure(portid, 1, 1, &port_conf); + if (ret < 0) + rte_exit(EXIT_FAILURE, + "Cannot configure device: err=%d, port=%u\n", + ret, (unsigned) portid); + + rte_eth_macaddr_get(portid, &l2fwd_ports_eth_addr[portid]); + + /* init one RX queue */ + fflush(stdout); + ret = rte_eth_rx_queue_setup(portid, 0, nb_rxd, + rte_eth_dev_socket_id(portid), + NULL, + l2fwd_pktmbuf_pool); + if (ret < 0) + rte_exit(EXIT_FAILURE, + "rte_eth_rx_queue_setup:err=%d, port=%u\n", + ret, (unsigned) portid); + + /* init one TX queue on each port */ + fflush(stdout); + if (virtio_setup) { + ret = rte_eth_tx_queue_setup(portid, 0, nb_txd, + rte_eth_dev_socket_id(portid), &tx_conf); + } else { + ret = rte_eth_tx_queue_setup(portid, 0, nb_txd, + rte_eth_dev_socket_id(portid), + NULL); + } + if (ret < 0) + rte_exit(EXIT_FAILURE, + "rte_eth_tx_queue_setup:err=%d, port=%u\n", + ret, (unsigned) portid); + } + + /* create a ethtool proxy thread */ + pthread_attr_t attr; + cpu_set_t cpus; + pthread_t ethtool_thread; + struct nic_info info; + + /* set core affinity to core 1 */ + CPU_ZERO(&cpus); + CPU_SET(2, &cpus); + pthread_attr_init(&attr); + pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpus); + /* Since the register size is more than 4K (1147*4) */ + pthread_attr_setstacksize(&attr, 4*PAGE_SIZE); + + info.num_of_ports = nb_ports; + info.port_mask = l2fwd_enabled_port_mask; + if (pthread_create(ð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..0964bea --- /dev/null +++ b/examples/l2fwd-ethtool/l2fwd-app/netdev_api.h @@ -0,0 +1,769 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2015 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NETDEV_API_H_ +#define _NETDEV_API_H_ + +#include <linux/ethtool.h> +#include <string.h> +#include "shared_fifo.h" + +#define MAC_ADDR_SIZE 6 +#define quad_aligned_size(x) ((x & 0x7) ? ((x+7)&0x7) : x) + +#define size16(data_type) (uint16_t)(sizeof(data_type)) + +/* NETDEV_STATUS = 0 if successful */ +#define NETDEV_UNSUPPORTED -1 +#define NETDEV_INVALID -1 +#define NETDEV_STATUS(data_size) (GOOD_RETURN(data_size) \ + ? 0 : NETDEV_INVALID) +#define UNUSED(x) (void)(x) + +#ifdef NETDEV_OP_REQUEST +static uint16_t +next_reqid(void) { + static uint16_t request_id; + + return request_id++; +} + +/* + * send request (with one or two variables) to request-pipe + * (invoked by non- DPDK process) + */ +static int +send_request(uint16_t req_id, uint8_t req_type, uint16_t param_size, + void *param_data) +{ + int fd; + uint32_t req[2]; + + req[0] = REQ_DWORD_LO(req_id, 0, req_type); + req[1] = REQ_DWORD_HI(param_size, 0); + + fd = open(REQ_PIPE, O_WRONLY); + write(fd, req, PIPE_CTL_BYTE_COUNT); + if (param_size) + write(fd, param_data, param_size); + close(fd); + + return 0; +} + +/* + * send request (with more than two variables) to request-pipe + * (invoked by non- DPDK process) + */ +static int +send_request2(uint16_t req_id, uint8_t req_type, uint16_t param1_size, + void *param1_data, int param2_size, void *param2_data) +{ + int fd; + uint32_t req[2]; + + req[0] = REQ_DWORD_LO(req_id, 1, req_type); + req[1] = REQ_DWORD_HI(param1_size, param2_size); + + fd = open(REQ_PIPE, O_WRONLY); + write(fd, req, PIPE_CTL_BYTE_COUNT); + + if (param1_size) + write(fd, param1_data, param1_size); + if (param2_size) + write(fd, param2_data, param2_size); + close(fd); + + return 0; +} + +/* read return variables from the reply-pipe (invoked by non- DPDK process) */ +static int +read_reply(uint16_t expected_id, uint16_t *byte_count, void *reply_data1, + void *reply_data2) +{ + int fd; + uint32_t req[2]; + uint16_t rx_id, data1_size; + + /* block on read if reply is not available */ + fd = open(REP_PIPE, O_RDONLY); + read(fd, req, PIPE_CTL_BYTE_COUNT); + + *byte_count = REP_DATA1_COUNT(req); + rx_id = REP_ID(req); + + if (!GOOD_RETURN(*byte_count)) { + close(fd); + return -1; + } + data1_size = BYTE_COUNT((*byte_count)); + read(fd, reply_data1, data1_size); + if (MULTIPLE_DATA(*byte_count)) { + assert(reply_data2); + read(fd, reply_data2, REP_DATA2_COUNT(req)); + } + close(fd); + + if (expected_id != rx_id) + return -1; + return 0; +} + +/* definition of netdev op request */ + +static int +netdev_ethtool_get_drvinfo(uint8_t port_id, struct ethtool_drvinfo *drvinfo) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + + send_request(req_id, get_drvinfo, 1, &port_id); + read_reply(req_id, &data_size, drvinfo, NULL); + + return NETDEV_STATUS(data_size); +}; + +static int +netdev_ethtool_get_regs_len(uint8_t port_id) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + int leng; + + send_request(req_id, get_regs_len, 1, &port_id); + read_reply(req_id, &data_size, &leng, NULL); + + if (GOOD_RETURN(data_size)) + return leng; + return NETDEV_STATUS(data_size); +}; + +static int +netdev_ethtool_get_regs(uint8_t port_id, struct ethtool_regs *regs, void *buf) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + uint8_t param_data[PARAM_SIZE(struct ethtool_regs)]; + + param_data[0] = port_id; + memcpy(FIRST_PARAM(param_data), regs, sizeof(struct ethtool_regs)); + + send_request(req_id, get_regs, PARAM_SIZE(struct ethtool_regs), + param_data); + read_reply(req_id, &data_size, regs, buf); + + return NETDEV_STATUS(data_size); +}; + +static int +netdev_ethtool_get_link(uint8_t port_id) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + int link_status; + + send_request(req_id, get_link, 1, &port_id); + read_reply(req_id, &data_size, &link_status, NULL); + if (GOOD_RETURN(data_size)) + return link_status; + return NETDEV_STATUS(data_size); +}; + +static int +netdev_ethtool_get_eeprom_len(uint8_t port_id) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + int leng; + + send_request(req_id, get_eeprom_len, 1, &port_id); + read_reply(req_id, &data_size, &leng, NULL); + + if (GOOD_RETURN(data_size)) + return leng; + return NETDEV_STATUS(data_size); +}; + +static int +netdev_ethtool_get_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom, + void *words) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + uint8_t param_data[PARAM_SIZE(struct ethtool_eeprom)]; + + param_data[0] = port_id; + memcpy(FIRST_PARAM(param_data), eeprom, sizeof(struct ethtool_eeprom)); + + send_request(req_id, get_eeprom, PARAM_SIZE(struct ethtool_eeprom), + param_data); + read_reply(req_id, &data_size, words, NULL); + + return NETDEV_STATUS(data_size); +}; + +static int +netdev_ethtool_set_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom, + void *words) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + uint8_t param_data[PARAM_SIZE(struct ethtool_eeprom)]; + + param_data[0] = port_id; + memcpy(FIRST_PARAM(param_data), eeprom, sizeof(struct ethtool_eeprom)); + + send_request2(req_id, set_eeprom, PARAM_SIZE(struct ethtool_eeprom), + param_data, eeprom->len, words); + read_reply(req_id, &data_size, NULL, NULL); + + return NETDEV_STATUS(data_size); +}; + +static int +netdev_ethtool_get_pauseparam(uint8_t port_id, struct ethtool_pauseparam *param) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + + send_request(req_id, get_pauseparam, 1, &port_id); + read_reply(req_id, &data_size, param, NULL); + + return NETDEV_STATUS(data_size); +}; + +static int +netdev_ethtool_set_pauseparam(uint8_t port_id, struct ethtool_pauseparam *param) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + + send_request(req_id, set_pauseparam, 1, &port_id); + read_reply(req_id, &data_size, param, NULL); + + return NETDEV_STATUS(data_size); +}; + +static int +netdev_net_open(uint8_t port_id) { + uint16_t req_id = next_reqid(); + uint16_t data_size; + + send_request(req_id, dev_open, 1, &port_id); + read_reply(req_id, &data_size, NULL, NULL); + + return NETDEV_STATUS(data_size); +}; + +static int +netdev_net_stop(uint8_t port_id) { + uint16_t req_id = next_reqid(); + uint16_t data_size; + + send_request(req_id, dev_open, 1, &port_id); + read_reply(req_id, &data_size, NULL, NULL); + + return NETDEV_STATUS(data_size); +}; + +static int +netdev_net_set_rx_mode(uint8_t port_id) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + + send_request(req_id, set_rx_mode, 1, &port_id); + read_reply(req_id, &data_size, NULL, NULL); + + return NETDEV_STATUS(data_size); +}; + +static int +netdev_net_get_mac_addr(uint8_t port_id, void *addr) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + + send_request(req_id, get_mac_addr, 1, &port_id); + read_reply(req_id, &data_size, addr, NULL); + + return NETDEV_STATUS(data_size); +}; + +static int +netdev_net_set_mac_addr(uint8_t port_id, void *addr) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + uint8_t param_data[FIRST_DATA_OFFSET+MAC_ADDR_SIZE]; + + param_data[0] = port_id; + memcpy(FIRST_PARAM(param_data), addr, MAC_ADDR_SIZE); + send_request(req_id, set_mac_addr, + (FIRST_DATA_OFFSET+MAC_ADDR_SIZE), param_data); + read_reply(req_id, &data_size, NULL, NULL); + + return NETDEV_STATUS(data_size); +}; + +static int +netdev_net_validate_addr(uint8_t port_id, void *addr) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + uint8_t param_data[FIRST_DATA_OFFSET+MAC_ADDR_SIZE]; + int valid; + + param_data[0] = port_id; + memcpy(FIRST_PARAM(param_data), addr, MAC_ADDR_SIZE); + send_request(req_id, validate_addr, + (FIRST_DATA_OFFSET+MAC_ADDR_SIZE), param_data); + read_reply(req_id, &data_size, &valid, NULL); + + if (GOOD_RETURN(data_size)) + return valid; + return NETDEV_STATUS(data_size); +}; + +static int +netdev_net_change_mtu(uint8_t port_id, int mtu) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + uint8_t param_data[PARAM_SIZE(int)]; + + param_data[0] = port_id; + memcpy(FIRST_PARAM(param_data), &mtu, sizeof(int)); + send_request(req_id, change_mtu, PARAM_SIZE(int), param_data); + read_reply(req_id, &data_size, NULL, NULL); + + return NETDEV_STATUS(data_size); +}; + +static int +netdev_net_get_stats64(uint8_t port_id, void *stats) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + + send_request(req_id, get_stats64, 1, &port_id); + read_reply(req_id, &data_size, stats, NULL); + + return NETDEV_STATUS(data_size); +}; + +static int +netdev_net_vlan_rx_add_vid(uint8_t port_id, uint16_t vid) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + uint8_t param_data[PARAM_SIZE(int)]; + + param_data[0] = port_id; + memcpy(FIRST_PARAM(param_data), &vid, sizeof(uint16_t)); + send_request(req_id, vlan_rx_add_vid, FIRST_DATA_OFFSET+sizeof(int), + param_data); + read_reply(req_id, &data_size, NULL, NULL); + + return NETDEV_STATUS(data_size); +}; + +static int +netdev_net_vlan_rx_kill_vid(uint8_t port_id, uint16_t vid) +{ + uint16_t req_id = next_reqid(); + uint16_t data_size; + uint8_t param_data[PARAM_SIZE(int)]; + + param_data[0] = port_id; + memcpy(FIRST_PARAM(param_data), &vid, sizeof(uint16_t)); + send_request(req_id, vlan_rx_kill_vid, FIRST_DATA_OFFSET+sizeof(int), + param_data); + read_reply(req_id, &data_size, NULL, NULL); + + return NETDEV_STATUS(data_size); +}; + +#endif /* NETDEV_OP_REQUEST */ + +#ifdef NETDEV_OP_REPLY +/* read request from request-pipe (invoked by rte-api server thread) */ +static int +read_request(uint16_t *req_id, uint8_t *req_type, uint16_t *param1_size, + uint8_t *param1_data, uint16_t *param2_size, void *param2_data) +{ + int fd; + uint32_t req[2]; + + /* block on read if request is not sent ... */ + fd = open(REQ_PIPE, O_RDONLY); + read(fd, req, PIPE_CTL_BYTE_COUNT); + + *req_id = REQ_ID(req); + *req_type = REQ_TYPE(req); + *param1_size = REQ_PARAM1_SIZE(req); + + if (*param1_size > 0) { + read(fd, param1_data, *param1_size); + if (REQ_IDTYPE(req)) { + *param2_size = REQ_PARAM2_SIZE(req); + read(fd, param2_data, *param2_size); + } else + *param2_size = 0; + } + close(fd); + + return 0; +} + +/* definition of netdev op service */ +/* + * rep[1:0]: request id + * rep[3:2]: data byte count; bit[15]: error status bit[14]: multiple return + * variables are requested + * + * send reply with one return variable to reply-pipe + * (invoked by rte-api server thread) + */ +static int +send_reply(uint16_t rx_id, uint16_t byte_count, void *reply_data) +{ + int fd; + uint32_t req[2]; + + req[0] = REP_DWORD_LO(rx_id, byte_count); + req[1] = REP_DWORD_HI(0); + + fd = open(REP_PIPE, O_WRONLY); + write(fd, req, PIPE_CTL_BYTE_COUNT); + + if (GOOD_RETURN(byte_count) && (byte_count > 0)) + write(fd, reply_data, byte_count); + close(fd); + + return 0; +} + +/* + * send reply with two or more variables to reply-pipe + * (invoked by rte-api server thread) + */ +static int +send_reply2(uint16_t rx_id, uint16_t byte_count1, void *reply_data1, + uint16_t byte_count2, void *reply_data2) +{ + int fd; + uint32_t req[2]; + + req[0] = REP_DWORD_LO(rx_id, REP_MUTILPLE_DATA(byte_count1)); + req[1] = REP_DWORD_HI(byte_count2); + + fd = open(REP_PIPE, O_WRONLY); + write(fd, req, PIPE_CTL_BYTE_COUNT); + + if (GOOD_RETURN(byte_count1) && (byte_count2 > 0)) { + write(fd, reply_data1, byte_count1); + write(fd, reply_data2, byte_count2); + } + close(fd); + + return 0; +} + +/* Functions for netdev service thread */ +static int +proc_ethtool_get_drvinfo(uint8_t port_id, uint16_t req_id, void *param_data) +{ + struct ethtool_drvinfo *drvinfo = param_data; + uint16_t data_size; + + if (rte_ethtool_get_drvinfo(port_id, drvinfo)) + data_size = STATUS_MASK; + else + data_size = size16(struct ethtool_drvinfo); + return send_reply(req_id, data_size, param_data); +}; + +static int +proc_ethtool_get_regs_len(uint8_t port_id, uint16_t req_id) +{ + int reg_len; + uint16_t data_size; + + reg_len = rte_ethtool_get_regs_len(port_id); + if (reg_len == 0) + data_size = STATUS_MASK; + else + data_size = size16(int); + return send_reply(req_id, data_size, ®_len); +}; + +static int +proc_ethtool_get_regs(uint8_t port_id, uint16_t req_id, void *param_data, + void *reply_data2) +{ + struct ethtool_regs *reg_info = param_data; + void *buf = reply_data2; + uint16_t data_size; + + if (rte_ethtool_get_regs(port_id, reg_info, buf)) + data_size = STATUS_MASK; + else + data_size = rte_ethtool_get_regs_len(port_id)*sizeof(int); + return send_reply2(req_id, data_size, reg_info, + rte_ethtool_get_regs_len(port_id)*sizeof(int), reply_data2); +}; + +static int +proc_ethtool_get_link(uint8_t port_id, uint16_t req_id) +{ + int link_status; + + link_status = rte_ethtool_get_link(port_id); + return send_reply(req_id, (uint16_t)sizeof(int), &link_status); +}; + +static int +proc_ethtool_get_eeprom_len(uint8_t port_id, uint16_t req_id) +{ + int eeprom_leng; + uint16_t data_size; + + eeprom_leng = rte_ethtool_get_eeprom_len(port_id); + if (eeprom_leng == 0) + data_size = STATUS_MASK; + else + data_size = size16(int); + return send_reply(req_id, data_size, &eeprom_leng); +}; + +static int +proc_ethtool_get_eeprom(uint8_t port_id, uint16_t req_id, void *param_data, + void *reply_data) +{ + struct ethtool_eeprom *eeprom_ptr = param_data; + uint16_t data_size; + + if (rte_ethtool_get_eeprom(port_id, eeprom_ptr, reply_data)) + data_size = STATUS_MASK; + else + data_size = (uint16_t)(eeprom_ptr->len & ~1); + return send_reply(req_id, data_size, reply_data); +}; + +static int +proc_ethtool_set_eeprom(uint8_t port_id, uint16_t req_id, void *param_data, + void *param2_data) +{ + struct ethtool_eeprom *eeprom_ptr = param_data; + uint16_t data_size; + + if (rte_ethtool_set_eeprom(port_id, eeprom_ptr, param2_data)) + data_size = STATUS_MASK; + else + data_size = 0; + return send_reply(req_id, data_size, &data_size); +}; + +static int +proc_ethtool_get_pauseparam(uint8_t port_id, uint16_t req_id, void *reply_data) +{ + uint16_t data_size; + + if (rte_ethtool_get_pauseparam(port_id, + (struct ethtool_pauseparam *)reply_data)) + data_size = STATUS_MASK; + else + data_size = (uint16_t)(sizeof(struct ethtool_pauseparam)); + return send_reply(req_id, data_size, reply_data); +}; + +static int +proc_ethtool_set_pauseparam(uint8_t port_id, uint16_t req_id, void *set_data) +{ + uint16_t data_size; + + if (rte_ethtool_set_pauseparam(port_id, + (struct ethtool_pauseparam *)set_data)) + data_size = STATUS_MASK; + else + data_size = (uint16_t)(sizeof(struct ethtool_pauseparam)); + return send_reply(req_id, data_size, set_data); +}; + +static int +proc_net_open(uint8_t port_id, uint16_t req_id) +{ + uint16_t data_size; + + if (rte_ethtool_net_open(port_id)) + data_size = STATUS_MASK; + else + data_size = 0; + + return send_reply(req_id, data_size, &data_size); +}; + +static int +proc_net_stop(uint8_t port_id, uint16_t req_id) +{ + uint16_t data_size; + + rte_ethtool_net_stop(port_id); + data_size = 0; + + return send_reply(req_id, data_size, &data_size); +}; + +static int +proc_net_set_rx_mode(uint8_t port_id, uint16_t req_id) +{ + uint16_t data_size; + + if (rte_ethtool_net_set_rx_mode(port_id)) + data_size = STATUS_MASK; + else + data_size = 0; + + return send_reply(req_id, data_size, &data_size); +}; + +static int +proc_net_get_mac_addr(uint8_t port_id, uint16_t req_id, void *param_data) +{ + uint16_t data_size; + + if (rte_ethtool_net_get_mac_addr(port_id, param_data)) + data_size = STATUS_MASK; + else + data_size = MAC_ADDR_SIZE; + + return send_reply(req_id, data_size, param_data); +}; + +static int +proc_net_set_mac_addr(uint8_t port_id, uint16_t req_id, void *param_data) +{ + uint16_t data_size; + + if (rte_ethtool_net_set_mac_addr(port_id, param_data)) + data_size = STATUS_MASK; + else + data_size = 0; + + return send_reply(req_id, data_size, &data_size); +}; + +static int +proc_net_validate_addr(uint8_t port_id, uint16_t req_id, void *param_data) +{ + int status; + + status = rte_ethtool_net_validate_addr(port_id, param_data); + + return send_reply(req_id, (uint16_t)sizeof(int), &status); +}; + +static int +proc_net_set_config(uint8_t port_id, uint16_t req_id, void *param_data) +{ + uint16_t data_size; + + if (rte_ethtool_net_set_config(port_id, param_data)) + data_size = STATUS_MASK; + else + data_size = 0; + + return send_reply(req_id, data_size, &data_size); +}; + +static int +proc_net_change_mtu(uint8_t port_id, uint16_t req_id, void *param_data) +{ + uint16_t data_size; + int mtu = *(int *)(param_data); + + if (rte_ethtool_net_change_mtu(port_id, mtu)) + data_size = STATUS_MASK; + else + data_size = 0; + + return send_reply(req_id, data_size, &data_size); +}; + +static int +proc_net_get_stats64(uint8_t port_id, uint16_t req_id, void *reply_data) +{ + uint16_t data_size; + + if (rte_ethtool_net_get_stats64(port_id, reply_data)) + data_size = STATUS_MASK; + else + data_size = size16(struct rte_eth_stats); + + return send_reply(req_id, data_size, reply_data); +}; + +static int +proc_net_vlan_rx_add_vid(uint8_t port_id, uint16_t req_id, + void *param_data) +{ + uint16_t data_size; + int *vid_ptr = (int *)param_data; + + if (rte_ethtool_net_vlan_rx_add_vid(port_id, *vid_ptr)) + data_size = STATUS_MASK; + else + data_size = 0; + + return send_reply(req_id, data_size, &data_size); +}; + +static int +proc_net_vlan_rx_kill_vid(uint8_t port_id, uint16_t req_id, + void *param_data) +{ + uint16_t data_size; + int *vid_ptr = (int *)param_data; + + if (rte_ethtool_net_vlan_rx_kill_vid(port_id, *vid_ptr)) + data_size = STATUS_MASK; + else + data_size = 0; + + return send_reply(req_id, data_size, &data_size); +}; + +#endif /* NETDEV_OP_REPLY */ +#endif /* _NETDEV_API_H_ */ diff --git a/examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h b/examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h new file mode 100644 index 0000000..82dd962 --- /dev/null +++ b/examples/l2fwd-ethtool/l2fwd-app/shared_fifo.h @@ -0,0 +1,158 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2015 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SHARED_FIFO_H_ +#define _SHARED_FIFO_H_ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <assert.h> + +#define REQ_PIPE "/tmp/nic_request" +#define REP_PIPE "/tmp/nic_reply" +#define PAGE_SIZE (4*1024) +#define STACK_SIZE (4*PAGE_SIZE) +#define MAXI_DATA (1024*6) +#define MAXI_PARA 1024 +#define STATUS_MASK 0x8000 +#define MULTIPLE_DATA_MASK 0x4000 +#define MAXI_REQ_TYPE 16 +#define FIRST_DATA_OFFSET 8 +#define to_ptr(new_ptr_type, data, offset) \ + (new_ptr_type)(&((unsigned char *)(void *)data)[offset]) +#define u8ptr(x) (uint8_t *)((void *)x) + + +/* + * req[1:0]: request-id + * req[2]: request-id type + * req[3]: request type + * req[4:5]: param1-size + * req[7:6]: param2-size + * + * rep[1:0] reply-id + * rep[3:2]: data1-size // bit[15]: status bit[14]: two return data + * rep[7:4]: data2-size + */ +#define PIPE_CTL_BYTE_COUNT (sizeof(uint32_t)*2) +#define REQ_DWORD_LO(req_id, id_type, req_tye) \ + (((uint32_t)req_type << 24) | ((uint32_t)id_type << 16) | req_id) +#define REQ_DWORD_HI(param1_size, param2_size) \ + (((uint32_t)param2_size << 16) | param1_size) + +#define REP_DWORD_LO(rep_id, data_bytes) \ + (((uint32_t)data_bytes << 16) | (uint32_t)rep_id) +#define REP_DWORD_HI(data2_bytes) (data2_bytes) + +#define REP_MUTILPLE_DATA(data1_size) (data1_size | MULTIPLE_DATA_MASK) +#define REQ_ID(dword_ptr) (dword_ptr[0] & 0xFFFF) +#define REQ_IDTYPE(dword_ptr) ((dword_ptr[0] >> 16) & 0xFF) +#define REQ_TYPE(dword_ptr) ((dword_ptr[0] >> 24) & 0xFF) +#define REQ_PARAM1_SIZE(dword_ptr) (dword_ptr[1] & 0xFFFF) +#define REQ_PARAM2_SIZE(dword_ptr) ((dword_ptr[1]>>16) & 0xFFFF) +#define REP_ID(dword_ptr) (dword_ptr[0] & 0xFFFF) +#define REP_DATA1_COUNT(dword_ptr) ((dword_ptr[0] >> 16) & 0xFFFF) +#define REP_DATA2_COUNT(dword_ptr) (dword_ptr[1]) + +#define BAD_RETURN(data_size) (data_size | STATUS_MASK) +#define GOOD_RETURN(data_size) ((data_size & STATUS_MASK) == 0) +#define MULTIPLE_DATA(data_size) (data_size & MULTIPLE_DATA_MASK) +#define BYTE_COUNT(data_size) \ + (data_size & ~(STATUS_MASK|MULTIPLE_DATA_MASK)) + +#define PARAM_SIZE(type) \ + ((uint16_t)(FIRST_DATA_OFFSET+sizeof(type))) +#define FIRST_PARAM(param_data) (void *)(&(param_data[FIRST_DATA_OFFSET])) +#define FIRST_PARAM_TYPE(param_data, ptr_type) \ + (ptr_type)(FIRST_PARAM(param_data)) + +void init_req_pipe(void); +void init_rep_pipe(void); + +struct nic_info { + uint8_t num_of_ports; + uint32_t port_mask; + uint32_t vf_port_mask; + uint32_t flag; +} nic_info; + +enum req_t { + get_drvinfo = 0, + get_setting, + set_setting, + get_regs_len, + get_regs, + get_link, + get_eeprom_len, + get_eeprom, + set_eeprom, + get_coalesce, + set_coalesce, + get_pauseparam, + set_pauseparam, + dump_data, + + dev_open, + dev_stop, + set_rx_mode, + get_mac_addr, + set_mac_addr, + validate_addr, + set_config, + change_mtu, + get_stats64, + get_stats, + vlan_rx_add_vid, + vlan_rx_kill_vid, + ipc_begin, /* request to start ipc, and get nic info ... */ + ipc_end, /* request to stop ipc ... */ + invalid_req, +}; + +void +init_req_pipe(void) +{ + mkfifo(REQ_PIPE, 0666); +} + +void +init_rep_pipe(void) +{ + mkfifo(REP_PIPE, 0666); +} + +#endif /* _SHARED_FIFO_H_ */ diff --git a/examples/l2fwd-ethtool/lib/Makefile b/examples/l2fwd-ethtool/lib/Makefile new file mode 100644 index 0000000..be33a81 --- /dev/null +++ b/examples/l2fwd-ethtool/lib/Makefile @@ -0,0 +1,55 @@ +# BSD LICENSE +# +# Copyright(c) 2015 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overwritten by command line or environment +RTE_TARGET ?= x86_64-native-linuxapp-gcc + +include $(RTE_SDK)/mk/rte.vars.mk + +ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp") +$(error This application can only operate in a linuxapp environment, \ +please change the definition of the RTE_TARGET environment variable) +endif + +# library name +LIB = librte_ethtool.a + +# all source are stored in SRC-Y +SRCS-y := rte_ethtool.c + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +include $(RTE_SDK)/mk/rte.extlib.mk diff --git a/examples/l2fwd-ethtool/lib/rte_ethtool.c b/examples/l2fwd-ethtool/lib/rte_ethtool.c new file mode 100644 index 0000000..744cb0c --- /dev/null +++ b/examples/l2fwd-ethtool/lib/rte_ethtool.c @@ -0,0 +1,308 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2015 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <rte_version.h> +#include <rte_ethdev.h> +#include "rte_ethtool.h" + +int +rte_ethtool_get_drvinfo(uint8_t port_id, struct ethtool_drvinfo *drvinfo) +{ + struct rte_eth_dev_info dev_info; + int n; + + memset(&dev_info, 0, sizeof(dev_info)); + rte_eth_dev_info_get(port_id, &dev_info); + + snprintf(drvinfo->driver, sizeof(drvinfo->driver), "%s", + dev_info.driver_name); + snprintf(drvinfo->version, sizeof(drvinfo->version), "%s", + rte_version()); + snprintf(drvinfo->bus_info, sizeof(drvinfo->bus_info), + "%04x:%02x:%02x.%x", + dev_info.pci_dev->addr.domain, dev_info.pci_dev->addr.bus, + dev_info.pci_dev->addr.devid, dev_info.pci_dev->addr.function); + + n = rte_eth_dev_reg_length(port_id); + if (n > 0) + drvinfo->regdump_len = n; + else + drvinfo->regdump_len = 0; + + n = rte_eth_dev_eeprom_length(port_id); + if (n > 0) + drvinfo->eedump_len = n; + else + drvinfo->eedump_len = 0; + + drvinfo->n_stats = sizeof(struct rte_eth_stats) / sizeof(uint64_t); + drvinfo->testinfo_len = 0; + + return 0; +} + +int +rte_ethtool_get_regs_len(uint8_t port_id) +{ + return rte_eth_dev_reg_length(port_id); +} + +int +rte_ethtool_get_regs(uint8_t port_id, struct ethtool_regs *regs, void *buf) +{ + struct rte_dev_reg_info reg_info; + int status; + + reg_info.buf = buf; + reg_info.leng = 0; + + status = rte_eth_dev_reg_info(port_id, ®_info); + if (status) + return status; + regs->version = reg_info.version; + + return 0; +} + +int +rte_ethtool_get_link(uint8_t port_id) +{ + struct rte_eth_link link; + + rte_eth_link_get(port_id, &link); + return link.link_status; +} + +int +rte_ethtool_get_eeprom_len(uint8_t port_id) +{ + return rte_eth_dev_eeprom_length(port_id); +} + +int +rte_ethtool_get_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom, + void *words) +{ + struct rte_dev_eeprom_info eeprom_info; + int status; + + eeprom_info.offset = eeprom->offset; + eeprom_info.leng = eeprom->len; + eeprom_info.buf = words; + + status = rte_eth_dev_get_eeprom(port_id, &eeprom_info); + if (status) + return status; + + eeprom->magic = eeprom_info.magic; + + return 0; +} + +int +rte_ethtool_set_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom, + void *words) +{ + struct rte_dev_eeprom_info eeprom_info; + int status; + + eeprom_info.offset = eeprom->offset; + eeprom_info.leng = eeprom->len; + eeprom_info.buf = words; + + status = rte_eth_dev_set_eeprom(port_id, &eeprom_info); + if (status) + return status; + + eeprom->magic = eeprom_info.magic; + + return 0; +} + +int +rte_ethtool_get_pauseparam(uint8_t port_id, + struct ethtool_pauseparam *pause_param) +{ + struct rte_eth_fc_conf fc_conf; + int status; + + status = rte_eth_dev_flow_ctrl_get(port_id, &fc_conf); + if (status) + return status; + + pause_param->tx_pause = 0; + pause_param->rx_pause = 0; + switch (fc_conf.mode) { + case RTE_FC_NONE: + /* dummy block to avoid compiler warning */ + break; + case RTE_FC_RX_PAUSE: + pause_param->rx_pause = 1; + break; + case RTE_FC_TX_PAUSE: + pause_param->tx_pause = 1; + break; + case RTE_FC_FULL: + pause_param->rx_pause = 1; + pause_param->tx_pause = 1; + } + pause_param->autoneg = (uint32_t)fc_conf.autoneg; + + return 0; +} + +int +rte_ethtool_set_pauseparam(uint8_t port_id, + struct ethtool_pauseparam *pause_param) +{ + struct rte_eth_fc_conf fc_conf; + int status; + /* + * Read device flow control parameter first since + * ethtool set_pauseparam op doesn't have all the information. + * as defined in struct rte_eth_fc_conf. + * This API requires the device to support both + * rte_eth_dev_flow_ctrl_get and rte_eth_dev_flow_ctrl_set, otherwise + * return -ENOTSUP + */ + status = rte_eth_dev_flow_ctrl_get(port_id, &fc_conf); + if (status) + return status; + + fc_conf.autoneg = (uint8_t)pause_param->autoneg; + + if (pause_param->tx_pause) { + if (pause_param->rx_pause) + fc_conf.mode = RTE_FC_FULL; + else + fc_conf.mode = RTE_FC_TX_PAUSE; + } else { + if (pause_param->rx_pause) + fc_conf.mode = RTE_FC_RX_PAUSE; + else + fc_conf.mode = RTE_FC_NONE; + } + + status = rte_eth_dev_flow_ctrl_set(port_id, &fc_conf); + if (status) + return status; + + return 0; +} + +int +rte_ethtool_net_open(uint8_t port_id) +{ + rte_eth_dev_stop(port_id); + + return rte_eth_dev_start(port_id); +} + +int +rte_ethtool_net_stop(uint8_t port_id) +{ + rte_eth_dev_stop(port_id); + + return 0; +} + +int +rte_ethtool_net_get_mac_addr(uint8_t port_id, struct ether_addr *addr) +{ + rte_eth_macaddr_get(port_id, addr); + + return 0; +} + +int +rte_ethtool_net_set_mac_addr(uint8_t port_id, struct ether_addr *addr) +{ + return rte_eth_dev_default_mac_addr_set(port_id, addr); +} + +int +rte_ethtool_net_validate_addr(uint8_t port_id __rte_unused, + struct ether_addr *addr) +{ + return is_valid_assigned_ether_addr(addr); +} + +int +rte_ethtool_net_set_config(uint8_t port_id, void *config __rte_unused) +{ + struct rte_eth_link link; + + memset(&link, 0, sizeof(link)); + rte_eth_link_get(port_id, &link); + if (link.link_status == 1) + return -EINVAL; + return 0; +} + +int +rte_ethtool_net_change_mtu(uint8_t port_id, int mtu) +{ + return rte_eth_dev_set_mtu(port_id, (uint16_t)mtu); +} + +int +rte_ethtool_net_get_stats64(uint8_t port_id, struct rte_eth_stats *stats) +{ + return rte_eth_stats_get(port_id, stats); +} + +int +rte_ethtool_net_vlan_rx_add_vid(uint8_t port_id, uint16_t vid) +{ + return rte_eth_dev_vlan_filter(port_id, vid, 1); +} + +int +rte_ethtool_net_vlan_rx_kill_vid(uint8_t port_id, uint16_t vid) +{ + return rte_eth_dev_vlan_filter(port_id, vid, 0); +} + +int +rte_ethtool_net_set_rx_mode(uint8_t port_id __rte_unused) +{ + /* + * The set_rx_mode op is part of pmd driver start operation, and + * the ethdev api maintains software configuration parameters and under- + * line hardware states consistent, so no operation is needed for + * rte_ethtool_net_set_rx_mode(). + */ + return 0; +} diff --git a/examples/l2fwd-ethtool/lib/rte_ethtool.h b/examples/l2fwd-ethtool/lib/rte_ethtool.h new file mode 100644 index 0000000..dc234be --- /dev/null +++ b/examples/l2fwd-ethtool/lib/rte_ethtool.h @@ -0,0 +1,384 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2015 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETHTOOL_H_ +#define _RTE_ETHTOOL_H_ + +/* + * This new interface is designed to provide a user-space shim layer for + * Ethtool and Netdevice op API. + * + * rte_ethtool_get_driver: ethtool_ops::get_driverinfo + * rte_ethtool_get_link: ethtool_ops::get_link + * rte_ethtool_get_regs_len: ethtool_ops::get_regs_len + * rte_ethtool_get_regs: ethtool_ops::get_regs + * rte_ethtool_get_eeprom_len: ethtool_ops::get_eeprom_len + * rte_ethtool_get_eeprom: ethtool_ops::get_eeprom + * rte_ethtool_set_eeprom: ethtool_ops::set_eeprom + * rte_ethtool_get_pauseparam: ethtool_ops::get_pauseparam + * rte_ethtool_set_pauseparam: ethtool_ops::set_pauseparam + * + * rte_ethtool_net_open: net_device_ops::ndo_open + * rte_ethtool_net_stop: net_device_ops::ndo_stop + * rte_ethtool_net_set_mac_addr: net_device_ops::ndo_set_mac_address + * rte_ethtool_net_validate_addr: net_device_ops::ndo_validate_addr + * rte_ethtool_net_set_config: net_device_ops::ndo_set_config + * rte_ethtool_net_change_mtu: net_device_ops::rte_net_change_mtu + * rte_ethtool_net_get_stats64: net_device_ops::ndo_get_stats64 + * rte_ethtool_net_vlan_rx_add_vid net_device_ops::ndo_vlan_rx_add_vid + * rte_ethtool_net_vlan_rx_kill_vid net_device_ops::ndo_vlan_rx_kill_vid + * rte_ethtool_net_set_rx_mode net_device_ops::ndo_set_rx_mode + * + */ +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <rte_ethdev.h> +#include <linux/ethtool.h> + +/** + * Retrieve the Ethernet device driver information according to attributes described by + * ethtool data structure, ethtool_drvinfo + * + * @param port_id + * The port identifier of the Ethernet device. + * @param drvinfo + * A pointer to get driver information + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_get_drvinfo(uint8_t port_id, struct ethtool_drvinfo *drvinfo); + +/** + * Retrieve the Ethernet device register length in bytes. + * + * @param port_id + * The port identifier of the Ethernet device. + * @return + * - (> 0) # of device registers (in bytes) available for dump + * - (0) no registers available for dump. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_get_regs_len(uint8_t port_id); + +/** + * Retrieve the Ethernet device register information according to attributes described by + * ethtool data structure, ethtool_regs + * + * @param port_id + * The port identifier of the Ethernet device. + * @param reg + * A pointer to ethtool_regs that has register information + * @param buf + * A pointer to a buffer that is used to retrieve device register content + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_get_regs(uint8_t port_id, struct ethtool_regs *regs, void *buf); + +/** + * Retrieve the Ethernet device link status + * + * @param port_id + * The port identifier of the Ethernet device. + * @return + * - (1) if link up. + * - (0) if link down. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_get_link(uint8_t port_id); + +/** + * Retrieve the Ethernet device EEPROM size + * + * @param port_id + * The port identifier of the Ethernet device. + * @return + * - (> 0) device EEPROM size in bytes + * - (0) device has NO EEPROM + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_get_eeprom_len(uint8_t port_id); + +/** + * Retrieve EEPROM content based upon eeprom range described in ethtool + * data structure, ethtool_eeprom + * + * @param port_id + * The port identifier of the Ethernet device. + * @param eeprom + * The pointer of ethtool_eeprom that provides eeprom range + * @param words + * A buffer that holds data read from eeprom + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_get_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom, + void *words); + +/** + * Setting EEPROM content based upon eeprom range described in ethtool + * data structure, ethtool_eeprom + * + * @param port_id + * The port identifier of the Ethernet device. + * @param eeprom + * The pointer of ethtool_eeprom that provides eeprom range + * @param words + * A buffer that holds data to be written into eeprom + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_set_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom, + void *words); + +/** + * Retrieve the Ethernet device pause frame configuration according to + * parameter attributes desribed by ethtool data structure, + * ethtool_pauseparam. + * + * @param port_id + * The port identifier of the Ethernet device. + * @param pause_param + * The pointer of ethtool_coalesce that gets pause frame + * configuration parameters + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_get_pauseparam(uint8_t port_id, + struct ethtool_pauseparam *pause_param); + +/** + * Setting the Ethernet device pause frame configuration according to parameter attributes + * desribed by ethtool data structure, ethtool_pauseparam. + * + * @param port_id + * The port identifier of the Ethernet device. + * @param pause_param + * The pointer of ethtool_coalesce that gets ring configuration parameters + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_set_pauseparam(uint8_t port_id, + struct ethtool_pauseparam *param); + +/** + * Start the Ethernet device. + * + * @param port_id + * The port identifier of the Ethernet device. + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_net_open(uint8_t port_id); + +/** + * Stop the Ethernet device. + * + * @param port_id + * The port identifier of the Ethernet device. + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_net_stop(uint8_t port_id); + +/** + * Get the Ethernet device MAC address. + * + * @param port_id + * The port identifier of the Ethernet device. + * @param addr + * MAC address of the Ethernet device. + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_net_get_mac_addr(uint8_t port_id, struct ether_addr *addr); + +/** + * Setting the Ethernet device MAC address. + * + * @param port_id + * The port identifier of the Ethernet device. + * @param addr + * The new MAC addr. + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_net_set_mac_addr(uint8_t port_id, struct ether_addr *addr); + +/** + * Validate if the provided MAC address is valid unicast address + * + * @param port_id + * The port identifier of the Ethernet device. + * @param addr + * A pointer to a buffer (6-byte, 48bit) for the target MAC address + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_net_validate_addr(uint8_t port_id, struct ether_addr *addr); + +/** + * Setting the Ethernet device configuration. + * + * @param port_id + * The port identifier of the Ethernet device. + * @param config + * A opintr to a configuration parameter. + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_net_set_config(uint8_t port_id, void *config); + +/** + * Setting the Ethernet device maximum Tx unit. + * + * @param port_id + * The port identifier of the Ethernet device. + * @param mtu + * New MTU + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_net_change_mtu(uint8_t port_id, int mtu); + +/** + * Retrieve the Ethernet device traffic statistics + * + * @param port_id + * The port identifier of the Ethernet device. + * @param stats + * A pointer to struct rte_eth_stats for statistics parameters + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_net_get_stats64(uint8_t port_id, struct rte_eth_stats *stats); + +/** + * Update the Ethernet device VLAN filter with new vid + * + * @param port_id + * The port identifier of the Ethernet device. + * @param vid + * A new VLAN id + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_net_vlan_rx_add_vid(uint8_t port_id, uint16_t vid); + +/** + * Remove VLAN id from Ethernet device. + * + * @param port_id + * The port identifier of the Ethernet device. + * @param vid + * A new VLAN id + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_net_vlan_rx_kill_vid(uint8_t port_id, uint16_t vid); + +/** + * Setting the Ethernet device rx mode. + * + * @param port_id + * The port identifier of the Ethernet device. + * @return + * - (0) if successful. + * - (-ENOTSUP) if hardware doesn't support. + * - (-ENODEV) if *port_id* invalid. + * - others depends on the specific operations implementation. + */ +int rte_ethtool_net_set_rx_mode(uint8_t port_id); + +#ifdef __cplusplus +} +#endif + +#endif /* _RTE_ETHTOOL_H_ */ diff --git a/examples/l2fwd-ethtool/nic-control/Makefile b/examples/l2fwd-ethtool/nic-control/Makefile new file mode 100644 index 0000000..17ab4a3 --- /dev/null +++ b/examples/l2fwd-ethtool/nic-control/Makefile @@ -0,0 +1,55 @@ +# BSD LICENSE +# +# Copyright(c) 2015 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overwritten by command line or environment +RTE_TARGET ?= x86_64-native-linuxapp-gcc + +include $(RTE_SDK)/mk/rte.vars.mk + +ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp") +$(error This application can only operate in a linuxapp environment, \ +please change the definition of the RTE_TARGET environment variable) +endif + +# binary name +APP = nic-control + +# all source are stored in SRCS-y +SRCS-y := nic_control.c + +CFLAGS += -O3 -I$(SRCDIR)/../l2fwd-app -I$(SRCDIR)/../lib +CFLAGS += $(WERROR_FLAGS) + +include $(RTE_SDK)/mk/rte.extapp.mk diff --git a/examples/l2fwd-ethtool/nic-control/nic_control.c b/examples/l2fwd-ethtool/nic-control/nic_control.c new file mode 100644 index 0000000..f99af58 --- /dev/null +++ b/examples/l2fwd-ethtool/nic-control/nic_control.c @@ -0,0 +1,471 @@ +/*- +* BSD LICENSE +* +* Copyright(c) 2015 Intel Corporation. All rights reserved. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in +* the documentation and/or other materials provided with the +* distribution. +* * Neither the name of Intel Corporation nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * This is a non- DPDK application that sends NIC device management request + * through named pipe to a DPDK data plan process. + * + */ +#define USE_NEW_TYPE +#include <stdio.h> +#include <stdlib.h> + +#include <sys/resource.h> + +#include "rte_ethtool.h" +#define NETDEV_OP_REQUEST 1 +#include "netdev_api.h" + +#define PACKET_THD 100000000 +#define ITER_LIMIT 30 +#define STOP_TIME 10 /* in seconds */ +#define CPU_CYCLES (double)(2400.0*1000000) + +#define PACKET_RATE(before_value, after_value, before_ts, after_ts) \ + ((double)(after_value - before_value) * \ + CPU_CYCLES/(after_ts - before_ts)) + +#define BYTE2BIT_RATE(before_value, after_value, before_ts, after_ts) \ + ((double)(after_value - before_value) * \ + CPU_CYCLES*8/(after_ts - before_ts)) + +#define PACKET2BIT_RATE(before_value, after_value, before_ts, after_ts) \ + ((double)(after_value - before_value) * \ + CPU_CYCLES*64*8/(after_ts - before_ts)) + +#define to_mac_type(x) (struct ether_addr *)(void *)(x) + +struct __time_stamp { + uint32_t hi; + uint32_t lo; +} time_stamp; + +static inline unsigned long long +rdtsc(void) +{ + unsigned hi, lo; + + __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); + return ((unsigned long long)lo) | (((unsigned long long)hi) << 32); +} + +static uint32_t port_mask; +static uint32_t vf_port_mask; +static uint8_t num_of_ports; +static int keep_traffic = 1; + +static inline int +is_port_enabled(uint8_t port_id) +{ + return (port_mask & (1 << port_id)) > 0; +} + +static inline int +is_vf_port(uint8_t port_id) +{ + return (vf_port_mask & (1 << port_id)) > 0; +} + +static int +netdev_ipc_begin(unsigned char *mac_addr) +{ + uint8_t reply_data[sizeof(double)]; + uint16_t req_id = next_reqid(); + uint16_t data_size; + uint32_t reply_data2[2]; + uint8_t param_data[FIRST_DATA_OFFSET+MAC_ADDR_SIZE]; + + param_data[0] = 0; + memcpy(FIRST_PARAM(param_data), mac_addr, MAC_ADDR_SIZE); + send_request(req_id, ipc_begin, + (FIRST_DATA_OFFSET+MAC_ADDR_SIZE), param_data); + read_reply(req_id, &data_size, reply_data, reply_data2); + num_of_ports = reply_data[0]; + port_mask = reply_data2[0]; + vf_port_mask = reply_data2[1]; + return reply_data[0]; +} + +static int +netdev_ipc_end(void) +{ + uint8_t reply_data[sizeof(double)]; + uint16_t req_id = next_reqid(); + uint16_t data_size; + + send_request(req_id, ipc_end, 0, NULL); + read_reply(req_id, &data_size, reply_data, NULL); + + return NETDEV_STATUS(data_size); +} + +static void +set_stacksize(void) +{ + struct rlimit rl; + int result; + + result = getrlimit(RLIMIT_STACK, &rl); + if (result == 0) { + if (rl.rlim_cur < (const rlim_t)STACK_SIZE) { + rl.rlim_cur = STACK_SIZE; + result = setrlimit(RLIMIT_STACK, &rl); + if (result != 0) + printf("setrlimit returned result = %d\n", + result); + else + printf("setrlimit succeed!!!\n"); + } else + printf("default stack size is 0x%x\n", + (int)(rl.rlim_cur)); + } +} + +static uint8_t +get_port(void) +{ + uint8_t port_id; + /* assume maximum of 32 ports */ + port_id = rand() & 0x1F; + while (!is_port_enabled(port_id)) + port_id = rand() & 0x1F; + + return port_id; +} + +static inline char* +mac_addr_str(unsigned char *mac_addr) +{ +#define MAC_STR_SIZE (3*MAC_ADDR_SIZE+1) + static char addr_string[MAC_STR_SIZE]; + + snprintf(addr_string, MAC_STR_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x", + mac_addr[0], mac_addr[1], mac_addr[2], + mac_addr[3], mac_addr[4], mac_addr[5]); + return addr_string; +} + +int +main(int argc, char **argv) +{ + struct ethtool_drvinfo drvinfo; + struct ethtool_regs regs; + struct ethtool_pauseparam pause_param; + struct ethtool_eeprom eeprom; + + int8_t reply_data[MAXI_DATA] __attribute__((aligned(8))); + uint8_t mac_addr[MAC_ADDR_SIZE] = {0x52, 0x54, 0, 0, 0, 0}; + uint8_t mac_base_addr[MAC_ADDR_SIZE] = {0x52, 0x54, 0, 0, 0, 1}; + uint8_t port_id; + const int mtu = 1024; + int iter_count = 0; + int count, link_up; + int *int_ptr; + + /* get command parameter */ + if (argc > 1) + keep_traffic = atoi(argv[1]); + /* set stack size */ + set_stacksize(); + + /* initialize request pipe */ + init_req_pipe(); + + printf("issue ipc begin\n"); + /* send a request to start the NIC device */ + num_of_ports = netdev_ipc_begin(mac_addr); + while (num_of_ports == 0) + num_of_ports = netdev_ipc_begin(mac_addr) & 0xFF; + + for (port_id = 0; port_id < num_of_ports; port_id++) { + link_up = netdev_ethtool_get_link(port_id); + printf("port #%d is %s\n", port_id, link_up?"up":"down"); + if (!link_up) { + if (netdev_net_open(port_id) == 0) + netdev_net_set_rx_mode(port_id); + else + printf("failed to start port #%d\n", port_id); + } + } + + memset(reply_data, 0xFF, MAXI_DATA); + /* Testing ethtool API */ + for (port_id = 0; port_id < num_of_ports; port_id++) { + if (!is_port_enabled(port_id)) + continue; + else { + /* print out mac address */ + if (netdev_net_get_mac_addr(port_id, mac_addr)) { + printf("Fail to get mac addr from port"); + printf(" #%d!!!\n", port_id); + } else + printf("\nPort #%d mac addr is %s\n", + port_id, mac_addr_str(mac_addr)); + + if (netdev_ethtool_get_drvinfo(port_id, &drvinfo)) { + printf("fail to get drvinfo ...\n"); + } else { + printf("driver: %s version: %s ", + drvinfo.driver, drvinfo.version); + printf("fw_version: %s bus_info=%s\n", + drvinfo.fw_version, drvinfo.bus_info); + printf("reg-size(bytes)=%d eeprom-size=%d\n", + drvinfo.regdump_len, + drvinfo.eedump_len); + } + + count = netdev_ethtool_get_regs_len(port_id); + if (count <= 0) { + printf("There are no registers available from"); + printf(" device/port #%d", port_id); + } else { + printf("Target device has %d registers ", + count); + printf("for dump\n"); + } + + if (count > 0) { + memset(®s, 0xFF, + sizeof(struct ethtool_regs)); + count = netdev_ethtool_get_regs(port_id, + ®s, reply_data); + if (count) { + printf("failed to run"); + printf(" ethtool_get_regs "); + printf("from port #%d (err=%d)\n", + port_id, count); + } else { + int_ptr = (int *)((void *)reply_data); + printf("reg[0]=%x reg[10]=%x ", + int_ptr[0], int_ptr[10]); + printf("version=0x%x\n", + regs.version); + } + } + + /* Only testing eeprom access over a PF */ + count = 0; + if (!is_vf_port(port_id)) { + count = netdev_ethtool_get_eeprom_len(0); + if (count == 0) { + printf("fail to retrieve eeprom"); + printf("count from port #%d\n", + port_id); + } + } + + if (count) { + printf("eeprom size is %d bytes\n", count); + eeprom.offset = 20; + eeprom.len = 80; + eeprom.magic = 0; + if (netdev_ethtool_get_eeprom(port_id, + &eeprom, reply_data)) { + printf("Fail to read eeprom"); + printf(" from port #%d\n", + port_id); + } else { + int i; + uint16_t *word = (uint16_t *) + ((void *)reply_data); + + printf("eeprom-magic: %x;", + eeprom.magic); + printf("eeprom data ...\n"); + count = 80; + for (i = 0; i < (int)(eeprom.len + >> 1); i++) { + if (((i+1) % 16) == 0) + printf("\n"); + printf("%4x ", word[i]); + } + printf("\n"); + } + } + } + } + + /* testing set/get mac address */ + printf("MAC base address is %s\n", mac_addr_str(mac_base_addr)); + for (port_id = 0; port_id < num_of_ports; port_id++) { + if (netdev_net_get_mac_addr(port_id, + to_mac_type(mac_addr))) + printf("Fail to get mac addr from port #%d!!!\n", + port_id); + else + printf("Port #%d, device mac addr is %s\n", port_id, + mac_addr_str(mac_addr)); + + if (!netdev_net_validate_addr(port_id, + to_mac_type(mac_addr))) { + printf("Default mac addr, %s, is not valid\n", + mac_addr_str(mac_addr)); + strncpy((char *)mac_addr, (char *)mac_base_addr, + MAC_ADDR_SIZE); + mac_addr[MAC_ADDR_SIZE-1] = 1+port_id; + printf("New mac address:%s is used.\n", + mac_addr_str(mac_addr)); + + if (netdev_net_set_mac_addr(port_id, + to_mac_type(mac_addr)) || + netdev_net_get_mac_addr(port_id, + to_mac_type(mac_addr))) { + printf("Fail to reset mac addr"); + printf(" @ port #%d!!!\n", port_id); + } else { + printf("After mac address re-assign"); + printf(" device mac addr is %s\n", + mac_addr_str(mac_addr)); + } + } + } + + printf("start nic statistics collection ...\n"); + + port_id = get_port(); + while (iter_count++ < ITER_LIMIT) { + uint64_t last_ts, ts; + struct rte_eth_stats last_stats, stats; + + if (netdev_net_get_stats64(port_id, &last_stats)) { + printf("Fail to query statistics from port %d\n", + port_id); + break; + } + last_ts = rdtsc(); + + sleep(10); + + if (netdev_net_get_stats64(port_id, &stats)) { + printf("Fail to query statistics from port %d\n", + port_id); + break; + } + ts = rdtsc(); + + printf("rx packet rate = %lf, tx packet rate = %lf\n", + PACKET_RATE(last_stats.ipackets, stats.ipackets, + last_ts, ts), + PACKET_RATE(last_stats.opackets, stats.opackets, + last_ts, ts)); + + + printf("rx bit rate = %lf, tx bit rate = %lf\n", + BYTE2BIT_RATE(last_stats.ibytes, stats.ibytes, + last_ts, ts), + BYTE2BIT_RATE(last_stats.obytes, stats.obytes, + last_ts, ts)); + + sleep(5); + } + + /* stop link for testing */ + if (!keep_traffic) { + int status; + + for (port_id = 0; port_id < num_of_ports; port_id++) { + link_up = netdev_ethtool_get_link(port_id); + if (link_up) + netdev_net_stop(port_id); + } + + for (port_id = 0; port_id < num_of_ports; port_id++) { + link_up = netdev_ethtool_get_link(port_id); + if (!is_vf_port(port_id) && !link_up) { + eeprom.offset = 20; + eeprom.len = 80; + if (netdev_ethtool_get_eeprom(port_id, + &eeprom, reply_data)) { + printf("failed to read eeprom"); + printf(" break from post-run"); + printf(" testing!!!\n"); + break; + } + if (netdev_ethtool_set_eeprom(port_id, + &eeprom, reply_data)) { + printf("Fail to write read-back"); + printf(" data to eeprom!!!\n"); + break; + } + /* checking mtu setting */ + if (netdev_net_change_mtu(port_id, mtu)) { + printf("failed to set mtu"); + printf("to %d\n", mtu); + } + + /* add/remove vlan to vid */ + status = netdev_net_vlan_rx_add_vid( + port_id, 0); + if (status == 0) { + status = netdev_net_vlan_rx_kill_vid( + port_id, 0); + + if (status) { + printf("fail kill vlan-vid\n"); + break; + } + } else { + printf("fail adding vlan/vid 0\n"); + break; + } + + /* testing pause parameter get/set functions */ + status = netdev_ethtool_get_pauseparam( + port_id, &pause_param); + if (status) { + printf("get pauseparam fail\n"); + break; + } + printf("pause setup: autoneg: %d ", + pause_param.autoneg); + printf("tx_pause: %d ", + pause_param.tx_pause); + printf("rx_pause: %d\n", + pause_param.rx_pause); + status = netdev_ethtool_set_pauseparam( + port_id, &pause_param); + if (status) { + printf("set pause param fail\n"); + break; + } + + } + } + } + + while (netdev_ipc_end() < 0) + ; + + printf("Done for ethtool service request!!!\n"); + return 0; +} -- 2.1.4