DPDK message forwarding

Keywords: PHP network Mac Attribute less

Reference:

"Deep and Simple DPDK"

DPDK

  https://software.intel.com/en-us/articles/introduction-to-the-data-plane-development-kit-dpdk-packet-framework

...........................................................................................................

This part belongs to the core part of DPDK. For the whole life cycle of a message, how to access the router from the external interface of the docking operator, connect the network card of the computer, and send the process, maybe this is what we are doubtful about, and it is also the key part of this study. Only by understanding each step and link, can we understand the network message office thoroughly. Reason, and then solve the problems that arise.

I. Division of DPDK Network Processing Modules

Processing and forwarding of network messages are mainly divided into hardware and software processing parts, which are composed of the following modules:

  • packet input
  • pre-processing: coarse-grained processing of messages
  • input classification: fine-grained shunting of messages
  • ingress queuing: Provides a descriptor-based queue FIFO
  • delivery/scheduling: Scheduling based on queue priority and CPU status
  • accelerator: provides hardware functions such as encryption, decryption and compression/decompression
  • egress queuing: Scheduling at export according to the QoS level
  • post processing: Late message processing releases the cache
  • packet output: Send from hardware

 

Figure 1

As shown in Figure 1, the light and shadow parts are related to the corresponding modules and hardware, so the best choice to improve the performance of this part is to choose as many uninstall features related to network specific functions as possible on the network card or the device chip. The dark software part can improve network performance by improving the efficiency of the algorithm and combining CPU-related parallel instructions to understand the network. After the basic components of the processing module, let's look at how different forwarding frameworks enable these modules to work together to complete network packet processing.

  

Introduction of forwarding framework

Traditional Network Process forwarding models can be divided into run to comletion (RTC) model and pipeline (pipeline) model.

2.1 pipeline Model

Pipeline refers to the industrial pipeline model, dividing the whole function into several independent stages, and delivering products through queues in different stages. In this way, for some CPU-intensive and I/O-intensive applications, I/O-intensive operations will be executed on another microprocessor engine. Through filters, different threads can be allocated for different operations, and two speeds can be matched by queues to achieve the best concurrency.

  

We can see that in the figure, the TOP(Task Optimized Processor) unit, each TOP unit is a special micro-unit for optimizing a particular thing.

2.2 run to completion Model

This model is DPDK's operation method for general programs. A program is divided into several different logical functions. Several logical functions will run on a CPU core. Let's look at the view of the model below.

  

This model has no special processing unit for messages, only two NP cores, two NP cores use the burned microcodes to process messages.

  

2.3 Forwarding Model Comparison

From the run to completion model, we can clearly see that each IA's physical core is responsible for processing the entire message life cycle from RX to TX, which is very similar to the role of the nP core of AMCC mentioned earlier. In pipeline model, message processing can be divided into different logical function units A, B and C. A message needs to go through three stages: A, B and C. These three stages can be divided into more than one functional unit and distributed on different physical cores. Different functional units can be distributed on the same core (or distributed on different cores). It can be seen that the classification and invocation of modules are more flexible than EZchip's hardware scheme.

  

Two advantages and disadvantages:

  

  

3. Forwarding algorithm

In addition to a good forwarding framework, a very important part of forwarding is the matching and recognition of message segments. In DPDK, Exact Match (Exact Match) algorithm and Longest Prefix Matching (LPM) algorithm are mainly used to match messages to obtain corresponding information. Accurate matching mainly needs to solve two problems: data signature (hashing) and hash conflict. CRC32 and J hash are mainly supported in DPDK.

Longest Prefix Matching (LPM) algorithm refers to an algorithm used by routers in IP protocol for routing table selection. The LPM algorithm currently used by DPDK uses memory consumption to improve the performance of LPM lookup. When the prefix length of the lookup table entries is less than 24 bits, only one memory access is needed to find the next one. According to probability statistics, this is a relatively probabilistic one. When the current prefix is larger than 24 bits, two memory visits are required, but this is a small probability event.

The ACL library uses the matching rules of N tuples to perform type matching, providing the following basic operations:

  

Packet distributor (Packet Distribution) is an API library provided by DPDK to users for package distribution. The main functions can be described in the following figure:

  

