]>
Commit | Line | Data |
---|---|---|
9f95a23c TL |
1 | /* SPDX-License-Identifier: BSD-3-Clause |
2 | * Copyright(c) 2016-2017 Intel Corporation | |
7c673cae FG |
3 | */ |
4 | ||
5 | #include <stdint.h> | |
6 | #include <stdio.h> | |
7 | #include <inttypes.h> | |
8 | #include <stdarg.h> | |
9 | #include <errno.h> | |
10 | #include <sys/queue.h> | |
11 | #include <stdlib.h> | |
12 | #include <getopt.h> | |
13 | #include <string.h> | |
14 | ||
15 | #include <rte_common.h> | |
16 | #include <rte_malloc.h> | |
17 | #include <rte_memory.h> | |
18 | #include <rte_memzone.h> | |
19 | #include <rte_eal.h> | |
20 | #include <rte_atomic.h> | |
21 | #include <rte_branch_prediction.h> | |
22 | #include <rte_log.h> | |
23 | #include <rte_per_lcore.h> | |
24 | #include <rte_launch.h> | |
25 | #include <rte_lcore.h> | |
26 | #include <rte_ring.h> | |
7c673cae FG |
27 | #include <rte_debug.h> |
28 | #include <rte_mempool.h> | |
29 | #include <rte_mbuf.h> | |
30 | #include <rte_interrupts.h> | |
7c673cae FG |
31 | #include <rte_ether.h> |
32 | #include <rte_ethdev.h> | |
33 | #include <rte_string_fns.h> | |
11fdf7f2 | 34 | #include <rte_ip.h> |
7c673cae FG |
35 | |
36 | #include "common.h" | |
37 | ||
38 | /* Number of packets to attempt to read from queue */ | |
39 | #define PKT_READ_SIZE ((uint16_t)32) | |
40 | ||
11fdf7f2 TL |
41 | /* |
42 | * Our node id number - tells us which rx queue to read, and NIC TX | |
43 | * queue to write to. | |
44 | */ | |
45 | static uint8_t node_id; | |
7c673cae FG |
46 | |
47 | #define MBQ_CAPACITY 32 | |
48 | ||
49 | /* maps input ports to output ports for packets */ | |
9f95a23c | 50 | static uint16_t output_ports[RTE_MAX_ETHPORTS]; |
7c673cae FG |
51 | |
52 | /* buffers up a set of packet that are ready to send */ | |
53 | struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS]; | |
54 | ||
55 | /* shared data from server. We update statistics here */ | |
11fdf7f2 | 56 | static struct tx_stats *tx_stats; |
7c673cae | 57 | |
11fdf7f2 | 58 | static struct filter_stats *filter_stats; |
7c673cae FG |
59 | |
60 | /* | |
61 | * print a usage message | |
62 | */ | |
63 | static void | |
64 | usage(const char *progname) | |
65 | { | |
11fdf7f2 | 66 | printf("Usage: %s [EAL args] -- -n <node_id>\n\n", progname); |
7c673cae FG |
67 | } |
68 | ||
69 | /* | |
11fdf7f2 | 70 | * Convert the node id number from a string to an int. |
7c673cae FG |
71 | */ |
72 | static int | |
11fdf7f2 | 73 | parse_node_num(const char *node) |
7c673cae FG |
74 | { |
75 | char *end = NULL; | |
76 | unsigned long temp; | |
77 | ||
11fdf7f2 | 78 | if (node == NULL || *node == '\0') |
7c673cae FG |
79 | return -1; |
80 | ||
11fdf7f2 | 81 | temp = strtoul(node, &end, 10); |
7c673cae FG |
82 | if (end == NULL || *end != '\0') |
83 | return -1; | |
84 | ||
11fdf7f2 | 85 | node_id = (uint8_t)temp; |
7c673cae FG |
86 | return 0; |
87 | } | |
88 | ||
89 | /* | |
11fdf7f2 | 90 | * Parse the application arguments to the node app. |
7c673cae FG |
91 | */ |
92 | static int | |
93 | parse_app_args(int argc, char *argv[]) | |
94 | { | |
95 | int option_index, opt; | |
96 | char **argvopt = argv; | |
97 | const char *progname = NULL; | |
98 | static struct option lgopts[] = { /* no long options */ | |
99 | {NULL, 0, 0, 0 } | |
100 | }; | |
101 | progname = argv[0]; | |
102 | ||
103 | while ((opt = getopt_long(argc, argvopt, "n:", lgopts, | |
11fdf7f2 TL |
104 | &option_index)) != EOF) { |
105 | switch (opt) { | |
106 | case 'n': | |
107 | if (parse_node_num(optarg) != 0) { | |
7c673cae FG |
108 | usage(progname); |
109 | return -1; | |
11fdf7f2 TL |
110 | } |
111 | break; | |
112 | default: | |
113 | usage(progname); | |
114 | return -1; | |
7c673cae FG |
115 | } |
116 | } | |
117 | return 0; | |
118 | } | |
119 | ||
120 | /* | |
121 | * Tx buffer error callback | |
122 | */ | |
123 | static void | |
124 | flush_tx_error_callback(struct rte_mbuf **unsent, uint16_t count, | |
125 | void *userdata) { | |
126 | int i; | |
9f95a23c | 127 | uint16_t port_id = (uintptr_t)userdata; |
7c673cae FG |
128 | |
129 | tx_stats->tx_drop[port_id] += count; | |
130 | ||
131 | /* free the mbufs which failed from transmit */ | |
132 | for (i = 0; i < count; i++) | |
133 | rte_pktmbuf_free(unsent[i]); | |
134 | ||
135 | } | |
136 | ||
137 | static void | |
9f95a23c | 138 | configure_tx_buffer(uint16_t port_id, uint16_t size) |
7c673cae FG |
139 | { |
140 | int ret; | |
141 | ||
142 | /* Initialize TX buffers */ | |
143 | tx_buffer[port_id] = rte_zmalloc_socket("tx_buffer", | |
144 | RTE_ETH_TX_BUFFER_SIZE(size), 0, | |
145 | rte_eth_dev_socket_id(port_id)); | |
146 | if (tx_buffer[port_id] == NULL) | |
9f95a23c TL |
147 | rte_exit(EXIT_FAILURE, |
148 | "Cannot allocate buffer for tx on port %u\n", port_id); | |
7c673cae FG |
149 | |
150 | rte_eth_tx_buffer_init(tx_buffer[port_id], size); | |
151 | ||
152 | ret = rte_eth_tx_buffer_set_err_callback(tx_buffer[port_id], | |
153 | flush_tx_error_callback, (void *)(intptr_t)port_id); | |
154 | if (ret < 0) | |
9f95a23c TL |
155 | rte_exit(EXIT_FAILURE, |
156 | "Cannot set error callback for tx buffer on port %u\n", | |
157 | port_id); | |
7c673cae FG |
158 | } |
159 | ||
160 | /* | |
161 | * set up output ports so that all traffic on port gets sent out | |
162 | * its paired port. Index using actual port numbers since that is | |
163 | * what comes in the mbuf structure. | |
164 | */ | |
165 | static void | |
11fdf7f2 | 166 | configure_output_ports(const struct shared_info *info) |
7c673cae FG |
167 | { |
168 | int i; | |
11fdf7f2 TL |
169 | |
170 | if (info->num_ports > RTE_MAX_ETHPORTS) | |
171 | rte_exit(EXIT_FAILURE, "Too many ethernet ports. " | |
172 | "RTE_MAX_ETHPORTS = %u\n", | |
173 | (unsigned int)RTE_MAX_ETHPORTS); | |
174 | for (i = 0; i < info->num_ports - 1; i += 2) { | |
175 | uint8_t p1 = info->id[i]; | |
176 | uint8_t p2 = info->id[i+1]; | |
177 | ||
7c673cae FG |
178 | output_ports[p1] = p2; |
179 | output_ports[p2] = p1; | |
180 | ||
181 | configure_tx_buffer(p1, MBQ_CAPACITY); | |
182 | configure_tx_buffer(p2, MBQ_CAPACITY); | |
183 | ||
184 | } | |
185 | } | |
186 | ||
11fdf7f2 TL |
187 | /* |
188 | * Create the hash table that will contain the flows that | |
189 | * the node will handle, which will be used to decide if packet | |
190 | * is transmitted or dropped. | |
191 | */ | |
192 | static struct rte_hash * | |
193 | create_hash_table(const struct shared_info *info) | |
194 | { | |
195 | uint32_t num_flows_node = info->num_flows / info->num_nodes; | |
196 | char name[RTE_HASH_NAMESIZE]; | |
197 | struct rte_hash *h; | |
198 | ||
199 | /* create table */ | |
200 | struct rte_hash_parameters hash_params = { | |
201 | .entries = num_flows_node * 2, /* table load = 50% */ | |
202 | .key_len = sizeof(uint32_t), /* Store IPv4 dest IP address */ | |
203 | .socket_id = rte_socket_id(), | |
204 | .hash_func_init_val = 0, | |
205 | }; | |
206 | ||
207 | snprintf(name, sizeof(name), "hash_table_%d", node_id); | |
208 | hash_params.name = name; | |
209 | h = rte_hash_create(&hash_params); | |
210 | ||
211 | if (h == NULL) | |
212 | rte_exit(EXIT_FAILURE, | |
213 | "Problem creating the hash table for node %d\n", | |
214 | node_id); | |
215 | return h; | |
216 | } | |
217 | ||
218 | static void | |
219 | populate_hash_table(const struct rte_hash *h, const struct shared_info *info) | |
220 | { | |
221 | unsigned int i; | |
222 | int32_t ret; | |
223 | uint32_t ip_dst; | |
224 | uint32_t num_flows_node = 0; | |
225 | uint64_t target_node; | |
226 | ||
227 | /* Add flows in table */ | |
228 | for (i = 0; i < info->num_flows; i++) { | |
229 | target_node = i % info->num_nodes; | |
230 | if (target_node != node_id) | |
231 | continue; | |
232 | ||
233 | ip_dst = rte_cpu_to_be_32(i); | |
234 | ||
235 | ret = rte_hash_add_key(h, (void *) &ip_dst); | |
236 | if (ret < 0) | |
237 | rte_exit(EXIT_FAILURE, "Unable to add entry %u " | |
238 | "in hash table\n", i); | |
239 | else | |
240 | num_flows_node++; | |
241 | ||
242 | } | |
243 | ||
244 | printf("Hash table: Adding 0x%x keys\n", num_flows_node); | |
245 | } | |
246 | ||
7c673cae FG |
247 | /* |
248 | * This function performs routing of packets | |
249 | * Just sends each input packet out an output port based solely on the input | |
250 | * port it arrived on. | |
251 | */ | |
11fdf7f2 TL |
252 | static inline void |
253 | transmit_packet(struct rte_mbuf *buf) | |
7c673cae FG |
254 | { |
255 | int sent; | |
9f95a23c TL |
256 | const uint16_t in_port = buf->port; |
257 | const uint16_t out_port = output_ports[in_port]; | |
7c673cae FG |
258 | struct rte_eth_dev_tx_buffer *buffer = tx_buffer[out_port]; |
259 | ||
11fdf7f2 | 260 | sent = rte_eth_tx_buffer(out_port, node_id, buffer, buf); |
7c673cae FG |
261 | if (sent) |
262 | tx_stats->tx[out_port] += sent; | |
263 | ||
264 | } | |
265 | ||
11fdf7f2 TL |
266 | static inline void |
267 | handle_packets(struct rte_hash *h, struct rte_mbuf **bufs, uint16_t num_packets) | |
268 | { | |
269 | struct ipv4_hdr *ipv4_hdr; | |
270 | uint32_t ipv4_dst_ip[PKT_READ_SIZE]; | |
271 | const void *key_ptrs[PKT_READ_SIZE]; | |
272 | unsigned int i; | |
273 | int32_t positions[PKT_READ_SIZE] = {0}; | |
274 | ||
275 | for (i = 0; i < num_packets; i++) { | |
276 | /* Handle IPv4 header.*/ | |
277 | ipv4_hdr = rte_pktmbuf_mtod_offset(bufs[i], struct ipv4_hdr *, | |
278 | sizeof(struct ether_hdr)); | |
279 | ipv4_dst_ip[i] = ipv4_hdr->dst_addr; | |
280 | key_ptrs[i] = &ipv4_dst_ip[i]; | |
281 | } | |
282 | /* Check if packets belongs to any flows handled by this node */ | |
283 | rte_hash_lookup_bulk(h, key_ptrs, num_packets, positions); | |
284 | ||
285 | for (i = 0; i < num_packets; i++) { | |
286 | if (likely(positions[i] >= 0)) { | |
287 | filter_stats->passed++; | |
288 | transmit_packet(bufs[i]); | |
289 | } else { | |
290 | filter_stats->drop++; | |
291 | /* Drop packet, as flow is not handled by this node */ | |
292 | rte_pktmbuf_free(bufs[i]); | |
293 | } | |
294 | } | |
295 | } | |
296 | ||
7c673cae FG |
297 | /* |
298 | * Application main function - loops through | |
299 | * receiving and processing packets. Never returns | |
300 | */ | |
301 | int | |
302 | main(int argc, char *argv[]) | |
303 | { | |
304 | const struct rte_memzone *mz; | |
305 | struct rte_ring *rx_ring; | |
11fdf7f2 | 306 | struct rte_hash *h; |
7c673cae | 307 | struct rte_mempool *mp; |
11fdf7f2 | 308 | struct shared_info *info; |
7c673cae FG |
309 | int need_flush = 0; /* indicates whether we have unsent packets */ |
310 | int retval; | |
311 | void *pkts[PKT_READ_SIZE]; | |
312 | uint16_t sent; | |
313 | ||
11fdf7f2 TL |
314 | retval = rte_eal_init(argc, argv); |
315 | if (retval < 0) | |
7c673cae FG |
316 | return -1; |
317 | argc -= retval; | |
318 | argv += retval; | |
319 | ||
320 | if (parse_app_args(argc, argv) < 0) | |
321 | rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); | |
322 | ||
9f95a23c | 323 | if (rte_eth_dev_count_avail() == 0) |
7c673cae FG |
324 | rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n"); |
325 | ||
11fdf7f2 | 326 | rx_ring = rte_ring_lookup(get_rx_queue_name(node_id)); |
7c673cae | 327 | if (rx_ring == NULL) |
11fdf7f2 TL |
328 | rte_exit(EXIT_FAILURE, "Cannot get RX ring - " |
329 | "is server process running?\n"); | |
7c673cae FG |
330 | |
331 | mp = rte_mempool_lookup(PKTMBUF_POOL_NAME); | |
332 | if (mp == NULL) | |
333 | rte_exit(EXIT_FAILURE, "Cannot get mempool for mbufs\n"); | |
334 | ||
11fdf7f2 | 335 | mz = rte_memzone_lookup(MZ_SHARED_INFO); |
7c673cae FG |
336 | if (mz == NULL) |
337 | rte_exit(EXIT_FAILURE, "Cannot get port info structure\n"); | |
11fdf7f2 TL |
338 | info = mz->addr; |
339 | tx_stats = &(info->tx_stats[node_id]); | |
340 | filter_stats = &(info->filter_stats[node_id]); | |
341 | ||
342 | configure_output_ports(info); | |
343 | ||
344 | h = create_hash_table(info); | |
7c673cae | 345 | |
11fdf7f2 | 346 | populate_hash_table(h, info); |
7c673cae FG |
347 | |
348 | RTE_LOG(INFO, APP, "Finished Process Init.\n"); | |
349 | ||
11fdf7f2 | 350 | printf("\nNode process %d handling packets\n", node_id); |
7c673cae FG |
351 | printf("[Press Ctrl-C to quit ...]\n"); |
352 | ||
353 | for (;;) { | |
11fdf7f2 | 354 | uint16_t rx_pkts = PKT_READ_SIZE; |
9f95a23c | 355 | uint16_t port; |
7c673cae | 356 | |
11fdf7f2 TL |
357 | /* |
358 | * Try dequeuing max possible packets first, if that fails, | |
359 | * get the most we can. Loop body should only execute once, | |
360 | * maximum | |
361 | */ | |
7c673cae | 362 | while (rx_pkts > 0 && |
11fdf7f2 TL |
363 | unlikely(rte_ring_dequeue_bulk(rx_ring, pkts, |
364 | rx_pkts, NULL) == 0)) | |
365 | rx_pkts = (uint16_t)RTE_MIN(rte_ring_count(rx_ring), | |
366 | PKT_READ_SIZE); | |
7c673cae | 367 | |
11fdf7f2 | 368 | if (unlikely(rx_pkts == 0)) { |
7c673cae | 369 | if (need_flush) |
11fdf7f2 TL |
370 | for (port = 0; port < info->num_ports; port++) { |
371 | sent = rte_eth_tx_buffer_flush( | |
372 | info->id[port], | |
373 | node_id, | |
7c673cae FG |
374 | tx_buffer[port]); |
375 | if (unlikely(sent)) | |
376 | tx_stats->tx[port] += sent; | |
377 | } | |
378 | need_flush = 0; | |
379 | continue; | |
380 | } | |
381 | ||
11fdf7f2 | 382 | handle_packets(h, (struct rte_mbuf **)pkts, rx_pkts); |
7c673cae FG |
383 | |
384 | need_flush = 1; | |
385 | } | |
386 | } |