89a6bb9a44d8acd134d6edd8b0eedb83d4cf0238
[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_event.h"
6 #include "l2fwd_poll.h"
7
8 /* display usage */
9 static void
10 l2fwd_event_usage(const char *prgname)
11 {
12         printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n"
13                "  -p PORTMASK: hexadecimal bitmask of ports to configure\n"
14                "  -q NQ: number of queue (=ports) per lcore (default is 1)\n"
15                "  -T PERIOD: statistics will be refreshed each PERIOD seconds "
16                "                (0 to disable, 10 default, 86400 maximum)\n"
17                "  --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n"
18                "      When enabled:\n"
19                "       - The source MAC address is replaced by the TX port MAC address\n"
20                "       - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n"
21                "  --mode: Packet transfer mode for I/O, poll or eventdev\n"
22                "          Default mode = eventdev\n"
23                "  --eventq-sched: Event queue schedule type, ordered, atomic or parallel.\n"
24                "                  Default: atomic\n"
25                "                  Valid only if --mode=eventdev\n\n",
26                prgname);
27 }
28
29 static int
30 l2fwd_event_parse_portmask(const char *portmask)
31 {
32         char *end = NULL;
33         unsigned long pm;
34
35         /* parse hexadecimal string */
36         pm = strtoul(portmask, &end, 16);
37         if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
38                 return -1;
39
40         if (pm == 0)
41                 return -1;
42
43         return pm;
44 }
45
46 static unsigned int
47 l2fwd_event_parse_nqueue(const char *q_arg)
48 {
49         char *end = NULL;
50         unsigned long n;
51
52         /* parse hexadecimal string */
53         n = strtoul(q_arg, &end, 10);
54         if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
55                 return 0;
56         if (n == 0)
57                 return 0;
58         if (n >= MAX_RX_QUEUE_PER_LCORE)
59                 return 0;
60
61         return n;
62 }
63
64 static int
65 l2fwd_event_parse_timer_period(const char *q_arg)
66 {
67         char *end = NULL;
68         int n;
69
70         /* parse number string */
71         n = strtol(q_arg, &end, 10);
72         if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
73                 return -1;
74         if (n >= MAX_TIMER_PERIOD)
75                 return -1;
76
77         return n;
78 }
79
80 static void
81 l2fwd_event_parse_mode(const char *optarg,
82                        struct l2fwd_resources *rsrc)
83 {
84         if (!strncmp(optarg, "poll", 4))
85                 rsrc->event_mode = false;
86         else if (!strncmp(optarg, "eventdev", 8))
87                 rsrc->event_mode = true;
88 }
89
90 static void
91 l2fwd_event_parse_eventq_sched(const char *optarg,
92                                struct l2fwd_resources *rsrc)
93 {
94         if (!strncmp(optarg, "ordered", 7))
95                 rsrc->sched_type = RTE_SCHED_TYPE_ORDERED;
96         else if (!strncmp(optarg, "atomic", 6))
97                 rsrc->sched_type = RTE_SCHED_TYPE_ATOMIC;
98         else if (!strncmp(optarg, "parallel", 8))
99                 rsrc->sched_type = RTE_SCHED_TYPE_PARALLEL;
100 }
101
102 static const char short_options[] =
103         "p:"  /* portmask */
104         "q:"  /* number of queues */
105         "T:"  /* timer period */
106         ;
107
108 #define CMD_LINE_OPT_MAC_UPDATING "mac-updating"
109 #define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating"
110 #define CMD_LINE_OPT_MODE "mode"
111 #define CMD_LINE_OPT_EVENTQ_SCHED "eventq-sched"
112
113 enum {
114         /* long options mapped to a short option */
115
116         /* first long only option value must be >= 256, so that we won't
117          * conflict with short options
118          */
119         CMD_LINE_OPT_MIN_NUM = 256,
120         CMD_LINE_OPT_MODE_NUM,
121         CMD_LINE_OPT_EVENTQ_SCHED_NUM,
122 };
123
124 /* Parse the argument given in the command line of the application */
125 static int
126 l2fwd_event_parse_args(int argc, char **argv,
127                 struct l2fwd_resources *rsrc)
128 {
129         int mac_updating = 1;
130         struct option lgopts[] = {
131                 { CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1},
132                 { CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0},
133                 { CMD_LINE_OPT_MODE, required_argument, NULL,
134                                                         CMD_LINE_OPT_MODE_NUM},
135                 { CMD_LINE_OPT_EVENTQ_SCHED, required_argument, NULL,
136                                                 CMD_LINE_OPT_EVENTQ_SCHED_NUM},
137                 {NULL, 0, 0, 0}
138         };
139         int opt, ret, timer_secs;
140         char *prgname = argv[0];
141         char **argvopt;
142         int option_index;
143
144         argvopt = argv;
145         while ((opt = getopt_long(argc, argvopt, short_options,
146                                   lgopts, &option_index)) != EOF) {
147
148                 switch (opt) {
149                 /* portmask */
150                 case 'p':
151                         rsrc->enabled_port_mask =
152                                         l2fwd_event_parse_portmask(optarg);
153                         if (rsrc->enabled_port_mask == 0) {
154                                 printf("invalid portmask\n");
155                                 l2fwd_event_usage(prgname);
156                                 return -1;
157                         }
158                         break;
159
160                 /* nqueue */
161                 case 'q':
162                         rsrc->rx_queue_per_lcore =
163                                         l2fwd_event_parse_nqueue(optarg);
164                         if (rsrc->rx_queue_per_lcore == 0) {
165                                 printf("invalid queue number\n");
166                                 l2fwd_event_usage(prgname);
167                                 return -1;
168                         }
169                         break;
170
171                 /* timer period */
172                 case 'T':
173                         timer_secs = l2fwd_event_parse_timer_period(optarg);
174                         if (timer_secs < 0) {
175                                 printf("invalid timer period\n");
176                                 l2fwd_event_usage(prgname);
177                                 return -1;
178                         }
179                         rsrc->timer_period = timer_secs;
180                         /* convert to number of cycles */
181                         rsrc->timer_period *= rte_get_timer_hz();
182                         break;
183
184                 case CMD_LINE_OPT_MODE_NUM:
185                         l2fwd_event_parse_mode(optarg, rsrc);
186                         break;
187
188                 case CMD_LINE_OPT_EVENTQ_SCHED_NUM:
189                         l2fwd_event_parse_eventq_sched(optarg, rsrc);
190                         break;
191
192                 /* long options */
193                 case 0:
194                         break;
195
196                 default:
197                         l2fwd_event_usage(prgname);
198                         return -1;
199                 }
200         }
201
202         rsrc->mac_updating = mac_updating;
203
204         if (optind >= 0)
205                 argv[optind-1] = prgname;
206
207         ret = optind-1;
208         optind = 1; /* reset getopt lib */
209         return ret;
210 }
211
212 static int
213 l2fwd_launch_one_lcore(void *args)
214 {
215         struct l2fwd_resources *rsrc = args;
216         struct l2fwd_poll_resources *poll_rsrc = rsrc->poll_rsrc;
217         struct l2fwd_event_resources *evt_rsrc = rsrc->evt_rsrc;
218
219         if (rsrc->event_mode)
220                 evt_rsrc->ops.l2fwd_event_loop(rsrc);
221         else
222                 poll_rsrc->poll_main_loop(rsrc);
223
224         return 0;
225 }
226
227 /* Check the link status of all ports in up to 9s, and print them finally */
228 static void
229 check_all_ports_link_status(struct l2fwd_resources *rsrc,
230                             uint32_t port_mask)
231 {
232 #define CHECK_INTERVAL 100 /* 100ms */
233 #define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
234         uint16_t port_id;
235         uint8_t count, all_ports_up, print_flag = 0;
236         struct rte_eth_link link;
237         int ret;
238
239         printf("\nChecking link status...");
240         fflush(stdout);
241         for (count = 0; count <= MAX_CHECK_TIME; count++) {
242                 if (rsrc->force_quit)
243                         return;
244                 all_ports_up = 1;
245                 RTE_ETH_FOREACH_DEV(port_id) {
246                         if (rsrc->force_quit)
247                                 return;
248                         if ((port_mask & (1 << port_id)) == 0)
249                                 continue;
250                         memset(&link, 0, sizeof(link));
251                         ret = rte_eth_link_get_nowait(port_id, &link);
252                         if (ret < 0) {
253                                 all_ports_up = 0;
254                                 if (print_flag == 1)
255                                         printf("Port %u link get failed: %s\n",
256                                                 port_id, rte_strerror(-ret));
257                                 continue;
258                         }
259                         /* print link status if flag set */
260                         if (print_flag == 1) {
261                                 if (link.link_status)
262                                         printf(
263                                         "Port%d Link Up. Speed %u Mbps - %s\n",
264                                                 port_id, link.link_speed,
265                                 (link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
266                                         ("full-duplex") : ("half-duplex\n"));
267                                 else
268                                         printf("Port %d Link Down\n", port_id);
269                                 continue;
270                         }
271                         /* clear all_ports_up flag if any link down */
272                         if (link.link_status == ETH_LINK_DOWN) {
273                                 all_ports_up = 0;
274                                 break;
275                         }
276                 }
277                 /* after finally printing all link status, get out */
278                 if (print_flag == 1)
279                         break;
280
281                 if (all_ports_up == 0) {
282                         printf(".");
283                         fflush(stdout);
284                         rte_delay_ms(CHECK_INTERVAL);
285                 }
286
287                 /* set the print_flag if all ports up or timeout */
288                 if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
289                         print_flag = 1;
290                         printf("done\n");
291                 }
292         }
293 }
294
295 /* Print out statistics on packets dropped */
296 static void
297 print_stats(struct l2fwd_resources *rsrc)
298 {
299         uint64_t total_packets_dropped, total_packets_tx, total_packets_rx;
300         uint32_t port_id;
301
302         total_packets_dropped = 0;
303         total_packets_tx = 0;
304         total_packets_rx = 0;
305
306         const char clr[] = {27, '[', '2', 'J', '\0' };
307         const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0' };
308
309                 /* Clear screen and move to top left */
310         printf("%s%s", clr, topLeft);
311
312         printf("\nPort statistics ====================================");
313
314         for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++) {
315                 /* skip disabled ports */
316                 if ((rsrc->enabled_port_mask & (1 << port_id)) == 0)
317                         continue;
318                 printf("\nStatistics for port %u ------------------------------"
319                            "\nPackets sent: %29"PRIu64
320                            "\nPackets received: %25"PRIu64
321                            "\nPackets dropped: %26"PRIu64,
322                            port_id,
323                            rsrc->port_stats[port_id].tx,
324                            rsrc->port_stats[port_id].rx,
325                            rsrc->port_stats[port_id].dropped);
326
327                 total_packets_dropped +=
328                                         rsrc->port_stats[port_id].dropped;
329                 total_packets_tx += rsrc->port_stats[port_id].tx;
330                 total_packets_rx += rsrc->port_stats[port_id].rx;
331         }
332
333         if (rsrc->event_mode) {
334                 struct l2fwd_event_resources *evt_rsrc = rsrc->evt_rsrc;
335                 struct rte_event_eth_rx_adapter_stats rx_adptr_stats;
336                 struct rte_event_eth_tx_adapter_stats tx_adptr_stats;
337                 int ret, i;
338
339                 for (i = 0; i < evt_rsrc->rx_adptr.nb_rx_adptr; i++) {
340                         ret = rte_event_eth_rx_adapter_stats_get(
341                                         evt_rsrc->rx_adptr.rx_adptr[i],
342                                         &rx_adptr_stats);
343                         if (ret < 0)
344                                 continue;
345                         printf("\nRx adapter[%d] statistics===================="
346                                    "\nReceive queue poll count: %17"PRIu64
347                                    "\nReceived packet count: %20"PRIu64
348                                    "\nEventdev enqueue count: %19"PRIu64
349                                    "\nEventdev enqueue retry count: %13"PRIu64
350                                    "\nReceived packet dropped count: %12"PRIu64
351                                    "\nRx enqueue start timestamp: %15"PRIu64
352                                    "\nRx enqueue block cycles: %18"PRIu64
353                                    "\nRx enqueue unblock timestamp: %13"PRIu64,
354                                    evt_rsrc->rx_adptr.rx_adptr[i],
355                                    rx_adptr_stats.rx_poll_count,
356                                    rx_adptr_stats.rx_packets,
357                                    rx_adptr_stats.rx_enq_count,
358                                    rx_adptr_stats.rx_enq_retry,
359                                    rx_adptr_stats.rx_dropped,
360                                    rx_adptr_stats.rx_enq_start_ts,
361                                    rx_adptr_stats.rx_enq_block_cycles,
362                                    rx_adptr_stats.rx_enq_end_ts);
363                 }
364                 for (i = 0; i <  evt_rsrc->tx_adptr.nb_tx_adptr; i++) {
365                         ret = rte_event_eth_tx_adapter_stats_get(
366                                         evt_rsrc->tx_adptr.tx_adptr[i],
367                                         &tx_adptr_stats);
368                         if (ret < 0)
369                                 continue;
370                         printf("\nTx adapter[%d] statistics===================="
371                                    "\nNumber of transmit retries: %15"PRIu64
372                                    "\nNumber of packets transmitted: %12"PRIu64
373                                    "\nNumber of packets dropped: %16"PRIu64,
374                                    evt_rsrc->tx_adptr.tx_adptr[i],
375                                    tx_adptr_stats.tx_retry,
376                                    tx_adptr_stats.tx_packets,
377                                    tx_adptr_stats.tx_dropped);
378                 }
379         }
380         printf("\nAggregate lcore statistics ========================="
381                    "\nTotal packets sent: %23"PRIu64
382                    "\nTotal packets received: %19"PRIu64
383                    "\nTotal packets dropped: %20"PRIu64,
384                    total_packets_tx,
385                    total_packets_rx,
386                    total_packets_dropped);
387         printf("\n====================================================\n");
388 }
389
390 static void
391 l2fwd_event_print_stats(struct l2fwd_resources *rsrc)
392 {
393         uint64_t prev_tsc = 0, diff_tsc, cur_tsc, timer_tsc = 0;
394         const uint64_t timer_period = rsrc->timer_period;
395
396         while (!rsrc->force_quit) {
397                 /* if timer is enabled */
398                 if (timer_period > 0) {
399                         cur_tsc = rte_rdtsc();
400                         diff_tsc = cur_tsc - prev_tsc;
401
402                         /* advance the timer */
403                         timer_tsc += diff_tsc;
404
405                         /* if timer has reached its timeout */
406                         if (unlikely(timer_tsc >= timer_period)) {
407                                 print_stats(rsrc);
408                                 /* reset the timer */
409                                 timer_tsc = 0;
410                         }
411                         prev_tsc = cur_tsc;
412                 }
413         }
414 }
415
416
417 static void
418 signal_handler(int signum)
419 {
420         struct l2fwd_resources *rsrc = l2fwd_get_rsrc();
421         if (signum == SIGINT || signum == SIGTERM) {
422                 printf("\n\nSignal %d received, preparing to exit...\n",
423                                 signum);
424                 rsrc->force_quit = true;
425         }
426 }
427
428 int
429 main(int argc, char **argv)
430 {
431         struct l2fwd_resources *rsrc;
432         uint16_t nb_ports_available = 0;
433         uint32_t nb_ports_in_mask = 0;
434         uint16_t port_id, last_port;
435         uint32_t nb_mbufs;
436         uint16_t nb_ports;
437         int i, ret;
438
439         /* init EAL */
440         ret = rte_eal_init(argc, argv);
441         if (ret < 0)
442                 rte_panic("Invalid EAL arguments\n");
443         argc -= ret;
444         argv += ret;
445
446         rsrc = l2fwd_get_rsrc();
447
448         signal(SIGINT, signal_handler);
449         signal(SIGTERM, signal_handler);
450
451         /* parse application arguments (after the EAL ones) */
452         ret = l2fwd_event_parse_args(argc, argv, rsrc);
453         if (ret < 0)
454                 rte_panic("Invalid L2FWD arguments\n");
455
456         printf("MAC updating %s\n", rsrc->mac_updating ? "enabled" :
457                         "disabled");
458
459         nb_ports = rte_eth_dev_count_avail();
460         if (nb_ports == 0)
461                 rte_panic("No Ethernet ports - bye\n");
462
463         /* check port mask to possible port mask */
464         if (rsrc->enabled_port_mask & ~((1 << nb_ports) - 1))
465                 rte_panic("Invalid portmask; possible (0x%x)\n",
466                         (1 << nb_ports) - 1);
467
468         /* reset l2fwd_dst_ports */
469         for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
470                 rsrc->dst_ports[port_id] = 0;
471         last_port = 0;
472
473         /*
474          * Each logical core is assigned a dedicated TX queue on each port.
475          */
476         RTE_ETH_FOREACH_DEV(port_id) {
477                 /* skip ports that are not enabled */
478                 if ((rsrc->enabled_port_mask & (1 << port_id)) == 0)
479                         continue;
480
481                 if (nb_ports_in_mask % 2) {
482                         rsrc->dst_ports[port_id] = last_port;
483                         rsrc->dst_ports[last_port] = port_id;
484                 } else {
485                         last_port = port_id;
486                 }
487
488                 nb_ports_in_mask++;
489         }
490         if (nb_ports_in_mask % 2) {
491                 printf("Notice: odd number of ports in portmask.\n");
492                 rsrc->dst_ports[last_port] = last_port;
493         }
494
495         nb_mbufs = RTE_MAX(nb_ports * (RTE_TEST_RX_DESC_DEFAULT +
496                                        RTE_TEST_TX_DESC_DEFAULT +
497                                        MAX_PKT_BURST + rte_lcore_count() *
498                                        MEMPOOL_CACHE_SIZE), 8192U);
499
500         /* create the mbuf pool */
501         rsrc->pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool",
502                         nb_mbufs, MEMPOOL_CACHE_SIZE, 0,
503                         RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
504         if (rsrc->pktmbuf_pool == NULL)
505                 rte_panic("Cannot init mbuf pool\n");
506
507         nb_ports_available = l2fwd_event_init_ports(rsrc);
508         if (!nb_ports_available)
509                 rte_panic("All available ports are disabled. Please set portmask.\n");
510
511         /* Configure eventdev parameters if required */
512         if (rsrc->event_mode)
513                 l2fwd_event_resource_setup(rsrc);
514         else
515                 l2fwd_poll_resource_setup(rsrc);
516
517         /* initialize port stats */
518         memset(&rsrc->port_stats, 0,
519                                         sizeof(struct l2fwd_port_statistics));
520
521         /* All settings are done. Now enable eth devices */
522         RTE_ETH_FOREACH_DEV(port_id) {
523                 /* skip ports that are not enabled */
524                 if ((rsrc->enabled_port_mask &
525                                         (1 << port_id)) == 0)
526                         continue;
527
528                 ret = rte_eth_dev_start(port_id);
529                 if (ret < 0)
530                         rte_panic("rte_eth_dev_start:err=%d, port=%u\n", ret,
531                                   port_id);
532         }
533
534         if (rsrc->event_mode)
535                 l2fwd_event_service_setup(rsrc);
536
537         check_all_ports_link_status(rsrc, rsrc->enabled_port_mask);
538
539         /* launch per-lcore init on every lcore */
540         rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, rsrc,
541                                  SKIP_MASTER);
542         l2fwd_event_print_stats(rsrc);
543         if (rsrc->event_mode) {
544                 struct l2fwd_event_resources *evt_rsrc =
545                                                         rsrc->evt_rsrc;
546                 for (i = 0; i < evt_rsrc->rx_adptr.nb_rx_adptr; i++)
547                         rte_event_eth_rx_adapter_stop(
548                                 evt_rsrc->rx_adptr.rx_adptr[i]);
549                 for (i = 0; i < evt_rsrc->tx_adptr.nb_tx_adptr; i++)
550                         rte_event_eth_tx_adapter_stop(
551                                 evt_rsrc->tx_adptr.tx_adptr[i]);
552
553                 RTE_ETH_FOREACH_DEV(port_id) {
554                         if ((rsrc->enabled_port_mask &
555                                                         (1 << port_id)) == 0)
556                                 continue;
557                         rte_eth_dev_stop(port_id);
558                 }
559
560                 rte_eal_mp_wait_lcore();
561                 RTE_ETH_FOREACH_DEV(port_id) {
562                         if ((rsrc->enabled_port_mask &
563                                                         (1 << port_id)) == 0)
564                                 continue;
565                         rte_eth_dev_close(port_id);
566                 }
567
568                 rte_event_dev_stop(evt_rsrc->event_d_id);
569                 rte_event_dev_close(evt_rsrc->event_d_id);
570
571         } else {
572                 rte_eal_mp_wait_lcore();
573
574                 RTE_ETH_FOREACH_DEV(port_id) {
575                         if ((rsrc->enabled_port_mask &
576                                                         (1 << port_id)) == 0)
577                                 continue;
578                         printf("Closing port %d...", port_id);
579                         rte_eth_dev_stop(port_id);
580                         rte_eth_dev_close(port_id);
581                         printf(" Done\n");
582                 }
583         }
584         printf("Bye...\n");
585
586         return 0;
587 }