examples/l2fwd-event: add default poll mode routines
[dpdk.git] / examples / l2fwd-event / main.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(C) 2019 Marvell International Ltd.
3  */
4
5 #include "l2fwd_poll.h"
6
7 /* display usage */
8 static void
9 l2fwd_event_usage(const char *prgname)
10 {
11         printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n"
12                "  -p PORTMASK: hexadecimal bitmask of ports to configure\n"
13                "  -q NQ: number of queue (=ports) per lcore (default is 1)\n"
14                "  -T PERIOD: statistics will be refreshed each PERIOD seconds "
15                "                (0 to disable, 10 default, 86400 maximum)\n"
16                "  --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n"
17                "      When enabled:\n"
18                "       - The source MAC address is replaced by the TX port MAC address\n"
19                "       - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n",
20                prgname);
21 }
22
23 static int
24 l2fwd_event_parse_portmask(const char *portmask)
25 {
26         char *end = NULL;
27         unsigned long pm;
28
29         /* parse hexadecimal string */
30         pm = strtoul(portmask, &end, 16);
31         if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
32                 return -1;
33
34         if (pm == 0)
35                 return -1;
36
37         return pm;
38 }
39
40 static unsigned int
41 l2fwd_event_parse_nqueue(const char *q_arg)
42 {
43         char *end = NULL;
44         unsigned long n;
45
46         /* parse hexadecimal string */
47         n = strtoul(q_arg, &end, 10);
48         if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
49                 return 0;
50         if (n == 0)
51                 return 0;
52         if (n >= MAX_RX_QUEUE_PER_LCORE)
53                 return 0;
54
55         return n;
56 }
57
58 static int
59 l2fwd_event_parse_timer_period(const char *q_arg)
60 {
61         char *end = NULL;
62         int n;
63
64         /* parse number string */
65         n = strtol(q_arg, &end, 10);
66         if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
67                 return -1;
68         if (n >= MAX_TIMER_PERIOD)
69                 return -1;
70
71         return n;
72 }
73
74 static const char short_options[] =
75         "p:"  /* portmask */
76         "q:"  /* number of queues */
77         "T:"  /* timer period */
78         ;
79
80 #define CMD_LINE_OPT_MAC_UPDATING "mac-updating"
81 #define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating"
82
83 enum {
84         /* long options mapped to a short option */
85
86         /* first long only option value must be >= 256, so that we won't
87          * conflict with short options
88          */
89         CMD_LINE_OPT_MIN_NUM = 256,
90 };
91
92 /* Parse the argument given in the command line of the application */
93 static int
94 l2fwd_event_parse_args(int argc, char **argv,
95                 struct l2fwd_resources *rsrc)
96 {
97         int mac_updating = 1;
98         struct option lgopts[] = {
99                 { CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1},
100                 { CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0},
101                 {NULL, 0, 0, 0}
102         };
103         int opt, ret, timer_secs;
104         char *prgname = argv[0];
105         char **argvopt;
106         int option_index;
107
108         argvopt = argv;
109         while ((opt = getopt_long(argc, argvopt, short_options,
110                                   lgopts, &option_index)) != EOF) {
111
112                 switch (opt) {
113                 /* portmask */
114                 case 'p':
115                         rsrc->enabled_port_mask =
116                                         l2fwd_event_parse_portmask(optarg);
117                         if (rsrc->enabled_port_mask == 0) {
118                                 printf("invalid portmask\n");
119                                 l2fwd_event_usage(prgname);
120                                 return -1;
121                         }
122                         break;
123
124                 /* nqueue */
125                 case 'q':
126                         rsrc->rx_queue_per_lcore =
127                                         l2fwd_event_parse_nqueue(optarg);
128                         if (rsrc->rx_queue_per_lcore == 0) {
129                                 printf("invalid queue number\n");
130                                 l2fwd_event_usage(prgname);
131                                 return -1;
132                         }
133                         break;
134
135                 /* timer period */
136                 case 'T':
137                         timer_secs = l2fwd_event_parse_timer_period(optarg);
138                         if (timer_secs < 0) {
139                                 printf("invalid timer period\n");
140                                 l2fwd_event_usage(prgname);
141                                 return -1;
142                         }
143                         rsrc->timer_period = timer_secs;
144                         /* convert to number of cycles */
145                         rsrc->timer_period *= rte_get_timer_hz();
146                         break;
147
148                 /* long options */
149                 case 0:
150                         break;
151
152                 default:
153                         l2fwd_event_usage(prgname);
154                         return -1;
155                 }
156         }
157
158         rsrc->mac_updating = mac_updating;
159
160         if (optind >= 0)
161                 argv[optind-1] = prgname;
162
163         ret = optind-1;
164         optind = 1; /* reset getopt lib */
165         return ret;
166 }
167
168 static int
169 l2fwd_launch_one_lcore(void *args)
170 {
171         struct l2fwd_resources *rsrc = args;
172         struct l2fwd_poll_resources *poll_rsrc = rsrc->poll_rsrc;
173
174         poll_rsrc->poll_main_loop(rsrc);
175
176         return 0;
177 }
178
179 /* Check the link status of all ports in up to 9s, and print them finally */
180 static void
181 check_all_ports_link_status(struct l2fwd_resources *rsrc,
182                             uint32_t port_mask)
183 {
184 #define CHECK_INTERVAL 100 /* 100ms */
185 #define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
186         uint16_t port_id;
187         uint8_t count, all_ports_up, print_flag = 0;
188         struct rte_eth_link link;
189
190         printf("\nChecking link status...");
191         fflush(stdout);
192         for (count = 0; count <= MAX_CHECK_TIME; count++) {
193                 if (rsrc->force_quit)
194                         return;
195                 all_ports_up = 1;
196                 RTE_ETH_FOREACH_DEV(port_id) {
197                         if (rsrc->force_quit)
198                                 return;
199                         if ((port_mask & (1 << port_id)) == 0)
200                                 continue;
201                         memset(&link, 0, sizeof(link));
202                         rte_eth_link_get_nowait(port_id, &link);
203                         /* print link status if flag set */
204                         if (print_flag == 1) {
205                                 if (link.link_status)
206                                         printf(
207                                         "Port%d Link Up. Speed %u Mbps - %s\n",
208                                                 port_id, link.link_speed,
209                                 (link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
210                                         ("full-duplex") : ("half-duplex\n"));
211                                 else
212                                         printf("Port %d Link Down\n", port_id);
213                                 continue;
214                         }
215                         /* clear all_ports_up flag if any link down */
216                         if (link.link_status == ETH_LINK_DOWN) {
217                                 all_ports_up = 0;
218                                 break;
219                         }
220                 }
221                 /* after finally printing all link status, get out */
222                 if (print_flag == 1)
223                         break;
224
225                 if (all_ports_up == 0) {
226                         printf(".");
227                         fflush(stdout);
228                         rte_delay_ms(CHECK_INTERVAL);
229                 }
230
231                 /* set the print_flag if all ports up or timeout */
232                 if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
233                         print_flag = 1;
234                         printf("done\n");
235                 }
236         }
237 }
238
239 /* Print out statistics on packets dropped */
240 static void
241 print_stats(struct l2fwd_resources *rsrc)
242 {
243         uint64_t total_packets_dropped, total_packets_tx, total_packets_rx;
244         uint32_t port_id;
245
246         total_packets_dropped = 0;
247         total_packets_tx = 0;
248         total_packets_rx = 0;
249
250         const char clr[] = {27, '[', '2', 'J', '\0' };
251         const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0' };
252
253                 /* Clear screen and move to top left */
254         printf("%s%s", clr, topLeft);
255
256         printf("\nPort statistics ====================================");
257
258         for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++) {
259                 /* skip disabled ports */
260                 if ((rsrc->enabled_port_mask & (1 << port_id)) == 0)
261                         continue;
262                 printf("\nStatistics for port %u ------------------------------"
263                            "\nPackets sent: %24"PRIu64
264                            "\nPackets received: %20"PRIu64
265                            "\nPackets dropped: %21"PRIu64,
266                            port_id,
267                            rsrc->port_stats[port_id].tx,
268                            rsrc->port_stats[port_id].rx,
269                            rsrc->port_stats[port_id].dropped);
270
271                 total_packets_dropped +=
272                                         rsrc->port_stats[port_id].dropped;
273                 total_packets_tx += rsrc->port_stats[port_id].tx;
274                 total_packets_rx += rsrc->port_stats[port_id].rx;
275         }
276         printf("\nAggregate statistics ==============================="
277                    "\nTotal packets sent: %18"PRIu64
278                    "\nTotal packets received: %14"PRIu64
279                    "\nTotal packets dropped: %15"PRIu64,
280                    total_packets_tx,
281                    total_packets_rx,
282                    total_packets_dropped);
283         printf("\n====================================================\n");
284 }
285
286 static void
287 l2fwd_event_print_stats(struct l2fwd_resources *rsrc)
288 {
289         uint64_t prev_tsc = 0, diff_tsc, cur_tsc, timer_tsc = 0;
290         const uint64_t timer_period = rsrc->timer_period;
291
292         while (!rsrc->force_quit) {
293                 /* if timer is enabled */
294                 if (timer_period > 0) {
295                         cur_tsc = rte_rdtsc();
296                         diff_tsc = cur_tsc - prev_tsc;
297
298                         /* advance the timer */
299                         timer_tsc += diff_tsc;
300
301                         /* if timer has reached its timeout */
302                         if (unlikely(timer_tsc >= timer_period)) {
303                                 print_stats(rsrc);
304                                 /* reset the timer */
305                                 timer_tsc = 0;
306                         }
307                         prev_tsc = cur_tsc;
308                 }
309         }
310 }
311
312
313 static void
314 signal_handler(int signum)
315 {
316         struct l2fwd_resources *rsrc = l2fwd_get_rsrc();
317         if (signum == SIGINT || signum == SIGTERM) {
318                 printf("\n\nSignal %d received, preparing to exit...\n",
319                                 signum);
320                 rsrc->force_quit = true;
321         }
322 }
323
324 int
325 main(int argc, char **argv)
326 {
327         struct l2fwd_resources *rsrc;
328         uint16_t nb_ports_available = 0;
329         uint32_t nb_ports_in_mask = 0;
330         uint16_t port_id, last_port;
331         uint32_t nb_mbufs;
332         uint16_t nb_ports;
333         int ret;
334
335         /* init EAL */
336         ret = rte_eal_init(argc, argv);
337         if (ret < 0)
338                 rte_panic("Invalid EAL arguments\n");
339         argc -= ret;
340         argv += ret;
341
342         rsrc = l2fwd_get_rsrc();
343
344         signal(SIGINT, signal_handler);
345         signal(SIGTERM, signal_handler);
346
347         /* parse application arguments (after the EAL ones) */
348         ret = l2fwd_event_parse_args(argc, argv, rsrc);
349         if (ret < 0)
350                 rte_panic("Invalid L2FWD arguments\n");
351
352         printf("MAC updating %s\n", rsrc->mac_updating ? "enabled" :
353                         "disabled");
354
355         nb_ports = rte_eth_dev_count_avail();
356         if (nb_ports == 0)
357                 rte_panic("No Ethernet ports - bye\n");
358
359         /* check port mask to possible port mask */
360         if (rsrc->enabled_port_mask & ~((1 << nb_ports) - 1))
361                 rte_panic("Invalid portmask; possible (0x%x)\n",
362                         (1 << nb_ports) - 1);
363
364         /* reset l2fwd_dst_ports */
365         for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
366                 rsrc->dst_ports[port_id] = 0;
367         last_port = 0;
368
369         /*
370          * Each logical core is assigned a dedicated TX queue on each port.
371          */
372         RTE_ETH_FOREACH_DEV(port_id) {
373                 /* skip ports that are not enabled */
374                 if ((rsrc->enabled_port_mask & (1 << port_id)) == 0)
375                         continue;
376
377                 if (nb_ports_in_mask % 2) {
378                         rsrc->dst_ports[port_id] = last_port;
379                         rsrc->dst_ports[last_port] = port_id;
380                 } else {
381                         last_port = port_id;
382                 }
383
384                 nb_ports_in_mask++;
385         }
386         if (nb_ports_in_mask % 2) {
387                 printf("Notice: odd number of ports in portmask.\n");
388                 rsrc->dst_ports[last_port] = last_port;
389         }
390
391         nb_mbufs = RTE_MAX(nb_ports * (RTE_TEST_RX_DESC_DEFAULT +
392                                        RTE_TEST_TX_DESC_DEFAULT +
393                                        MAX_PKT_BURST + rte_lcore_count() *
394                                        MEMPOOL_CACHE_SIZE), 8192U);
395
396         /* create the mbuf pool */
397         rsrc->pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool",
398                         nb_mbufs, MEMPOOL_CACHE_SIZE, 0,
399                         RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
400         if (rsrc->pktmbuf_pool == NULL)
401                 rte_panic("Cannot init mbuf pool\n");
402
403         nb_ports_available = l2fwd_event_init_ports(rsrc);
404         if (!nb_ports_available)
405                 rte_panic("All available ports are disabled. Please set portmask.\n");
406
407         l2fwd_poll_resource_setup(rsrc);
408
409         /* initialize port stats */
410         memset(&rsrc->port_stats, 0,
411                                         sizeof(struct l2fwd_port_statistics));
412
413         /* All settings are done. Now enable eth devices */
414         RTE_ETH_FOREACH_DEV(port_id) {
415                 /* skip ports that are not enabled */
416                 if ((rsrc->enabled_port_mask &
417                                         (1 << port_id)) == 0)
418                         continue;
419
420                 ret = rte_eth_dev_start(port_id);
421                 if (ret < 0)
422                         rte_panic("rte_eth_dev_start:err=%d, port=%u\n", ret,
423                                   port_id);
424         }
425
426         check_all_ports_link_status(rsrc, rsrc->enabled_port_mask);
427
428         /* launch per-lcore init on every lcore */
429         rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, rsrc,
430                                  SKIP_MASTER);
431         l2fwd_event_print_stats(rsrc);
432         rte_eal_mp_wait_lcore();
433
434         RTE_ETH_FOREACH_DEV(port_id) {
435                 if ((rsrc->enabled_port_mask &
436                                                 (1 << port_id)) == 0)
437                         continue;
438                 printf("Closing port %d...", port_id);
439                 rte_eth_dev_stop(port_id);
440                 rte_eth_dev_close(port_id);
441                 printf(" Done\n");
442         }
443         printf("Bye...\n");
444
445         return 0;
446 }