1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2016-2017 Intel Corporation
10 #include <sys/queue.h>
15 #include <rte_common.h>
16 #include <rte_malloc.h>
17 #include <rte_memory.h>
18 #include <rte_memzone.h>
20 #include <rte_atomic.h>
21 #include <rte_branch_prediction.h>
23 #include <rte_per_lcore.h>
24 #include <rte_launch.h>
25 #include <rte_lcore.h>
27 #include <rte_debug.h>
28 #include <rte_mempool.h>
30 #include <rte_interrupts.h>
31 #include <rte_ether.h>
32 #include <rte_ethdev.h>
33 #include <rte_string_fns.h>
38 /* Number of packets to attempt to read from queue */
39 #define PKT_READ_SIZE ((uint16_t)32)
42 * Our node id number - tells us which rx queue to read, and NIC TX
45 static uint8_t node_id
;
47 #define MBQ_CAPACITY 32
49 /* maps input ports to output ports for packets */
50 static uint16_t output_ports
[RTE_MAX_ETHPORTS
];
52 /* buffers up a set of packet that are ready to send */
53 struct rte_eth_dev_tx_buffer
*tx_buffer
[RTE_MAX_ETHPORTS
];
55 /* shared data from server. We update statistics here */
56 static struct tx_stats
*tx_stats
;
58 static struct filter_stats
*filter_stats
;
61 * print a usage message
64 usage(const char *progname
)
66 printf("Usage: %s [EAL args] -- -n <node_id>\n\n", progname
);
70 * Convert the node id number from a string to an int.
73 parse_node_num(const char *node
)
78 if (node
== NULL
|| *node
== '\0')
81 temp
= strtoul(node
, &end
, 10);
82 if (end
== NULL
|| *end
!= '\0')
85 node_id
= (uint8_t)temp
;
90 * Parse the application arguments to the node app.
93 parse_app_args(int argc
, char *argv
[])
95 int option_index
, opt
;
96 char **argvopt
= argv
;
97 const char *progname
= NULL
;
98 static struct option lgopts
[] = { /* no long options */
103 while ((opt
= getopt_long(argc
, argvopt
, "n:", lgopts
,
104 &option_index
)) != EOF
) {
107 if (parse_node_num(optarg
) != 0) {
121 * Tx buffer error callback
124 flush_tx_error_callback(struct rte_mbuf
**unsent
, uint16_t count
,
127 uint16_t port_id
= (uintptr_t)userdata
;
129 tx_stats
->tx_drop
[port_id
] += count
;
131 /* free the mbufs which failed from transmit */
132 for (i
= 0; i
< count
; i
++)
133 rte_pktmbuf_free(unsent
[i
]);
138 configure_tx_buffer(uint16_t port_id
, uint16_t size
)
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
)
147 rte_exit(EXIT_FAILURE
,
148 "Cannot allocate buffer for tx on port %u\n", port_id
);
150 rte_eth_tx_buffer_init(tx_buffer
[port_id
], size
);
152 ret
= rte_eth_tx_buffer_set_err_callback(tx_buffer
[port_id
],
153 flush_tx_error_callback
, (void *)(intptr_t)port_id
);
155 rte_exit(EXIT_FAILURE
,
156 "Cannot set error callback for tx buffer on port %u\n",
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.
166 configure_output_ports(const struct shared_info
*info
)
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];
178 output_ports
[p1
] = p2
;
179 output_ports
[p2
] = p1
;
181 configure_tx_buffer(p1
, MBQ_CAPACITY
);
182 configure_tx_buffer(p2
, MBQ_CAPACITY
);
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.
192 static struct rte_hash
*
193 create_hash_table(const struct shared_info
*info
)
195 uint32_t num_flows_node
= info
->num_flows
/ info
->num_nodes
;
196 char name
[RTE_HASH_NAMESIZE
];
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,
207 snprintf(name
, sizeof(name
), "hash_table_%d", node_id
);
208 hash_params
.name
= name
;
209 h
= rte_hash_create(&hash_params
);
212 rte_exit(EXIT_FAILURE
,
213 "Problem creating the hash table for node %d\n",
219 populate_hash_table(const struct rte_hash
*h
, const struct shared_info
*info
)
224 uint32_t num_flows_node
= 0;
225 uint64_t target_node
;
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
)
233 ip_dst
= rte_cpu_to_be_32(i
);
235 ret
= rte_hash_add_key(h
, (void *) &ip_dst
);
237 rte_exit(EXIT_FAILURE
, "Unable to add entry %u "
238 "in hash table\n", i
);
244 printf("Hash table: Adding 0x%x keys\n", num_flows_node
);
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.
253 transmit_packet(struct rte_mbuf
*buf
)
256 const uint16_t in_port
= buf
->port
;
257 const uint16_t out_port
= output_ports
[in_port
];
258 struct rte_eth_dev_tx_buffer
*buffer
= tx_buffer
[out_port
];
260 sent
= rte_eth_tx_buffer(out_port
, node_id
, buffer
, buf
);
262 tx_stats
->tx
[out_port
] += sent
;
267 handle_packets(struct rte_hash
*h
, struct rte_mbuf
**bufs
, uint16_t num_packets
)
269 struct ipv4_hdr
*ipv4_hdr
;
270 uint32_t ipv4_dst_ip
[PKT_READ_SIZE
];
271 const void *key_ptrs
[PKT_READ_SIZE
];
273 int32_t positions
[PKT_READ_SIZE
] = {0};
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
];
282 /* Check if packets belongs to any flows handled by this node */
283 rte_hash_lookup_bulk(h
, key_ptrs
, num_packets
, positions
);
285 for (i
= 0; i
< num_packets
; i
++) {
286 if (likely(positions
[i
] >= 0)) {
287 filter_stats
->passed
++;
288 transmit_packet(bufs
[i
]);
290 filter_stats
->drop
++;
291 /* Drop packet, as flow is not handled by this node */
292 rte_pktmbuf_free(bufs
[i
]);
298 * Application main function - loops through
299 * receiving and processing packets. Never returns
302 main(int argc
, char *argv
[])
304 const struct rte_memzone
*mz
;
305 struct rte_ring
*rx_ring
;
307 struct rte_mempool
*mp
;
308 struct shared_info
*info
;
309 int need_flush
= 0; /* indicates whether we have unsent packets */
311 void *pkts
[PKT_READ_SIZE
];
314 retval
= rte_eal_init(argc
, argv
);
320 if (parse_app_args(argc
, argv
) < 0)
321 rte_exit(EXIT_FAILURE
, "Invalid command-line arguments\n");
323 if (rte_eth_dev_count_avail() == 0)
324 rte_exit(EXIT_FAILURE
, "No Ethernet ports - bye\n");
326 rx_ring
= rte_ring_lookup(get_rx_queue_name(node_id
));
328 rte_exit(EXIT_FAILURE
, "Cannot get RX ring - "
329 "is server process running?\n");
331 mp
= rte_mempool_lookup(PKTMBUF_POOL_NAME
);
333 rte_exit(EXIT_FAILURE
, "Cannot get mempool for mbufs\n");
335 mz
= rte_memzone_lookup(MZ_SHARED_INFO
);
337 rte_exit(EXIT_FAILURE
, "Cannot get port info structure\n");
339 tx_stats
= &(info
->tx_stats
[node_id
]);
340 filter_stats
= &(info
->filter_stats
[node_id
]);
342 configure_output_ports(info
);
344 h
= create_hash_table(info
);
346 populate_hash_table(h
, info
);
348 RTE_LOG(INFO
, APP
, "Finished Process Init.\n");
350 printf("\nNode process %d handling packets\n", node_id
);
351 printf("[Press Ctrl-C to quit ...]\n");
354 uint16_t rx_pkts
= PKT_READ_SIZE
;
358 * Try dequeuing max possible packets first, if that fails,
359 * get the most we can. Loop body should only execute once,
362 while (rx_pkts
> 0 &&
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
),
368 if (unlikely(rx_pkts
== 0)) {
370 for (port
= 0; port
< info
->num_ports
; port
++) {
371 sent
= rte_eth_tx_buffer_flush(
376 tx_stats
->tx
[port
] += sent
;
382 handle_packets(h
, (struct rte_mbuf
**)pkts
, rx_pkts
);