13998e2a0SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
23998e2a0SBruce Richardson * Copyright(c) 2016-2017 Intel Corporation
3ed2a80fdSPablo de Lara */
4ed2a80fdSPablo de Lara
5ed2a80fdSPablo de Lara #include <stdio.h>
6ed2a80fdSPablo de Lara #include <stdlib.h>
7ed2a80fdSPablo de Lara #include <string.h>
8ed2a80fdSPablo de Lara #include <unistd.h>
9ed2a80fdSPablo de Lara #include <stdint.h>
10ed2a80fdSPablo de Lara #include <stdarg.h>
11ed2a80fdSPablo de Lara #include <inttypes.h>
12ed2a80fdSPablo de Lara #include <sys/queue.h>
13ed2a80fdSPablo de Lara #include <errno.h>
1432c97c80SBruce Richardson #include <sys/types.h>
1532c97c80SBruce Richardson #include <netinet/in.h>
16ed2a80fdSPablo de Lara #include <netinet/ip.h>
17ed2a80fdSPablo de Lara
18ed2a80fdSPablo de Lara #include <rte_common.h>
19ed2a80fdSPablo de Lara #include <rte_memory.h>
20ed2a80fdSPablo de Lara #include <rte_eal.h>
21ed2a80fdSPablo de Lara #include <rte_launch.h>
22ed2a80fdSPablo de Lara #include <rte_per_lcore.h>
23ed2a80fdSPablo de Lara #include <rte_lcore.h>
24ed2a80fdSPablo de Lara #include <rte_branch_prediction.h>
25ed2a80fdSPablo de Lara #include <rte_ring.h>
26ed2a80fdSPablo de Lara #include <rte_log.h>
27ed2a80fdSPablo de Lara #include <rte_debug.h>
28ed2a80fdSPablo de Lara #include <rte_mempool.h>
29ed2a80fdSPablo de Lara #include <rte_memcpy.h>
30ed2a80fdSPablo de Lara #include <rte_mbuf.h>
31ed2a80fdSPablo de Lara #include <rte_ether.h>
32ed2a80fdSPablo de Lara #include <rte_interrupts.h>
33ed2a80fdSPablo de Lara #include <rte_ethdev.h>
34ed2a80fdSPablo de Lara #include <rte_byteorder.h>
35ed2a80fdSPablo de Lara #include <rte_malloc.h>
36ed2a80fdSPablo de Lara #include <rte_string_fns.h>
37ed2a80fdSPablo de Lara #include <rte_efd.h>
38ed2a80fdSPablo de Lara #include <rte_ip.h>
39ed2a80fdSPablo de Lara
40ed2a80fdSPablo de Lara #include "common.h"
41ed2a80fdSPablo de Lara #include "args.h"
42ed2a80fdSPablo de Lara #include "init.h"
43ed2a80fdSPablo de Lara
44ed2a80fdSPablo de Lara /*
45ed2a80fdSPablo de Lara * When doing reads from the NIC or the node queues,
46ed2a80fdSPablo de Lara * use this batch size
47ed2a80fdSPablo de Lara */
48ed2a80fdSPablo de Lara #define PACKET_READ_SIZE 32
49ed2a80fdSPablo de Lara
50ed2a80fdSPablo de Lara /*
51ed2a80fdSPablo de Lara * Local buffers to put packets in, used to send packets in bursts to the
52ed2a80fdSPablo de Lara * nodes
53ed2a80fdSPablo de Lara */
54ed2a80fdSPablo de Lara struct node_rx_buf {
55ed2a80fdSPablo de Lara struct rte_mbuf *buffer[PACKET_READ_SIZE];
56ed2a80fdSPablo de Lara uint16_t count;
57ed2a80fdSPablo de Lara };
58ed2a80fdSPablo de Lara
59ed2a80fdSPablo de Lara struct efd_stats {
60ed2a80fdSPablo de Lara uint64_t distributed;
61ed2a80fdSPablo de Lara uint64_t drop;
62ed2a80fdSPablo de Lara } flow_dist_stats;
63ed2a80fdSPablo de Lara
64ed2a80fdSPablo de Lara /* One buffer per node rx queue - dynamically allocate array */
65ed2a80fdSPablo de Lara static struct node_rx_buf *cl_rx_buf;
66ed2a80fdSPablo de Lara
67ed2a80fdSPablo de Lara static const char *
get_printable_mac_addr(uint16_t port)68f8244c63SZhiyong Yang get_printable_mac_addr(uint16_t port)
69ed2a80fdSPablo de Lara {
70ed2a80fdSPablo de Lara static const char err_address[] = "00:00:00:00:00:00";
71ed2a80fdSPablo de Lara static char addresses[RTE_MAX_ETHPORTS][sizeof(err_address)];
726d13ea8eSOlivier Matz struct rte_ether_addr mac;
7370febdcfSIgor Romanov int ret;
74ed2a80fdSPablo de Lara
75ed2a80fdSPablo de Lara if (unlikely(port >= RTE_MAX_ETHPORTS))
76ed2a80fdSPablo de Lara return err_address;
77ed2a80fdSPablo de Lara if (unlikely(addresses[port][0] == '\0')) {
7870febdcfSIgor Romanov ret = rte_eth_macaddr_get(port, &mac);
7970febdcfSIgor Romanov if (ret != 0) {
8070febdcfSIgor Romanov printf("Failed to get MAC address (port %u): %s\n",
8170febdcfSIgor Romanov port, rte_strerror(-ret));
8270febdcfSIgor Romanov return err_address;
8370febdcfSIgor Romanov }
8470febdcfSIgor Romanov
85ed2a80fdSPablo de Lara snprintf(addresses[port], sizeof(addresses[port]),
86c2c4f87bSAman Deep Singh RTE_ETHER_ADDR_PRT_FMT "\n",
87a7db3afcSAman Deep Singh RTE_ETHER_ADDR_BYTES(&mac));
88ed2a80fdSPablo de Lara }
89ed2a80fdSPablo de Lara return addresses[port];
90ed2a80fdSPablo de Lara }
91ed2a80fdSPablo de Lara
92ed2a80fdSPablo de Lara /*
93ed2a80fdSPablo de Lara * This function displays the recorded statistics for each port
94ed2a80fdSPablo de Lara * and for each node. It uses ANSI terminal codes to clear
95cb056611SStephen Hemminger * screen when called. It is called from a single worker
96ed2a80fdSPablo de Lara * thread in the server process, when the process is run with more
97ed2a80fdSPablo de Lara * than one lcore enabled.
98ed2a80fdSPablo de Lara */
999a212dc0SConor Fogarty
1009a212dc0SConor Fogarty /* Display recorded statistics. 8< */
101ed2a80fdSPablo de Lara static void
do_stats_display(void)102ed2a80fdSPablo de Lara do_stats_display(void)
103ed2a80fdSPablo de Lara {
104ed2a80fdSPablo de Lara unsigned int i, j;
105ed2a80fdSPablo de Lara const char clr[] = {27, '[', '2', 'J', '\0'};
106ed2a80fdSPablo de Lara const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'};
107ed2a80fdSPablo de Lara uint64_t port_tx[RTE_MAX_ETHPORTS], port_tx_drop[RTE_MAX_ETHPORTS];
108ed2a80fdSPablo de Lara uint64_t node_tx[MAX_NODES], node_tx_drop[MAX_NODES];
109ed2a80fdSPablo de Lara
110ed2a80fdSPablo de Lara /* to get TX stats, we need to do some summing calculations */
111ed2a80fdSPablo de Lara memset(port_tx, 0, sizeof(port_tx));
112ed2a80fdSPablo de Lara memset(port_tx_drop, 0, sizeof(port_tx_drop));
113ed2a80fdSPablo de Lara memset(node_tx, 0, sizeof(node_tx));
114ed2a80fdSPablo de Lara memset(node_tx_drop, 0, sizeof(node_tx_drop));
115ed2a80fdSPablo de Lara
116ed2a80fdSPablo de Lara for (i = 0; i < num_nodes; i++) {
117ed2a80fdSPablo de Lara const struct tx_stats *tx = &info->tx_stats[i];
118ed2a80fdSPablo de Lara
119ed2a80fdSPablo de Lara for (j = 0; j < info->num_ports; j++) {
120ed2a80fdSPablo de Lara const uint64_t tx_val = tx->tx[info->id[j]];
121ed2a80fdSPablo de Lara const uint64_t drop_val = tx->tx_drop[info->id[j]];
122ed2a80fdSPablo de Lara
123ed2a80fdSPablo de Lara port_tx[j] += tx_val;
124ed2a80fdSPablo de Lara port_tx_drop[j] += drop_val;
125ed2a80fdSPablo de Lara node_tx[i] += tx_val;
126ed2a80fdSPablo de Lara node_tx_drop[i] += drop_val;
127ed2a80fdSPablo de Lara }
128ed2a80fdSPablo de Lara }
129ed2a80fdSPablo de Lara
130ed2a80fdSPablo de Lara /* Clear screen and move to top left */
131ed2a80fdSPablo de Lara printf("%s%s", clr, topLeft);
132ed2a80fdSPablo de Lara
133ed2a80fdSPablo de Lara printf("PORTS\n");
134ed2a80fdSPablo de Lara printf("-----\n");
135ed2a80fdSPablo de Lara for (i = 0; i < info->num_ports; i++)
136ed2a80fdSPablo de Lara printf("Port %u: '%s'\t", (unsigned int)info->id[i],
137ed2a80fdSPablo de Lara get_printable_mac_addr(info->id[i]));
138ed2a80fdSPablo de Lara printf("\n\n");
139ed2a80fdSPablo de Lara for (i = 0; i < info->num_ports; i++) {
140ed2a80fdSPablo de Lara printf("Port %u - rx: %9"PRIu64"\t"
141ed2a80fdSPablo de Lara "tx: %9"PRIu64"\n",
142ed2a80fdSPablo de Lara (unsigned int)info->id[i], info->rx_stats.rx[i],
143ed2a80fdSPablo de Lara port_tx[i]);
144ed2a80fdSPablo de Lara }
145ed2a80fdSPablo de Lara
146ed2a80fdSPablo de Lara printf("\nSERVER\n");
147ed2a80fdSPablo de Lara printf("-----\n");
148ed2a80fdSPablo de Lara printf("distributed: %9"PRIu64", drop: %9"PRIu64"\n",
149ed2a80fdSPablo de Lara flow_dist_stats.distributed, flow_dist_stats.drop);
150ed2a80fdSPablo de Lara
151ed2a80fdSPablo de Lara printf("\nNODES\n");
152ed2a80fdSPablo de Lara printf("-------\n");
153ed2a80fdSPablo de Lara for (i = 0; i < num_nodes; i++) {
154ed2a80fdSPablo de Lara const unsigned long long rx = nodes[i].stats.rx;
155ed2a80fdSPablo de Lara const unsigned long long rx_drop = nodes[i].stats.rx_drop;
156ed2a80fdSPablo de Lara const struct filter_stats *filter = &info->filter_stats[i];
157ed2a80fdSPablo de Lara
158ed2a80fdSPablo de Lara printf("Node %2u - rx: %9llu, rx_drop: %9llu\n"
159ed2a80fdSPablo de Lara " tx: %9"PRIu64", tx_drop: %9"PRIu64"\n"
160ed2a80fdSPablo de Lara " filter_passed: %9"PRIu64", "
161ed2a80fdSPablo de Lara "filter_drop: %9"PRIu64"\n",
162ed2a80fdSPablo de Lara i, rx, rx_drop, node_tx[i], node_tx_drop[i],
163ed2a80fdSPablo de Lara filter->passed, filter->drop);
164ed2a80fdSPablo de Lara }
165ed2a80fdSPablo de Lara
166ed2a80fdSPablo de Lara printf("\n");
167ed2a80fdSPablo de Lara }
1689a212dc0SConor Fogarty /* >8 End of displaying the recorded statistics. */
169ed2a80fdSPablo de Lara
170ed2a80fdSPablo de Lara /*
171cb056611SStephen Hemminger * The function called from each non-main lcore used by the process.
172ed2a80fdSPablo de Lara * The test_and_set function is used to randomly pick a single lcore on which
173ed2a80fdSPablo de Lara * the code to display the statistics will run. Otherwise, the code just
174ed2a80fdSPablo de Lara * repeatedly sleeps.
175ed2a80fdSPablo de Lara */
176ed2a80fdSPablo de Lara static int
sleep_lcore(__rte_unused void * dummy)177f2fc83b4SThomas Monjalon sleep_lcore(__rte_unused void *dummy)
178ed2a80fdSPablo de Lara {
179ed2a80fdSPablo de Lara /* Used to pick a display thread - static, so zero-initialised */
180*4773be58SJoyce Kong static uint32_t display_stats;
181ed2a80fdSPablo de Lara
182ed2a80fdSPablo de Lara /* Only one core should display stats */
183*4773be58SJoyce Kong uint32_t display_init = 0;
184*4773be58SJoyce Kong if (__atomic_compare_exchange_n(&display_stats, &display_init, 1, 0,
185*4773be58SJoyce Kong __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
186ed2a80fdSPablo de Lara const unsigned int sleeptime = 1;
187ed2a80fdSPablo de Lara
188ed2a80fdSPablo de Lara printf("Core %u displaying statistics\n", rte_lcore_id());
189ed2a80fdSPablo de Lara
190ed2a80fdSPablo de Lara /* Longer initial pause so above printf is seen */
191ed2a80fdSPablo de Lara sleep(sleeptime * 3);
192ed2a80fdSPablo de Lara
193ed2a80fdSPablo de Lara /* Loop forever: sleep always returns 0 or <= param */
194ed2a80fdSPablo de Lara while (sleep(sleeptime) <= sleeptime)
195ed2a80fdSPablo de Lara do_stats_display();
196ed2a80fdSPablo de Lara }
197ed2a80fdSPablo de Lara return 0;
198ed2a80fdSPablo de Lara }
199ed2a80fdSPablo de Lara
200ed2a80fdSPablo de Lara /*
201ed2a80fdSPablo de Lara * Function to set all the node statistic values to zero.
202ed2a80fdSPablo de Lara * Called at program startup.
203ed2a80fdSPablo de Lara */
204ed2a80fdSPablo de Lara static void
clear_stats(void)205ed2a80fdSPablo de Lara clear_stats(void)
206ed2a80fdSPablo de Lara {
207ed2a80fdSPablo de Lara unsigned int i;
208ed2a80fdSPablo de Lara
209ed2a80fdSPablo de Lara for (i = 0; i < num_nodes; i++)
210ed2a80fdSPablo de Lara nodes[i].stats.rx = nodes[i].stats.rx_drop = 0;
211ed2a80fdSPablo de Lara }
212ed2a80fdSPablo de Lara
213ed2a80fdSPablo de Lara /*
214ed2a80fdSPablo de Lara * send a burst of traffic to a node, assuming there are packets
215ed2a80fdSPablo de Lara * available to be sent to this node
216ed2a80fdSPablo de Lara */
2179a212dc0SConor Fogarty
2189a212dc0SConor Fogarty /* Flush rx queue. 8< */
219ed2a80fdSPablo de Lara static void
flush_rx_queue(uint16_t node)220ed2a80fdSPablo de Lara flush_rx_queue(uint16_t node)
221ed2a80fdSPablo de Lara {
222ed2a80fdSPablo de Lara uint16_t j;
223ed2a80fdSPablo de Lara struct node *cl;
224ed2a80fdSPablo de Lara
225ed2a80fdSPablo de Lara if (cl_rx_buf[node].count == 0)
226ed2a80fdSPablo de Lara return;
227ed2a80fdSPablo de Lara
228ed2a80fdSPablo de Lara cl = &nodes[node];
229ed2a80fdSPablo de Lara if (rte_ring_enqueue_bulk(cl->rx_q, (void **)cl_rx_buf[node].buffer,
23014fbffb0SBruce Richardson cl_rx_buf[node].count, NULL) != cl_rx_buf[node].count){
231ed2a80fdSPablo de Lara for (j = 0; j < cl_rx_buf[node].count; j++)
232ed2a80fdSPablo de Lara rte_pktmbuf_free(cl_rx_buf[node].buffer[j]);
233ed2a80fdSPablo de Lara cl->stats.rx_drop += cl_rx_buf[node].count;
234ed2a80fdSPablo de Lara } else
235ed2a80fdSPablo de Lara cl->stats.rx += cl_rx_buf[node].count;
236ed2a80fdSPablo de Lara
237ed2a80fdSPablo de Lara cl_rx_buf[node].count = 0;
238ed2a80fdSPablo de Lara }
2399a212dc0SConor Fogarty /* >8 End of sending a burst of traffic to a node. */
240ed2a80fdSPablo de Lara
241ed2a80fdSPablo de Lara /*
242ed2a80fdSPablo de Lara * marks a packet down to be sent to a particular node process
243ed2a80fdSPablo de Lara */
244ed2a80fdSPablo de Lara static inline void
enqueue_rx_packet(uint8_t node,struct rte_mbuf * buf)245ed2a80fdSPablo de Lara enqueue_rx_packet(uint8_t node, struct rte_mbuf *buf)
246ed2a80fdSPablo de Lara {
247ed2a80fdSPablo de Lara cl_rx_buf[node].buffer[cl_rx_buf[node].count++] = buf;
248ed2a80fdSPablo de Lara }
249ed2a80fdSPablo de Lara
250ed2a80fdSPablo de Lara /*
251ed2a80fdSPablo de Lara * This function takes a group of packets and routes them
252ed2a80fdSPablo de Lara * individually to the node process. Very simply round-robins the packets
2539a212dc0SConor Fogarty * without checking any of the packet contents. 8<
254ed2a80fdSPablo de Lara */
2559a212dc0SConor Fogarty
2569a212dc0SConor Fogarty /* Processing packets. 8< */
257ed2a80fdSPablo de Lara static void
process_packets(uint32_t port_num __rte_unused,struct rte_mbuf * pkts[],uint16_t rx_count,unsigned int socket_id)258ed2a80fdSPablo de Lara process_packets(uint32_t port_num __rte_unused, struct rte_mbuf *pkts[],
259ed2a80fdSPablo de Lara uint16_t rx_count, unsigned int socket_id)
260ed2a80fdSPablo de Lara {
261ed2a80fdSPablo de Lara uint16_t i;
262ed2a80fdSPablo de Lara uint8_t node;
263ed2a80fdSPablo de Lara efd_value_t data[RTE_EFD_BURST_MAX];
264ed2a80fdSPablo de Lara const void *key_ptrs[RTE_EFD_BURST_MAX];
265ed2a80fdSPablo de Lara
266a7c528e5SOlivier Matz struct rte_ipv4_hdr *ipv4_hdr;
267ed2a80fdSPablo de Lara uint32_t ipv4_dst_ip[RTE_EFD_BURST_MAX];
268ed2a80fdSPablo de Lara
269ed2a80fdSPablo de Lara for (i = 0; i < rx_count; i++) {
270ed2a80fdSPablo de Lara /* Handle IPv4 header.*/
271a7c528e5SOlivier Matz ipv4_hdr = rte_pktmbuf_mtod_offset(pkts[i],
272a7c528e5SOlivier Matz struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));
273ed2a80fdSPablo de Lara ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
274ed2a80fdSPablo de Lara key_ptrs[i] = (void *)&ipv4_dst_ip[i];
275ed2a80fdSPablo de Lara }
276ed2a80fdSPablo de Lara
277ed2a80fdSPablo de Lara rte_efd_lookup_bulk(efd_table, socket_id, rx_count,
278ed2a80fdSPablo de Lara (const void **) key_ptrs, data);
279ed2a80fdSPablo de Lara for (i = 0; i < rx_count; i++) {
280ed2a80fdSPablo de Lara node = (uint8_t) ((uintptr_t)data[i]);
281ed2a80fdSPablo de Lara
282ed2a80fdSPablo de Lara if (node >= num_nodes) {
283ed2a80fdSPablo de Lara /*
284ed2a80fdSPablo de Lara * Node is out of range, which means that
285ed2a80fdSPablo de Lara * flow has not been inserted
286ed2a80fdSPablo de Lara */
287ed2a80fdSPablo de Lara flow_dist_stats.drop++;
288ed2a80fdSPablo de Lara rte_pktmbuf_free(pkts[i]);
289ed2a80fdSPablo de Lara } else {
290ed2a80fdSPablo de Lara flow_dist_stats.distributed++;
291ed2a80fdSPablo de Lara enqueue_rx_packet(node, pkts[i]);
292ed2a80fdSPablo de Lara }
293ed2a80fdSPablo de Lara }
294ed2a80fdSPablo de Lara
295ed2a80fdSPablo de Lara for (i = 0; i < num_nodes; i++)
296ed2a80fdSPablo de Lara flush_rx_queue(i);
297ed2a80fdSPablo de Lara }
2989a212dc0SConor Fogarty /* >8 End of process_packets. */
299ed2a80fdSPablo de Lara
300ed2a80fdSPablo de Lara /*
301cb056611SStephen Hemminger * Function called by the main lcore of the DPDK process.
302ed2a80fdSPablo de Lara */
303ed2a80fdSPablo de Lara static void
do_packet_forwarding(void)304ed2a80fdSPablo de Lara do_packet_forwarding(void)
305ed2a80fdSPablo de Lara {
306ed2a80fdSPablo de Lara unsigned int port_num = 0; /* indexes the port[] array */
307ed2a80fdSPablo de Lara unsigned int socket_id = rte_socket_id();
308ed2a80fdSPablo de Lara
309ed2a80fdSPablo de Lara for (;;) {
310ed2a80fdSPablo de Lara struct rte_mbuf *buf[PACKET_READ_SIZE];
311ed2a80fdSPablo de Lara uint16_t rx_count;
312ed2a80fdSPablo de Lara
313ed2a80fdSPablo de Lara /* read a port */
314ed2a80fdSPablo de Lara rx_count = rte_eth_rx_burst(info->id[port_num], 0,
315ed2a80fdSPablo de Lara buf, PACKET_READ_SIZE);
316ed2a80fdSPablo de Lara info->rx_stats.rx[port_num] += rx_count;
317ed2a80fdSPablo de Lara
318ed2a80fdSPablo de Lara /* Now process the NIC packets read */
319ed2a80fdSPablo de Lara if (likely(rx_count > 0))
320ed2a80fdSPablo de Lara process_packets(port_num, buf, rx_count, socket_id);
321ed2a80fdSPablo de Lara
322ed2a80fdSPablo de Lara /* move to next port */
323ed2a80fdSPablo de Lara if (++port_num == info->num_ports)
324ed2a80fdSPablo de Lara port_num = 0;
325ed2a80fdSPablo de Lara }
326ed2a80fdSPablo de Lara }
327ed2a80fdSPablo de Lara
328ed2a80fdSPablo de Lara int
main(int argc,char * argv[])329ed2a80fdSPablo de Lara main(int argc, char *argv[])
330ed2a80fdSPablo de Lara {
331ed2a80fdSPablo de Lara /* initialise the system */
332ed2a80fdSPablo de Lara if (init(argc, argv) < 0)
333ed2a80fdSPablo de Lara return -1;
334ed2a80fdSPablo de Lara RTE_LOG(INFO, APP, "Finished Process Init.\n");
335ed2a80fdSPablo de Lara
336ed2a80fdSPablo de Lara cl_rx_buf = calloc(num_nodes, sizeof(cl_rx_buf[0]));
337ed2a80fdSPablo de Lara
338ed2a80fdSPablo de Lara /* clear statistics */
339ed2a80fdSPablo de Lara clear_stats();
340ed2a80fdSPablo de Lara
341cb056611SStephen Hemminger /* put all other cores to sleep except main */
342cb056611SStephen Hemminger rte_eal_mp_remote_launch(sleep_lcore, NULL, SKIP_MAIN);
343ed2a80fdSPablo de Lara
344ed2a80fdSPablo de Lara do_packet_forwarding();
34510aa3757SChengchang Tang
34610aa3757SChengchang Tang /* clean up the EAL */
34710aa3757SChengchang Tang rte_eal_cleanup();
34810aa3757SChengchang Tang
349ed2a80fdSPablo de Lara return 0;
350ed2a80fdSPablo de Lara }
351