]>
Commit | Line | Data |
---|---|---|
9f95a23c TL |
1 | /* SPDX-License-Identifier: BSD-3-Clause |
2 | * Copyright(c) 2010-2014 Intel Corporation | |
7c673cae FG |
3 | */ |
4 | ||
5 | #include <stdio.h> | |
6 | #include <stdlib.h> | |
7 | #include <string.h> | |
8 | #include <unistd.h> | |
9 | #include <stdint.h> | |
10 | #include <stdarg.h> | |
11 | #include <inttypes.h> | |
7c673cae FG |
12 | #include <sys/queue.h> |
13 | #include <errno.h> | |
14 | #include <netinet/ip.h> | |
9f95a23c | 15 | #include <signal.h> |
7c673cae FG |
16 | |
17 | #include <rte_common.h> | |
18 | #include <rte_memory.h> | |
7c673cae | 19 | #include <rte_eal.h> |
7c673cae FG |
20 | #include <rte_launch.h> |
21 | #include <rte_per_lcore.h> | |
22 | #include <rte_lcore.h> | |
23 | #include <rte_branch_prediction.h> | |
24 | #include <rte_atomic.h> | |
25 | #include <rte_ring.h> | |
26 | #include <rte_log.h> | |
27 | #include <rte_debug.h> | |
28 | #include <rte_mempool.h> | |
29 | #include <rte_memcpy.h> | |
30 | #include <rte_mbuf.h> | |
31 | #include <rte_ether.h> | |
32 | #include <rte_interrupts.h> | |
7c673cae FG |
33 | #include <rte_ethdev.h> |
34 | #include <rte_byteorder.h> | |
35 | #include <rte_malloc.h> | |
36 | #include <rte_string_fns.h> | |
37 | ||
38 | #include "common.h" | |
39 | #include "args.h" | |
40 | #include "init.h" | |
41 | ||
42 | /* | |
43 | * When doing reads from the NIC or the client queues, | |
44 | * use this batch size | |
45 | */ | |
46 | #define PACKET_READ_SIZE 32 | |
47 | ||
48 | /* | |
49 | * Local buffers to put packets in, used to send packets in bursts to the | |
50 | * clients | |
51 | */ | |
52 | struct client_rx_buf { | |
53 | struct rte_mbuf *buffer[PACKET_READ_SIZE]; | |
54 | uint16_t count; | |
55 | }; | |
56 | ||
57 | /* One buffer per client rx queue - dynamically allocate array */ | |
58 | static struct client_rx_buf *cl_rx_buf; | |
59 | ||
60 | static const char * | |
9f95a23c | 61 | get_printable_mac_addr(uint16_t port) |
7c673cae FG |
62 | { |
63 | static const char err_address[] = "00:00:00:00:00:00"; | |
64 | static char addresses[RTE_MAX_ETHPORTS][sizeof(err_address)]; | |
65 | ||
66 | if (unlikely(port >= RTE_MAX_ETHPORTS)) | |
67 | return err_address; | |
68 | if (unlikely(addresses[port][0]=='\0')){ | |
69 | struct ether_addr mac; | |
70 | rte_eth_macaddr_get(port, &mac); | |
71 | snprintf(addresses[port], sizeof(addresses[port]), | |
72 | "%02x:%02x:%02x:%02x:%02x:%02x\n", | |
73 | mac.addr_bytes[0], mac.addr_bytes[1], mac.addr_bytes[2], | |
74 | mac.addr_bytes[3], mac.addr_bytes[4], mac.addr_bytes[5]); | |
75 | } | |
76 | return addresses[port]; | |
77 | } | |
78 | ||
79 | /* | |
80 | * This function displays the recorded statistics for each port | |
81 | * and for each client. It uses ANSI terminal codes to clear | |
82 | * screen when called. It is called from a single non-master | |
83 | * thread in the server process, when the process is run with more | |
84 | * than one lcore enabled. | |
85 | */ | |
86 | static void | |
87 | do_stats_display(void) | |
88 | { | |
89 | unsigned i, j; | |
90 | const char clr[] = { 27, '[', '2', 'J', '\0' }; | |
91 | const char topLeft[] = { 27, '[', '1', ';', '1', 'H','\0' }; | |
92 | uint64_t port_tx[RTE_MAX_ETHPORTS], port_tx_drop[RTE_MAX_ETHPORTS]; | |
93 | uint64_t client_tx[MAX_CLIENTS], client_tx_drop[MAX_CLIENTS]; | |
94 | ||
95 | /* to get TX stats, we need to do some summing calculations */ | |
96 | memset(port_tx, 0, sizeof(port_tx)); | |
97 | memset(port_tx_drop, 0, sizeof(port_tx_drop)); | |
98 | memset(client_tx, 0, sizeof(client_tx)); | |
99 | memset(client_tx_drop, 0, sizeof(client_tx_drop)); | |
100 | ||
101 | for (i = 0; i < num_clients; i++){ | |
102 | const volatile struct tx_stats *tx = &ports->tx_stats[i]; | |
103 | for (j = 0; j < ports->num_ports; j++){ | |
104 | /* assign to local variables here, save re-reading volatile vars */ | |
105 | const uint64_t tx_val = tx->tx[ports->id[j]]; | |
106 | const uint64_t drop_val = tx->tx_drop[ports->id[j]]; | |
107 | port_tx[j] += tx_val; | |
108 | port_tx_drop[j] += drop_val; | |
109 | client_tx[i] += tx_val; | |
110 | client_tx_drop[i] += drop_val; | |
111 | } | |
112 | } | |
113 | ||
114 | /* Clear screen and move to top left */ | |
115 | printf("%s%s", clr, topLeft); | |
116 | ||
117 | printf("PORTS\n"); | |
118 | printf("-----\n"); | |
119 | for (i = 0; i < ports->num_ports; i++) | |
120 | printf("Port %u: '%s'\t", (unsigned)ports->id[i], | |
121 | get_printable_mac_addr(ports->id[i])); | |
122 | printf("\n\n"); | |
123 | for (i = 0; i < ports->num_ports; i++){ | |
124 | printf("Port %u - rx: %9"PRIu64"\t" | |
125 | "tx: %9"PRIu64"\n", | |
126 | (unsigned)ports->id[i], ports->rx_stats.rx[i], | |
127 | port_tx[i]); | |
128 | } | |
129 | ||
130 | printf("\nCLIENTS\n"); | |
131 | printf("-------\n"); | |
132 | for (i = 0; i < num_clients; i++){ | |
133 | const unsigned long long rx = clients[i].stats.rx; | |
134 | const unsigned long long rx_drop = clients[i].stats.rx_drop; | |
135 | printf("Client %2u - rx: %9llu, rx_drop: %9llu\n" | |
136 | " tx: %9"PRIu64", tx_drop: %9"PRIu64"\n", | |
137 | i, rx, rx_drop, client_tx[i], client_tx_drop[i]); | |
138 | } | |
139 | ||
140 | printf("\n"); | |
141 | } | |
142 | ||
143 | /* | |
144 | * The function called from each non-master lcore used by the process. | |
145 | * The test_and_set function is used to randomly pick a single lcore on which | |
146 | * the code to display the statistics will run. Otherwise, the code just | |
147 | * repeatedly sleeps. | |
148 | */ | |
149 | static int | |
150 | sleep_lcore(__attribute__((unused)) void *dummy) | |
151 | { | |
152 | /* Used to pick a display thread - static, so zero-initialised */ | |
153 | static rte_atomic32_t display_stats; | |
154 | ||
155 | /* Only one core should display stats */ | |
156 | if (rte_atomic32_test_and_set(&display_stats)) { | |
157 | const unsigned sleeptime = 1; | |
158 | printf("Core %u displaying statistics\n", rte_lcore_id()); | |
159 | ||
160 | /* Longer initial pause so above printf is seen */ | |
161 | sleep(sleeptime * 3); | |
162 | ||
163 | /* Loop forever: sleep always returns 0 or <= param */ | |
164 | while (sleep(sleeptime) <= sleeptime) | |
165 | do_stats_display(); | |
166 | } | |
167 | return 0; | |
168 | } | |
169 | ||
170 | /* | |
171 | * Function to set all the client statistic values to zero. | |
172 | * Called at program startup. | |
173 | */ | |
174 | static void | |
175 | clear_stats(void) | |
176 | { | |
177 | unsigned i; | |
178 | ||
179 | for (i = 0; i < num_clients; i++) | |
180 | clients[i].stats.rx = clients[i].stats.rx_drop = 0; | |
181 | } | |
182 | ||
183 | /* | |
184 | * send a burst of traffic to a client, assuming there are packets | |
185 | * available to be sent to this client | |
186 | */ | |
187 | static void | |
188 | flush_rx_queue(uint16_t client) | |
189 | { | |
190 | uint16_t j; | |
191 | struct client *cl; | |
192 | ||
193 | if (cl_rx_buf[client].count == 0) | |
194 | return; | |
195 | ||
196 | cl = &clients[client]; | |
197 | if (rte_ring_enqueue_bulk(cl->rx_q, (void **)cl_rx_buf[client].buffer, | |
11fdf7f2 | 198 | cl_rx_buf[client].count, NULL) == 0){ |
7c673cae FG |
199 | for (j = 0; j < cl_rx_buf[client].count; j++) |
200 | rte_pktmbuf_free(cl_rx_buf[client].buffer[j]); | |
201 | cl->stats.rx_drop += cl_rx_buf[client].count; | |
202 | } | |
203 | else | |
204 | cl->stats.rx += cl_rx_buf[client].count; | |
205 | ||
206 | cl_rx_buf[client].count = 0; | |
207 | } | |
208 | ||
209 | /* | |
210 | * marks a packet down to be sent to a particular client process | |
211 | */ | |
212 | static inline void | |
213 | enqueue_rx_packet(uint8_t client, struct rte_mbuf *buf) | |
214 | { | |
215 | cl_rx_buf[client].buffer[cl_rx_buf[client].count++] = buf; | |
216 | } | |
217 | ||
218 | /* | |
219 | * This function takes a group of packets and routes them | |
220 | * individually to the client process. Very simply round-robins the packets | |
221 | * without checking any of the packet contents. | |
222 | */ | |
223 | static void | |
224 | process_packets(uint32_t port_num __rte_unused, | |
225 | struct rte_mbuf *pkts[], uint16_t rx_count) | |
226 | { | |
227 | uint16_t i; | |
228 | uint8_t client = 0; | |
229 | ||
230 | for (i = 0; i < rx_count; i++) { | |
231 | enqueue_rx_packet(client, pkts[i]); | |
232 | ||
233 | if (++client == num_clients) | |
234 | client = 0; | |
235 | } | |
236 | ||
237 | for (i = 0; i < num_clients; i++) | |
238 | flush_rx_queue(i); | |
239 | } | |
240 | ||
241 | /* | |
242 | * Function called by the master lcore of the DPDK process. | |
243 | */ | |
244 | static void | |
245 | do_packet_forwarding(void) | |
246 | { | |
247 | unsigned port_num = 0; /* indexes the port[] array */ | |
248 | ||
249 | for (;;) { | |
250 | struct rte_mbuf *buf[PACKET_READ_SIZE]; | |
251 | uint16_t rx_count; | |
252 | ||
253 | /* read a port */ | |
254 | rx_count = rte_eth_rx_burst(ports->id[port_num], 0, \ | |
255 | buf, PACKET_READ_SIZE); | |
256 | ports->rx_stats.rx[port_num] += rx_count; | |
257 | ||
258 | /* Now process the NIC packets read */ | |
259 | if (likely(rx_count > 0)) | |
260 | process_packets(port_num, buf, rx_count); | |
261 | ||
262 | /* move to next port */ | |
263 | if (++port_num == ports->num_ports) | |
264 | port_num = 0; | |
265 | } | |
266 | } | |
267 | ||
9f95a23c TL |
268 | static void |
269 | signal_handler(int signal) | |
270 | { | |
271 | uint16_t port_id; | |
272 | ||
273 | if (signal == SIGINT) | |
274 | RTE_ETH_FOREACH_DEV(port_id) { | |
275 | rte_eth_dev_stop(port_id); | |
276 | rte_eth_dev_close(port_id); | |
277 | } | |
278 | exit(0); | |
279 | } | |
280 | ||
7c673cae FG |
281 | int |
282 | main(int argc, char *argv[]) | |
283 | { | |
9f95a23c | 284 | signal(SIGINT, signal_handler); |
7c673cae FG |
285 | /* initialise the system */ |
286 | if (init(argc, argv) < 0 ) | |
287 | return -1; | |
288 | RTE_LOG(INFO, APP, "Finished Process Init.\n"); | |
289 | ||
290 | cl_rx_buf = calloc(num_clients, sizeof(cl_rx_buf[0])); | |
291 | ||
292 | /* clear statistics */ | |
293 | clear_stats(); | |
294 | ||
295 | /* put all other cores to sleep bar master */ | |
296 | rte_eal_mp_remote_launch(sleep_lcore, NULL, SKIP_MASTER); | |
297 | ||
298 | do_packet_forwarding(); | |
299 | return 0; | |
300 | } |