From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id C2A15A2EDB for ; Wed, 2 Oct 2019 22:58:10 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id F411B1BF68; Wed, 2 Oct 2019 22:58:05 +0200 (CEST) Received: from mx0b-0016f401.pphosted.com (mx0a-0016f401.pphosted.com [67.231.148.174]) by dpdk.org (Postfix) with ESMTP id 8E43E1BF67 for ; Wed, 2 Oct 2019 22:58:04 +0200 (CEST) Received: from pps.filterd (m0045849.ppops.net [127.0.0.1]) by mx0a-0016f401.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id x92Ktpui029134; Wed, 2 Oct 2019 13:58:03 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=pfpt0818; bh=Um7fRjpes1MMWnKlGUsmncRfonZdh0ECG/vuNoPUA4Q=; b=cRD2R80Fepi+Q0NtOTupDmP+0uYyLkoekZKDDYIn0OkxCmwxPWp4vPYgwlSY/WBd2CqU khFBB5g/X0xHlsmo1Q/Ufrr2mdWcyicO7jv6T2USfjtAskSKsOuRImyQnEoDPooqblOp K+LBGcKW/2ErZFT0YK/Anc3tqsJNxWizBzLDkwHQlQkljKT5bZqxWd4UD13io1WVrOv/ NLcOv4u4RJcGj8FLFF/Lia1g0GwtLxLMxyq7Nz9FO2FIh8YahYUg/qAm/KqiW5tVW4ds H5jRH1cK9kEYnaegsieyv5AnaRloF3aWd0BMT7QLFEleN/KJ9bJ0Zmi5PyjQQGii5++s 7A== Received: from sc-exch01.marvell.com ([199.233.58.181]) by mx0a-0016f401.pphosted.com with ESMTP id 2vd0y70m7j-2 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT); Wed, 02 Oct 2019 13:58:03 -0700 Received: from SC-EXCH03.marvell.com (10.93.176.83) by SC-EXCH01.marvell.com (10.93.176.81) with Microsoft SMTP Server (TLS) id 15.0.1367.3; Wed, 2 Oct 2019 13:58:02 -0700 Received: from maili.marvell.com (10.93.176.43) by SC-EXCH03.marvell.com (10.93.176.83) with Microsoft SMTP Server id 15.0.1367.3 via Frontend Transport; Wed, 2 Oct 2019 13:58:02 -0700 Received: from BG-LT7430.marvell.com (unknown [10.28.17.68]) by maili.marvell.com (Postfix) with ESMTP id A49923F703F; Wed, 2 Oct 2019 13:57:58 -0700 (PDT) From: To: , , , Thomas Monjalon , Marko Kovacevic , Ori Kam , Radu Nicolau , Tomasz Kantecki , "Sunil Kumar Kori" , Pavan Nikhilesh CC: Date: Thu, 3 Oct 2019 02:27:45 +0530 Message-ID: <20191002205754.11746-2-pbhagavatula@marvell.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191002205754.11746-1-pbhagavatula@marvell.com> References: <20190924094209.3827-1-pbhagavatula@marvell.com> <20191002205754.11746-1-pbhagavatula@marvell.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.95,1.0.8 definitions=2019-10-02_08:2019-10-01,2019-10-02 signatures=0 Subject: [dpdk-dev] [PATCH v5 01/10] examples/l2fwd-event: add default poll mode routines X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Pavan Nikhilesh Add the default l2fwd poll mode routines similar to examples/l2fwd. Signed-off-by: Sunil Kumar Kori Signed-off-by: Pavan Nikhilesh --- MAINTAINERS | 5 + examples/Makefile | 1 + examples/l2fwd-event/Makefile | 59 +++++ examples/l2fwd-event/l2fwd_common.c | 142 +++++++++++ examples/l2fwd-event/l2fwd_common.h | 129 ++++++++++ examples/l2fwd-event/l2fwd_poll.c | 197 +++++++++++++++ examples/l2fwd-event/l2fwd_poll.h | 25 ++ examples/l2fwd-event/main.c | 374 ++++++++++++++++++++++++++++ examples/l2fwd-event/meson.build | 14 ++ 9 files changed, 946 insertions(+) create mode 100644 examples/l2fwd-event/Makefile create mode 100644 examples/l2fwd-event/l2fwd_common.c create mode 100644 examples/l2fwd-event/l2fwd_common.h create mode 100644 examples/l2fwd-event/l2fwd_poll.c create mode 100644 examples/l2fwd-event/l2fwd_poll.h create mode 100644 examples/l2fwd-event/main.c create mode 100644 examples/l2fwd-event/meson.build diff --git a/MAINTAINERS b/MAINTAINERS index b3d9aaddd..292ac10c3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1458,6 +1458,11 @@ M: Tomasz Kantecki F: doc/guides/sample_app_ug/l2_forward_cat.rst F: examples/l2fwd-cat/ +M: Sunil Kumar Kori +M: Pavan Nikhilesh +F: examples/l2fwd-event/ +T: git://dpdk.org/next/dpdk-next-eventdev + F: examples/l3fwd/ F: doc/guides/sample_app_ug/l3_forward.rst diff --git a/examples/Makefile b/examples/Makefile index de11dd487..d18504bd2 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -34,6 +34,7 @@ endif DIRS-$(CONFIG_RTE_LIBRTE_HASH) += ipv4_multicast DIRS-$(CONFIG_RTE_LIBRTE_KNI) += kni DIRS-y += l2fwd +DIRS-y += l2fwd-event ifneq ($(PQOS_INSTALL_PATH),) DIRS-y += l2fwd-cat endif diff --git a/examples/l2fwd-event/Makefile b/examples/l2fwd-event/Makefile new file mode 100644 index 000000000..73f02dd3b --- /dev/null +++ b/examples/l2fwd-event/Makefile @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(C) 2019 Marvell International Ltd. +# + +# binary name +APP = l2fwd-event + +# all source are stored in SRCS-y +SRCS-y := main.c +SRCS-y += l2fwd_poll.c +SRCS-y += l2fwd_common.c + +# Build using pkg-config variables if possible +ifeq ($(shell pkg-config --exists libdpdk && echo 0),0) + +all: shared +.PHONY: shared static +shared: build/$(APP)-shared + ln -sf $(APP)-shared build/$(APP) +static: build/$(APP)-static + ln -sf $(APP)-static build/$(APP) + +PKGCONF=pkg-config --define-prefix + +PC_FILE := $(shell $(PKGCONF) --path libdpdk) +CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk) +LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk) +LDFLAGS_STATIC = -Wl,-Bstatic $(shell $(PKGCONF) --static --libs libdpdk) + +build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build + $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED) + +build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build + $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC) + +build: + @mkdir -p $@ + +.PHONY: clean +clean: + rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared + test -d build && rmdir -p build || true + +else # Build using legacy build system + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, detect a build directory, by looking for a path with a .config +RTE_TARGET ?= $(notdir $(abspath $(dir $(firstword $(wildcard $(RTE_SDK)/*/.config))))) + +include $(RTE_SDK)/mk/rte.vars.mk + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +include $(RTE_SDK)/mk/rte.extapp.mk +endif diff --git a/examples/l2fwd-event/l2fwd_common.c b/examples/l2fwd-event/l2fwd_common.c new file mode 100644 index 000000000..213652d72 --- /dev/null +++ b/examples/l2fwd-event/l2fwd_common.c @@ -0,0 +1,142 @@ +#include "l2fwd_common.h" + +/* Print out statistics on packets dropped */ +void +print_stats(struct l2fwd_resources *l2fwd_rsrc) +{ + uint64_t total_packets_dropped, total_packets_tx, total_packets_rx; + uint32_t port_id; + + 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 (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++) { + /* skip disabled ports */ + if ((l2fwd_rsrc->enabled_port_mask & (1 << port_id)) == 0) + continue; + printf("\nStatistics for port %u ------------------------------" + "\nPackets sent: %24"PRIu64 + "\nPackets received: %20"PRIu64 + "\nPackets dropped: %21"PRIu64, + port_id, + l2fwd_rsrc->port_stats[port_id].tx, + l2fwd_rsrc->port_stats[port_id].rx, + l2fwd_rsrc->port_stats[port_id].dropped); + + total_packets_dropped += + l2fwd_rsrc->port_stats[port_id].dropped; + total_packets_tx += l2fwd_rsrc->port_stats[port_id].tx; + total_packets_rx += l2fwd_rsrc->port_stats[port_id].rx; + } + printf("\nAggregate statistics ===============================" + "\nTotal packets sent: %18"PRIu64 + "\nTotal packets received: %14"PRIu64 + "\nTotal packets dropped: %15"PRIu64, + total_packets_tx, + total_packets_rx, + total_packets_dropped); + printf("\n====================================================\n"); +} + +int +l2fwd_event_init_ports(struct l2fwd_resources *l2fwd_rsrc) +{ + uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT; + uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT; + struct rte_eth_conf port_conf = { + .rxmode = { + .max_rx_pkt_len = RTE_ETHER_MAX_LEN, + .split_hdr_size = 0, + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, + }; + uint16_t nb_ports_available = 0; + uint16_t port_id; + int ret; + + /* Initialise each port */ + RTE_ETH_FOREACH_DEV(port_id) { + struct rte_eth_conf local_port_conf = port_conf; + struct rte_eth_dev_info dev_info; + struct rte_eth_rxconf rxq_conf; + struct rte_eth_txconf txq_conf; + + /* skip ports that are not enabled */ + if ((l2fwd_rsrc->enabled_port_mask & (1 << port_id)) == 0) { + printf("Skipping disabled port %u\n", port_id); + continue; + } + nb_ports_available++; + + /* init port */ + printf("Initializing port %u... ", port_id); + fflush(stdout); + rte_eth_dev_info_get(port_id, &dev_info); + if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE) + local_port_conf.txmode.offloads |= + DEV_TX_OFFLOAD_MBUF_FAST_FREE; + ret = rte_eth_dev_configure(port_id, 1, 1, &local_port_conf); + if (ret < 0) + rte_exit(EXIT_FAILURE, + "Cannot configure device: err=%d, port=%u\n", + ret, port_id); + + ret = rte_eth_dev_adjust_nb_rx_tx_desc(port_id, &nb_rxd, + &nb_txd); + if (ret < 0) + rte_exit(EXIT_FAILURE, + "Cannot adjust number of descriptors: err=%d, port=%u\n", + ret, port_id); + + rte_eth_macaddr_get(port_id, &l2fwd_rsrc->eth_addr[port_id]); + + /* init one RX queue */ + fflush(stdout); + rxq_conf = dev_info.default_rxconf; + rxq_conf.offloads = local_port_conf.rxmode.offloads; + ret = rte_eth_rx_queue_setup(port_id, 0, nb_rxd, + rte_eth_dev_socket_id(port_id), + &rxq_conf, + l2fwd_rsrc->pktmbuf_pool); + if (ret < 0) + rte_exit(EXIT_FAILURE, + "rte_eth_rx_queue_setup:err=%d, port=%u\n", + ret, port_id); + + /* init one TX queue on each port */ + fflush(stdout); + txq_conf = dev_info.default_txconf; + txq_conf.offloads = local_port_conf.txmode.offloads; + ret = rte_eth_tx_queue_setup(port_id, 0, nb_txd, + rte_eth_dev_socket_id(port_id), + &txq_conf); + if (ret < 0) + rte_exit(EXIT_FAILURE, + "rte_eth_tx_queue_setup:err=%d, port=%u\n", + ret, port_id); + + rte_eth_promiscuous_enable(port_id); + + printf("Port %u,MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n\n", + port_id, + l2fwd_rsrc->eth_addr[port_id].addr_bytes[0], + l2fwd_rsrc->eth_addr[port_id].addr_bytes[1], + l2fwd_rsrc->eth_addr[port_id].addr_bytes[2], + l2fwd_rsrc->eth_addr[port_id].addr_bytes[3], + l2fwd_rsrc->eth_addr[port_id].addr_bytes[4], + l2fwd_rsrc->eth_addr[port_id].addr_bytes[5]); + } + + return nb_ports_available; +} diff --git a/examples/l2fwd-event/l2fwd_common.h b/examples/l2fwd-event/l2fwd_common.h new file mode 100644 index 000000000..7b5958c7d --- /dev/null +++ b/examples/l2fwd-event/l2fwd_common.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2019 Marvell International Ltd. + */ + +#ifndef __L2FWD_COMMON_H__ +#define __L2FWD_COMMON_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_PKT_BURST 32 +#define MAX_RX_QUEUE_PER_LCORE 16 +#define MAX_TX_QUEUE_PER_PORT 16 + +#define RTE_TEST_RX_DESC_DEFAULT 1024 +#define RTE_TEST_TX_DESC_DEFAULT 1024 + +#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */ +#define MEMPOOL_CACHE_SIZE 256 + +#define DEFAULT_TIMER_PERIOD 10 /* default period is 10 seconds */ +#define MAX_TIMER_PERIOD 86400 /* 1 day max */ + +/* Per-port statistics struct */ +struct l2fwd_port_statistics { + uint64_t dropped; + uint64_t tx; + uint64_t rx; +} __rte_cache_aligned; + +struct l2fwd_resources { + volatile uint8_t force_quit; + uint8_t mac_updating; + uint8_t rx_queue_per_lcore; + uint16_t nb_rxd; + uint16_t nb_txd; + uint32_t enabled_port_mask; + uint64_t timer_period; + struct rte_mempool *pktmbuf_pool; + uint32_t dst_ports[RTE_MAX_ETHPORTS]; + struct rte_ether_addr eth_addr[RTE_MAX_ETHPORTS]; + struct l2fwd_port_statistics port_stats[RTE_MAX_ETHPORTS]; + void *poll_rsrc; +} __rte_cache_aligned; + +static __rte_always_inline void +l2fwd_mac_updating(struct rte_mbuf *m, uint32_t dest_port_id, + struct rte_ether_addr *addr) +{ + struct rte_ether_hdr *eth; + void *tmp; + + eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *); + + /* 02:00:00:00:00:xx */ + tmp = ð->d_addr.addr_bytes[0]; + *((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dest_port_id << 40); + + /* src addr */ + rte_ether_addr_copy(addr, ð->s_addr); +} + +static __rte_always_inline struct l2fwd_resources * +l2fwd_get_rsrc(void) +{ + static const char name[RTE_MEMZONE_NAMESIZE] = "l2fwd_rsrc"; + const struct rte_memzone *mz; + + mz = rte_memzone_lookup(name); + if (mz != NULL) + return mz->addr; + + mz = rte_memzone_reserve(name, sizeof(struct l2fwd_resources), 0, 0); + if (mz != NULL) { + struct l2fwd_resources *l2fwd_rsrc = mz->addr; + + memset(l2fwd_rsrc, 0, sizeof(struct l2fwd_resources)); + l2fwd_rsrc->mac_updating = true; + l2fwd_rsrc->rx_queue_per_lcore = 1; + l2fwd_rsrc->timer_period = 10 * rte_get_timer_hz(); + + return mz->addr; + } + + rte_exit(EXIT_FAILURE, + "Unable to allocate memory for l2fwd resources\n"); + + return NULL; +} + +void print_stats(struct l2fwd_resources *l2fwd_rsrc); +int l2fwd_event_init_ports(struct l2fwd_resources *l2fwd_rsrc); + +#endif /* __L2FWD_COMMON_H__ */ diff --git a/examples/l2fwd-event/l2fwd_poll.c b/examples/l2fwd-event/l2fwd_poll.c new file mode 100644 index 000000000..a0faadc52 --- /dev/null +++ b/examples/l2fwd-event/l2fwd_poll.c @@ -0,0 +1,197 @@ + +#include "l2fwd_poll.h" + +/* main poll mode processing loop */ +static void +l2fwd_poll_main_loop(struct l2fwd_resources *l2fwd_rsrc) +{ + struct l2fwd_poll_resources *poll_rsrc = l2fwd_rsrc->poll_rsrc; + uint64_t prev_tsc, diff_tsc, cur_tsc, timer_tsc, drain_tsc; + uint64_t timer_period = l2fwd_rsrc->timer_period; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + struct rte_eth_dev_tx_buffer *buffer; + struct lcore_queue_conf *qconf; + uint32_t i, j, port_id, nb_rx; + struct rte_mbuf *m; + uint32_t lcore_id; + uint16_t dst_port; + int32_t sent; + + 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 = &poll_rsrc->lcore_queue_conf[lcore_id]; + + if (qconf->n_rx_port == 0) { + printf("lcore %u has nothing to do\n", lcore_id); + return; + } + + printf("entering main loop on lcore %u\n", lcore_id); + + for (i = 0; i < qconf->n_rx_port; i++) { + + port_id = qconf->rx_port_list[i]; + printf(" -- lcoreid=%u port_id=%u\n", lcore_id, port_id); + + } + + while (!l2fwd_rsrc->force_quit) { + + cur_tsc = rte_rdtsc(); + + /* + * TX burst queue drain + */ + diff_tsc = cur_tsc - prev_tsc; + if (unlikely(diff_tsc > drain_tsc)) { + for (i = 0; i < qconf->n_rx_port; i++) { + port_id = + l2fwd_rsrc->dst_ports[ + qconf->rx_port_list[i]]; + buffer = poll_rsrc->tx_buffer[port_id]; + sent = rte_eth_tx_buffer_flush(port_id, 0, + buffer); + if (sent) + l2fwd_rsrc->port_stats[port_id].tx += + sent; + } + + /* 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 >= timer_period)) { + /* do this only on master core */ + if (lcore_id == + rte_get_master_lcore()) { + print_stats(l2fwd_rsrc); + /* reset the timer */ + timer_tsc = 0; + } + } + } + + prev_tsc = cur_tsc; + } + + /* + * Read packet from RX queues + */ + for (i = 0; i < qconf->n_rx_port; i++) { + + port_id = qconf->rx_port_list[i]; + nb_rx = rte_eth_rx_burst(port_id, 0, + pkts_burst, MAX_PKT_BURST); + + l2fwd_rsrc->port_stats[port_id].rx += nb_rx; + + for (j = 0; j < nb_rx; j++) { + m = pkts_burst[j]; + rte_prefetch0(rte_pktmbuf_mtod(m, void *)); + dst_port = l2fwd_rsrc->dst_ports[port_id]; + + if (l2fwd_rsrc->mac_updating) + l2fwd_mac_updating(m, dst_port, + &l2fwd_rsrc->eth_addr[ + dst_port]); + + buffer = poll_rsrc->tx_buffer[dst_port]; + sent = rte_eth_tx_buffer(dst_port, 0, buffer, + m); + if (sent) + l2fwd_rsrc->port_stats[dst_port].tx + += sent; + + } + } + } +} + +static void +l2fwd_poll_lcore_config(struct l2fwd_resources *l2fwd_rsrc) +{ + struct l2fwd_poll_resources *poll_rsrc = l2fwd_rsrc->poll_rsrc; + struct lcore_queue_conf *qconf = NULL; + uint32_t rx_lcore_id = 0; + uint16_t port_id; + + /* Initialize the port/queue configuration of each logical core */ + RTE_ETH_FOREACH_DEV(port_id) { + /* skip ports that are not enabled */ + if ((l2fwd_rsrc->enabled_port_mask & (1 << port_id)) == 0) + continue; + + /* get the lcore_id for this port */ + while (rte_lcore_is_enabled(rx_lcore_id) == 0 || + poll_rsrc->lcore_queue_conf[rx_lcore_id].n_rx_port == + l2fwd_rsrc->rx_queue_per_lcore) { + rx_lcore_id++; + if (rx_lcore_id >= RTE_MAX_LCORE) + rte_exit(EXIT_FAILURE, "Not enough cores\n"); + } + + if (qconf != &poll_rsrc->lcore_queue_conf[rx_lcore_id]) { + /* Assigned a new logical core in the loop above. */ + qconf = &poll_rsrc->lcore_queue_conf[rx_lcore_id]; + } + + qconf->rx_port_list[qconf->n_rx_port] = port_id; + qconf->n_rx_port++; + printf("Lcore %u: RX port %u\n", rx_lcore_id, port_id); + } +} + +static void +l2fwd_poll_init_tx_buffers(struct l2fwd_resources *l2fwd_rsrc) +{ + struct l2fwd_poll_resources *poll_rsrc = l2fwd_rsrc->poll_rsrc; + uint16_t port_id; + int ret; + + RTE_ETH_FOREACH_DEV(port_id) { + /* Initialize TX buffers */ + poll_rsrc->tx_buffer[port_id] = rte_zmalloc_socket("tx_buffer", + RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST), 0, + rte_eth_dev_socket_id(port_id)); + if (poll_rsrc->tx_buffer[port_id] == NULL) + rte_exit(EXIT_FAILURE, + "Cannot allocate buffer for tx on port %u\n", + port_id); + + rte_eth_tx_buffer_init(poll_rsrc->tx_buffer[port_id], + MAX_PKT_BURST); + + ret = rte_eth_tx_buffer_set_err_callback( + poll_rsrc->tx_buffer[port_id], + rte_eth_tx_buffer_count_callback, + &l2fwd_rsrc->port_stats[port_id].dropped); + if (ret < 0) + rte_exit(EXIT_FAILURE, + "Cannot set error callback for tx buffer on port %u\n", + port_id); + } +} + +void +l2fwd_poll_resource_setup(struct l2fwd_resources *l2fwd_rsrc) +{ + struct l2fwd_poll_resources *poll_rsrc; + + poll_rsrc = rte_zmalloc("l2fwd_poll_rsrc", + sizeof(struct l2fwd_poll_resources), 0); + if (poll_rsrc == NULL) + rte_exit(EXIT_FAILURE, + "failed to allocate resources for l2fwd poll mode\n"); + + l2fwd_rsrc->poll_rsrc = poll_rsrc; + l2fwd_poll_lcore_config(l2fwd_rsrc); + l2fwd_poll_init_tx_buffers(l2fwd_rsrc); + + poll_rsrc->poll_main_loop = l2fwd_poll_main_loop; +} diff --git a/examples/l2fwd-event/l2fwd_poll.h b/examples/l2fwd-event/l2fwd_poll.h new file mode 100644 index 000000000..18b30870b --- /dev/null +++ b/examples/l2fwd-event/l2fwd_poll.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2019 Marvell International Ltd. + */ + +#ifndef __L2FWD_POLL_H__ +#define __L2FWD_POLL_H__ + +#include "l2fwd_common.h" + +typedef void (*poll_main_loop_cb)(struct l2fwd_resources *l2fwd_rsrc); + +struct lcore_queue_conf { + uint32_t rx_port_list[MAX_RX_QUEUE_PER_LCORE]; + uint32_t n_rx_port; +} __rte_cache_aligned; + +struct l2fwd_poll_resources { + poll_main_loop_cb poll_main_loop; + struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS]; + struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE]; +}; + +void l2fwd_poll_resource_setup(struct l2fwd_resources *l2fwd_rsrc); + +#endif diff --git a/examples/l2fwd-event/main.c b/examples/l2fwd-event/main.c new file mode 100644 index 000000000..887a979d5 --- /dev/null +++ b/examples/l2fwd-event/main.c @@ -0,0 +1,374 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2019 Marvell International Ltd. + */ + +#include "l2fwd_poll.h" + +/* display usage */ +static void +l2fwd_event_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" + " -T PERIOD: statistics will be refreshed each PERIOD seconds " + " (0 to disable, 10 default, 86400 maximum)\n" + " --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n" + " When enabled:\n" + " - The source MAC address is replaced by the TX port MAC address\n" + " - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n", + prgname); +} + +static int +l2fwd_event_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_event_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_event_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 const char short_options[] = + "p:" /* portmask */ + "q:" /* number of queues */ + "T:" /* timer period */ + ; + +#define CMD_LINE_OPT_MAC_UPDATING "mac-updating" +#define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating" + +enum { + /* long options mapped to a short option */ + + /* first long only option value must be >= 256, so that we won't + * conflict with short options + */ + CMD_LINE_OPT_MIN_NUM = 256, +}; + +/* Parse the argument given in the command line of the application */ +static int +l2fwd_event_parse_args(int argc, char **argv, + struct l2fwd_resources *l2fwd_rsrc) +{ + int mac_updating = 1; + struct option lgopts[] = { + { CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1}, + { CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0}, + {NULL, 0, 0, 0} + }; + int opt, ret, timer_secs; + char *prgname = argv[0]; + char **argvopt; + int option_index; + + argvopt = argv; + while ((opt = getopt_long(argc, argvopt, short_options, + lgopts, &option_index)) != EOF) { + + switch (opt) { + /* portmask */ + case 'p': + l2fwd_rsrc->enabled_port_mask = + l2fwd_event_parse_portmask(optarg); + if (l2fwd_rsrc->enabled_port_mask == 0) { + printf("invalid portmask\n"); + l2fwd_event_usage(prgname); + return -1; + } + break; + + /* nqueue */ + case 'q': + l2fwd_rsrc->rx_queue_per_lcore = + l2fwd_event_parse_nqueue(optarg); + if (l2fwd_rsrc->rx_queue_per_lcore == 0) { + printf("invalid queue number\n"); + l2fwd_event_usage(prgname); + return -1; + } + break; + + /* timer period */ + case 'T': + timer_secs = l2fwd_event_parse_timer_period(optarg); + if (timer_secs < 0) { + printf("invalid timer period\n"); + l2fwd_event_usage(prgname); + return -1; + } + l2fwd_rsrc->timer_period = timer_secs; + /* convert to number of cycles */ + l2fwd_rsrc->timer_period *= rte_get_timer_hz(); + break; + + /* long options */ + case 0: + break; + + default: + l2fwd_event_usage(prgname); + return -1; + } + } + + l2fwd_rsrc->mac_updating = mac_updating; + + if (optind >= 0) + argv[optind-1] = prgname; + + ret = optind-1; + optind = 1; /* reset getopt lib */ + return ret; +} + +static int +l2fwd_launch_one_lcore(void *args) +{ + struct l2fwd_resources *l2fwd_rsrc = args; + struct l2fwd_poll_resources *poll_rsrc = l2fwd_rsrc->poll_rsrc; + + poll_rsrc->poll_main_loop(l2fwd_rsrc); + + return 0; +} + +/* Check the link status of all ports in up to 9s, and print them finally */ +static void +check_all_ports_link_status(struct l2fwd_resources *l2fwd_rsrc, + uint32_t port_mask) +{ +#define CHECK_INTERVAL 100 /* 100ms */ +#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */ + uint16_t port_id; + uint8_t 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++) { + if (l2fwd_rsrc->force_quit) + return; + all_ports_up = 1; + RTE_ETH_FOREACH_DEV(port_id) { + if (l2fwd_rsrc->force_quit) + return; + if ((port_mask & (1 << port_id)) == 0) + continue; + memset(&link, 0, sizeof(link)); + rte_eth_link_get_nowait(port_id, &link); + /* print link status if flag set */ + if (print_flag == 1) { + if (link.link_status) + printf( + "Port%d Link Up. Speed %u Mbps - %s\n", + port_id, link.link_speed, + (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? + ("full-duplex") : ("half-duplex\n")); + else + printf("Port %d Link Down\n", port_id); + continue; + } + /* clear all_ports_up flag if any link down */ + if (link.link_status == ETH_LINK_DOWN) { + 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 void +signal_handler(int signum) +{ + struct l2fwd_resources *l2fwd_rsrc = l2fwd_get_rsrc(); + if (signum == SIGINT || signum == SIGTERM) { + printf("\n\nSignal %d received, preparing to exit...\n", + signum); + l2fwd_rsrc->force_quit = true; + } +} + +int +main(int argc, char **argv) +{ + struct l2fwd_resources *l2fwd_rsrc; + uint16_t nb_ports_available = 0; + uint32_t nb_ports_in_mask = 0; + uint16_t port_id, last_port; + uint32_t nb_mbufs; + uint16_t nb_ports; + int ret; + + /* init EAL */ + ret = rte_eal_init(argc, argv); + if (ret < 0) + rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n"); + argc -= ret; + argv += ret; + + l2fwd_rsrc = l2fwd_get_rsrc(); + + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + /* parse application arguments (after the EAL ones) */ + ret = l2fwd_event_parse_args(argc, argv, l2fwd_rsrc); + if (ret < 0) + rte_exit(EXIT_FAILURE, "Invalid L2FWD arguments\n"); + + printf("MAC updating %s\n", l2fwd_rsrc->mac_updating ? "enabled" : + "disabled"); + + nb_ports = rte_eth_dev_count_avail(); + if (nb_ports == 0) + rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n"); + + /* check port mask to possible port mask */ + if (l2fwd_rsrc->enabled_port_mask & ~((1 << nb_ports) - 1)) + rte_exit(EXIT_FAILURE, "Invalid portmask; possible (0x%x)\n", + (1 << nb_ports) - 1); + + /* reset l2fwd_dst_ports */ + for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++) + l2fwd_rsrc->dst_ports[port_id] = 0; + last_port = 0; + + /* + * Each logical core is assigned a dedicated TX queue on each port. + */ + RTE_ETH_FOREACH_DEV(port_id) { + /* skip ports that are not enabled */ + if ((l2fwd_rsrc->enabled_port_mask & (1 << port_id)) == 0) + continue; + + if (nb_ports_in_mask % 2) { + l2fwd_rsrc->dst_ports[port_id] = last_port; + l2fwd_rsrc->dst_ports[last_port] = port_id; + } else { + last_port = port_id; + } + + nb_ports_in_mask++; + } + if (nb_ports_in_mask % 2) { + printf("Notice: odd number of ports in portmask.\n"); + l2fwd_rsrc->dst_ports[last_port] = last_port; + } + + nb_mbufs = RTE_MAX(nb_ports * (RTE_TEST_RX_DESC_DEFAULT + + RTE_TEST_TX_DESC_DEFAULT + + MAX_PKT_BURST + rte_lcore_count() * + MEMPOOL_CACHE_SIZE), 8192U); + + /* create the mbuf pool */ + l2fwd_rsrc->pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", + nb_mbufs, MEMPOOL_CACHE_SIZE, 0, + RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); + if (l2fwd_rsrc->pktmbuf_pool == NULL) + rte_exit(EXIT_FAILURE, "Cannot init mbuf pool\n"); + + nb_ports_available = l2fwd_event_init_ports(l2fwd_rsrc); + if (!nb_ports_available) { + rte_exit(EXIT_FAILURE, + "All available ports are disabled. Please set portmask.\n"); + } + + l2fwd_poll_resource_setup(l2fwd_rsrc); + + /* initialize port stats */ + memset(&l2fwd_rsrc->port_stats, 0, + sizeof(struct l2fwd_port_statistics)); + + /* All settings are done. Now enable eth devices */ + RTE_ETH_FOREACH_DEV(port_id) { + /* skip ports that are not enabled */ + if ((l2fwd_rsrc->enabled_port_mask & + (1 << port_id)) == 0) + continue; + + ret = rte_eth_dev_start(port_id); + if (ret < 0) + rte_exit(EXIT_FAILURE, + "rte_eth_dev_start:err=%d, port=%u\n", ret, + port_id); + } + + check_all_ports_link_status(l2fwd_rsrc, l2fwd_rsrc->enabled_port_mask); + + /* launch per-lcore init on every lcore */ + rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, l2fwd_rsrc, + CALL_MASTER); + rte_eal_mp_wait_lcore(); + + RTE_ETH_FOREACH_DEV(port_id) { + if ((l2fwd_rsrc->enabled_port_mask & + (1 << port_id)) == 0) + continue; + printf("Closing port %d...", port_id); + rte_eth_dev_stop(port_id); + rte_eth_dev_close(port_id); + printf(" Done\n"); + } + printf("Bye...\n"); + + return 0; +} diff --git a/examples/l2fwd-event/meson.build b/examples/l2fwd-event/meson.build new file mode 100644 index 000000000..f482e1817 --- /dev/null +++ b/examples/l2fwd-event/meson.build @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(C) 2019 Marvell International Ltd. +# + +# meson file, for building this example as part of a main DPDK build. +# +# To build this example as a standalone application with an already-installed +# DPDK instance, use 'make' + +sources = files( + 'main.c', + 'l2fwd_poll.c', + 'l2fwd_common.c', +) -- 2.17.1