1d30ea906Sjfb8856606.. SPDX-License-Identifier: BSD-3-Clause 2d30ea906Sjfb8856606 Copyright(c) 2016-2017 Intel Corporation. 32bfe3f2eSlogwang 42bfe3f2eSlogwangServer-Node EFD Sample Application 52bfe3f2eSlogwang================================== 62bfe3f2eSlogwang 72bfe3f2eSlogwangThis sample application demonstrates the use of EFD library as a flow-level 82bfe3f2eSlogwangload balancer, for more information about the EFD Library please refer to the 92bfe3f2eSlogwangDPDK programmer's guide. 102bfe3f2eSlogwang 112bfe3f2eSlogwangThis sample application is a variant of the 122bfe3f2eSlogwang:ref:`client-server sample application <multi_process_app>` 132bfe3f2eSlogwangwhere a specific target node is specified for every and each flow 142bfe3f2eSlogwang(not in a round-robin fashion as the original load balancing sample application). 152bfe3f2eSlogwang 162bfe3f2eSlogwangOverview 172bfe3f2eSlogwang-------- 182bfe3f2eSlogwang 192bfe3f2eSlogwangThe architecture of the EFD flow-based load balancer sample application is 202bfe3f2eSlogwangpresented in the following figure. 212bfe3f2eSlogwang 222bfe3f2eSlogwang.. _figure_efd_sample_app_overview: 232bfe3f2eSlogwang 242bfe3f2eSlogwang.. figure:: img/server_node_efd.* 252bfe3f2eSlogwang 262bfe3f2eSlogwang Using EFD as a Flow-Level Load Balancer 272bfe3f2eSlogwang 282bfe3f2eSlogwangAs shown in :numref:`figure_efd_sample_app_overview`, 292bfe3f2eSlogwangthe sample application consists of a front-end node (server) 302bfe3f2eSlogwangusing the EFD library to create a load-balancing table for flows, 312bfe3f2eSlogwangfor each flow a target backend worker node is specified. The EFD table does not 322bfe3f2eSlogwangstore the flow key (unlike a regular hash table), and hence, it can 332bfe3f2eSlogwangindividually load-balance millions of flows (number of targets * maximum number 342bfe3f2eSlogwangof flows fit in a flow table per target) while still fitting in CPU cache. 352bfe3f2eSlogwang 362bfe3f2eSlogwangIt should be noted that although they are referred to as nodes, the frontend 372bfe3f2eSlogwangserver and worker nodes are processes running on the same platform. 382bfe3f2eSlogwang 392bfe3f2eSlogwangFront-end Server 402bfe3f2eSlogwang~~~~~~~~~~~~~~~~ 412bfe3f2eSlogwang 422bfe3f2eSlogwangUpon initializing, the frontend server node (process) creates a flow 432bfe3f2eSlogwangdistributor table (based on the EFD library) which is populated with flow 442bfe3f2eSlogwanginformation and its intended target node. 452bfe3f2eSlogwang 462bfe3f2eSlogwangThe sample application assigns a specific target node_id (process) for each of 472bfe3f2eSlogwangthe IP destination addresses as follows: 482bfe3f2eSlogwang 492bfe3f2eSlogwang.. code-block:: c 502bfe3f2eSlogwang 512bfe3f2eSlogwang node_id = i % num_nodes; /* Target node id is generated */ 522bfe3f2eSlogwang ip_dst = rte_cpu_to_be_32(i); /* Specific ip destination address is 532bfe3f2eSlogwang assigned to this target node */ 542bfe3f2eSlogwang 552bfe3f2eSlogwangthen the pair of <key,target> is inserted into the flow distribution table. 562bfe3f2eSlogwang 572bfe3f2eSlogwangThe main loop of the server process receives a burst of packets, then for 582bfe3f2eSlogwangeach packet, a flow key (IP destination address) is extracted. The flow 592bfe3f2eSlogwangdistributor table is looked up and the target node id is returned. Packets are 602bfe3f2eSlogwangthen enqueued to the specified target node id. 612bfe3f2eSlogwang 622bfe3f2eSlogwangIt should be noted that flow distributor table is not a membership test table. 632bfe3f2eSlogwangI.e. if the key has already been inserted the target node id will be correct, 642bfe3f2eSlogwangbut for new keys the flow distributor table will return a value (which can be 652bfe3f2eSlogwangvalid). 662bfe3f2eSlogwang 672bfe3f2eSlogwangBackend Worker Nodes 682bfe3f2eSlogwang~~~~~~~~~~~~~~~~~~~~ 692bfe3f2eSlogwang 702bfe3f2eSlogwangUpon initializing, the worker node (process) creates a flow table (a regular 712bfe3f2eSlogwanghash table that stores the key default size 1M flows) which is populated with 722bfe3f2eSlogwangonly the flow information that is serviced at this node. This flow key is 732bfe3f2eSlogwangessential to point out new keys that have not been inserted before. 742bfe3f2eSlogwang 752bfe3f2eSlogwangThe worker node's main loop is simply receiving packets then doing a hash table 762bfe3f2eSlogwanglookup. If a match occurs then statistics are updated for flows serviced by 772bfe3f2eSlogwangthis node. If no match is found in the local hash table then this indicates 782bfe3f2eSlogwangthat this is a new flow, which is dropped. 792bfe3f2eSlogwang 802bfe3f2eSlogwang 812bfe3f2eSlogwangCompiling the Application 822bfe3f2eSlogwang------------------------- 832bfe3f2eSlogwang 842bfe3f2eSlogwangTo compile the sample application see :doc:`compiling`. 852bfe3f2eSlogwang 862bfe3f2eSlogwangThe application is located in the ``server_node_efd`` sub-directory. 872bfe3f2eSlogwang 882bfe3f2eSlogwangRunning the Application 892bfe3f2eSlogwang----------------------- 902bfe3f2eSlogwang 912bfe3f2eSlogwangThe application has two binaries to be run: the front-end server 922bfe3f2eSlogwangand the back-end node. 932bfe3f2eSlogwang 942bfe3f2eSlogwangThe frontend server (server) has the following command line options:: 952bfe3f2eSlogwang 96*2d9fd380Sjfb8856606 ./<build_dir>/examples/dpdk-server [EAL options] -- -p PORTMASK -n NUM_NODES -f NUM_FLOWS 972bfe3f2eSlogwang 982bfe3f2eSlogwangWhere, 992bfe3f2eSlogwang 1002bfe3f2eSlogwang* ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure 1012bfe3f2eSlogwang* ``-n NUM_NODES:`` Number of back-end nodes that will be used 1022bfe3f2eSlogwang* ``-f NUM_FLOWS:`` Number of flows to be added in the EFD table (1 million, by default) 1032bfe3f2eSlogwang 1042bfe3f2eSlogwangThe back-end node (node) has the following command line options:: 1052bfe3f2eSlogwang 1062bfe3f2eSlogwang ./node [EAL options] -- -n NODE_ID 1072bfe3f2eSlogwang 1082bfe3f2eSlogwangWhere, 1092bfe3f2eSlogwang 1102bfe3f2eSlogwang* ``-n NODE_ID:`` Node ID, which cannot be equal or higher than NUM_MODES 1112bfe3f2eSlogwang 1122bfe3f2eSlogwang 1132bfe3f2eSlogwangFirst, the server app must be launched, with the number of nodes that will be run. 1142bfe3f2eSlogwangOnce it has been started, the node instances can be run, with different NODE_ID. 1152bfe3f2eSlogwangThese instances have to be run as secondary processes, with ``--proc-type=secondary`` 1162bfe3f2eSlogwangin the EAL options, which will attach to the primary process memory, and therefore, 1172bfe3f2eSlogwangthey can access the queues created by the primary process to distribute packets. 1182bfe3f2eSlogwang 1192bfe3f2eSlogwangTo successfully run the application, the command line used to start the 1202bfe3f2eSlogwangapplication has to be in sync with the traffic flows configured on the traffic 1212bfe3f2eSlogwanggenerator side. 1222bfe3f2eSlogwang 1232bfe3f2eSlogwangFor examples of application command lines and traffic generator flows, please 1242bfe3f2eSlogwangrefer to the DPDK Test Report. For more details on how to set up and run the 1252bfe3f2eSlogwangsample applications provided with DPDK package, please refer to the 1262bfe3f2eSlogwang:ref:`DPDK Getting Started Guide for Linux <linux_gsg>` and 1272bfe3f2eSlogwang:ref:`DPDK Getting Started Guide for FreeBSD <freebsd_gsg>`. 1282bfe3f2eSlogwang 1292bfe3f2eSlogwang 1302bfe3f2eSlogwangExplanation 1312bfe3f2eSlogwang----------- 1322bfe3f2eSlogwang 1332bfe3f2eSlogwangAs described in previous sections, there are two processes in this example. 1342bfe3f2eSlogwang 1352bfe3f2eSlogwangThe first process, the front-end server, creates and populates the EFD table, 1362bfe3f2eSlogwangwhich is used to distribute packets to nodes, which the number of flows 1372bfe3f2eSlogwangspecified in the command line (1 million, by default). 1382bfe3f2eSlogwang 1392bfe3f2eSlogwang 1402bfe3f2eSlogwang.. code-block:: c 1412bfe3f2eSlogwang 1422bfe3f2eSlogwang static void 1432bfe3f2eSlogwang create_efd_table(void) 1442bfe3f2eSlogwang { 1452bfe3f2eSlogwang uint8_t socket_id = rte_socket_id(); 1462bfe3f2eSlogwang 1472bfe3f2eSlogwang /* create table */ 1482bfe3f2eSlogwang efd_table = rte_efd_create("flow table", num_flows * 2, sizeof(uint32_t), 1492bfe3f2eSlogwang 1 << socket_id, socket_id); 1502bfe3f2eSlogwang 1512bfe3f2eSlogwang if (efd_table == NULL) 1522bfe3f2eSlogwang rte_exit(EXIT_FAILURE, "Problem creating the flow table\n"); 1532bfe3f2eSlogwang } 1542bfe3f2eSlogwang 1552bfe3f2eSlogwang static void 1562bfe3f2eSlogwang populate_efd_table(void) 1572bfe3f2eSlogwang { 1582bfe3f2eSlogwang unsigned int i; 1592bfe3f2eSlogwang int32_t ret; 1602bfe3f2eSlogwang uint32_t ip_dst; 1612bfe3f2eSlogwang uint8_t socket_id = rte_socket_id(); 1622bfe3f2eSlogwang uint64_t node_id; 1632bfe3f2eSlogwang 1642bfe3f2eSlogwang /* Add flows in table */ 1652bfe3f2eSlogwang for (i = 0; i < num_flows; i++) { 1662bfe3f2eSlogwang node_id = i % num_nodes; 1672bfe3f2eSlogwang 1682bfe3f2eSlogwang ip_dst = rte_cpu_to_be_32(i); 1692bfe3f2eSlogwang ret = rte_efd_update(efd_table, socket_id, 1702bfe3f2eSlogwang (void *)&ip_dst, (efd_value_t)node_id); 1712bfe3f2eSlogwang if (ret < 0) 1722bfe3f2eSlogwang rte_exit(EXIT_FAILURE, "Unable to add entry %u in " 1732bfe3f2eSlogwang "EFD table\n", i); 1742bfe3f2eSlogwang } 1752bfe3f2eSlogwang 1762bfe3f2eSlogwang printf("EFD table: Adding 0x%x keys\n", num_flows); 1772bfe3f2eSlogwang } 1782bfe3f2eSlogwang 1792bfe3f2eSlogwangAfter initialization, packets are received from the enabled ports, and the IPv4 1802bfe3f2eSlogwangaddress from the packets is used as a key to look up in the EFD table, 1812bfe3f2eSlogwangwhich tells the node where the packet has to be distributed. 1822bfe3f2eSlogwang 1832bfe3f2eSlogwang.. code-block:: c 1842bfe3f2eSlogwang 1852bfe3f2eSlogwang static void 1862bfe3f2eSlogwang process_packets(uint32_t port_num __rte_unused, struct rte_mbuf *pkts[], 1872bfe3f2eSlogwang uint16_t rx_count, unsigned int socket_id) 1882bfe3f2eSlogwang { 1892bfe3f2eSlogwang uint16_t i; 1902bfe3f2eSlogwang uint8_t node; 1912bfe3f2eSlogwang efd_value_t data[EFD_BURST_MAX]; 1922bfe3f2eSlogwang const void *key_ptrs[EFD_BURST_MAX]; 1932bfe3f2eSlogwang 1944418919fSjohnjiang struct rte_ipv4_hdr *ipv4_hdr; 1952bfe3f2eSlogwang uint32_t ipv4_dst_ip[EFD_BURST_MAX]; 1962bfe3f2eSlogwang 1972bfe3f2eSlogwang for (i = 0; i < rx_count; i++) { 1982bfe3f2eSlogwang /* Handle IPv4 header.*/ 1994418919fSjohnjiang ipv4_hdr = rte_pktmbuf_mtod_offset(pkts[i], struct rte_ipv4_hdr *, 2004418919fSjohnjiang sizeof(struct rte_ether_hdr)); 2012bfe3f2eSlogwang ipv4_dst_ip[i] = ipv4_hdr->dst_addr; 2022bfe3f2eSlogwang key_ptrs[i] = (void *)&ipv4_dst_ip[i]; 2032bfe3f2eSlogwang } 2042bfe3f2eSlogwang 2052bfe3f2eSlogwang rte_efd_lookup_bulk(efd_table, socket_id, rx_count, 2062bfe3f2eSlogwang (const void **) key_ptrs, data); 2072bfe3f2eSlogwang for (i = 0; i < rx_count; i++) { 2082bfe3f2eSlogwang node = (uint8_t) ((uintptr_t)data[i]); 2092bfe3f2eSlogwang 2102bfe3f2eSlogwang if (node >= num_nodes) { 2112bfe3f2eSlogwang /* 2122bfe3f2eSlogwang * Node is out of range, which means that 2132bfe3f2eSlogwang * flow has not been inserted 2142bfe3f2eSlogwang */ 2152bfe3f2eSlogwang flow_dist_stats.drop++; 2162bfe3f2eSlogwang rte_pktmbuf_free(pkts[i]); 2172bfe3f2eSlogwang } else { 2182bfe3f2eSlogwang flow_dist_stats.distributed++; 2192bfe3f2eSlogwang enqueue_rx_packet(node, pkts[i]); 2202bfe3f2eSlogwang } 2212bfe3f2eSlogwang } 2222bfe3f2eSlogwang 2232bfe3f2eSlogwang for (i = 0; i < num_nodes; i++) 2242bfe3f2eSlogwang flush_rx_queue(i); 2252bfe3f2eSlogwang } 2262bfe3f2eSlogwang 2272bfe3f2eSlogwangThe burst of packets received is enqueued in temporary buffers (per node), 2282bfe3f2eSlogwangand enqueued in the shared ring between the server and the node. 2292bfe3f2eSlogwangAfter this, a new burst of packets is received and this process is 2302bfe3f2eSlogwangrepeated infinitely. 2312bfe3f2eSlogwang 2322bfe3f2eSlogwang.. code-block:: c 2332bfe3f2eSlogwang 2342bfe3f2eSlogwang static void 2352bfe3f2eSlogwang flush_rx_queue(uint16_t node) 2362bfe3f2eSlogwang { 2372bfe3f2eSlogwang uint16_t j; 2382bfe3f2eSlogwang struct node *cl; 2392bfe3f2eSlogwang 2402bfe3f2eSlogwang if (cl_rx_buf[node].count == 0) 2412bfe3f2eSlogwang return; 2422bfe3f2eSlogwang 2432bfe3f2eSlogwang cl = &nodes[node]; 2442bfe3f2eSlogwang if (rte_ring_enqueue_bulk(cl->rx_q, (void **)cl_rx_buf[node].buffer, 2452bfe3f2eSlogwang cl_rx_buf[node].count, NULL) != cl_rx_buf[node].count){ 2462bfe3f2eSlogwang for (j = 0; j < cl_rx_buf[node].count; j++) 2472bfe3f2eSlogwang rte_pktmbuf_free(cl_rx_buf[node].buffer[j]); 2482bfe3f2eSlogwang cl->stats.rx_drop += cl_rx_buf[node].count; 2492bfe3f2eSlogwang } else 2502bfe3f2eSlogwang cl->stats.rx += cl_rx_buf[node].count; 2512bfe3f2eSlogwang 2522bfe3f2eSlogwang cl_rx_buf[node].count = 0; 2532bfe3f2eSlogwang } 2542bfe3f2eSlogwang 2552bfe3f2eSlogwangThe second process, the back-end node, receives the packets from the shared 2562bfe3f2eSlogwangring with the server and send them out, if they belong to the node. 2572bfe3f2eSlogwang 2582bfe3f2eSlogwangAt initialization, it attaches to the server process memory, to have 2592bfe3f2eSlogwangaccess to the shared ring, parameters and statistics. 2602bfe3f2eSlogwang 2612bfe3f2eSlogwang.. code-block:: c 2622bfe3f2eSlogwang 2632bfe3f2eSlogwang rx_ring = rte_ring_lookup(get_rx_queue_name(node_id)); 2642bfe3f2eSlogwang if (rx_ring == NULL) 2652bfe3f2eSlogwang rte_exit(EXIT_FAILURE, "Cannot get RX ring - " 2662bfe3f2eSlogwang "is server process running?\n"); 2672bfe3f2eSlogwang 2682bfe3f2eSlogwang mp = rte_mempool_lookup(PKTMBUF_POOL_NAME); 2692bfe3f2eSlogwang if (mp == NULL) 2702bfe3f2eSlogwang rte_exit(EXIT_FAILURE, "Cannot get mempool for mbufs\n"); 2712bfe3f2eSlogwang 2722bfe3f2eSlogwang mz = rte_memzone_lookup(MZ_SHARED_INFO); 2732bfe3f2eSlogwang if (mz == NULL) 2742bfe3f2eSlogwang rte_exit(EXIT_FAILURE, "Cannot get port info structure\n"); 2752bfe3f2eSlogwang info = mz->addr; 2762bfe3f2eSlogwang tx_stats = &(info->tx_stats[node_id]); 2772bfe3f2eSlogwang filter_stats = &(info->filter_stats[node_id]); 2782bfe3f2eSlogwang 2792bfe3f2eSlogwangThen, the hash table that contains the flows that will be handled 2802bfe3f2eSlogwangby the node is created and populated. 2812bfe3f2eSlogwang 2822bfe3f2eSlogwang.. code-block:: c 2832bfe3f2eSlogwang 2842bfe3f2eSlogwang static struct rte_hash * 2852bfe3f2eSlogwang create_hash_table(const struct shared_info *info) 2862bfe3f2eSlogwang { 2872bfe3f2eSlogwang uint32_t num_flows_node = info->num_flows / info->num_nodes; 2882bfe3f2eSlogwang char name[RTE_HASH_NAMESIZE]; 2892bfe3f2eSlogwang struct rte_hash *h; 2902bfe3f2eSlogwang 2912bfe3f2eSlogwang /* create table */ 2922bfe3f2eSlogwang struct rte_hash_parameters hash_params = { 2932bfe3f2eSlogwang .entries = num_flows_node * 2, /* table load = 50% */ 2942bfe3f2eSlogwang .key_len = sizeof(uint32_t), /* Store IPv4 dest IP address */ 2952bfe3f2eSlogwang .socket_id = rte_socket_id(), 2962bfe3f2eSlogwang .hash_func_init_val = 0, 2972bfe3f2eSlogwang }; 2982bfe3f2eSlogwang 2992bfe3f2eSlogwang snprintf(name, sizeof(name), "hash_table_%d", node_id); 3002bfe3f2eSlogwang hash_params.name = name; 3012bfe3f2eSlogwang h = rte_hash_create(&hash_params); 3022bfe3f2eSlogwang 3032bfe3f2eSlogwang if (h == NULL) 3042bfe3f2eSlogwang rte_exit(EXIT_FAILURE, 3052bfe3f2eSlogwang "Problem creating the hash table for node %d\n", 3062bfe3f2eSlogwang node_id); 3072bfe3f2eSlogwang return h; 3082bfe3f2eSlogwang } 3092bfe3f2eSlogwang 3102bfe3f2eSlogwang static void 3112bfe3f2eSlogwang populate_hash_table(const struct rte_hash *h, const struct shared_info *info) 3122bfe3f2eSlogwang { 3132bfe3f2eSlogwang unsigned int i; 3142bfe3f2eSlogwang int32_t ret; 3152bfe3f2eSlogwang uint32_t ip_dst; 3162bfe3f2eSlogwang uint32_t num_flows_node = 0; 3172bfe3f2eSlogwang uint64_t target_node; 3182bfe3f2eSlogwang 3192bfe3f2eSlogwang /* Add flows in table */ 3202bfe3f2eSlogwang for (i = 0; i < info->num_flows; i++) { 3212bfe3f2eSlogwang target_node = i % info->num_nodes; 3222bfe3f2eSlogwang if (target_node != node_id) 3232bfe3f2eSlogwang continue; 3242bfe3f2eSlogwang 3252bfe3f2eSlogwang ip_dst = rte_cpu_to_be_32(i); 3262bfe3f2eSlogwang 3272bfe3f2eSlogwang ret = rte_hash_add_key(h, (void *) &ip_dst); 3282bfe3f2eSlogwang if (ret < 0) 3292bfe3f2eSlogwang rte_exit(EXIT_FAILURE, "Unable to add entry %u " 3302bfe3f2eSlogwang "in hash table\n", i); 3312bfe3f2eSlogwang else 3322bfe3f2eSlogwang num_flows_node++; 3332bfe3f2eSlogwang 3342bfe3f2eSlogwang } 3352bfe3f2eSlogwang 3362bfe3f2eSlogwang printf("Hash table: Adding 0x%x keys\n", num_flows_node); 3372bfe3f2eSlogwang } 3382bfe3f2eSlogwang 3392bfe3f2eSlogwangAfter initialization, packets are dequeued from the shared ring 3402bfe3f2eSlogwang(from the server) and, like in the server process, 3412bfe3f2eSlogwangthe IPv4 address from the packets is used as a key to look up in the hash table. 3422bfe3f2eSlogwangIf there is a hit, packet is stored in a buffer, to be eventually transmitted 3432bfe3f2eSlogwangin one of the enabled ports. If key is not there, packet is dropped, since the 3442bfe3f2eSlogwangflow is not handled by the node. 3452bfe3f2eSlogwang 3462bfe3f2eSlogwang.. code-block:: c 3472bfe3f2eSlogwang 3482bfe3f2eSlogwang static inline void 3492bfe3f2eSlogwang handle_packets(struct rte_hash *h, struct rte_mbuf **bufs, uint16_t num_packets) 3502bfe3f2eSlogwang { 3514418919fSjohnjiang struct rte_ipv4_hdr *ipv4_hdr; 3522bfe3f2eSlogwang uint32_t ipv4_dst_ip[PKT_READ_SIZE]; 3532bfe3f2eSlogwang const void *key_ptrs[PKT_READ_SIZE]; 3542bfe3f2eSlogwang unsigned int i; 3552bfe3f2eSlogwang int32_t positions[PKT_READ_SIZE] = {0}; 3562bfe3f2eSlogwang 3572bfe3f2eSlogwang for (i = 0; i < num_packets; i++) { 3582bfe3f2eSlogwang /* Handle IPv4 header.*/ 3594418919fSjohnjiang ipv4_hdr = rte_pktmbuf_mtod_offset(bufs[i], struct rte_ipv4_hdr *, 3604418919fSjohnjiang sizeof(struct rte_ether_hdr)); 3612bfe3f2eSlogwang ipv4_dst_ip[i] = ipv4_hdr->dst_addr; 3622bfe3f2eSlogwang key_ptrs[i] = &ipv4_dst_ip[i]; 3632bfe3f2eSlogwang } 3642bfe3f2eSlogwang /* Check if packets belongs to any flows handled by this node */ 3652bfe3f2eSlogwang rte_hash_lookup_bulk(h, key_ptrs, num_packets, positions); 3662bfe3f2eSlogwang 3672bfe3f2eSlogwang for (i = 0; i < num_packets; i++) { 3682bfe3f2eSlogwang if (likely(positions[i] >= 0)) { 3692bfe3f2eSlogwang filter_stats->passed++; 3702bfe3f2eSlogwang transmit_packet(bufs[i]); 3712bfe3f2eSlogwang } else { 3722bfe3f2eSlogwang filter_stats->drop++; 3732bfe3f2eSlogwang /* Drop packet, as flow is not handled by this node */ 3742bfe3f2eSlogwang rte_pktmbuf_free(bufs[i]); 3752bfe3f2eSlogwang } 3762bfe3f2eSlogwang } 3772bfe3f2eSlogwang } 3782bfe3f2eSlogwang 3792bfe3f2eSlogwangFinally, note that both processes updates statistics, such as transmitted, received 3802bfe3f2eSlogwangand dropped packets, which are shown and refreshed by the server app. 3812bfe3f2eSlogwang 3822bfe3f2eSlogwang.. code-block:: c 3832bfe3f2eSlogwang 3842bfe3f2eSlogwang static void 3852bfe3f2eSlogwang do_stats_display(void) 3862bfe3f2eSlogwang { 3872bfe3f2eSlogwang unsigned int i, j; 3882bfe3f2eSlogwang const char clr[] = {27, '[', '2', 'J', '\0'}; 3892bfe3f2eSlogwang const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; 3902bfe3f2eSlogwang uint64_t port_tx[RTE_MAX_ETHPORTS], port_tx_drop[RTE_MAX_ETHPORTS]; 3912bfe3f2eSlogwang uint64_t node_tx[MAX_NODES], node_tx_drop[MAX_NODES]; 3922bfe3f2eSlogwang 3932bfe3f2eSlogwang /* to get TX stats, we need to do some summing calculations */ 3942bfe3f2eSlogwang memset(port_tx, 0, sizeof(port_tx)); 3952bfe3f2eSlogwang memset(port_tx_drop, 0, sizeof(port_tx_drop)); 3962bfe3f2eSlogwang memset(node_tx, 0, sizeof(node_tx)); 3972bfe3f2eSlogwang memset(node_tx_drop, 0, sizeof(node_tx_drop)); 3982bfe3f2eSlogwang 3992bfe3f2eSlogwang for (i = 0; i < num_nodes; i++) { 4002bfe3f2eSlogwang const struct tx_stats *tx = &info->tx_stats[i]; 4012bfe3f2eSlogwang 4022bfe3f2eSlogwang for (j = 0; j < info->num_ports; j++) { 4032bfe3f2eSlogwang const uint64_t tx_val = tx->tx[info->id[j]]; 4042bfe3f2eSlogwang const uint64_t drop_val = tx->tx_drop[info->id[j]]; 4052bfe3f2eSlogwang 4062bfe3f2eSlogwang port_tx[j] += tx_val; 4072bfe3f2eSlogwang port_tx_drop[j] += drop_val; 4082bfe3f2eSlogwang node_tx[i] += tx_val; 4092bfe3f2eSlogwang node_tx_drop[i] += drop_val; 4102bfe3f2eSlogwang } 4112bfe3f2eSlogwang } 4122bfe3f2eSlogwang 4132bfe3f2eSlogwang /* Clear screen and move to top left */ 4142bfe3f2eSlogwang printf("%s%s", clr, topLeft); 4152bfe3f2eSlogwang 4162bfe3f2eSlogwang printf("PORTS\n"); 4172bfe3f2eSlogwang printf("-----\n"); 4182bfe3f2eSlogwang for (i = 0; i < info->num_ports; i++) 4192bfe3f2eSlogwang printf("Port %u: '%s'\t", (unsigned int)info->id[i], 4202bfe3f2eSlogwang get_printable_mac_addr(info->id[i])); 4212bfe3f2eSlogwang printf("\n\n"); 4222bfe3f2eSlogwang for (i = 0; i < info->num_ports; i++) { 4232bfe3f2eSlogwang printf("Port %u - rx: %9"PRIu64"\t" 4242bfe3f2eSlogwang "tx: %9"PRIu64"\n", 4252bfe3f2eSlogwang (unsigned int)info->id[i], info->rx_stats.rx[i], 4262bfe3f2eSlogwang port_tx[i]); 4272bfe3f2eSlogwang } 4282bfe3f2eSlogwang 4292bfe3f2eSlogwang printf("\nSERVER\n"); 4302bfe3f2eSlogwang printf("-----\n"); 4312bfe3f2eSlogwang printf("distributed: %9"PRIu64", drop: %9"PRIu64"\n", 4322bfe3f2eSlogwang flow_dist_stats.distributed, flow_dist_stats.drop); 4332bfe3f2eSlogwang 4342bfe3f2eSlogwang printf("\nNODES\n"); 4352bfe3f2eSlogwang printf("-------\n"); 4362bfe3f2eSlogwang for (i = 0; i < num_nodes; i++) { 4372bfe3f2eSlogwang const unsigned long long rx = nodes[i].stats.rx; 4382bfe3f2eSlogwang const unsigned long long rx_drop = nodes[i].stats.rx_drop; 4392bfe3f2eSlogwang const struct filter_stats *filter = &info->filter_stats[i]; 4402bfe3f2eSlogwang 4412bfe3f2eSlogwang printf("Node %2u - rx: %9llu, rx_drop: %9llu\n" 4422bfe3f2eSlogwang " tx: %9"PRIu64", tx_drop: %9"PRIu64"\n" 4432bfe3f2eSlogwang " filter_passed: %9"PRIu64", " 4442bfe3f2eSlogwang "filter_drop: %9"PRIu64"\n", 4452bfe3f2eSlogwang i, rx, rx_drop, node_tx[i], node_tx_drop[i], 4462bfe3f2eSlogwang filter->passed, filter->drop); 4472bfe3f2eSlogwang } 4482bfe3f2eSlogwang 4492bfe3f2eSlogwang printf("\n"); 4502bfe3f2eSlogwang } 451