Examples (refer to Intel website: https://software.intel.com/en-us/articles/introduction-to-the-data-plane-development-kit-dpdk-package-framework)

The ip_pipline application shows how to use the pipeline module by providing several sample applications and configuration files
The following figure shows the three-tier forwarding configuration application

  

In this example, it sets up simple routing.
The "core" entry thread ID (socket ID, physical CPU ID and hyperthreading ID) determines the CPU core used to run the pipeline.
The parameters "pktq_in" and "pktq_out" define the interface for data packet transmission, which refers to receiving and transmitting data packets.
The "encap" parameter can be set to "ethernet", "qinq" or "mpls" to encapsulate appropriate headers for all outbound packages.
The last parameter "ip_hdr_offset" can be used to set the offset bytes required for the starting position of ip-header in DPDK packet structure (mbuf).

The following figure is a script file of ip_pipeline, in which the contents will be added as routing table entries to the longest matching table of the routing pipeline:

  

  

p <pipeline_id> route add <ip_addr> <depth> port <port_id> ether <next hop mac_addr>

The following commands can be used to run L3 forwarding applications:

$./build/ip_pipeline -f l3fwd.cfg -p 0xf -s l3fwd.sh

Sometimes, applications have complex topologies when they are interconnected between different functional modules. Once the application configuration file is ready, run the following commands to generate the topology diagram

$./diagram-generator.py -f <configuration file>

Topology generated by the ip_pipeline sample program:

 

Various network functions (pipelines) built using this standard pipeline model can be used as part of the DPDK ip_pipeline sample application.

DPDK supports pipeline in the following ways:

  1)Packet I/O

  2)Flow classification

  3)Firewall

  4)Routing

  5)Metering

  6)Traffic Mgmt

Let's take an example of Guan l2 forwarding to see how the whole code works.

Source download: http://core.dpdk.org/download/

Code path: dpdk/examples/l2fwd

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <stdint.h>
  5 #include <inttypes.h>
  6 #include <sys/types.h>
  7 #include <sys/queue.h>
  8 #include <netinet/in.h>
  9 #include <setjmp.h>
 10 #include <stdarg.h>
 11 #include <ctype.h>
 12 #include <errno.h>
 13 #include <getopt.h>
 14 #include <signal.h>
 15 #include <stdbool.h>
 16 
 17 #include <rte_common.h>
 18 #include <rte_log.h>
 19 #include <rte_malloc.h>
 20 #include <rte_memory.h>
 21 #include <rte_memcpy.h>
 22 #include <rte_memzone.h>
 23 #include <rte_eal.h>
 24 #include <rte_per_lcore.h>
 25 #include <rte_launch.h>
 26 #include <rte_atomic.h>
 27 #include <rte_cycles.h>
 28 #include <rte_prefetch.h>
 29 #include <rte_lcore.h>
 30 #include <rte_per_lcore.h>
 31 #include <rte_branch_prediction.h>
 32 #include <rte_interrupts.h>
 33 #include <rte_pci.h>
 34 #include <rte_random.h>
 35 #include <rte_debug.h>
 36 #include <rte_ether.h>
 37 #include <rte_ethdev.h>
 38 #include <rte_mempool.h>
 39 #include <rte_mbuf.h>
 40 
 41 static volatile bool force_quit;
 42 
 43 /* MAC updating enabled by default */
 44 static int mac_updating = 1;
 45 
 46 #define RTE_LOGTYPE_L2FWD RTE_LOGTYPE_USER1
 47 
 48 #define NB_MBUF   8192
 49 
 50 #define MAX_PKT_BURST 32
 51 #define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */
 52 #define MEMPOOL_CACHE_SIZE 256
 53 
 54 /*
 55  * Configurable number of RX/TX ring descriptors
 56  */
 57 #define RTE_TEST_RX_DESC_DEFAULT 128
 58 #define RTE_TEST_TX_DESC_DEFAULT 512
 59 static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
 60 static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
 61 
 62 /* ethernet addresses of ports */
 63 //Ethernet address of port
 64 static struct ether_addr l2fwd_ports_eth_addr[RTE_MAX_ETHPORTS];
 65 
 66 /* mask of enabled ports */
 67 //Port-enabled mask
 68 static uint32_t l2fwd_enabled_port_mask = 0;
 69 
 70 /* list of enabled ports */
 71 //Enable Port List
 72 static uint32_t l2fwd_dst_ports[RTE_MAX_ETHPORTS];
 73 
 74 static unsigned int l2fwd_rx_queue_per_lcore = 1;
 75 
 76 #define MAX_RX_QUEUE_PER_LCORE 16
 77 #define MAX_TX_QUEUE_PER_PORT 16
 78 struct lcore_queue_conf {
 79     unsigned n_rx_port;
 80     unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE];
 81 } __rte_cache_aligned;
 82 struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE];
 83 
 84 static struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS];
 85 
 86 static const struct rte_eth_conf port_conf = {
 87     .rxmode = {
 88         .split_hdr_size = 0,
 89         .header_split   = 0, /**< Header Split disabled */  //Header separation
 90         .hw_ip_checksum = 0, /**< IP checksum offload disabled */ //IP Checksum offload
 91         .hw_vlan_filter = 0, /**< VLAN filtering disabled */  //vlan filter
 92         .jumbo_frame    = 0, /**< Jumbo Frame Support disabled */ //Supporting Giant Star Frames
 93         .hw_strip_crc   = 0, /**< CRC stripped by hardware */  //Use Hardware Clearance CRC
 94     },
 95     .txmode = {
 96         .mq_mode = ETH_MQ_TX_NONE,
 97     },
 98 };
 99 
