d9a42a69fe
Some DPDK applications wrongly assume these requirements: - no hotplug, i.e. ports are never detached - all allocated ports are available to the application Such application iterates over ports by its own mean. The most common pattern is to request the port count and assume ports with index in the range [0..count[ can be used. In order to fix this common mistake in all external applications, the function rte_eth_dev_count is deprecated, while introducing the new functions rte_eth_dev_count_avail and rte_eth_dev_count_total. Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
1088 lines
27 KiB
C
1088 lines
27 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2010-2014 Intel Corporation
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <inttypes.h>
|
|
#include <sys/types.h>
|
|
#include <string.h>
|
|
#include <sys/queue.h>
|
|
#include <stdarg.h>
|
|
#include <errno.h>
|
|
#include <getopt.h>
|
|
#include <signal.h>
|
|
|
|
#include <rte_common.h>
|
|
#include <rte_byteorder.h>
|
|
#include <rte_log.h>
|
|
#include <rte_memory.h>
|
|
#include <rte_memcpy.h>
|
|
#include <rte_eal.h>
|
|
#include <rte_launch.h>
|
|
#include <rte_atomic.h>
|
|
#include <rte_spinlock.h>
|
|
#include <rte_cycles.h>
|
|
#include <rte_prefetch.h>
|
|
#include <rte_lcore.h>
|
|
#include <rte_per_lcore.h>
|
|
#include <rte_branch_prediction.h>
|
|
#include <rte_interrupts.h>
|
|
#include <rte_random.h>
|
|
#include <rte_debug.h>
|
|
#include <rte_ether.h>
|
|
#include <rte_ethdev.h>
|
|
#include <rte_mempool.h>
|
|
#include <rte_mbuf.h>
|
|
#include <rte_ip.h>
|
|
#include <rte_tcp.h>
|
|
#include <rte_udp.h>
|
|
#include <rte_string_fns.h>
|
|
|
|
#define APP_LOOKUP_EXACT_MATCH 0
|
|
#define APP_LOOKUP_LPM 1
|
|
#define DO_RFC_1812_CHECKS
|
|
|
|
//#define APP_LOOKUP_METHOD APP_LOOKUP_EXACT_MATCH
|
|
#ifndef APP_LOOKUP_METHOD
|
|
#define APP_LOOKUP_METHOD APP_LOOKUP_LPM
|
|
#endif
|
|
|
|
#if (APP_LOOKUP_METHOD == APP_LOOKUP_EXACT_MATCH)
|
|
#include <rte_hash.h>
|
|
#elif (APP_LOOKUP_METHOD == APP_LOOKUP_LPM)
|
|
#include <rte_lpm.h>
|
|
#else
|
|
#error "APP_LOOKUP_METHOD set to incorrect value"
|
|
#endif
|
|
|
|
#define RTE_LOGTYPE_L3FWD RTE_LOGTYPE_USER1
|
|
|
|
#define MEMPOOL_CACHE_SIZE 256
|
|
|
|
/*
|
|
* 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 RTE_MAX ( \
|
|
(nb_ports*nb_rx_queue*nb_rxd + \
|
|
nb_ports*nb_lcores*MAX_PKT_BURST + \
|
|
nb_ports*n_tx_queue*nb_txd + \
|
|
nb_lcores*MEMPOOL_CACHE_SIZE), \
|
|
(unsigned)8192)
|
|
|
|
/*
|
|
* RX and TX Prefetch, Host, and Write-back threshold values should be
|
|
* carefully set for optimal performance. Consult the network
|
|
* controller's datasheet and supporting DPDK documentation for guidance
|
|
* on how these parameters should be set.
|
|
*/
|
|
#define RX_PTHRESH 8 /**< Default values of RX prefetch threshold reg. */
|
|
#define RX_HTHRESH 8 /**< Default values of RX host threshold reg. */
|
|
#define RX_WTHRESH 4 /**< Default values of RX write-back threshold reg. */
|
|
|
|
/*
|
|
* These default values are optimized for use with the Intel(R) 82599 10 GbE
|
|
* Controller and the DPDK ixgbe PMD. Consider using other values for other
|
|
* network controllers and/or network drivers.
|
|
*/
|
|
#define TX_PTHRESH 36 /**< Default values of TX prefetch threshold reg. */
|
|
#define TX_HTHRESH 0 /**< Default values of TX host threshold reg. */
|
|
#define TX_WTHRESH 0 /**< Default values of TX write-back threshold reg. */
|
|
|
|
#define MAX_PKT_BURST 32
|
|
#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */
|
|
|
|
#define NB_SOCKETS 8
|
|
|
|
#define SOCKET0 0
|
|
|
|
/* Configure how many packets ahead to prefetch, when reading packets */
|
|
#define PREFETCH_OFFSET 3
|
|
|
|
/*
|
|
* Configurable number of RX/TX ring descriptors
|
|
*/
|
|
#define RTE_TEST_RX_DESC_DEFAULT 1024
|
|
#define RTE_TEST_TX_DESC_DEFAULT 1024
|
|
static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
|
|
static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
|
|
|
|
/* ethernet addresses of ports */
|
|
static struct ether_addr ports_eth_addr[RTE_MAX_ETHPORTS];
|
|
|
|
/* mask of enabled ports */
|
|
static uint32_t enabled_port_mask = 0;
|
|
static int numa_on = 1; /**< NUMA is enabled by default. */
|
|
|
|
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;
|
|
|
|
#define MAX_RX_QUEUE_PER_LCORE 16
|
|
#define MAX_TX_QUEUE_PER_PORT 1
|
|
#define MAX_RX_QUEUE_PER_PORT 1
|
|
|
|
#define MAX_LCORE_PARAMS 1024
|
|
struct lcore_params {
|
|
uint16_t port_id;
|
|
uint8_t queue_id;
|
|
uint8_t lcore_id;
|
|
} __rte_cache_aligned;
|
|
|
|
static struct lcore_params lcore_params_array[MAX_LCORE_PARAMS];
|
|
static struct lcore_params lcore_params_array_default[] = {
|
|
{0, 0, 2},
|
|
{0, 1, 2},
|
|
{0, 2, 2},
|
|
{1, 0, 2},
|
|
{1, 1, 2},
|
|
{1, 2, 2},
|
|
{2, 0, 2},
|
|
{3, 0, 3},
|
|
{3, 1, 3},
|
|
};
|
|
|
|
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 = ETHER_MAX_LEN,
|
|
.split_hdr_size = 0,
|
|
.ignore_offload_bitfield = 1,
|
|
.offloads = (DEV_RX_OFFLOAD_CRC_STRIP |
|
|
DEV_RX_OFFLOAD_CHECKSUM),
|
|
},
|
|
.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[NB_SOCKETS];
|
|
|
|
|
|
#if (APP_LOOKUP_METHOD == APP_LOOKUP_EXACT_MATCH)
|
|
|
|
#ifdef RTE_ARCH_X86
|
|
#include <rte_hash_crc.h>
|
|
#define DEFAULT_HASH_FUNC rte_hash_crc
|
|
#else
|
|
#include <rte_jhash.h>
|
|
#define DEFAULT_HASH_FUNC rte_jhash
|
|
#endif
|
|
|
|
struct ipv4_5tuple {
|
|
uint32_t ip_dst;
|
|
uint32_t ip_src;
|
|
uint16_t port_dst;
|
|
uint16_t port_src;
|
|
uint8_t proto;
|
|
} __attribute__((__packed__));
|
|
|
|
struct l3fwd_route {
|
|
struct ipv4_5tuple key;
|
|
uint8_t if_out;
|
|
};
|
|
|
|
static struct l3fwd_route l3fwd_route_array[] = {
|
|
{{IPv4(100,10,0,1), IPv4(200,10,0,1), 101, 11, IPPROTO_TCP}, 0},
|
|
{{IPv4(100,20,0,2), IPv4(200,20,0,2), 102, 12, IPPROTO_TCP}, 1},
|
|
{{IPv4(100,30,0,3), IPv4(200,30,0,3), 103, 13, IPPROTO_TCP}, 2},
|
|
{{IPv4(100,40,0,4), IPv4(200,40,0,4), 104, 14, IPPROTO_TCP}, 3},
|
|
};
|
|
|
|
typedef struct rte_hash lookup_struct_t;
|
|
static lookup_struct_t *l3fwd_lookup_struct[NB_SOCKETS];
|
|
|
|
#define L3FWD_HASH_ENTRIES 1024
|
|
struct rte_hash_parameters l3fwd_hash_params = {
|
|
.name = "l3fwd_hash_0",
|
|
.entries = L3FWD_HASH_ENTRIES,
|
|
.key_len = sizeof(struct ipv4_5tuple),
|
|
.hash_func = DEFAULT_HASH_FUNC,
|
|
.hash_func_init_val = 0,
|
|
.socket_id = SOCKET0,
|
|
};
|
|
|
|
#define L3FWD_NUM_ROUTES \
|
|
(sizeof(l3fwd_route_array) / sizeof(l3fwd_route_array[0]))
|
|
|
|
static uint8_t l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
|
|
#endif
|
|
|
|
#if (APP_LOOKUP_METHOD == APP_LOOKUP_LPM)
|
|
struct l3fwd_route {
|
|
uint32_t ip;
|
|
uint8_t depth;
|
|
uint8_t if_out;
|
|
};
|
|
|
|
static struct l3fwd_route l3fwd_route_array[] = {
|
|
{IPv4(1,1,1,0), 24, 0},
|
|
{IPv4(2,1,1,0), 24, 1},
|
|
{IPv4(3,1,1,0), 24, 2},
|
|
{IPv4(4,1,1,0), 24, 3},
|
|
{IPv4(5,1,1,0), 24, 4},
|
|
{IPv4(6,1,1,0), 24, 5},
|
|
{IPv4(7,1,1,0), 24, 6},
|
|
{IPv4(8,1,1,0), 24, 7},
|
|
};
|
|
|
|
#define L3FWD_NUM_ROUTES \
|
|
(sizeof(l3fwd_route_array) / sizeof(l3fwd_route_array[0]))
|
|
|
|
#define L3FWD_LPM_MAX_RULES 1024
|
|
|
|
typedef struct rte_lpm lookup_struct_t;
|
|
static lookup_struct_t *l3fwd_lookup_struct[NB_SOCKETS];
|
|
#endif
|
|
|
|
struct lcore_conf {
|
|
uint16_t n_rx_queue;
|
|
struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
|
|
uint16_t tx_queue_id;
|
|
struct mbuf_table tx_mbufs[RTE_MAX_ETHPORTS];
|
|
lookup_struct_t * lookup_struct;
|
|
} __rte_cache_aligned;
|
|
|
|
static struct lcore_conf lcore_conf[RTE_MAX_LCORE];
|
|
static rte_spinlock_t spinlock_conf[RTE_MAX_ETHPORTS] = {RTE_SPINLOCK_INITIALIZER};
|
|
/* 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;
|
|
m_table = (struct rte_mbuf **)qconf->tx_mbufs[port].m_table;
|
|
|
|
rte_spinlock_lock(&spinlock_conf[port]);
|
|
ret = rte_eth_tx_burst(port, queueid, m_table, n);
|
|
rte_spinlock_unlock(&spinlock_conf[port]);
|
|
|
|
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 rte_mbuf *m, uint16_t port)
|
|
{
|
|
uint32_t lcore_id;
|
|
uint16_t len;
|
|
struct lcore_conf *qconf;
|
|
|
|
lcore_id = rte_lcore_id();
|
|
|
|
qconf = &lcore_conf[lcore_id];
|
|
len = qconf->tx_mbufs[port].len;
|
|
qconf->tx_mbufs[port].m_table[len] = m;
|
|
len++;
|
|
|
|
/* enough pkts to be sent */
|
|
if (unlikely(len == MAX_PKT_BURST)) {
|
|
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 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 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 ipv4_hdr))
|
|
return -5;
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if (APP_LOOKUP_METHOD == APP_LOOKUP_EXACT_MATCH)
|
|
static void
|
|
print_key(struct ipv4_5tuple key)
|
|
{
|
|
printf("IP dst = %08x, IP src = %08x, port dst = %d, port src = %d, proto = %d\n",
|
|
(unsigned)key.ip_dst, (unsigned)key.ip_src, key.port_dst, key.port_src, key.proto);
|
|
}
|
|
|
|
static inline uint16_t
|
|
get_dst_port(struct ipv4_hdr *ipv4_hdr, uint16_t portid,
|
|
lookup_struct_t *l3fwd_lookup_struct)
|
|
{
|
|
struct ipv4_5tuple key;
|
|
struct tcp_hdr *tcp;
|
|
struct udp_hdr *udp;
|
|
int ret = 0;
|
|
|
|
key.ip_dst = rte_be_to_cpu_32(ipv4_hdr->dst_addr);
|
|
key.ip_src = rte_be_to_cpu_32(ipv4_hdr->src_addr);
|
|
key.proto = ipv4_hdr->next_proto_id;
|
|
|
|
switch (ipv4_hdr->next_proto_id) {
|
|
case IPPROTO_TCP:
|
|
tcp = (struct tcp_hdr *)((unsigned char *) ipv4_hdr +
|
|
sizeof(struct ipv4_hdr));
|
|
key.port_dst = rte_be_to_cpu_16(tcp->dst_port);
|
|
key.port_src = rte_be_to_cpu_16(tcp->src_port);
|
|
break;
|
|
|
|
case IPPROTO_UDP:
|
|
udp = (struct udp_hdr *)((unsigned char *) ipv4_hdr +
|
|
sizeof(struct ipv4_hdr));
|
|
key.port_dst = rte_be_to_cpu_16(udp->dst_port);
|
|
key.port_src = rte_be_to_cpu_16(udp->src_port);
|
|
break;
|
|
|
|
default:
|
|
key.port_dst = 0;
|
|
key.port_src = 0;
|
|
}
|
|
|
|
/* Find destination port */
|
|
ret = rte_hash_lookup(l3fwd_lookup_struct, (const void *)&key);
|
|
return ((ret < 0) ? portid : l3fwd_out_if[ret]);
|
|
}
|
|
#endif
|
|
|
|
#if (APP_LOOKUP_METHOD == APP_LOOKUP_LPM)
|
|
static inline uint32_t
|
|
get_dst_port(struct ipv4_hdr *ipv4_hdr, uint16_t portid,
|
|
lookup_struct_t *l3fwd_lookup_struct)
|
|
{
|
|
uint32_t next_hop;
|
|
|
|
return ((rte_lpm_lookup(l3fwd_lookup_struct,
|
|
rte_be_to_cpu_32(ipv4_hdr->dst_addr), &next_hop) == 0) ?
|
|
next_hop : portid);
|
|
}
|
|
#endif
|
|
|
|
static inline void
|
|
l3fwd_simple_forward(struct rte_mbuf *m, uint16_t portid,
|
|
lookup_struct_t *l3fwd_lookup_struct)
|
|
{
|
|
struct ether_hdr *eth_hdr;
|
|
struct ipv4_hdr *ipv4_hdr;
|
|
void *tmp;
|
|
uint16_t dst_port;
|
|
|
|
eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
|
|
|
|
ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct ipv4_hdr *,
|
|
sizeof(struct 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 = get_dst_port(ipv4_hdr, portid, l3fwd_lookup_struct);
|
|
if (dst_port >= RTE_MAX_ETHPORTS || (enabled_port_mask & 1 << dst_port) == 0)
|
|
dst_port = portid;
|
|
|
|
/* 02:00:00:00:00:xx */
|
|
tmp = ð_hdr->d_addr.addr_bytes[0];
|
|
*((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40);
|
|
|
|
#ifdef DO_RFC_1812_CHECKS
|
|
/* Update time to live and header checksum */
|
|
--(ipv4_hdr->time_to_live);
|
|
++(ipv4_hdr->hdr_checksum);
|
|
#endif
|
|
|
|
/* src addr */
|
|
ether_addr_copy(&ports_eth_addr[dst_port], ð_hdr->s_addr);
|
|
|
|
send_single_packet(m, dst_port);
|
|
|
|
}
|
|
|
|
/* main processing loop */
|
|
static int
|
|
main_loop(__attribute__((unused)) void *dummy)
|
|
{
|
|
struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
|
|
unsigned lcore_id;
|
|
uint64_t prev_tsc, diff_tsc, cur_tsc;
|
|
int i, j, nb_rx;
|
|
uint8_t queueid;
|
|
uint16_t portid;
|
|
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);
|
|
}
|
|
|
|
while (1) {
|
|
|
|
cur_tsc = rte_rdtsc();
|
|
|
|
/*
|
|
* TX burst queue drain
|
|
*/
|
|
diff_tsc = cur_tsc - prev_tsc;
|
|
if (unlikely(diff_tsc > drain_tsc)) {
|
|
|
|
/*
|
|
* This could be optimized (use queueid instead of
|
|
* portid), but it is not called so often
|
|
*/
|
|
for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
|
|
if (qconf->tx_mbufs[portid].len == 0)
|
|
continue;
|
|
send_burst(&lcore_conf[lcore_id],
|
|
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);
|
|
|
|
/* 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_simple_forward(pkts_burst[j], portid, qconf->lookup_struct);
|
|
}
|
|
|
|
/* Forward remaining prefetched packets */
|
|
for (; j < nb_rx; j++) {
|
|
l3fwd_simple_forward(pkts_burst[j], portid, qconf->lookup_struct);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
check_lcore_params(void)
|
|
{
|
|
uint8_t queue, lcore;
|
|
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;
|
|
}
|
|
if ((socketid = rte_lcore_to_socket_id(lcore) != 0) &&
|
|
(numa_on == 0)) {
|
|
printf("warning: lcore %hhu is on socket %d with numa off \n",
|
|
lcore, socketid);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
check_port_config(void)
|
|
{
|
|
unsigned 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 && lcore_params[i].queue_id > queue)
|
|
queue = lcore_params[i].queue_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",
|
|
(unsigned)nb_rx_queue + 1, (unsigned)lcore);
|
|
return -1;
|
|
} else {
|
|
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].n_rx_queue++;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* display usage */
|
|
static void
|
|
print_usage(const char *prgname)
|
|
{
|
|
printf ("%s [EAL options] -- -p PORTMASK"
|
|
" [--config (port,queue,lcore)[,(port,queue,lcore]]\n"
|
|
" -p PORTMASK: hexadecimal bitmask of ports to configure\n"
|
|
" --config (port,queue,lcore): rx queues configuration\n"
|
|
" --no-numa: optional, disable numa awareness\n",
|
|
prgname);
|
|
}
|
|
|
|
/* Custom handling of signals to handle process terminal */
|
|
static void
|
|
signal_handler(int signum)
|
|
{
|
|
uint16_t portid;
|
|
|
|
/* When we receive a SIGINT signal */
|
|
if (signum == SIGINT) {
|
|
RTE_ETH_FOREACH_DEV(portid) {
|
|
/* skip ports that are not enabled */
|
|
if ((enabled_port_mask & (1 << portid)) == 0)
|
|
continue;
|
|
rte_eth_dev_close(portid);
|
|
}
|
|
}
|
|
rte_exit(EXIT_SUCCESS, "\n User forced exit\n");
|
|
}
|
|
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,
|
|
_NUM_FLD
|
|
};
|
|
unsigned long int_fld[_NUM_FLD];
|
|
char *str_fld[_NUM_FLD];
|
|
int i;
|
|
unsigned size;
|
|
|
|
nb_lcore_params = 0;
|
|
|
|
while ((p = strchr(p0,'(')) != NULL) {
|
|
++p;
|
|
if((p0 = strchr(p,')')) == 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 = 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];
|
|
++nb_lcore_params;
|
|
}
|
|
lcore_params = lcore_params_array;
|
|
return 0;
|
|
}
|
|
|
|
/* 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];
|
|
static struct option lgopts[] = {
|
|
{"config", 1, 0, 0},
|
|
{"no-numa", 0, 0, 0},
|
|
{NULL, 0, 0, 0}
|
|
};
|
|
|
|
argvopt = argv;
|
|
|
|
while ((opt = getopt_long(argc, argvopt, "p:",
|
|
lgopts, &option_index)) != EOF) {
|
|
|
|
switch (opt) {
|
|
/* portmask */
|
|
case 'p':
|
|
enabled_port_mask = parse_portmask(optarg);
|
|
if (enabled_port_mask == 0) {
|
|
printf("invalid portmask\n");
|
|
print_usage(prgname);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
/* long options */
|
|
case 0:
|
|
if (!strcmp(lgopts[option_index].name, "config")) {
|
|
ret = parse_config(optarg);
|
|
if (ret) {
|
|
printf("invalid config\n");
|
|
print_usage(prgname);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (!strcmp(lgopts[option_index].name, "no-numa")) {
|
|
printf("numa is disabled \n");
|
|
numa_on = 0;
|
|
}
|
|
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 ether_addr *eth_addr)
|
|
{
|
|
char buf[ETHER_ADDR_FMT_SIZE];
|
|
ether_format_addr(buf, ETHER_ADDR_FMT_SIZE, eth_addr);
|
|
printf("%s%s", name, buf);
|
|
}
|
|
|
|
#if (APP_LOOKUP_METHOD == APP_LOOKUP_EXACT_MATCH)
|
|
static void
|
|
setup_hash(int socketid)
|
|
{
|
|
unsigned i;
|
|
int ret;
|
|
char s[64];
|
|
|
|
/* create hashes */
|
|
snprintf(s, sizeof(s), "l3fwd_hash_%d", socketid);
|
|
l3fwd_hash_params.name = s;
|
|
l3fwd_hash_params.socket_id = socketid;
|
|
l3fwd_lookup_struct[socketid] = rte_hash_create(&l3fwd_hash_params);
|
|
if (l3fwd_lookup_struct[socketid] == NULL)
|
|
rte_exit(EXIT_FAILURE, "Unable to create the l3fwd hash on "
|
|
"socket %d\n", socketid);
|
|
|
|
/* populate the hash */
|
|
for (i = 0; i < L3FWD_NUM_ROUTES; i++) {
|
|
ret = rte_hash_add_key (l3fwd_lookup_struct[socketid],
|
|
(void *) &l3fwd_route_array[i].key);
|
|
if (ret < 0) {
|
|
rte_exit(EXIT_FAILURE, "Unable to add entry %u to the"
|
|
"l3fwd hash on socket %d\n", i, socketid);
|
|
}
|
|
l3fwd_out_if[ret] = l3fwd_route_array[i].if_out;
|
|
printf("Hash: Adding key\n");
|
|
print_key(l3fwd_route_array[i].key);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if (APP_LOOKUP_METHOD == APP_LOOKUP_LPM)
|
|
static void
|
|
setup_lpm(int socketid)
|
|
{
|
|
unsigned i;
|
|
int ret;
|
|
char s[64];
|
|
|
|
struct rte_lpm_config lpm_ipv4_config;
|
|
|
|
lpm_ipv4_config.max_rules = L3FWD_LPM_MAX_RULES;
|
|
lpm_ipv4_config.number_tbl8s = 256;
|
|
lpm_ipv4_config.flags = 0;
|
|
|
|
/* create the LPM table */
|
|
snprintf(s, sizeof(s), "L3FWD_LPM_%d", socketid);
|
|
l3fwd_lookup_struct[socketid] =
|
|
rte_lpm_create(s, socketid, &lpm_ipv4_config);
|
|
if (l3fwd_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 < L3FWD_NUM_ROUTES; i++) {
|
|
ret = rte_lpm_add(l3fwd_lookup_struct[socketid],
|
|
l3fwd_route_array[i].ip,
|
|
l3fwd_route_array[i].depth,
|
|
l3fwd_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 0x%08x / %d (%d)\n",
|
|
(unsigned)l3fwd_route_array[i].ip,
|
|
l3fwd_route_array[i].depth,
|
|
l3fwd_route_array[i].if_out);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
init_mem(unsigned nb_mbuf)
|
|
{
|
|
struct lcore_conf *qconf;
|
|
int socketid;
|
|
unsigned lcore_id;
|
|
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[socketid] == NULL) {
|
|
snprintf(s, sizeof(s), "mbuf_pool_%d", socketid);
|
|
pktmbuf_pool[socketid] = rte_pktmbuf_pool_create(s,
|
|
nb_mbuf, MEMPOOL_CACHE_SIZE, 0,
|
|
RTE_MBUF_DEFAULT_BUF_SIZE, socketid);
|
|
if (pktmbuf_pool[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);
|
|
|
|
#if (APP_LOOKUP_METHOD == APP_LOOKUP_LPM)
|
|
setup_lpm(socketid);
|
|
#else
|
|
setup_hash(socketid);
|
|
#endif
|
|
}
|
|
qconf = &lcore_conf[lcore_id];
|
|
qconf->lookup_struct = l3fwd_lookup_struct[socketid];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
struct lcore_conf *qconf;
|
|
struct rte_eth_dev_info dev_info;
|
|
struct rte_eth_txconf *txconf;
|
|
int ret;
|
|
unsigned nb_ports;
|
|
uint16_t queueid, portid;
|
|
unsigned lcore_id;
|
|
uint32_t nb_lcores;
|
|
uint16_t n_tx_queue;
|
|
uint8_t nb_rx_queue, queue, socketid;
|
|
|
|
signal(SIGINT, signal_handler);
|
|
/* init EAL */
|
|
ret = rte_eal_init(argc, argv);
|
|
if (ret < 0)
|
|
rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
|
|
argc -= ret;
|
|
argv += ret;
|
|
|
|
/* parse application arguments (after the EAL ones) */
|
|
ret = parse_args(argc, argv);
|
|
if (ret < 0)
|
|
rte_exit(EXIT_FAILURE, "Invalid L3FWD-VF parameters\n");
|
|
|
|
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);
|
|
|
|
/* must always equal(=1) */
|
|
nb_rx_queue = get_port_n_rx_queues(portid);
|
|
n_tx_queue = MAX_TX_QUEUE_PER_PORT;
|
|
|
|
printf("Creating queues: nb_rxq=%d nb_txq=%u... ",
|
|
nb_rx_queue, (unsigned)1 );
|
|
|
|
rte_eth_dev_info_get(portid, &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(portid, nb_rx_queue,
|
|
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);
|
|
|
|
rte_eth_macaddr_get(portid, &ports_eth_addr[portid]);
|
|
print_ethaddr(" Address:", &ports_eth_addr[portid]);
|
|
printf(", ");
|
|
|
|
ret = init_mem(NB_MBUF);
|
|
if (ret < 0)
|
|
rte_exit(EXIT_FAILURE, "init_mem failed\n");
|
|
|
|
/* init one TX queue */
|
|
socketid = (uint8_t)rte_lcore_to_socket_id(rte_get_master_lcore());
|
|
|
|
printf("txq=%d,%d,%d ", portid, 0, socketid);
|
|
fflush(stdout);
|
|
|
|
txconf = &dev_info.default_txconf;
|
|
txconf->txq_flags = ETH_TXQ_FLAGS_IGNORE;
|
|
txconf->offloads = local_port_conf.txmode.offloads;
|
|
ret = rte_eth_tx_queue_setup(portid, 0, nb_txd,
|
|
socketid, txconf);
|
|
if (ret < 0)
|
|
rte_exit(EXIT_FAILURE, "rte_eth_tx_queue_setup: err=%d, "
|
|
"port=%d\n", ret, portid);
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
|
|
struct rte_eth_rxconf rxq_conf;
|
|
|
|
if (rte_lcore_is_enabled(lcore_id) == 0)
|
|
continue;
|
|
qconf = &lcore_conf[lcore_id];
|
|
qconf->tx_queue_id = 0;
|
|
|
|
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_dev *dev;
|
|
struct rte_eth_conf *conf;
|
|
|
|
portid = qconf->rx_queue_list[queue].port_id;
|
|
queueid = qconf->rx_queue_list[queue].queue_id;
|
|
dev = &rte_eth_devices[portid];
|
|
conf = &dev->data->dev_conf;
|
|
|
|
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);
|
|
|
|
rte_eth_dev_info_get(portid, &dev_info);
|
|
rxq_conf = dev_info.default_rxconf;
|
|
rxq_conf.offloads = conf->rxmode.offloads;
|
|
ret = rte_eth_rx_queue_setup(portid, queueid, nb_rxd,
|
|
socketid, &rxq_conf,
|
|
pktmbuf_pool[socketid]);
|
|
if (ret < 0)
|
|
rte_exit(EXIT_FAILURE, "rte_eth_rx_queue_setup: err=%d,"
|
|
"port=%d\n", ret, portid);
|
|
}
|
|
}
|
|
printf("\n");
|
|
|
|
/* 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);
|
|
|
|
printf("done: Port %d\n", portid);
|
|
|
|
}
|
|
|
|
/* launch per-lcore init on every lcore */
|
|
rte_eal_mp_remote_launch(main_loop, NULL, CALL_MASTER);
|
|
RTE_LCORE_FOREACH_SLAVE(lcore_id) {
|
|
if (rte_eal_wait_lcore(lcore_id) < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|