From patchwork Sun Oct 18 12:03:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Guy Kaneti X-Patchwork-Id: 81255 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id E0362A04B0; Sun, 18 Oct 2020 14:06:13 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 8A4A8D03D; Sun, 18 Oct 2020 14:05:39 +0200 (CEST) Received: from mx0b-0016f401.pphosted.com (mx0a-0016f401.pphosted.com [67.231.148.174]) by dpdk.org (Postfix) with ESMTP id 2AB12D01D for ; Sun, 18 Oct 2020 14:05:34 +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 09IBu4qL023624; Sun, 18 Oct 2020 05:05:32 -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=pfpt0220; bh=IioR7cnyIMgTOjtvQWhO/lxa/78FHO0f/Z/z9bHtZmM=; b=h9hw9cctpnM2kXeatAEvX/JA57Dlmi1DBKkLDIPMLO42bykHOrTUaXvDrnH/BZivmnZu 5vs2Wa/+iqP5SAwY+1swxJGLN2+YrdixoDQRJSyEY4mvPit/6CYNcH+K42fpgu/nxyVX UzghxUEpWR2uvpROXFNiFnli8PLVjbX1ukBS3bykBLJVRsLsvw4FsFqm2IETgcFTB8uA 3WZT6T6zkCWYYIMMPqDnR+Lo7/9oKbsyhG7zBw1E6FA/GgACSfSm7HRFpK0esBl649K/ ogN8yXZfJtIJNFQN2HCI1X3g9hEtLWMOXAmgCRCOtQV/QmhzDhcAkJ4cNMCfHhoVONQr og== Received: from sc-exch01.marvell.com ([199.233.58.181]) by mx0a-0016f401.pphosted.com with ESMTP id 347wyq2dfp-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT); Sun, 18 Oct 2020 05:05:32 -0700 Received: from DC5-EXCH01.marvell.com (10.69.176.38) by SC-EXCH01.marvell.com (10.93.176.81) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sun, 18 Oct 2020 05:05:30 -0700 Received: from DC5-EXCH01.marvell.com (10.69.176.38) by DC5-EXCH01.marvell.com (10.69.176.38) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sun, 18 Oct 2020 05:05:29 -0700 Received: from maili.marvell.com (10.69.176.80) by DC5-EXCH01.marvell.com (10.69.176.38) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sun, 18 Oct 2020 05:05:29 -0700 Received: from vvenus124.il.marvell.com (unknown [10.5.116.64]) by maili.marvell.com (Postfix) with ESMTP id CEE0E3F703F; Sun, 18 Oct 2020 05:05:26 -0700 (PDT) From: To: , , , CC: , , , , Date: Sun, 18 Oct 2020 15:03:52 +0300 Message-ID: <20201018120353.11325-2-guyk@marvell.com> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20201018120353.11325-1-guyk@marvell.com> References: <20200908123144.26444-3-guyk@marvell.com> <20201018120353.11325-1-guyk@marvell.com> MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.235, 18.0.687 definitions=2020-10-18_02:2020-10-16, 2020-10-18 signatures=0 Subject: [dpdk-dev] [PATCH v3 1/2] examples/l3fwd-regex: add regex based l3fwd 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: Guy Kaneti Add regex based l3fwd application inline with normal l3fwd. only LPM is supported. Signed-off-by: Guy Kaneti Acked-by: Ori Kam --- MAINTAINERS | 2 + examples/l3fwd-regex/l3fwd.h | 207 ++++++ examples/l3fwd-regex/l3fwd_lpm.c | 484 ++++++++++++ examples/l3fwd-regex/l3fwd_regex.c | 487 ++++++++++++ examples/l3fwd-regex/l3fwd_regex.h | 38 + examples/l3fwd-regex/main.c | 1117 ++++++++++++++++++++++++++++ examples/l3fwd-regex/meson.build | 10 + examples/meson.build | 2 +- 8 files changed, 2346 insertions(+), 1 deletion(-) create mode 100644 examples/l3fwd-regex/l3fwd.h create mode 100644 examples/l3fwd-regex/l3fwd_lpm.c create mode 100644 examples/l3fwd-regex/l3fwd_regex.c create mode 100644 examples/l3fwd-regex/l3fwd_regex.h create mode 100644 examples/l3fwd-regex/main.c create mode 100644 examples/l3fwd-regex/meson.build diff --git a/MAINTAINERS b/MAINTAINERS index f7ecb5305..9880efa3a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -431,6 +431,8 @@ F: lib/librte_regexdev/ F: app/test-regex/ F: doc/guides/prog_guide/regexdev.rst F: doc/guides/regexdevs/features/default.ini +M: Guy Kaneti +F: examples/l3fwd-regex/ Eventdev API M: Jerin Jacob diff --git a/examples/l3fwd-regex/l3fwd.h b/examples/l3fwd-regex/l3fwd.h new file mode 100644 index 000000000..3fb3647bb --- /dev/null +++ b/examples/l3fwd-regex/l3fwd.h @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2016 Intel Corporation + * Copyright(C) 2020 Marvell International Ltd. + */ + +#ifndef __L3_FWD_H__ +#define __L3_FWD_H__ + +#include +#include + +#define DO_RFC_1812_CHECKS + +#define RTE_LOGTYPE_L3FWD RTE_LOGTYPE_USER1 + +/* + * Configurable number of RX/TX ring descriptors + */ +#define RTE_TEST_RX_DESC_DEFAULT 1024 +#define RTE_TEST_TX_DESC_DEFAULT 1024 + +#define MAX_PKT_BURST 32 +#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */ + +#define MEMPOOL_CACHE_SIZE 256 +#define MAX_RX_QUEUE_PER_LCORE 16 + +/* + * Try to avoid TX buffering if we have at least MAX_TX_BURST packets to send. + */ +#define MAX_TX_BURST (MAX_PKT_BURST / 2) + +#define NB_SOCKETS 8 + +/* Configure how many packets ahead to prefetch, when reading packets */ +#define PREFETCH_OFFSET 3 + +/* Used to mark destination port as 'invalid'. */ +#define BAD_PORT ((uint16_t)-1) + +#define FWDSTEP 4 + +/* replace first 12B of the ethernet header. */ +#define MASK_ETH 0x3f + +struct mbuf_table { + uint16_t len; + struct rte_mbuf *m_table[MAX_PKT_BURST]; +}; + +struct lcore_rx_queue { + uint16_t port_id; + uint8_t queue_id; +} __rte_cache_aligned; + +struct lcore_conf { + uint16_t n_rx_queue; + struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE]; + uint8_t regex_dev_id; + uint16_t regex_qp_id; + uint16_t n_tx_port; + uint16_t tx_port_id[RTE_MAX_ETHPORTS]; + uint16_t tx_queue_id[RTE_MAX_ETHPORTS]; + struct mbuf_table tx_mbufs[RTE_MAX_ETHPORTS]; + struct rte_mbuf **pkts_burst; + void *ipv4_lookup_struct; + void *ipv6_lookup_struct; +} __rte_cache_aligned; + +extern volatile bool force_quit; + +/* ethernet addresses of ports */ +extern uint64_t dest_eth_addr[RTE_MAX_ETHPORTS]; +extern struct rte_ether_addr ports_eth_addr[RTE_MAX_ETHPORTS]; + +/* mask of enabled ports */ +extern uint32_t enabled_port_mask; + +/* Used only in exact match mode. */ +extern int ipv6; /**< ipv6 is false by default. */ +extern uint32_t hash_entry_number; + +extern xmm_t val_eth[RTE_MAX_ETHPORTS]; + +extern struct lcore_conf lcore_conf[RTE_MAX_LCORE]; + +/* Send burst of packets on an output interface */ +static inline int +send_burst(struct lcore_conf *qconf, uint16_t n, uint16_t port) +{ + struct rte_mbuf **m_table; + int ret; + uint16_t queueid; + + queueid = qconf->tx_queue_id[port]; + m_table = (struct rte_mbuf **)qconf->tx_mbufs[port].m_table; + + ret = rte_eth_tx_burst(port, queueid, m_table, n); + if (unlikely(ret < n)) { + do { + rte_pktmbuf_free(m_table[ret]); + } while (++ret < n); + } + + return 0; +} + +/* Enqueue a single packet, and send burst if queue is filled */ +static inline int +send_single_packet(struct lcore_conf *qconf, + struct rte_mbuf *m, uint16_t port) +{ + uint16_t len; + + 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)) { + send_burst(qconf, MAX_PKT_BURST, port); + len = 0; + } + + qconf->tx_mbufs[port].len = len; + return 0; +} + +#ifdef DO_RFC_1812_CHECKS +static inline int +is_valid_ipv4_pkt(struct rte_ipv4_hdr *pkt, uint32_t link_len) +{ + /* From http://www.rfc-editor.org/rfc/rfc1812.txt section 5.2.2 */ + /* + * 1. The packet length reported by the Link Layer must be large + * enough to hold the minimum length legal IP datagram (20 bytes). + */ + if (link_len < sizeof(struct rte_ipv4_hdr)) + return -1; + + /* 2. The IP checksum must be correct. */ + /* this is checked in H/W */ + + /* + * 3. The IP version number must be 4. If the version number is not 4 + * then the packet may be another version of IP, such as IPng or + * ST-II. + */ + if (((pkt->version_ihl) >> 4) != 4) + return -3; + /* + * 4. The IP header length field must be large enough to hold the + * minimum length legal IP datagram (20 bytes = 5 words). + */ + if ((pkt->version_ihl & 0xf) < 5) + return -4; + + /* + * 5. The IP total length field must be large enough to hold the IP + * datagram header, whose length is specified in the IP header length + * field. + */ + if (rte_cpu_to_be_16(pkt->total_length) < sizeof(struct rte_ipv4_hdr)) + return -5; + + return 0; +} +#endif /* DO_RFC_1812_CHECKS */ + +int +init_mem(uint16_t portid, unsigned int nb_mbuf); + +/* Function pointers for LPM functionality. */ +void +setup_lpm(const int socketid); + +void +setup_hash(const int socketid); + +int +lpm_check_ptype(int portid); + +uint16_t +lpm_cb_parse_ptype(uint16_t port, uint16_t queue, struct rte_mbuf *pkts[], + uint16_t nb_pkts, uint16_t max_pkts, void *user_param); + +int +lpm_main_loop(__rte_unused void *dummy); + +int +lpm_event_main_loop_tx_d(__rte_unused void *dummy); +int +lpm_event_main_loop_tx_d_burst(__rte_unused void *dummy); +int +lpm_event_main_loop_tx_q(__rte_unused void *dummy); +int +lpm_event_main_loop_tx_q_burst(__rte_unused void *dummy); + + +/* Return ipv4/ipv6 fwd lookup struct for LPM*/ +void * +lpm_get_ipv4_l3fwd_lookup_struct(const int socketid); + +void * +lpm_get_ipv6_l3fwd_lookup_struct(const int socketid); + +#endif /* __L3_FWD_H__ */ diff --git a/examples/l3fwd-regex/l3fwd_lpm.c b/examples/l3fwd-regex/l3fwd_lpm.c new file mode 100644 index 000000000..5af69403c --- /dev/null +++ b/examples/l3fwd-regex/l3fwd_lpm.c @@ -0,0 +1,484 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2016 Intel Corporation + * Copyright(C) 2020 Marvell International Ltd. + */ + +#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 "l3fwd.h" +#include "l3fwd_regex.h" + +struct ipv4_l3fwd_lpm_route { + uint32_t ip; + uint8_t depth; + uint8_t if_out; +}; + +struct ipv6_l3fwd_lpm_route { + uint8_t ip[16]; + uint8_t depth; + uint8_t if_out; +}; + +/* 198.18.0.0/16 are set aside for RFC2544 benchmarking (RFC5735). */ +static const struct ipv4_l3fwd_lpm_route ipv4_l3fwd_lpm_route_array[] = { + {RTE_IPV4(198, 18, 0, 0), 24, 0}, + {RTE_IPV4(198, 18, 1, 0), 24, 1}, + {RTE_IPV4(198, 18, 2, 0), 24, 2}, + {RTE_IPV4(198, 18, 3, 0), 24, 3}, + {RTE_IPV4(198, 18, 4, 0), 24, 4}, + {RTE_IPV4(198, 18, 5, 0), 24, 5}, + {RTE_IPV4(198, 18, 6, 0), 24, 6}, + {RTE_IPV4(198, 18, 7, 0), 24, 7}, +}; + +/* 2001:0200::/48 is IANA reserved range for IPv6 benchmarking (RFC5180) */ +static const struct ipv6_l3fwd_lpm_route ipv6_l3fwd_lpm_route_array[] = { + {{32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 48, 0}, + {{32, 1, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 48, 1}, + {{32, 1, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}, 48, 2}, + {{32, 1, 2, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0}, 48, 3}, + {{32, 1, 2, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, 48, 4}, + {{32, 1, 2, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0}, 48, 5}, + {{32, 1, 2, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0}, 48, 6}, + {{32, 1, 2, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0}, 48, 7}, +}; + +#define IPV4_L3FWD_LPM_MAX_RULES 1024 +#define IPV4_L3FWD_LPM_NUMBER_TBL8S (1 << 8) +#define IPV6_L3FWD_LPM_MAX_RULES 1024 +#define IPV6_L3FWD_LPM_NUMBER_TBL8S (1 << 16) + +static struct rte_lpm *ipv4_l3fwd_lpm_lookup_struct[NB_SOCKETS]; +static struct rte_lpm6 *ipv6_l3fwd_lpm_lookup_struct[NB_SOCKETS]; + +static inline uint16_t +lpm_get_ipv4_dst_port(const struct rte_ipv4_hdr *ipv4_hdr, + uint16_t portid, + struct rte_lpm *ipv4_l3fwd_lookup_struct) +{ + uint32_t dst_ip = rte_be_to_cpu_32(ipv4_hdr->dst_addr); + uint32_t next_hop; + + if (rte_lpm_lookup(ipv4_l3fwd_lookup_struct, dst_ip, &next_hop) == 0) + return next_hop; + else + return portid; +} + +static inline uint16_t +lpm_get_ipv6_dst_port(const struct rte_ipv6_hdr *ipv6_hdr, + uint16_t portid, + struct rte_lpm6 *ipv6_l3fwd_lookup_struct) +{ + const uint8_t *dst_ip = ipv6_hdr->dst_addr; + uint32_t next_hop; + + if (rte_lpm6_lookup(ipv6_l3fwd_lookup_struct, dst_ip, &next_hop) == 0) + return next_hop; + else + return portid; +} + +static __rte_always_inline void +l3fwd_lpm_simple_forward(struct rte_mbuf *m, uint16_t portid, + struct lcore_conf *qconf) +{ + struct rte_ether_hdr *eth_hdr; + struct rte_ipv4_hdr *ipv4_hdr; + uint16_t dst_port; + + eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *); + + if (RTE_ETH_IS_IPV4_HDR(m->packet_type)) { + /* Handle IPv4 headers.*/ + ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *, + sizeof(struct rte_ether_hdr)); + +#ifdef DO_RFC_1812_CHECKS + /* Check to make sure the packet is valid (RFC1812) */ + if (is_valid_ipv4_pkt(ipv4_hdr, m->pkt_len) < 0) { + rte_pktmbuf_free(m); + return; + } +#endif + dst_port = lpm_get_ipv4_dst_port(ipv4_hdr, portid, + qconf->ipv4_lookup_struct); + + if (dst_port >= RTE_MAX_ETHPORTS || + (enabled_port_mask & 1 << dst_port) == 0) + dst_port = portid; + +#ifdef DO_RFC_1812_CHECKS + /* Update time to live and header checksum */ + --(ipv4_hdr->time_to_live); + ++(ipv4_hdr->hdr_checksum); +#endif + /* dst addr */ + *(uint64_t *)ð_hdr->d_addr = dest_eth_addr[dst_port]; + + /* src addr */ + rte_ether_addr_copy(&ports_eth_addr[dst_port], + ð_hdr->s_addr); + + send_single_packet(qconf, m, dst_port); + } else if (RTE_ETH_IS_IPV6_HDR(m->packet_type)) { + /* Handle IPv6 headers.*/ + struct rte_ipv6_hdr *ipv6_hdr; + + ipv6_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv6_hdr *, + sizeof(struct rte_ether_hdr)); + + dst_port = lpm_get_ipv6_dst_port(ipv6_hdr, portid, + qconf->ipv6_lookup_struct); + + if (dst_port >= RTE_MAX_ETHPORTS || + (enabled_port_mask & 1 << dst_port) == 0) + dst_port = portid; + + /* dst addr */ + *(uint64_t *)ð_hdr->d_addr = dest_eth_addr[dst_port]; + + /* src addr */ + rte_ether_addr_copy(&ports_eth_addr[dst_port], + ð_hdr->s_addr); + + send_single_packet(qconf, m, dst_port); + } else { + /* Free the mbuf that contains non-IPV4/IPV6 packet */ + rte_pktmbuf_free(m); + } +} + +static inline void +l3fwd_lpm_no_opt_send_packets(int nb_rx, struct rte_mbuf **pkts_burst, + uint16_t portid, struct lcore_conf *qconf) +{ + int32_t j; + + /* Prefetch first packets */ + for (j = 0; j < PREFETCH_OFFSET && j < nb_rx; j++) + rte_prefetch0(rte_pktmbuf_mtod(pkts_burst[j], void *)); + + /* Prefetch and forward already prefetched packets. */ + for (j = 0; j < (nb_rx - PREFETCH_OFFSET); j++) { + rte_prefetch0(rte_pktmbuf_mtod(pkts_burst[ + j + PREFETCH_OFFSET], void *)); + l3fwd_lpm_simple_forward(pkts_burst[j], portid, qconf); + } + + /* Forward remaining prefetched packets */ + for (; j < nb_rx; j++) + l3fwd_lpm_simple_forward(pkts_burst[j], portid, qconf); +} + +/* main processing loop */ +int +lpm_main_loop(__rte_unused void *dummy) +{ + struct rte_mbuf **pkts_burst; + unsigned int lcore_id, regex_nb_ops = 0; + uint64_t prev_tsc, diff_tsc, cur_tsc; + int i, nb_rx, nb_ops, deq_cnt; + uint16_t portid, regex_qp_id; + uint8_t queueid, regex_dev_id; + struct lcore_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; + + lcore_id = rte_lcore_id(); + qconf = &lcore_conf[lcore_id]; + + if (qconf->n_rx_queue == 0) { + RTE_LOG(INFO, L3FWD, "lcore %u has nothing to do\n", lcore_id); + return 0; + } + + RTE_LOG(INFO, L3FWD, "entering main loop on lcore %u\n", lcore_id); + + for (i = 0; i < qconf->n_rx_queue; i++) { + + portid = qconf->rx_queue_list[i].port_id; + queueid = qconf->rx_queue_list[i].queue_id; + RTE_LOG(INFO, L3FWD, + " -- lcoreid=%u portid=%u rxqueueid=%hhu\n", + lcore_id, portid, queueid); + } + regex_dev_id = qconf->regex_dev_id; + regex_qp_id = qconf->regex_qp_id; + pkts_burst = qconf->pkts_burst; + + while (!force_quit) { + + cur_tsc = rte_rdtsc(); + + /* + * TX burst queue drain + */ + diff_tsc = cur_tsc - prev_tsc; + if (unlikely(diff_tsc > drain_tsc)) { + + if (regex_nb_ops) { + deq_cnt = regex_dequeue_burst_ops(regex_dev_id, + lcore_id, regex_qp_id, + pkts_burst, REGEX_NB_OPS); + if (deq_cnt) { + /* only one rx queue is supported */ + portid = + qconf->rx_queue_list[0].port_id; + l3fwd_lpm_no_opt_send_packets(deq_cnt, + pkts_burst, + portid, qconf); + regex_nb_ops -= deq_cnt; + } + } + + for (i = 0; i < qconf->n_tx_port; ++i) { + portid = qconf->tx_port_id[i]; + if (qconf->tx_mbufs[portid].len == 0) + continue; + send_burst(qconf, + qconf->tx_mbufs[portid].len, + portid); + qconf->tx_mbufs[portid].len = 0; + } + + prev_tsc = cur_tsc; + } + + /* + * Read packet from RX queues + */ + for (i = 0; i < qconf->n_rx_queue; ++i) { + portid = qconf->rx_queue_list[i].port_id; + queueid = qconf->rx_queue_list[i].queue_id; + nb_rx = rte_eth_rx_burst(portid, queueid, pkts_burst, + MAX_PKT_BURST); + if (nb_rx == 0) + continue; + nb_ops = regex_enqueue_burst_ops(regex_dev_id, + lcore_id, regex_qp_id, + pkts_burst, nb_rx); + if (unlikely(nb_ops != nb_rx)) + printf("failed to enqueue all ops, %d/%d", + nb_ops, nb_rx); + + regex_nb_ops += nb_ops; + + deq_cnt = regex_dequeue_burst_ops(regex_dev_id, + lcore_id, regex_qp_id, + pkts_burst, REGEX_NB_OPS); + if (deq_cnt) { + l3fwd_lpm_no_opt_send_packets(deq_cnt, + pkts_burst, + portid, qconf); + regex_nb_ops -= deq_cnt; + } + + } + } + regex_stats_print(lcore_id); + + return 0; +} + + +void +setup_lpm(const int socketid) +{ + struct rte_lpm6_config config; + struct rte_lpm_config config_ipv4; + unsigned int i; + int ret; + char s[64]; + char abuf[INET6_ADDRSTRLEN]; + + /* create the LPM table */ + config_ipv4.max_rules = IPV4_L3FWD_LPM_MAX_RULES; + config_ipv4.number_tbl8s = IPV4_L3FWD_LPM_NUMBER_TBL8S; + config_ipv4.flags = 0; + snprintf(s, sizeof(s), "IPV4_L3FWD_LPM_%d", socketid); + ipv4_l3fwd_lpm_lookup_struct[socketid] = + rte_lpm_create(s, socketid, &config_ipv4); + if (ipv4_l3fwd_lpm_lookup_struct[socketid] == NULL) + rte_exit(EXIT_FAILURE, + "Unable to create the l3fwd LPM table on socket %d\n", + socketid); + + /* populate the LPM table */ + for (i = 0; i < RTE_DIM(ipv4_l3fwd_lpm_route_array); i++) { + struct in_addr in; + + /* skip unused ports */ + if ((1 << ipv4_l3fwd_lpm_route_array[i].if_out & + enabled_port_mask) == 0) + continue; + + ret = rte_lpm_add(ipv4_l3fwd_lpm_lookup_struct[socketid], + ipv4_l3fwd_lpm_route_array[i].ip, + ipv4_l3fwd_lpm_route_array[i].depth, + ipv4_l3fwd_lpm_route_array[i].if_out); + + if (ret < 0) { + rte_exit(EXIT_FAILURE, + "Unable to add entry %u to the l3fwd LPM table on socket %d\n", + i, socketid); + } + + in.s_addr = htonl(ipv4_l3fwd_lpm_route_array[i].ip); + printf("LPM: Adding route %s / %d (%d)\n", + inet_ntop(AF_INET, &in, abuf, sizeof(abuf)), + ipv4_l3fwd_lpm_route_array[i].depth, + ipv4_l3fwd_lpm_route_array[i].if_out); + } + + /* create the LPM6 table */ + snprintf(s, sizeof(s), "IPV6_L3FWD_LPM_%d", socketid); + + config.max_rules = IPV6_L3FWD_LPM_MAX_RULES; + config.number_tbl8s = IPV6_L3FWD_LPM_NUMBER_TBL8S; + config.flags = 0; + ipv6_l3fwd_lpm_lookup_struct[socketid] = rte_lpm6_create(s, socketid, + &config); + if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL) + rte_exit(EXIT_FAILURE, + "Unable to create the l3fwd LPM table on socket %d\n", + socketid); + + /* populate the LPM table */ + for (i = 0; i < RTE_DIM(ipv6_l3fwd_lpm_route_array); i++) { + + /* skip unused ports */ + if ((1 << ipv6_l3fwd_lpm_route_array[i].if_out & + enabled_port_mask) == 0) + continue; + + ret = rte_lpm6_add(ipv6_l3fwd_lpm_lookup_struct[socketid], + ipv6_l3fwd_lpm_route_array[i].ip, + ipv6_l3fwd_lpm_route_array[i].depth, + ipv6_l3fwd_lpm_route_array[i].if_out); + + if (ret < 0) { + rte_exit(EXIT_FAILURE, + "Unable to add entry %u to the l3fwd LPM table on socket %d\n", + i, socketid); + } + + printf("LPM: Adding route %s / %d (%d)\n", + inet_ntop(AF_INET6, ipv6_l3fwd_lpm_route_array[i].ip, + abuf, sizeof(abuf)), + ipv6_l3fwd_lpm_route_array[i].depth, + ipv6_l3fwd_lpm_route_array[i].if_out); + } +} + +int +lpm_check_ptype(int portid) +{ + int i, ret; + int ptype_l3_ipv4 = 0, ptype_l3_ipv6 = 0; + uint32_t ptype_mask = RTE_PTYPE_L3_MASK; + + ret = rte_eth_dev_get_supported_ptypes(portid, ptype_mask, NULL, 0); + if (ret <= 0) + return 0; + + uint32_t ptypes[ret]; + + ret = rte_eth_dev_get_supported_ptypes(portid, ptype_mask, ptypes, ret); + for (i = 0; i < ret; ++i) { + if (ptypes[i] & RTE_PTYPE_L3_IPV4) + ptype_l3_ipv4 = 1; + if (ptypes[i] & RTE_PTYPE_L3_IPV6) + ptype_l3_ipv6 = 1; + } + + if (ptype_l3_ipv4 == 0) + printf("port %d cannot parse RTE_PTYPE_L3_IPV4\n", portid); + + if (ptype_l3_ipv6 == 0) + printf("port %d cannot parse RTE_PTYPE_L3_IPV6\n", portid); + + if (ptype_l3_ipv4 && ptype_l3_ipv6) + return 1; + + return 0; + +} + +static inline void +lpm_parse_ptype(struct rte_mbuf *m) +{ + struct rte_ether_hdr *eth_hdr; + uint32_t packet_type = RTE_PTYPE_UNKNOWN; + uint16_t ether_type; + + eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *); + ether_type = eth_hdr->ether_type; + if (ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)) + packet_type |= RTE_PTYPE_L3_IPV4_EXT_UNKNOWN; + else if (ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6)) + packet_type |= RTE_PTYPE_L3_IPV6_EXT_UNKNOWN; + + m->packet_type = packet_type; +} + +uint16_t +lpm_cb_parse_ptype(uint16_t port __rte_unused, uint16_t queue __rte_unused, + struct rte_mbuf *pkts[], uint16_t nb_pkts, + uint16_t max_pkts __rte_unused, + void *user_param __rte_unused) +{ + unsigned int i; + + if (unlikely(nb_pkts == 0)) + return nb_pkts; + rte_prefetch0(rte_pktmbuf_mtod(pkts[0], struct ether_hdr *)); + for (i = 0; i < (unsigned int) (nb_pkts - 1); ++i) { + rte_prefetch0(rte_pktmbuf_mtod(pkts[i+1], + struct ether_hdr *)); + lpm_parse_ptype(pkts[i]); + } + lpm_parse_ptype(pkts[i]); + + return nb_pkts; +} + +/* Return ipv4/ipv6 lpm fwd lookup struct. */ +void * +lpm_get_ipv4_l3fwd_lookup_struct(const int socketid) +{ + return ipv4_l3fwd_lpm_lookup_struct[socketid]; +} + +void * +lpm_get_ipv6_l3fwd_lookup_struct(const int socketid) +{ + return ipv6_l3fwd_lpm_lookup_struct[socketid]; +} diff --git a/examples/l3fwd-regex/l3fwd_regex.c b/examples/l3fwd-regex/l3fwd_regex.c new file mode 100644 index 000000000..c24cb6d17 --- /dev/null +++ b/examples/l3fwd-regex/l3fwd_regex.c @@ -0,0 +1,487 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2020 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "l3fwd.h" +#include "l3fwd_regex.h" + +#define REGEX_OPS_DATA_SIZE (0x80 +\ + 254*sizeof(struct rte_regexdev_match)) +/* The optimum size (in terms of memory usage) for a mempool + * is when n is a power of two minus one: n = (2^q - 1). + */ +#define REGEX_RULE_FILE_GROUP_ID_STR "subset_id" +#define REGEX_RULE_FILE_GROUP_ID_LEN 9 + +#define test_bitmap(i, val) (val & (1ull << i)) +#define REGEX_MOD_INC(i, l) ((i) == (l - 1) ? (i) = 0 : (i)++) + +#define REGEX_DEBUG(fmt, args...) \ + do {\ + if (unlikely(conf->debug_print))\ + printf("regex %d:"fmt, rte_lcore_id(), ##args);\ + } while (0) +#define REGEX_LOG(fmt, args...) printf("regex %d:"fmt, rte_lcore_id(), ##args) + +#define REGEX_ERR(fmt, args...) printf("error %d:"fmt, rte_lcore_id(), ##args) + +const char * +regex_dev_capa_strings[] = { + [0] = "compilation", +}; + + +const char * +rule_flags_strings[] = { + [0] = "ALLOW_EMPTY", + [1] = "ANCHORED", + [2] = "CASELESS", + [3] = "DOTALL", + [4] = "DUPNAMES", + [5] = "EXTENDED", + [6] = "MATCH_UNSET_BACKREF", + [7] = "MULTILINE", + [8] = "NO_AUTO_CAPTURE", + [9] = "UCP", + [10] = "UNGREEDY", + [11] = "UTF", + [12] = "BACKSLASH", +}; + +struct regex_rule_db_entry { + uint8_t type; + uint32_t addr; + uint64_t value; +}; + +struct regex_rule_db { + uint32_t version; + uint32_t revision; + uint32_t number_of_entries; + struct regex_rule_db_entry *entries; +}; + +struct regex_stats { + uint64_t matches; +}; + +struct regex_conf { + uint32_t rule_db_len; + char *rule_db; + uint8_t debug_print; + uint8_t nb_lcore; + uint8_t drop_on_match; +}; + +struct regex_lcore_conf { + uint16_t dev_id; + uint16_t qp_id; + struct rte_regex_ops **ops; + struct rte_regex_ops **ops_pool; +}; + +struct regex_lcore_params { + uint32_t ops_head; + uint32_t ops_tail; + uint32_t ops_avail; + struct regex_stats stats; +}; + +static struct regex_lcore_params regex_lcore_params[RTE_MAX_LCORE]; +static struct regex_lcore_conf regex_lcore_conf[RTE_MAX_LCORE]; + +struct regex_conf conf[] = { + { + .rule_db_len = 0, + .rule_db = NULL, + .debug_print = 0, + .nb_lcore = 0, + .drop_on_match = 0, + } +}; + +int +regex_read_rule_db_file(char *filename) +{ + uint32_t new_len; + long buf_len; + + FILE *fp = fopen(filename, "rb"); + if (fp == NULL) { + printf("Error opening file\n"); + return -EIO; + } + if (fseek(fp, 0L, SEEK_END) == 0) { + buf_len = ftell(fp); + if (buf_len == -1) + goto error; + conf->rule_db = rte_malloc(NULL, sizeof(char) * (buf_len + 1), + 0); + if (conf->rule_db == NULL) + goto error; + + if (fseek(fp, 0L, SEEK_SET) != 0) + goto error; + new_len = fread(conf->rule_db, sizeof(char), buf_len, fp); + if (new_len != buf_len) + goto error; + } else + goto error; + + fclose(fp); + conf->rule_db_len = buf_len; + + return 0; +error: + if (fp) + fclose(fp); + if (conf->rule_db) + rte_free(conf->rule_db); + return -EIO; +} + +void +regex_debug_enable(void) +{ + conf->debug_print = 1; +} + +void +regex_drop_on_match(void) +{ + conf->drop_on_match = 1; +} + +static inline int +regex_opspool_get_bulk(uint32_t lcore, struct rte_regex_ops **ops, uint32_t n) +{ + struct rte_regex_ops **ops_pool; + uint32_t i, ops_head; + + ops_pool = regex_lcore_conf[lcore].ops_pool; + ops_head = regex_lcore_params[lcore].ops_head; + + if (regex_lcore_params[lcore].ops_avail < n) { + REGEX_LOG("cannot allocate ops buffer\n"); + return 0; + } + + for (i = 0; i < n; i++) { + ops[i] = ops_pool[ops_head]; + REGEX_MOD_INC(ops_head, REGEX_NB_OPS); + } + + regex_lcore_params[lcore].ops_avail -= n; + regex_lcore_params[lcore].ops_head = ops_head; + return n; +} + +static inline void +regex_opspool_put_bulk(uint32_t lcore, struct rte_regex_ops **ops, uint32_t n) +{ + struct rte_regex_ops **ops_pool; + uint32_t i, ops_tail; + + ops_pool = regex_lcore_conf[lcore].ops_pool; + ops_tail = regex_lcore_params[lcore].ops_tail; + for (i = 0; i < n; i++) { + if (ops_pool[ops_tail] != ops[i]) { + REGEX_ERR("ops pool out of sync\n" + "ops_pool[%d] = %p\n" + "ops[%d] = %p\n" + "exiting...\n", ops_tail, + ops_pool[ops_tail], i, ops[i]); + force_quit = true; + return; + } + ops_pool[ops_tail] = ops[i]; + REGEX_MOD_INC(ops_tail, REGEX_NB_OPS); + } + regex_lcore_params[lcore].ops_avail += n; + regex_lcore_params[lcore].ops_tail = ops_tail; +} + +static inline void +regex_opspool_put(uint32_t lcore, struct rte_regex_ops *ops) +{ + struct rte_regex_ops **ops_pool; + uint32_t ops_tail; + + ops_pool = regex_lcore_conf[lcore].ops_pool; + ops_tail = regex_lcore_params[lcore].ops_tail; + if (ops_pool[ops_tail] != ops) { + REGEX_ERR("ops pool out of sync\n" + "ops_pool[%d] = %p\n" + "ops = %p\n" + "exiting...\n", ops_tail, + ops_pool[ops_tail], ops); + force_quit = true; + return; + } + ops_pool[ops_tail] = ops; + REGEX_MOD_INC(ops_tail, REGEX_NB_OPS); + regex_lcore_params[lcore].ops_avail++; + regex_lcore_params[lcore].ops_tail = ops_tail; +} + +static inline uint32_t +regex_fill_ops(uint32_t lcore, struct rte_regex_ops **ops, uint16_t nb_ops, + struct rte_mbuf **pkts_burst) +{ + struct rte_mbuf *mbuf; + uint32_t i; + int ret; + + ret = regex_opspool_get_bulk(lcore, ops, nb_ops); + if (unlikely(!ret)) { + REGEX_LOG("cannot allocate ops buffer\n"); + return 0; + } + + for (i = 0; i < nb_ops; i++) { + + mbuf = pkts_burst[i]; + if (unlikely(mbuf == NULL)) { + REGEX_LOG("Cannot allocate more mbuf, %d allocated\n", + i); + regex_opspool_put(lcore, ops[i]); + return i; + } + + ops[i]->mbuf = mbuf; + ops[i]->user_ptr = mbuf; + ops[i]->req_flags = 0; + ops[i]->group_id0 = 1; + ops[i]->group_id1 = 0; + ops[i]->group_id2 = 0; + ops[i]->group_id3 = 0; + } + + return i; +} + +static inline void +regex_check_match(struct rte_regex_ops **ops, uint32_t deq_cnt, + struct rte_mbuf **pkts_burst, uint32_t lcore) +{ + uint32_t i; + + for (i = 0; i < deq_cnt; i++) { + pkts_burst[i] = ops[i]->user_ptr; + if (ops[i]->nb_matches != 0) { + REGEX_DEBUG("op %d matches %d\n", + i, ops[i]->nb_matches); + regex_lcore_params[lcore].stats.matches++; + /* mark packet to be dropped + * in l3fwd_lpm_simple_forward() non-IP packets are + * dropped. + */ + if (conf->drop_on_match) + pkts_burst[i]->packet_type = RTE_PTYPE_UNKNOWN; + } + } +} + +uint32_t +regex_enqueue_burst_ops(int dev_id, uint32_t lcore_id, uint16_t qp_id, + struct rte_mbuf **pkts_burst, uint16_t nb_pkts) +{ + uint32_t nb_ops, ret; + struct rte_regex_ops **ops = regex_lcore_conf[lcore_id].ops; + + nb_ops = regex_fill_ops(lcore_id, ops, nb_pkts, pkts_burst); + + if (unlikely(nb_ops < nb_pkts)) + return 0; + + REGEX_DEBUG("Enqueue single burst %d\n", nb_ops); + ret = rte_regexdev_enqueue_burst(dev_id, qp_id, ops, nb_ops); + if (unlikely(ret != nb_ops)) { + REGEX_ERR("rte_regexdev_enqueue_burst(): Failed, %d/%d enqueue\n", + ret, nb_ops); + return 0; + } + return nb_ops; +} + +uint32_t +regex_dequeue_burst_ops(int dev_id, uint32_t lcore_id, uint16_t qp_id, + struct rte_mbuf **pkts_burst, uint16_t nb_pkts) +{ + struct rte_regex_ops **ops = regex_lcore_conf[lcore_id].ops; + uint32_t deq_cnt; + + deq_cnt = rte_regexdev_dequeue_burst(dev_id, qp_id, + ops, nb_pkts); + REGEX_DEBUG("dequeue burst %d\n", deq_cnt); + if (deq_cnt) + regex_check_match(ops, deq_cnt, pkts_burst, lcore_id); + + + regex_opspool_put_bulk(lcore_id, ops, deq_cnt); + + return deq_cnt; +} + + +void +regex_stats_print(uint32_t lcore) +{ + REGEX_LOG("Number of matches: %"PRIu64"\n", + regex_lcore_params[lcore].stats.matches); +} + +void +regex_dev_uninit(uint32_t dev_id) +{ + if (rte_regexdev_close(dev_id) < 0) + printf("rte_regexdev_close(dev %d): Failed\n", dev_id); +} + +void +regex_lcore_uninit(uint32_t lcore_id) +{ + uint32_t i; + if (regex_lcore_conf[lcore_id].ops_pool) { + for (i = 0; i < REGEX_NB_OPS; i++) { + if (regex_lcore_conf[lcore_id].ops_pool[i]) + rte_free( + regex_lcore_conf[lcore_id].ops_pool[i]); + } + rte_free(regex_lcore_conf[lcore_id].ops_pool); + } + if (regex_lcore_conf[lcore_id].ops) + rte_free(regex_lcore_conf[lcore_id].ops); +} + +void +regex_mem_free(void) +{ + if (conf->rule_db) + rte_free(conf->rule_db); +} + +int +regex_dev_init(uint32_t dev_id, uint16_t nb_queue_pairs) +{ + struct rte_regexdev_qp_conf qp_conf; + struct rte_regexdev_info dev_info; + struct rte_regexdev_config cfg; + uint32_t i; + int ret; + + printf("info: dev id is %d\n", dev_id); + ret = rte_regexdev_info_get(dev_id, &dev_info); + if (ret < 0) { + printf("rte_regexdev_info_get(): Failed\n"); + return ret; + } + printf("get info:\n"); + printf("driver_name %s\n", dev_info.driver_name); + printf("max_matches %d\n", dev_info.max_matches); + printf("max_queue_pairs %d\n", dev_info.max_queue_pairs); + printf("max_payloadsize %d\n", dev_info.max_payload_size); + printf("max_rules_per_group %d\n", + dev_info.max_rules_per_group); + printf("max_groups %d\n", dev_info.max_groups); + printf("regex_dev_capa 0x%x\n", dev_info.regexdev_capa); + for (i = 0; i < 32; i++) + if (test_bitmap(i, dev_info.regexdev_capa)) + printf("%d %s\n", i, + regex_dev_capa_strings[i]); + printf("rule_flags 0x%lx\n", dev_info.rule_flags); + for (i = 0; i < 64; i++) + if (test_bitmap(i, dev_info.rule_flags)) + printf("%d %s\n", i, + rule_flags_strings[i]); + + cfg.dev_cfg_flags = 0; + cfg.nb_max_matches = dev_info.max_matches; + cfg.nb_queue_pairs = nb_queue_pairs; + cfg.nb_rules_per_group = dev_info.max_rules_per_group; + cfg.nb_groups = dev_info.max_groups; + cfg.rule_db = conf->rule_db; + cfg.rule_db_len = conf->rule_db_len; + ret = rte_regexdev_configure(dev_id, &cfg); + if (ret < 0) { + printf("rte_regexdev_configure(): Failed\n"); + return ret; + } + + qp_conf.qp_conf_flags = 0; + qp_conf.nb_desc = 8192; + qp_conf.cb = NULL; + for (i = 0; i < nb_queue_pairs; i++) { + ret = rte_regexdev_queue_pair_setup(dev_id, i, + &qp_conf); + if (ret < 0) { + printf("rte_regexdev_queue_pair_setup(): Failed for queue %d\n", + i); + return ret; + } + } + ret = rte_regexdev_start(dev_id); + if (ret < 0) { + printf("rte_regexdev_start(): Failed\n"); + return ret; + } + return 0; +} + +int +regex_lcore_init(uint32_t lcore_id, uint32_t dev_id, uint32_t qp_id) +{ + uint32_t i; + + printf("%s lcore %u dev_id %d qp %d\n", __func__, + lcore_id, dev_id, qp_id); + + regex_lcore_conf[lcore_id].qp_id = qp_id; + regex_lcore_conf[lcore_id].dev_id = dev_id; + + memset(®ex_lcore_params[lcore_id].stats, 0, + sizeof(struct regex_stats)); + + regex_lcore_conf[lcore_id].ops = rte_malloc("regex_ops", + REGEX_NB_OPS*sizeof(struct rte_regex_ops *), + 0); + if (regex_lcore_conf[lcore_id].ops == NULL) { + REGEX_ERR("cannot allocate ops memory"); + return -1; + } + + regex_lcore_conf[lcore_id].ops_pool = rte_malloc("regex_ops_pool", + REGEX_NB_OPS*sizeof(struct rte_regex_ops *), + 0); + if (regex_lcore_conf[lcore_id].ops_pool == NULL) { + REGEX_ERR("cannot allocate ops pool memory"); + return -1; + } + for (i = 0; i < REGEX_NB_OPS; i++) { + regex_lcore_conf[lcore_id].ops_pool[i] = rte_malloc("", + REGEX_OPS_DATA_SIZE, 0); + if (regex_lcore_conf[lcore_id].ops_pool[i] == NULL) { + REGEX_ERR("cannot allocate ops memory"); + return -1; + } + } + regex_lcore_params[lcore_id].ops_head = 0; + regex_lcore_params[lcore_id].ops_tail = 0; + regex_lcore_params[lcore_id].ops_avail = REGEX_NB_OPS; + conf->nb_lcore++; + + return 0; +} diff --git a/examples/l3fwd-regex/l3fwd_regex.h b/examples/l3fwd-regex/l3fwd_regex.h new file mode 100644 index 000000000..d01a8e6e1 --- /dev/null +++ b/examples/l3fwd-regex/l3fwd_regex.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2020 Marvell International Ltd. + */ + +#ifndef __L3FWD_REGEX_H__ +#define __L3FWD_REGEX_H__ + +#define REGEX_NB_OPS (8192) + +int +regex_dev_init(uint32_t dev_id, uint16_t nb_queue_pairs); + +int +regex_lcore_init(uint32_t lcore_id, uint32_t dev_id, uint32_t qp_id); + +uint32_t +regex_enqueue_burst_ops(int dev_id, uint32_t lcore_id, uint16_t qp_id, + struct rte_mbuf **pkts_burst, uint16_t nb_pkts); +uint32_t +regex_dequeue_burst_ops(int dev_id, uint32_t lcore_id, uint16_t qp_id, + struct rte_mbuf **pkts_burst, uint16_t nb_pkts); +int +regex_read_rule_db_file(char *filename); +void +regex_debug_enable(void); +void +regex_drop_on_match(void); + +void +regex_stats_print(uint32_t lcore); +void +regex_dev_uninit(uint32_t dev_id); +void +regex_lcore_uninit(uint32_t lcore_id); +void +regex_mem_free(void); + +#endif /* __L3FWD_REGEX_H__ */ diff --git a/examples/l3fwd-regex/main.c b/examples/l3fwd-regex/main.c new file mode 100644 index 000000000..eed3a1d32 --- /dev/null +++ b/examples/l3fwd-regex/main.c @@ -0,0 +1,1117 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2016 Intel Corporation + * Copyright(C) 2020 Marvell International Ltd. + */ + +#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 +#include +#include +#include + +#include +#include + +#include "l3fwd.h" +#include "l3fwd_regex.h" + +#define MAX_TX_QUEUE_PER_PORT RTE_MAX_ETHPORTS +#define MAX_RX_QUEUE_PER_PORT 128 + +#define MAX_LCORE_PARAMS 1024 + +/* Static global variables used within this file. */ +static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT; +static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT; + +/**< Ports set in promiscuous mode off by default. */ +static int promiscuous_on; + +/* Global variables. */ + +static int numa_on = 1; /**< NUMA is enabled by default. */ +static int parse_ptype; /**< Parse packet type using rx callback, and */ + /**< disabled by default */ +static int per_port_pool; /**< Use separate buffer pools per port; disabled */ + /**< by default */ + +volatile bool force_quit; + +/* ethernet addresses of ports */ +uint64_t dest_eth_addr[RTE_MAX_ETHPORTS]; +struct rte_ether_addr ports_eth_addr[RTE_MAX_ETHPORTS]; + +xmm_t val_eth[RTE_MAX_ETHPORTS]; + +/* mask of enabled ports */ +uint32_t enabled_port_mask; + +struct lcore_conf lcore_conf[RTE_MAX_LCORE]; + +struct lcore_params { + uint16_t port_id; + uint8_t queue_id; + uint8_t lcore_id; + uint8_t regex_dev_id; + uint16_t regex_qp_id; +} __rte_cache_aligned; + + +static struct lcore_params lcore_params_array[MAX_LCORE_PARAMS]; +static struct lcore_params lcore_params_array_default[] = { + {0, 0, 1, 0, 0}, +}; + + +static struct lcore_params *lcore_params = lcore_params_array_default; +static uint16_t nb_lcore_params = sizeof(lcore_params_array_default) / + sizeof(lcore_params_array_default[0]); + +static struct rte_eth_conf port_conf = { + .rxmode = { + .mq_mode = ETH_MQ_RX_RSS, + .max_rx_pkt_len = RTE_ETHER_MAX_LEN, + .split_hdr_size = 0, + .offloads = 0, + }, + .rx_adv_conf = { + .rss_conf = { + .rss_key = NULL, + .rss_hf = ETH_RSS_IP, + }, + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, +}; + +static struct rte_mempool *pktmbuf_pool[RTE_MAX_ETHPORTS][NB_SOCKETS]; +static uint8_t lkp_per_socket[NB_SOCKETS]; + +struct l3fwd_lkp_mode { + void (*setup)(int socketid); + int (*check_ptype)(int portid); + rte_rx_callback_fn cb_parse_ptype; + int (*main_loop)(void *arg); + void* (*get_ipv4_lookup_struct)(int socketid); + void* (*get_ipv6_lookup_struct)(int socketid); +}; + +static struct l3fwd_lkp_mode l3fwd_lkp; + +static struct l3fwd_lkp_mode l3fwd_lpm_lkp = { + .setup = setup_lpm, + .check_ptype = lpm_check_ptype, + .cb_parse_ptype = lpm_cb_parse_ptype, + .main_loop = lpm_main_loop, + .get_ipv4_lookup_struct = lpm_get_ipv4_l3fwd_lookup_struct, + .get_ipv6_lookup_struct = lpm_get_ipv6_l3fwd_lookup_struct, +}; + +/* + * Setup lookup methods for forwarding. + * Currently longest-prefix-match is supported. + */ +static void +setup_l3fwd_lookup_tables(void) +{ + /* Setup LPM lookup functions. */ + l3fwd_lkp = l3fwd_lpm_lkp; +} + +static int +check_lcore_params(void) +{ + uint8_t queue, lcore, regex_queue, regex_dev; + struct rte_regexdev_info dev_info; + uint16_t i; + int socketid; + + for (i = 0; i < nb_lcore_params; ++i) { + queue = lcore_params[i].queue_id; + if (queue >= MAX_RX_QUEUE_PER_PORT) { + printf("invalid queue number: %hhu\n", queue); + return -1; + } + lcore = lcore_params[i].lcore_id; + if (!rte_lcore_is_enabled(lcore)) { + printf("error: lcore %hhu is not enabled in lcore mask\n", + lcore); + return -1; + } + socketid = rte_lcore_to_socket_id(lcore); + if ((socketid != 0) && (numa_on == 0)) { + printf("warning: lcore %hhu is on socket %d with numa off\n", + lcore, socketid); + } + regex_dev = lcore_params[i].regex_dev_id; + if ((rte_regexdev_is_valid_dev(regex_dev) == 0)) { + printf("invalid regex dev number: %hhu\n", regex_dev); + return -1; + } + regex_queue = lcore_params[i].regex_qp_id; + rte_regexdev_info_get(regex_dev, &dev_info); + if (regex_queue >= dev_info.max_queue_pairs) { + printf("invalid regex queue number: %hhu\n", + regex_queue); + return -1; + } + } + return 0; +} + +static int +check_port_config(void) +{ + uint16_t portid; + uint16_t i; + + for (i = 0; i < nb_lcore_params; ++i) { + portid = lcore_params[i].port_id; + if ((enabled_port_mask & (1 << portid)) == 0) { + printf("port %u is not enabled in port mask\n", portid); + return -1; + } + if (!rte_eth_dev_is_valid_port(portid)) { + printf("port %u is not present on the board\n", portid); + return -1; + } + } + return 0; +} + +static uint8_t +get_port_n_rx_queues(const uint16_t port) +{ + int queue = -1; + uint16_t i; + + for (i = 0; i < nb_lcore_params; ++i) { + if (lcore_params[i].port_id == port) { + if (lcore_params[i].queue_id == queue+1) + queue = lcore_params[i].queue_id; + else + rte_exit(EXIT_FAILURE, "queue ids of the port %d must be" + " in sequence and must start with 0\n", + lcore_params[i].port_id); + } + } + return (uint8_t)(++queue); +} + +static uint8_t +get_regex_dev_n_queues(const uint8_t dev_id) +{ + int queue = -1; + uint16_t i; + + for (i = 0; i < nb_lcore_params; ++i) { + if (lcore_params[i].regex_dev_id == dev_id) { + if (lcore_params[i].regex_qp_id == queue+1) + queue = lcore_params[i].regex_qp_id; + else + rte_exit(EXIT_FAILURE, "regex queue ids of a Regex device %d must be" + " in sequence and must start with 0\n", + lcore_params[i].regex_dev_id); + } + } + return (uint8_t)(++queue); +} + +static int +init_lcore_rx_queues(void) +{ + uint16_t i, nb_rx_queue; + uint8_t lcore; + + for (i = 0; i < nb_lcore_params; ++i) { + lcore = lcore_params[i].lcore_id; + nb_rx_queue = lcore_conf[lcore].n_rx_queue; + if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) { + printf("error: too many queues (%u) for lcore: %u\n", + (uint16_t)nb_rx_queue + 1, (uint8_t)lcore); + return -1; + } + lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id = + lcore_params[i].port_id; + lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id = + lcore_params[i].queue_id; + lcore_conf[lcore].regex_dev_id = lcore_params[i].regex_dev_id; + lcore_conf[lcore].regex_qp_id = lcore_params[i].regex_qp_id; + lcore_conf[lcore].n_rx_queue++; + } + return 0; +} + +/* display usage */ +static void +print_usage(const char *prgname) +{ + fprintf(stderr, "%s [EAL options] --" + " -p PORTMASK" + " [-P]" + " --config (port,queue,lcore,regexdev,regex q)[,(port,queue,lcore,regexdev,regex q)]" + " --regex-rule-db-file FILENAME" + " [--eth-dest=X,MM:MM:MM:MM:MM:MM]" + " [--enable-jumbo [--max-pkt-len PKTLEN]]" + " [--no-numa]" + " [--parse-ptype]" + " [--per-port-pool]" + " [--regex-drop]" + " [--regex-debug]\n\n" + + " -p PORTMASK: Hexadecimal bitmask of ports to configure\n" + " -P : Enable promiscuous mode\n" + " --config (port,queue,lcore,regexdev,regex q): Rx queue configuration\n" + " --regex-rule-db-file FILENAME prebuilt rule database for regex device.\n" + " --eth-dest=X,MM:MM:MM:MM:MM:MM: Ethernet destination for port X\n" + " --enable-jumbo: Enable jumbo frames\n" + " --max-pkt-len: Under the premise of enabling jumbo,\n" + " maximum packet length in decimal (64-9600)\n" + " --no-numa: Disable numa awareness\n" + " --parse-ptype: Set to use software to analyze packet type\n" + " --per-port-pool: Use separate buffer pool per port\n" + " --regex-drop: Enable regex drop on match\n" + " --regex-debug: Enable regex debug printing\n", + prgname); +} + +static int +parse_max_pkt_len(const char *pktlen) +{ + char *end = NULL; + unsigned long len; + + /* parse decimal string */ + len = strtoul(pktlen, &end, 10); + if ((pktlen[0] == '\0') || (end == NULL) || (*end != '\0')) + return -1; + + if (len == 0) + return -1; + + return len; +} + +static int +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 int +parse_config(const char *q_arg) +{ + char s[256]; + const char *p, *p0 = q_arg; + char *end; + enum fieldnames { + FLD_PORT = 0, + FLD_QUEUE, + FLD_LCORE, + FLD_REGEX_DEV, + FLD_REGEX_QUEUE, + _NUM_FLD + }; + unsigned long int_fld[_NUM_FLD]; + char *str_fld[_NUM_FLD]; + int i; + unsigned int size; + + nb_lcore_params = 0; + + while ((p = strchr(p0, '(')) != NULL) { + ++p; + p0 = strchr(p, ')'); + if (p0 == NULL) + return -1; + + size = p0 - p; + if (size >= sizeof(s)) + return -1; + + snprintf(s, sizeof(s), "%.*s", size, p); + if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') != + _NUM_FLD) + return -1; + for (i = 0; i < _NUM_FLD; i++) { + errno = 0; + int_fld[i] = strtoul(str_fld[i], &end, 0); + if (errno != 0 || end == str_fld[i] || int_fld[i] > 255) + return -1; + } + if (nb_lcore_params >= MAX_LCORE_PARAMS) { + printf("exceeded max number of lcore params: %hu\n", + nb_lcore_params); + return -1; + } + lcore_params_array[nb_lcore_params].port_id = + (uint8_t)int_fld[FLD_PORT]; + lcore_params_array[nb_lcore_params].queue_id = + (uint8_t)int_fld[FLD_QUEUE]; + lcore_params_array[nb_lcore_params].lcore_id = + (uint8_t)int_fld[FLD_LCORE]; + lcore_params_array[nb_lcore_params].regex_dev_id = + (uint8_t)int_fld[FLD_REGEX_DEV]; + lcore_params_array[nb_lcore_params].regex_qp_id = + (uint8_t)int_fld[FLD_REGEX_QUEUE]; + ++nb_lcore_params; + } + lcore_params = lcore_params_array; + return 0; +} + +static void +parse_eth_dest(const char *optarg) +{ + uint16_t portid; + char *port_end; + uint8_t c, *dest, peer_addr[6]; + + errno = 0; + portid = strtoul(optarg, &port_end, 10); + if (errno != 0 || port_end == optarg || *port_end++ != ',') + rte_exit(EXIT_FAILURE, + "Invalid eth-dest: %s", optarg); + if (portid >= RTE_MAX_ETHPORTS) + rte_exit(EXIT_FAILURE, + "eth-dest: port %d >= RTE_MAX_ETHPORTS(%d)\n", + portid, RTE_MAX_ETHPORTS); + + if (cmdline_parse_etheraddr(NULL, port_end, + &peer_addr, sizeof(peer_addr)) < 0) + rte_exit(EXIT_FAILURE, + "Invalid ethernet address: %s\n", + port_end); + dest = (uint8_t *)&dest_eth_addr[portid]; + for (c = 0; c < 6; c++) + dest[c] = peer_addr[c]; + *(uint64_t *)(val_eth + portid) = dest_eth_addr[portid]; +} + +#define MAX_JUMBO_PKT_LEN 9600 + +static const char short_options[] = + "p:" /* portmask */ + "P" /* promiscuous */ + ; + +#define CMD_LINE_OPT_CONFIG "config" +#define CMD_LINE_OPT_ETH_DEST "eth-dest" +#define CMD_LINE_OPT_NO_NUMA "no-numa" +#define CMD_LINE_OPT_ENABLE_JUMBO "enable-jumbo" +#define CMD_LINE_OPT_PARSE_PTYPE "parse-ptype" +#define CMD_LINE_OPT_PER_PORT_POOL "per-port-pool" +#define CMD_LINE_OPT_REGEX_RULE_DB_FILE "regex-rule-db-file" +#define CMD_LINE_OPT_REGEX_RULE_FILE "regex-rule-file" +#define CMD_LINE_OPT_REGEX_DEBUG "regex-debug" +#define CMD_LINE_OPT_REGEX_DROP "regex-drop" + +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, + CMD_LINE_OPT_CONFIG_NUM, + CMD_LINE_OPT_ETH_DEST_NUM, + CMD_LINE_OPT_NO_NUMA_NUM, + CMD_LINE_OPT_ENABLE_JUMBO_NUM, + CMD_LINE_OPT_PARSE_PTYPE_NUM, + CMD_LINE_OPT_PARSE_PER_PORT_POOL, + CMD_LINE_OPT_REGEX_RULE_DB_FILE_NUM, + CMD_LINE_OPT_REGEX_DEBUG_NUM, + CMD_LINE_OPT_REGEX_DROP_NUM +}; + +static const struct option lgopts[] = { + {CMD_LINE_OPT_CONFIG, 1, 0, CMD_LINE_OPT_CONFIG_NUM}, + {CMD_LINE_OPT_ETH_DEST, 1, 0, CMD_LINE_OPT_ETH_DEST_NUM}, + {CMD_LINE_OPT_NO_NUMA, 0, 0, CMD_LINE_OPT_NO_NUMA_NUM}, + {CMD_LINE_OPT_ENABLE_JUMBO, 0, 0, CMD_LINE_OPT_ENABLE_JUMBO_NUM}, + {CMD_LINE_OPT_PARSE_PTYPE, 0, 0, CMD_LINE_OPT_PARSE_PTYPE_NUM}, + {CMD_LINE_OPT_PER_PORT_POOL, 0, 0, CMD_LINE_OPT_PARSE_PER_PORT_POOL}, + {CMD_LINE_OPT_REGEX_RULE_DB_FILE, 1, 0, + CMD_LINE_OPT_REGEX_RULE_DB_FILE_NUM}, + {CMD_LINE_OPT_REGEX_DEBUG, 0, 0, CMD_LINE_OPT_REGEX_DEBUG_NUM}, + {CMD_LINE_OPT_REGEX_DROP, 0, 0, CMD_LINE_OPT_REGEX_DROP_NUM}, + {NULL, 0, 0, 0} +}; + +/* + * This expression is used to calculate the number of mbufs needed + * depending on user input, taking into account memory for rx and + * tx hardware rings, cache per lcore and mtable per port per lcore. + * RTE_MAX is used to ensure that NB_MBUF never goes below a minimum + * value of 8192 + */ +#define NB_MBUF(nports) RTE_MAX( \ + (nports*nb_rx_queue*nb_rxd + \ + nports*nb_lcores*MAX_PKT_BURST + \ + nports*n_tx_queue*nb_txd + \ + nb_lcores*MEMPOOL_CACHE_SIZE), \ + 8192U) + +/* Parse the argument given in the command line of the application */ +static int +parse_args(int argc, char **argv) +{ + int opt, ret; + char **argvopt; + int option_index; + char *prgname = argv[0]; + argvopt = argv; + + /* Error or normal output strings. */ + while ((opt = getopt_long(argc, argvopt, short_options, + lgopts, &option_index)) != EOF) { + + switch (opt) { + /* portmask */ + case 'p': + enabled_port_mask = parse_portmask(optarg); + if (enabled_port_mask == 0) { + fprintf(stderr, "Invalid portmask\n"); + print_usage(prgname); + return -1; + } + break; + + case 'P': + promiscuous_on = 1; + break; + + /* long options */ + case CMD_LINE_OPT_CONFIG_NUM: + ret = parse_config(optarg); + if (ret) { + fprintf(stderr, "Invalid config\n"); + print_usage(prgname); + return -1; + } + break; + + case CMD_LINE_OPT_ETH_DEST_NUM: + parse_eth_dest(optarg); + break; + + case CMD_LINE_OPT_NO_NUMA_NUM: + numa_on = 0; + break; + + + case CMD_LINE_OPT_ENABLE_JUMBO_NUM: { + const struct option lenopts = { + "max-pkt-len", required_argument, 0, 0 + }; + + port_conf.rxmode.offloads |= DEV_RX_OFFLOAD_JUMBO_FRAME; + port_conf.txmode.offloads |= DEV_TX_OFFLOAD_MULTI_SEGS; + + /* + * if no max-pkt-len set, use the default + * value RTE_ETHER_MAX_LEN. + */ + if (getopt_long(argc, argvopt, "", + &lenopts, &option_index) == 0) { + ret = parse_max_pkt_len(optarg); + if (ret < 64 || ret > MAX_JUMBO_PKT_LEN) { + fprintf(stderr, + "invalid maximum packet length\n"); + print_usage(prgname); + return -1; + } + port_conf.rxmode.max_rx_pkt_len = ret; + } + break; + } + + case CMD_LINE_OPT_PARSE_PTYPE_NUM: + printf("soft parse-ptype is enabled\n"); + parse_ptype = 1; + break; + + case CMD_LINE_OPT_PARSE_PER_PORT_POOL: + printf("per port buffer pool is enabled\n"); + per_port_pool = 1; + break; + case CMD_LINE_OPT_REGEX_RULE_DB_FILE_NUM: + ret = regex_read_rule_db_file(optarg); + if (ret) { + fprintf(stderr, "%s", rte_strerror(ret)); + return -1; + } + break; + case CMD_LINE_OPT_REGEX_DEBUG_NUM: + regex_debug_enable(); + break; + case CMD_LINE_OPT_REGEX_DROP_NUM: + regex_drop_on_match(); + break; + default: + print_usage(prgname); + return -1; + } + } + + if (optind >= 0) + argv[optind-1] = prgname; + + ret = optind-1; + optind = 1; /* reset getopt lib */ + return ret; +} + +static void +print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr) +{ + char buf[RTE_ETHER_ADDR_FMT_SIZE]; + rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE, eth_addr); + printf("%s%s", name, buf); +} + +int +init_mem(uint16_t portid, unsigned int nb_mbuf) +{ + struct lcore_conf *qconf; + unsigned int lcore_id; + int socketid; + char s[64]; + + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { + if (rte_lcore_is_enabled(lcore_id) == 0) + continue; + + if (numa_on) + socketid = rte_lcore_to_socket_id(lcore_id); + else + socketid = 0; + + if (socketid >= NB_SOCKETS) { + rte_exit(EXIT_FAILURE, + "Socket %d of lcore %u is out of range %d\n", + socketid, lcore_id, NB_SOCKETS); + } + + if (pktmbuf_pool[portid][socketid] == NULL) { + snprintf(s, sizeof(s), "mbuf_pool_%d:%d", + portid, socketid); + pktmbuf_pool[portid][socketid] = + rte_pktmbuf_pool_create(s, nb_mbuf, + MEMPOOL_CACHE_SIZE, 0, + RTE_MBUF_DEFAULT_BUF_SIZE, socketid); + if (pktmbuf_pool[portid][socketid] == NULL) + rte_exit(EXIT_FAILURE, + "Cannot init mbuf pool on socket %d\n", + socketid); + else + printf("Allocated mbuf pool on socket %d\n", + socketid); + + /* Setup either LPM or EM(f.e Hash). But, only once per + * available socket. + */ + if (!lkp_per_socket[socketid]) { + l3fwd_lkp.setup(socketid); + lkp_per_socket[socketid] = 1; + } + } + qconf = &lcore_conf[lcore_id]; + qconf->ipv4_lookup_struct = + l3fwd_lkp.get_ipv4_lookup_struct(socketid); + qconf->ipv6_lookup_struct = + l3fwd_lkp.get_ipv6_lookup_struct(socketid); + qconf->pkts_burst = rte_malloc("pkts_burst", + REGEX_NB_OPS*sizeof(struct rte_mbuf *), + 0); + if (qconf->pkts_burst == NULL) + rte_exit(EXIT_FAILURE, + "Cannot allocate memory for pkts_burst\n"); + } + return 0; +} + +/* Check the link status of all ports in up to 9s, and print them finally */ +static void +check_all_ports_link_status(uint32_t port_mask) +{ +#define CHECK_INTERVAL 100 /* 100ms */ +#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */ + uint16_t portid; + uint8_t count, all_ports_up, print_flag = 0; + struct rte_eth_link link; + int ret; + + printf("\nChecking link status"); + fflush(stdout); + for (count = 0; count <= MAX_CHECK_TIME; count++) { + if (force_quit) + return; + all_ports_up = 1; + RTE_ETH_FOREACH_DEV(portid) { + if (force_quit) + return; + if ((port_mask & (1 << portid)) == 0) + continue; + memset(&link, 0, sizeof(link)); + ret = rte_eth_link_get_nowait(portid, &link); + if (ret < 0) { + all_ports_up = 0; + if (print_flag == 1) + printf("Port %u link get failed: %s\n", + portid, rte_strerror(-ret)); + continue; + } + /* print link status if flag set */ + if (print_flag == 1) { + if (link.link_status) + printf( + "Port%d Link Up. Speed %u Mbps -%s\n", + portid, link.link_speed, + (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? + ("full-duplex") : ("half-duplex")); + else + printf("Port %d Link Down\n", portid); + 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) +{ + if (signum == SIGINT || signum == SIGTERM) { + printf("\n\nSignal %d received, preparing to exit...\n", + signum); + force_quit = true; + } +} + +static int +prepare_ptype_parser(uint16_t portid, uint16_t queueid) +{ + if (parse_ptype) { + printf("Port %d: softly parse packet type info\n", portid); + if (rte_eth_add_rx_callback(portid, queueid, + l3fwd_lkp.cb_parse_ptype, + NULL)) + return 1; + + printf("Failed to add rx callback: port=%d\n", portid); + return 0; + } + + if (l3fwd_lkp.check_ptype(portid)) + return 1; + + printf("port %d cannot parse packet type, please add --%s\n", + portid, CMD_LINE_OPT_PARSE_PTYPE); + return 0; +} + +static void +l3fwd_poll_resource_setup(void) +{ + uint8_t nb_rx_queue, queue, socketid; + struct rte_eth_dev_info dev_info; + uint32_t n_tx_queue, nb_lcores, nb_regex_queue; + struct rte_eth_txconf *txconf; + struct lcore_conf *qconf; + uint16_t queueid, portid; + unsigned int nb_ports; + unsigned int lcore_id, i; + int ret; + + if (check_lcore_params() < 0) + rte_exit(EXIT_FAILURE, "check_lcore_params failed\n"); + + ret = init_lcore_rx_queues(); + if (ret < 0) + rte_exit(EXIT_FAILURE, "init_lcore_rx_queues failed\n"); + + nb_ports = rte_eth_dev_count_avail(); + + if (check_port_config() < 0) + rte_exit(EXIT_FAILURE, "check_port_config failed\n"); + + nb_lcores = rte_lcore_count(); + + /* initialize all ports */ + RTE_ETH_FOREACH_DEV(portid) { + struct rte_eth_conf local_port_conf = port_conf; + + /* skip ports that are not enabled */ + if ((enabled_port_mask & (1 << portid)) == 0) { + printf("\nSkipping disabled port %d\n", portid); + continue; + } + + /* init port */ + printf("Initializing port %d ... ", portid); + fflush(stdout); + + nb_rx_queue = get_port_n_rx_queues(portid); + if (nb_rx_queue > 1) + rte_exit(EXIT_FAILURE, + "only one rx queue per (port %u) is supported (queue num %u)\n", + portid, nb_rx_queue); + n_tx_queue = nb_lcores; + if (n_tx_queue > MAX_TX_QUEUE_PER_PORT) + n_tx_queue = MAX_TX_QUEUE_PER_PORT; + printf("Creating queues: nb_rxq=%d nb_txq=%u... ", + nb_rx_queue, (uint32_t)n_tx_queue); + + ret = rte_eth_dev_info_get(portid, &dev_info); + if (ret != 0) + rte_exit(EXIT_FAILURE, + "Error during getting device (port %u) info: %s\n", + portid, strerror(-ret)); + + if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE) + local_port_conf.txmode.offloads |= + DEV_TX_OFFLOAD_MBUF_FAST_FREE; + + local_port_conf.rx_adv_conf.rss_conf.rss_hf &= + dev_info.flow_type_rss_offloads; + if (local_port_conf.rx_adv_conf.rss_conf.rss_hf != + port_conf.rx_adv_conf.rss_conf.rss_hf) { + printf("Port %u modified RSS hash function based on hardware support," + "requested:%#"PRIx64" configured:%#"PRIx64"\n", + portid, + port_conf.rx_adv_conf.rss_conf.rss_hf, + local_port_conf.rx_adv_conf.rss_conf.rss_hf); + } + + ret = rte_eth_dev_configure(portid, nb_rx_queue, + (uint16_t)n_tx_queue, &local_port_conf); + if (ret < 0) + rte_exit(EXIT_FAILURE, + "Cannot configure device: err=%d, port=%d\n", + ret, portid); + + ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd, + &nb_txd); + if (ret < 0) + rte_exit(EXIT_FAILURE, + "Cannot adjust number of descriptors: err=%d, " + "port=%d\n", ret, portid); + + ret = rte_eth_macaddr_get(portid, &ports_eth_addr[portid]); + if (ret < 0) + rte_exit(EXIT_FAILURE, + "Cannot get MAC address: err=%d, port=%d\n", + ret, portid); + + print_ethaddr(" Address:", &ports_eth_addr[portid]); + printf(", "); + print_ethaddr("Destination:", + (const struct rte_ether_addr *)&dest_eth_addr[portid]); + printf(", "); + + /* + * prepare src MACs for each port. + */ + rte_ether_addr_copy(&ports_eth_addr[portid], + (struct rte_ether_addr *)(val_eth + portid) + 1); + + /* init memory */ + if (!per_port_pool) { + /* portid = 0; this is *not* signifying the first port, + * rather, it signifies that portid is ignored. + */ + ret = init_mem(0, NB_MBUF(nb_ports)); + } else { + ret = init_mem(portid, NB_MBUF(1)); + } + if (ret < 0) + rte_exit(EXIT_FAILURE, "init_mem failed\n"); + + /* init one TX queue per couple (lcore,port) */ + queueid = 0; + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { + if (rte_lcore_is_enabled(lcore_id) == 0) + continue; + + if (numa_on) + socketid = + (uint8_t)rte_lcore_to_socket_id(lcore_id); + else + socketid = 0; + + printf("txq=%u,%d,%d ", lcore_id, queueid, socketid); + fflush(stdout); + + txconf = &dev_info.default_txconf; + txconf->offloads = local_port_conf.txmode.offloads; + ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd, + socketid, txconf); + if (ret < 0) + rte_exit(EXIT_FAILURE, + "rte_eth_tx_queue_setup: err=%d, " + "port=%d\n", ret, portid); + + qconf = &lcore_conf[lcore_id]; + qconf->tx_queue_id[portid] = queueid; + queueid++; + + qconf->tx_port_id[qconf->n_tx_port] = portid; + qconf->n_tx_port++; + } + printf("\n"); + } + + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { + if (rte_lcore_is_enabled(lcore_id) == 0) + continue; + qconf = &lcore_conf[lcore_id]; + printf("\nInitializing rx queues on lcore %u ... ", lcore_id); + fflush(stdout); + /* init RX queues */ + for (queue = 0; queue < qconf->n_rx_queue; ++queue) { + struct rte_eth_rxconf rxq_conf; + + portid = qconf->rx_queue_list[queue].port_id; + queueid = qconf->rx_queue_list[queue].queue_id; + + if (numa_on) + socketid = + (uint8_t)rte_lcore_to_socket_id(lcore_id); + else + socketid = 0; + + printf("rxq=%d,%d,%d ", portid, queueid, socketid); + fflush(stdout); + + ret = rte_eth_dev_info_get(portid, &dev_info); + if (ret != 0) + rte_exit(EXIT_FAILURE, + "Error during getting device (port %u) info: %s\n", + portid, strerror(-ret)); + + rxq_conf = dev_info.default_rxconf; + rxq_conf.offloads = port_conf.rxmode.offloads; + if (!per_port_pool) + ret = rte_eth_rx_queue_setup(portid, queueid, + nb_rxd, socketid, + &rxq_conf, + pktmbuf_pool[0][socketid]); + else + ret = rte_eth_rx_queue_setup(portid, queueid, + nb_rxd, socketid, + &rxq_conf, + pktmbuf_pool[portid][socketid]); + if (ret < 0) + rte_exit(EXIT_FAILURE, + "rte_eth_rx_queue_setup: err=%d, port=%d\n", + ret, portid); + } + } + for (i = 0; i < RTE_MAX_REGEXDEV_DEVS; i++) { + if (rte_regexdev_is_valid_dev(i) == 0) + continue; + nb_regex_queue = get_regex_dev_n_queues(i); + if (nb_regex_queue) { + ret = regex_dev_init(i, nb_regex_queue); + if (ret != 0) + rte_exit(EXIT_FAILURE, + "regex_dev_init: err=%s\n", + rte_strerror(-ret)); + } + } +} + +int +main(int argc, char **argv) +{ + struct lcore_conf *qconf; + uint16_t queueid, portid; + unsigned int lcore_id, i; + uint8_t queue, regex_nb_q; + int ret; + + /* init EAL */ + ret = rte_eal_init(argc, argv); + if (ret < 0) + rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n"); + argc -= ret; + argv += ret; + + force_quit = false; + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + /* pre-init dst MACs for all ports to 02:00:00:00:00:xx */ + for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) { + dest_eth_addr[portid] = + RTE_ETHER_LOCAL_ADMIN_ADDR + ((uint64_t)portid << 40); + *(uint64_t *)(val_eth + portid) = dest_eth_addr[portid]; + } + + /* parse application arguments (after the EAL ones) */ + ret = parse_args(argc, argv); + if (ret < 0) + rte_exit(EXIT_FAILURE, "Invalid L3FWD parameters\n"); + + /* Setup function pointers for lookup method. */ + setup_l3fwd_lookup_tables(); + + l3fwd_poll_resource_setup(); + + /* start ports */ + RTE_ETH_FOREACH_DEV(portid) { + if ((enabled_port_mask & (1 << portid)) == 0) + continue; + + /* Start device */ + ret = rte_eth_dev_start(portid); + if (ret < 0) + rte_exit(EXIT_FAILURE, + "rte_eth_dev_start: err=%d, port=%d\n", + ret, portid); + + /* + * If enabled, put device in promiscuous mode. + * This allows IO forwarding mode to forward packets + * to itself through 2 cross-connected ports of the + * target machine. + */ + if (promiscuous_on) { + ret = rte_eth_promiscuous_enable(portid); + if (ret != 0) + rte_exit(EXIT_FAILURE, + "rte_eth_promiscuous_enable: err=%s, port=%u\n", + rte_strerror(-ret), portid); + } + } + + printf("\n"); + + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { + if (rte_lcore_is_enabled(lcore_id) == 0) + continue; + qconf = &lcore_conf[lcore_id]; + for (queue = 0; queue < qconf->n_rx_queue; ++queue) { + portid = qconf->rx_queue_list[queue].port_id; + queueid = qconf->rx_queue_list[queue].queue_id; + if (prepare_ptype_parser(portid, queueid) == 0) + rte_exit(EXIT_FAILURE, "ptype check fails\n"); + } + + ret = regex_lcore_init(lcore_id, qconf->regex_dev_id, + qconf->regex_qp_id); + if (ret != 0) + rte_exit(EXIT_FAILURE, + "regex_lcore_init: err=%s, dev_id=%u, queueid=%u\n", + rte_strerror(-ret), qconf->regex_dev_id, + qconf->regex_qp_id); + } + + check_all_ports_link_status(enabled_port_mask); + + ret = 0; + /* launch per-lcore init on every lcore */ + rte_eal_mp_remote_launch(l3fwd_lkp.main_loop, NULL, CALL_MASTER); + + rte_eal_mp_wait_lcore(); + + RTE_ETH_FOREACH_DEV(portid) { + if ((enabled_port_mask & (1 << portid)) == 0) + continue; + printf("Closing port %d...", portid); + rte_eth_dev_stop(portid); + rte_eth_dev_close(portid); + printf(" Done\n"); + } + + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { + if (rte_lcore_is_enabled(lcore_id) == 0) + continue; + regex_lcore_uninit(lcore_id); + rte_free(lcore_conf[lcore_id].pkts_burst); + } + + for (i = 0; i < RTE_MAX_REGEXDEV_DEVS; i++) { + if (rte_regexdev_is_valid_dev(i) == 0) + continue; + regex_nb_q = get_regex_dev_n_queues(i); + if (regex_nb_q) { + printf("Closing regegdev %d...", i); + regex_dev_uninit(i); + printf(" Done\n"); + } + } + regex_mem_free(); + + printf("Bye...\n"); + + return ret; +} diff --git a/examples/l3fwd-regex/meson.build b/examples/l3fwd-regex/meson.build new file mode 100644 index 000000000..a5f602510 --- /dev/null +++ b/examples/l3fwd-regex/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(C) 2020 Marvell International Ltd. + +# meson file, for building this example as part of a main DPDK build. + +allow_experimental_apis = true +deps += ['lpm', 'regexdev'] +sources = files( + 'l3fwd_lpm.c', 'l3fwd_regex.c', 'main.c' +) diff --git a/examples/meson.build b/examples/meson.build index 414ec55cc..1378d3f0b 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -23,7 +23,7 @@ all_examples = [ 'l2fwd', 'l2fwd-cat', 'l2fwd-event', 'l2fwd-crypto', 'l2fwd-jobstats', 'l2fwd-keepalive', 'l3fwd', - 'l3fwd-acl', 'l3fwd-power', 'l3fwd-graph', + 'l3fwd-acl', 'l3fwd-power', 'l3fwd-graph', 'l3fwd-regex', 'link_status_interrupt', 'multi_process/client_server_mp/mp_client', 'multi_process/client_server_mp/mp_server',