100 struct rte_mempool * l2fwd_pktmbuf_pool = NULL;
101 
102 /* Per-port statistics struct */
103 struct l2fwd_port_statistics {
104     uint64_t tx;
105     uint64_t rx;
106     uint64_t dropped;
107 } __rte_cache_aligned;
108 struct l2fwd_port_statistics port_statistics[RTE_MAX_ETHPORTS];
109 
110 #define MAX_TIMER_PERIOD 86400 /* 1 day max */
111 /* A tsc-based timer responsible for triggering statistics printout */
112 static uint64_t timer_period = 10; /* default period is 10 seconds */
113 
114 /* Print out statistics on packets dropped */
115 //Printed attributes skip...
116 static void
117 print_stats(void)
118 {
119     uint64_t total_packets_dropped, total_packets_tx, total_packets_rx;
120     unsigned portid;
121 
122     total_packets_dropped = 0;
123     total_packets_tx = 0;
124     total_packets_rx = 0;
125 
126     const char clr[] = { 27, '[', '2', 'J', '\0' };
127     const char topLeft[] = { 27, '[', '1', ';', '1', 'H','\0' };
128 
129         /* Clear screen and move to top left */
130     printf("%s%s", clr, topLeft);
131 
132     printf("\nPort statistics ====================================");
133 
134     for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
135         /* skip disabled ports */
136         if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
137             continue;
138         printf("\nStatistics for port %u ------------------------------"
139                "\nPackets sent: %24"PRIu64
140                "\nPackets received: %20"PRIu64
141                "\nPackets dropped: %21"PRIu64,
142                portid,
143                port_statistics[portid].tx,
144                port_statistics[portid].rx,
145                port_statistics[portid].dropped);
146 
147         total_packets_dropped += port_statistics[portid].dropped;
148         total_packets_tx += port_statistics[portid].tx;
149         total_packets_rx += port_statistics[portid].rx;
150     }
151     printf("\nAggregate statistics ==============================="
152            "\nTotal packets sent: %18"PRIu64
153            "\nTotal packets received: %14"PRIu64
154            "\nTotal packets dropped: %15"PRIu64,
155            total_packets_tx,
156            total_packets_rx,
157            total_packets_dropped);
158     printf("\n====================================================\n");
159 }
160 
161 static void
162 l2fwd_mac_updating(struct rte_mbuf *m, unsigned dest_portid)
163 {
164     struct ether_hdr *eth;
165     void *tmp;
166 
167     eth = rte_pktmbuf_mtod(m, struct ether_hdr *);
168 
169     /* 02:00:00:00:00:xx */
170     tmp = &eth->d_addr.addr_bytes[0];
171     *((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dest_portid << 40);
172 
173     /* src addr */
174     ether_addr_copy(&l2fwd_ports_eth_addr[dest_portid], &eth->s_addr);
175 }
176 
177 //Processing incoming packets
178 static void
179 l2fwd_simple_forward(struct rte_mbuf *m, unsigned portid)
180 {
181     unsigned dst_port;
182     int sent;
183     struct rte_eth_dev_tx_buffer *buffer;
184 
185     //Get the destination port ID
186     dst_port = l2fwd_dst_ports[portid];
187 
188     //To update MAC address
189     if (mac_updating)
190         l2fwd_mac_updating(m, dst_port);
191 
192     //Getting the destination port tx cache
193     buffer = tx_buffer[dst_port];
194     //Send a packet to the destination port tx cache
195     sent = rte_eth_tx_buffer(dst_port, 0, buffer, m);
196     
197     if (sent)
198         port_statistics[dst_port].tx += sent;  //If the outgoing is successful, the outgoing number+1
199 }
200 
201 /* main processing loop */
202 static void
203 l2fwd_main_loop(void)
204 {
205     struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
206     struct rte_mbuf *m;
207     int sent;
208     unsigned lcore_id;
209     uint64_t prev_tsc, diff_tsc, cur_tsc, timer_tsc;
210     unsigned i, j, portid, nb_rx;
211     struct lcore_queue_conf *qconf;
212     const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) / US_PER_S *
213             BURST_TX_DRAIN_US;
214     struct rte_eth_dev_tx_buffer *buffer;
215 
216     prev_tsc = 0;
217     timer_tsc = 0;
218 
219     //Acquiring the Current Core ID
220     lcore_id = rte_lcore_id();
221     //Get the current core configuration
222     qconf = &lcore_queue_conf[lcore_id];
223 
224     //If rx_port The number of logs is 0. (It's actually a mistake)
225     if (qconf->n_rx_port == 0) {
226         RTE_LOG(INFO, L2FWD, "lcore %u has nothing to do\n", lcore_id);
227         return;
228     }
229 
230     //Logging,
231     RTE_LOG(INFO, L2FWD, "entering main loop on lcore %u\n", lcore_id);
232 
233     //Traveling through all port
234     for (i = 0; i < qconf->n_rx_port; i++) {
235         //Obtained portID
236         portid = qconf->rx_port_list[i];
237         //Logging
238         RTE_LOG(INFO, L2FWD, " -- lcoreid=%u portid=%u\n", lcore_id,
239             portid);
240 
241     }
242 
243     //If it does not time out (the run time of the first two-tier forwarding set)
244     while (!force_quit) {
245 
246         //Get the timestamp
247         cur_tsc = rte_rdtsc();
248 
249         /*
250          * TX burst queue drain
251          */
252          //Contrast timestamps
253         diff_tsc = cur_tsc - prev_tsc;
254         if (unlikely(diff_tsc > drain_tsc)) {
255 
256             for (i = 0; i < qconf->n_rx_port; i++) {
257                 //Get portid and buffer
258                 portid = l2fwd_dst_ports[qconf->rx_port_list[i]];
259                 buffer = tx_buffer[portid];
260                 
261                 //hold buffer Data sent in portid Corresponding port
262                 sent = rte_eth_tx_buffer_flush(portid, 0, buffer);
263                 if (sent)
264                     port_statistics[portid].tx += sent;  //If the outgoing is successful, the outgoing number+1
265 
266             }
267 
268             /* if timer is enabled */
269             //If the timer is turned on
270             if (timer_period > 0) {
271 
272                 /* advance the timer */
273                 //Adjust timer
274                 timer_tsc += diff_tsc;
275 
276                 /* if timer has reached its timeout */
277                 //If the timer times out
278                 if (unlikely(timer_tsc >= timer_period)) {
279 
280                     /* do this only on master core */
281                     //The main thread prints some properties, and only the main thread executes them. print_stats
282                     if (lcore_id == rte_get_master_lcore()) {
283                         print_stats();   //Print attributes,
284                         /* reset the timer */
285                         timer_tsc = 0;  //Set timer to 0
286                     }
287                 }
288             }
289 
290             prev_tsc = cur_tsc;
291         }
292 
293         /*
294          * Read packet from RX queues   //Packet Receiving Module
295          */
296         for (i = 0; i < qconf->n_rx_port; i++) {
297             //Obtain portID
298             portid = qconf->rx_port_list[i];
299             //from portID Corresponding Port Received nb_rx Package
300             nb_rx = rte_eth_rx_burst((uint8_t) portid, 0,
301                          pkts_burst, MAX_PKT_BURST);
302 
303             port_statistics[portid].rx += nb_rx;   //Packet Receiving Count+=nb_rx
304 
305             for (j = 0; j < nb_rx; j++) {  //Traverse the received package
306                 m = pkts_burst[j];
307                 rte_prefetch0(rte_pktmbuf_mtod(m, void *));    //Prefetch pktmbuf
308                 l2fwd_simple_forward(m, portid);   //Call functions to process incoming packages
309             }
310         }
311     }
312 }
313 
314 //__attribute__((unused))Represents that this parameter may not be available and that the compiler will not report errors
315 static int
316 l2fwd_launch_one_lcore(__attribute__((unused)) void *dummy)
317 {
318     l2fwd_main_loop(); //Layer 2 Forwarding main loop
319     return 0;
320 }
321 
322 /* display usage */
323 static void
324 l2fwd_usage(const char *prgname)
325 {
326     printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n"
327            "  -p PORTMASK: hexadecimal bitmask of ports to configure\n"
328            "  -q NQ: number of queue (=ports) per lcore (default is 1)\n"
329            "  -T PERIOD: statistics will be refreshed each PERIOD seconds (0 to disable, 10 default, 86400 maximum)\n"
330            "  --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n"
331            "      When enabled:\n"
332            "       - The source MAC address is replaced by the TX port MAC address\n"
333            "       - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n",
334            prgname);
335 }
336 
337 static int
338 l2fwd_parse_portmask(const char *portmask)
339 {
340     char *end = NULL;
341     unsigned long pm;
342 
343     /* parse hexadecimal string */
344     //Resolve hexadecimal strings and convert them to hexadecimal strings unsigned long
345     pm = strtoul(portmask, &end, 16);
346     if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
347         return -1;
348 
349     if (pm == 0)
350         return -1;
351 
352     return pm;
353 }
354 
355 static unsigned int
356 l2fwd_parse_nqueue(const char *q_arg)
357 {
358     char *end = NULL;
359     unsigned long n;
360 
361     /* parse hexadecimal string */
362     n = strtoul(q_arg, &end, 10);
363     if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
364         return 0;
365     if (n == 0)
366         return 0;
367     if (n >= MAX_RX_QUEUE_PER_LCORE)
368         return 0;
369 
370     return n;
371 }
372 
373 static int
374 l2fwd_parse_timer_period(const char *q_arg)
375 {
376     char *end = NULL;
377     int n;
378 
379     /* parse number string */
380     n = strtol(q_arg, &end, 10);
381     if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
382         return -1;
383     if (n >= MAX_TIMER_PERIOD)
384         return -1;
385 
386     return n;
387 }
388 
389 //Parsing command-line parameters
390 /* Parse the argument given in the command line of the application */
391 static int
392 l2fwd_parse_args(int argc, char **argv)
393 {
394     int opt, ret, timer_secs;
395     char **argvopt;
396     int option_index;
397     char *prgname = argv[0];
398     static struct option lgopts[] = {
399         { "mac-updating", no_argument, &mac_updating, 1},
400         { "no-mac-updating", no_argument, &mac_updating, 0},
401         {NULL, 0, 0, 0}
402     };
403 
404     argvopt = argv;     //copy argv Pointer
405 
406     //Parse the command line, getopt_long()Can parse command line parameters
407     while ((opt = getopt_long(argc, argvopt, "p:q:T:",
408                   lgopts, &option_index)) != EOF) {
409 
410         //The command line has three parameters. p:portmask,n:nqueue,T: timer period 
411         switch (opt) {
412         /* portmask */
413         //port Number, expressed in hexadecimal, as 0. x0f Representation 15
414         case 'p':
415             //l2fwd_parse_portmask()Functions are custom functions
416             //Put the hexadecimal mask String conversion unsigned long. 
417             //Port enablement
418             l2fwd_enabled_port_mask = l2fwd_parse_portmask(optarg);
419             if (l2fwd_enabled_port_mask == 0) {  //mask 0 represents error
420                 printf("invalid portmask\n");
421                 l2fwd_usage(prgname);   //Print user options, similar to --help
422                 return -1;    //The parameter passing error returns-1,The result is an exit procedure.
423             }
424             break;
425 
426         /* nqueue */
427         //The number of queues is also expressed in hexadecimal strings.
428         case 'q':
429             //Several for each core configuration rx Queue.
430             l2fwd_rx_queue_per_lcore = l2fwd_parse_nqueue(optarg);
431             if (l2fwd_rx_queue_per_lcore == 0) {   //lcore 0 Represent an error
432                 printf("invalid queue number\n");
433                 l2fwd_usage(prgname);
434                 return -1;   //Return-1,The result is withdrawal from the process.
435             }
436             break;
437 
438         /* timer period */  //Timer cycle
439         
440         case 'T':
441             //Configure timer cycle in seconds
442             //Namely l2fwd How many seconds to run, expressed in hexadecimal.
443             timer_secs = l2fwd_parse_timer_period(optarg);
444             if (timer_secs < 0) {
445                 printf("invalid timer period\n");
446                 l2fwd_usage(prgname);
447                 return -1;
448             }
449             //timer_period Represents the test time of Layer 2 forwarding, which defaults to 10 seconds.-T To regulate time
450             timer_period = timer_secs;
451             break;
452 
453         /* long options */
454         //--The first option is not handled
455         case 0:
456             break;
457 
458         default:
459             //Parameter Passing Error, Printing--help
460             l2fwd_usage(prgname);
461             return -1;
462         }
463     }
464 
465     if (optind >= 0)
466         argv[optind-1] = prgname;
467 
468     ret = optind-1;
469     optind = 0; /* reset getopt lib */
470     return ret;
471 }
472 
473 /* Check the link status of all ports in up to 9s, and print them finally */
474 static void
475 check_all_ports_link_status(uint8_t port_num, uint32_t port_mask)
476 {
477 #define CHECK_INTERVAL 100 /* 100ms */
478 #define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
479     uint8_t portid, count, all_ports_up, print_flag = 0;
480     struct rte_eth_link link;
481 
482     printf("\nChecking link status");
483     fflush(stdout);
484     for (count = 0; count <= MAX_CHECK_TIME; count++) {
485         if (force_quit)
486             return;
487         all_ports_up = 1;
488         for (portid = 0; portid < port_num; portid++) {
489             if (force_quit)
490                 return;
491             if ((port_mask & (1 << portid)) == 0)
492                 continue;
493             memset(&link, 0, sizeof(link));
494             rte_eth_link_get_nowait(portid, &link);
495             /* print link status if flag set */
496             if (print_flag == 1) {
497                 if (link.link_status)
498                     printf("Port %d Link Up - speed %u "
499                         "Mbps - %s\n", (uint8_t)portid,
500                         (unsigned)link.link_speed,
501                 (link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
502                     ("full-duplex") : ("half-duplex\n"));
503                 else
504                     printf("Port %d Link Down\n",
505                         (uint8_t)portid);
506                 continue;
507             }
508             /* clear all_ports_up flag if any link down */
509             if (link.link_status == ETH_LINK_DOWN) {
510                 all_ports_up = 0;
511                 break;
512             }
513         }
514         /* after finally printing all link status, get out */
515         if (print_flag == 1)
516             break;
517 
518         if (all_ports_up == 0) {
519             printf(".");
520             fflush(stdout);
521             rte_delay_ms(CHECK_INTERVAL);
522         }
523 
524         /* set the print_flag if all ports up or timeout */
525         if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
526             print_flag = 1;
527             printf("done\n");
528         }
529     }
530 }
531 
532 static void
533 signal_handler(int signum)
534 {
535     if (signum == SIGINT || signum == SIGTERM) {
536         printf("\n\nSignal %d received, preparing to exit...\n",
537                 signum);
538         force_quit = true;    //Forced exit button is true
539     }
540 }
541 
542 int
543 main(int argc, char **argv)
544 {
545     /*
546     //Customized structure in front of the program
547     // __rte_cache_aligned Is a macro that represents memory for it
548     struct lcore_queue_conf {
549         unsigned n_rx_port;        //rx_port Number
550         unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE];    //rx_port list
551     } __rte_cache_aligned;
552     struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE];
553     //One computer, with multiple cores, and one core processing multiple rx_port s
554     */
555     struct lcore_queue_conf *qconf;    
556     
557     
558     struct rte_eth_dev_info dev_info;    //Equipment Information
559     int ret;                            //Return value
560     uint8_t nb_ports;                    //total port Number
561     uint8_t nb_ports_available;            //available port Number
562     uint8_t portid, last_port;            //current portid,Previous portid. 
563     unsigned lcore_id, rx_lcore_id;        //core id,rx_lcore_id
564     unsigned nb_ports_in_mask = 0;        //??
565 
566     /* init EAL */
567     //Initialization environment
568     ret = rte_eal_init(argc, argv);
569     if (ret < 0)
570         rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n");
571     argc -= ret;    //??Unknown, seemingly useless, so unknown
572     argv += ret;    //??Unknown, seemingly useless, so unknown
573 
574     force_quit = false;                    //Mandatory push button is false
575     //When captured SIGINT or SIGTERM When signal, push button is true. See details. signal_handler
576     //signal_handler For custom functions
577     signal(SIGINT, signal_handler);        //Signal acquisition processing 1
578     signal(SIGTERM, signal_handler);    //Signal acquisition processing 2
579 
580     /* parse application arguments (after the EAL ones) */
581     //Analytical Reference Transfer (Must be eal_init After that, this is a custom function, as detailed in the function definition
582     ret = l2fwd_parse_args(argc, argv);
583     if (ret < 0)
584         rte_exit(EXIT_FAILURE, "Invalid L2FWD arguments\n");
585 
586     printf("MAC updating %s\n", mac_updating ? "enabled" : "disabled");
587 
588     /* convert to number of cycles */
589     //Getting the clock cycle of the timer
590     timer_period *= rte_get_timer_hz();
591 
592     /* create the mbuf pool */
593     //Create a cache pool for storing data packets.
594     l2fwd_pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", NB_MBUF,
595         MEMPOOL_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE,
596         rte_socket_id());
597     if (l2fwd_pktmbuf_pool == NULL)
598         rte_exit(EXIT_FAILURE, "Cannot init mbuf pool\n");
599 
600     //Obtain Eth Number of mouths
601     nb_ports = rte_eth_dev_count();
602     if (nb_ports == 0)
603         rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
604 
605     /* reset l2fwd_dst_ports */
606     //Reset (clear 0) destination port. This block of code is used to initialize l2fwd_dst_ports
607     //Personal Comparisons of Writing low
608     for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++)
609         l2fwd_dst_ports[portid] = 0;
610     last_port = 0;
611 
612     /* 
613      * Each logical core is assigned a dedicated TX queue on each port.
614      */
615     //Set each port The purpose of the received packet to be forwarded port. 
616     for (portid = 0; portid < nb_ports; portid++) {
617         /* skip ports that are not enabled */
618         //Skip the Unenabled Port
619         if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
620             continue;
621 
622         //Every two port Two-tier forwarding for one pair
623         //Send singular to singular+1,Send a digit to a digit-1
624         if (nb_ports_in_mask % 2) {
625             l2fwd_dst_ports[portid] = last_port;
626             l2fwd_dst_ports[last_port] = portid;
627         }
628         else
629             last_port = portid;  //Yes last_port Assignment.
630 
631         nb_ports_in_mask++;   //Marking ++
632 
633         rte_eth_dev_info_get(portid, &dev_info);   //Acquire correspondence portid Of dev information
634     }
635     //If there is only one network port left, the network port forwards itself to itself.
636     if (nb_ports_in_mask % 2) {   
637         printf("Notice: odd number of ports in portmask.\n");
638         l2fwd_dst_ports[last_port] = last_port;
639     }
640 
641     rx_lcore_id = 0;   
642     qconf = NULL;      
643 
644     //Initialize the queue configuration for each core
645     /* Initialize the port/queue configuration of each logical core */
646     //Travel through all port
647     for (portid = 0; portid < nb_ports; portid++) {
648         /* skip ports that are not enabled */
649         //Skip the Unenabled Port
650         if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
651             continue;
652 
653         /* get the lcore_id for this port */
654         //port Allocate one lcore_id. 
655         //rte_lcore_is_enabled(rx_lcore_id) test core Enabling or not
656         //lcore_queue_conf Is a custom structure that contains port Number sum portid
657         //Meaning is one core, for multiple port Of rx queue
658         //Here, a core can only match one port
659         while (rte_lcore_is_enabled(rx_lcore_id) == 0 ||
660                lcore_queue_conf[rx_lcore_id].n_rx_port ==
661                l2fwd_rx_queue_per_lcore) {
662             rx_lcore_id++;
663             if (rx_lcore_id >= RTE_MAX_LCORE)
664                 rte_exit(EXIT_FAILURE, "Not enough cores\n");
665         }
666 
667         //assignment qconf,qconf Start with NULL
668         if (qconf != &lcore_queue_conf[rx_lcore_id])
669             /* Assigned a new logical core in the loop above. */
670             qconf = &lcore_queue_conf[rx_lcore_id];
671 
672         //current lcore Add a port
673         //current lcore Management rx_port Total+1
674         qconf->rx_port_list[qconf->n_rx_port] = portid;
675         qconf->n_rx_port++;
676         printf("Lcore %u: RX port %u\n", rx_lcore_id, (unsigned) portid);
677     }
678 
679     //Enabling port Total
680     nb_ports_available = nb_ports;
681 
682     /* Initialise each port */
683     //Getting Available port Number
684     for (portid = 0; portid < nb_ports; portid++) {
685         /* skip ports that are not enabled */
686         //If it is not available, it will be cut off.
687         if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) {
688             printf("Skipping disabled port %u\n", (unsigned) portid);
689             nb_ports_available--;
690             continue;
691         }
692         /* init port */
693         //Initialization port
694         printf("Initializing port %u... ", (unsigned) portid);
695         fflush(stdout);
696         //To configure port,among port_conf Is a structure that contains information about port Configuration
697         //vlan Separation, crc Hardware validation considers these two useful.
698         ret = rte_eth_dev_configure(portid, 1, 1, &port_conf);
699         if (ret < 0)
700             rte_exit(EXIT_FAILURE, "Cannot configure device: err=%d, port=%u\n",
701                   ret, (unsigned) portid);
702     
703         //Obtain port Of mac address
704         //l2fwd_ports_eth_addr[] The type is a structure and is a global array.
705         rte_eth_macaddr_get(portid,&l2fwd_ports_eth_addr[portid]);
706 
707         /* init one RX queue */
708         //Initialization RX queue
709         fflush(stdout);
710         //rx The queue is bound to the current port Up, l2fwd_pktmbuf_pool For memory pool
711         ret = rte_eth_rx_queue_setup(portid, 0, nb_rxd,
712                          rte_eth_dev_socket_id(portid),
713                          NULL,
714                          l2fwd_pktmbuf_pool);
715         if (ret < 0)
716             rte_exit(EXIT_FAILURE, "rte_eth_rx_queue_setup:err=%d, port=%u\n",
717                   ret, (unsigned) portid);
718 
719         /* init one TX queue on each port */
720         //Initialization tx queue
721         fflush(stdout);
722         //tx Queues are bound to port upper
723         ret = rte_eth_tx_queue_setup(portid, 0, nb_txd,
724                 rte_eth_dev_socket_id(portid),
725                 NULL);
726         if (ret < 0)
727             rte_exit(EXIT_FAILURE, "rte_eth_tx_queue_setup:err=%d, port=%u\n",
728                 ret, (unsigned) portid);
729 
730         /* Initialize TX buffers */
731         //malloc tx Caching, tx_buffer For pointer arrays, each port Correspond to one tx cache
732         tx_buffer[portid] = rte_zmalloc_socket("tx_buffer",
733                 RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST), 0,
734                 rte_eth_dev_socket_id(portid));
735         if (tx_buffer[portid] == NULL)
736             rte_exit(EXIT_FAILURE, "Cannot allocate buffer for tx on port %u\n",
737                     (unsigned) portid);
738         
739         //Initialization tx cache
740         rte_eth_tx_buffer_init(tx_buffer[portid], MAX_PKT_BURST);
741 
742         //tx When the cache is full, set the callback function
743         ret = rte_eth_tx_buffer_set_err_callback(tx_buffer[portid],
744                 rte_eth_tx_buffer_count_callback,
745                 &port_statistics[portid].dropped);
746         if (ret < 0)
747                 rte_exit(EXIT_FAILURE, "Cannot set error callback for "
748                         "tx buffer on port %u\n", (unsigned) portid);
749 
750         /* Start device */
751         //Start-up equipment
752         ret = rte_eth_dev_start(portid);
753         if (ret < 0)
754             rte_exit(EXIT_FAILURE, "rte_eth_dev_start:err=%d, port=%u\n",
755                   ret, (unsigned) portid);
756 
757         printf("done: \n");
758 
759         //Setting Network Card to Hybrid Mode
760         rte_eth_promiscuous_enable(portid);
761 
762         //Printing port Of MAC address
763         printf("Port %u, MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n\n",
764                 (unsigned) portid,
765                 l2fwd_ports_eth_addr[portid].addr_bytes[0],
766                 l2fwd_ports_eth_addr[portid].addr_bytes[1],
767                 l2fwd_ports_eth_addr[portid].addr_bytes[2],
768                 l2fwd_ports_eth_addr[portid].addr_bytes[3],
769                 l2fwd_ports_eth_addr[portid].addr_bytes[4],
770                 l2fwd_ports_eth_addr[portid].addr_bytes[5]);
771 
772         /* initialize port stats */
773         //Initialization port attribute
774         memset(&port_statistics, 0, sizeof(port_statistics));
775     }
776 
777     //If the network card can be made to zero, then an error will be reported.
778     if (!nb_ports_available) {
779         rte_exit(EXIT_FAILURE,
780             "All available ports are disabled. Please set portmask.\n");
781     }
782 
783     //Check all port Of link attribute
784     check_all_ports_link_status(nb_ports, l2fwd_enabled_port_mask);
785 
786     ret = 0;
787     /* launch per-lcore init on every lcore */
788     //Start all lcore,Callback l2fwd_launch_one_lcore Function, parameter is NULL
789     rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, NULL, CALL_MASTER);
790     //Travel through all lcore_id
791     RTE_LCORE_FOREACH_SLAVE(lcore_id) {
792         //Wait for all threads to end
793         if (rte_eal_wait_lcore(lcore_id) < 0) {
794             ret = -1;
795             break;
796         }
797     }
798 
799     //Stop and close all port
800     for (portid = 0; portid < nb_ports; portid++) {
801         if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
802             continue;
803         printf("Closing port %d...", portid);
804         rte_eth_dev_stop(portid);  
805         rte_eth_dev_close(portid);
806         printf(" Done\n");
807     }
808     printf("Bye...\n");
809 
810     return ret;
811 }

Posted by kristalys on Wed, 26 Jun 2019 13:01:59 -0700