18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright(c) 2017 - 2018 Intel Corporation. */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <asm/barrier.h>
58c2ecf20Sopenharmony_ci#include <errno.h>
68c2ecf20Sopenharmony_ci#include <getopt.h>
78c2ecf20Sopenharmony_ci#include <libgen.h>
88c2ecf20Sopenharmony_ci#include <linux/bpf.h>
98c2ecf20Sopenharmony_ci#include <linux/compiler.h>
108c2ecf20Sopenharmony_ci#include <linux/if_link.h>
118c2ecf20Sopenharmony_ci#include <linux/if_xdp.h>
128c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
138c2ecf20Sopenharmony_ci#include <linux/ip.h>
148c2ecf20Sopenharmony_ci#include <linux/limits.h>
158c2ecf20Sopenharmony_ci#include <linux/udp.h>
168c2ecf20Sopenharmony_ci#include <arpa/inet.h>
178c2ecf20Sopenharmony_ci#include <locale.h>
188c2ecf20Sopenharmony_ci#include <net/ethernet.h>
198c2ecf20Sopenharmony_ci#include <net/if.h>
208c2ecf20Sopenharmony_ci#include <poll.h>
218c2ecf20Sopenharmony_ci#include <pthread.h>
228c2ecf20Sopenharmony_ci#include <signal.h>
238c2ecf20Sopenharmony_ci#include <stdbool.h>
248c2ecf20Sopenharmony_ci#include <stdio.h>
258c2ecf20Sopenharmony_ci#include <stdlib.h>
268c2ecf20Sopenharmony_ci#include <string.h>
278c2ecf20Sopenharmony_ci#include <sys/mman.h>
288c2ecf20Sopenharmony_ci#include <sys/resource.h>
298c2ecf20Sopenharmony_ci#include <sys/socket.h>
308c2ecf20Sopenharmony_ci#include <sys/types.h>
318c2ecf20Sopenharmony_ci#include <time.h>
328c2ecf20Sopenharmony_ci#include <unistd.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include <bpf/libbpf.h>
358c2ecf20Sopenharmony_ci#include <bpf/xsk.h>
368c2ecf20Sopenharmony_ci#include <bpf/bpf.h>
378c2ecf20Sopenharmony_ci#include "xdpsock.h"
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#ifndef SOL_XDP
408c2ecf20Sopenharmony_ci#define SOL_XDP 283
418c2ecf20Sopenharmony_ci#endif
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#ifndef AF_XDP
448c2ecf20Sopenharmony_ci#define AF_XDP 44
458c2ecf20Sopenharmony_ci#endif
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#ifndef PF_XDP
488c2ecf20Sopenharmony_ci#define PF_XDP AF_XDP
498c2ecf20Sopenharmony_ci#endif
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define NUM_FRAMES (4 * 1024)
528c2ecf20Sopenharmony_ci#define MIN_PKT_SIZE 64
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define DEBUG_HEXDUMP 0
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_citypedef __u64 u64;
578c2ecf20Sopenharmony_citypedef __u32 u32;
588c2ecf20Sopenharmony_citypedef __u16 u16;
598c2ecf20Sopenharmony_citypedef __u8  u8;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic unsigned long prev_time;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cienum benchmark_type {
648c2ecf20Sopenharmony_ci	BENCH_RXDROP = 0,
658c2ecf20Sopenharmony_ci	BENCH_TXONLY = 1,
668c2ecf20Sopenharmony_ci	BENCH_L2FWD = 2,
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic enum benchmark_type opt_bench = BENCH_RXDROP;
708c2ecf20Sopenharmony_cistatic u32 opt_xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
718c2ecf20Sopenharmony_cistatic const char *opt_if = "";
728c2ecf20Sopenharmony_cistatic int opt_ifindex;
738c2ecf20Sopenharmony_cistatic int opt_queue;
748c2ecf20Sopenharmony_cistatic unsigned long opt_duration;
758c2ecf20Sopenharmony_cistatic unsigned long start_time;
768c2ecf20Sopenharmony_cistatic bool benchmark_done;
778c2ecf20Sopenharmony_cistatic u32 opt_batch_size = 64;
788c2ecf20Sopenharmony_cistatic int opt_pkt_count;
798c2ecf20Sopenharmony_cistatic u16 opt_pkt_size = MIN_PKT_SIZE;
808c2ecf20Sopenharmony_cistatic u32 opt_pkt_fill_pattern = 0x12345678;
818c2ecf20Sopenharmony_cistatic bool opt_extra_stats;
828c2ecf20Sopenharmony_cistatic bool opt_quiet;
838c2ecf20Sopenharmony_cistatic bool opt_app_stats;
848c2ecf20Sopenharmony_cistatic const char *opt_irq_str = "";
858c2ecf20Sopenharmony_cistatic u32 irq_no;
868c2ecf20Sopenharmony_cistatic int irqs_at_init = -1;
878c2ecf20Sopenharmony_cistatic int opt_poll;
888c2ecf20Sopenharmony_cistatic int opt_interval = 1;
898c2ecf20Sopenharmony_cistatic u32 opt_xdp_bind_flags = XDP_USE_NEED_WAKEUP;
908c2ecf20Sopenharmony_cistatic u32 opt_umem_flags;
918c2ecf20Sopenharmony_cistatic int opt_unaligned_chunks;
928c2ecf20Sopenharmony_cistatic int opt_mmap_flags;
938c2ecf20Sopenharmony_cistatic int opt_xsk_frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE;
948c2ecf20Sopenharmony_cistatic int opt_timeout = 1000;
958c2ecf20Sopenharmony_cistatic bool opt_need_wakeup = true;
968c2ecf20Sopenharmony_cistatic u32 opt_num_xsks = 1;
978c2ecf20Sopenharmony_cistatic u32 prog_id;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistruct xsk_ring_stats {
1008c2ecf20Sopenharmony_ci	unsigned long rx_npkts;
1018c2ecf20Sopenharmony_ci	unsigned long tx_npkts;
1028c2ecf20Sopenharmony_ci	unsigned long rx_dropped_npkts;
1038c2ecf20Sopenharmony_ci	unsigned long rx_invalid_npkts;
1048c2ecf20Sopenharmony_ci	unsigned long tx_invalid_npkts;
1058c2ecf20Sopenharmony_ci	unsigned long rx_full_npkts;
1068c2ecf20Sopenharmony_ci	unsigned long rx_fill_empty_npkts;
1078c2ecf20Sopenharmony_ci	unsigned long tx_empty_npkts;
1088c2ecf20Sopenharmony_ci	unsigned long prev_rx_npkts;
1098c2ecf20Sopenharmony_ci	unsigned long prev_tx_npkts;
1108c2ecf20Sopenharmony_ci	unsigned long prev_rx_dropped_npkts;
1118c2ecf20Sopenharmony_ci	unsigned long prev_rx_invalid_npkts;
1128c2ecf20Sopenharmony_ci	unsigned long prev_tx_invalid_npkts;
1138c2ecf20Sopenharmony_ci	unsigned long prev_rx_full_npkts;
1148c2ecf20Sopenharmony_ci	unsigned long prev_rx_fill_empty_npkts;
1158c2ecf20Sopenharmony_ci	unsigned long prev_tx_empty_npkts;
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistruct xsk_driver_stats {
1198c2ecf20Sopenharmony_ci	unsigned long intrs;
1208c2ecf20Sopenharmony_ci	unsigned long prev_intrs;
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistruct xsk_app_stats {
1248c2ecf20Sopenharmony_ci	unsigned long rx_empty_polls;
1258c2ecf20Sopenharmony_ci	unsigned long fill_fail_polls;
1268c2ecf20Sopenharmony_ci	unsigned long copy_tx_sendtos;
1278c2ecf20Sopenharmony_ci	unsigned long tx_wakeup_sendtos;
1288c2ecf20Sopenharmony_ci	unsigned long opt_polls;
1298c2ecf20Sopenharmony_ci	unsigned long prev_rx_empty_polls;
1308c2ecf20Sopenharmony_ci	unsigned long prev_fill_fail_polls;
1318c2ecf20Sopenharmony_ci	unsigned long prev_copy_tx_sendtos;
1328c2ecf20Sopenharmony_ci	unsigned long prev_tx_wakeup_sendtos;
1338c2ecf20Sopenharmony_ci	unsigned long prev_opt_polls;
1348c2ecf20Sopenharmony_ci};
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistruct xsk_umem_info {
1378c2ecf20Sopenharmony_ci	struct xsk_ring_prod fq;
1388c2ecf20Sopenharmony_ci	struct xsk_ring_cons cq;
1398c2ecf20Sopenharmony_ci	struct xsk_umem *umem;
1408c2ecf20Sopenharmony_ci	void *buffer;
1418c2ecf20Sopenharmony_ci};
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistruct xsk_socket_info {
1448c2ecf20Sopenharmony_ci	struct xsk_ring_cons rx;
1458c2ecf20Sopenharmony_ci	struct xsk_ring_prod tx;
1468c2ecf20Sopenharmony_ci	struct xsk_umem_info *umem;
1478c2ecf20Sopenharmony_ci	struct xsk_socket *xsk;
1488c2ecf20Sopenharmony_ci	struct xsk_ring_stats ring_stats;
1498c2ecf20Sopenharmony_ci	struct xsk_app_stats app_stats;
1508c2ecf20Sopenharmony_ci	struct xsk_driver_stats drv_stats;
1518c2ecf20Sopenharmony_ci	u32 outstanding_tx;
1528c2ecf20Sopenharmony_ci};
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic int num_socks;
1558c2ecf20Sopenharmony_cistruct xsk_socket_info *xsks[MAX_SOCKS];
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic unsigned long get_nsecs(void)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct timespec ts;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	clock_gettime(CLOCK_MONOTONIC, &ts);
1628c2ecf20Sopenharmony_ci	return ts.tv_sec * 1000000000UL + ts.tv_nsec;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic void print_benchmark(bool running)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	const char *bench_str = "INVALID";
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (opt_bench == BENCH_RXDROP)
1708c2ecf20Sopenharmony_ci		bench_str = "rxdrop";
1718c2ecf20Sopenharmony_ci	else if (opt_bench == BENCH_TXONLY)
1728c2ecf20Sopenharmony_ci		bench_str = "txonly";
1738c2ecf20Sopenharmony_ci	else if (opt_bench == BENCH_L2FWD)
1748c2ecf20Sopenharmony_ci		bench_str = "l2fwd";
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	printf("%s:%d %s ", opt_if, opt_queue, bench_str);
1778c2ecf20Sopenharmony_ci	if (opt_xdp_flags & XDP_FLAGS_SKB_MODE)
1788c2ecf20Sopenharmony_ci		printf("xdp-skb ");
1798c2ecf20Sopenharmony_ci	else if (opt_xdp_flags & XDP_FLAGS_DRV_MODE)
1808c2ecf20Sopenharmony_ci		printf("xdp-drv ");
1818c2ecf20Sopenharmony_ci	else
1828c2ecf20Sopenharmony_ci		printf("	");
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (opt_poll)
1858c2ecf20Sopenharmony_ci		printf("poll() ");
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (running) {
1888c2ecf20Sopenharmony_ci		printf("running...");
1898c2ecf20Sopenharmony_ci		fflush(stdout);
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int xsk_get_xdp_stats(int fd, struct xsk_socket_info *xsk)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct xdp_statistics stats;
1968c2ecf20Sopenharmony_ci	socklen_t optlen;
1978c2ecf20Sopenharmony_ci	int err;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	optlen = sizeof(stats);
2008c2ecf20Sopenharmony_ci	err = getsockopt(fd, SOL_XDP, XDP_STATISTICS, &stats, &optlen);
2018c2ecf20Sopenharmony_ci	if (err)
2028c2ecf20Sopenharmony_ci		return err;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (optlen == sizeof(struct xdp_statistics)) {
2058c2ecf20Sopenharmony_ci		xsk->ring_stats.rx_dropped_npkts = stats.rx_dropped;
2068c2ecf20Sopenharmony_ci		xsk->ring_stats.rx_invalid_npkts = stats.rx_invalid_descs;
2078c2ecf20Sopenharmony_ci		xsk->ring_stats.tx_invalid_npkts = stats.tx_invalid_descs;
2088c2ecf20Sopenharmony_ci		xsk->ring_stats.rx_full_npkts = stats.rx_ring_full;
2098c2ecf20Sopenharmony_ci		xsk->ring_stats.rx_fill_empty_npkts = stats.rx_fill_ring_empty_descs;
2108c2ecf20Sopenharmony_ci		xsk->ring_stats.tx_empty_npkts = stats.tx_ring_empty_descs;
2118c2ecf20Sopenharmony_ci		return 0;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	return -EINVAL;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic void dump_app_stats(long dt)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	int i;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	for (i = 0; i < num_socks && xsks[i]; i++) {
2228c2ecf20Sopenharmony_ci		char *fmt = "%-18s %'-14.0f %'-14lu\n";
2238c2ecf20Sopenharmony_ci		double rx_empty_polls_ps, fill_fail_polls_ps, copy_tx_sendtos_ps,
2248c2ecf20Sopenharmony_ci				tx_wakeup_sendtos_ps, opt_polls_ps;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		rx_empty_polls_ps = (xsks[i]->app_stats.rx_empty_polls -
2278c2ecf20Sopenharmony_ci					xsks[i]->app_stats.prev_rx_empty_polls) * 1000000000. / dt;
2288c2ecf20Sopenharmony_ci		fill_fail_polls_ps = (xsks[i]->app_stats.fill_fail_polls -
2298c2ecf20Sopenharmony_ci					xsks[i]->app_stats.prev_fill_fail_polls) * 1000000000. / dt;
2308c2ecf20Sopenharmony_ci		copy_tx_sendtos_ps = (xsks[i]->app_stats.copy_tx_sendtos -
2318c2ecf20Sopenharmony_ci					xsks[i]->app_stats.prev_copy_tx_sendtos) * 1000000000. / dt;
2328c2ecf20Sopenharmony_ci		tx_wakeup_sendtos_ps = (xsks[i]->app_stats.tx_wakeup_sendtos -
2338c2ecf20Sopenharmony_ci					xsks[i]->app_stats.prev_tx_wakeup_sendtos)
2348c2ecf20Sopenharmony_ci										* 1000000000. / dt;
2358c2ecf20Sopenharmony_ci		opt_polls_ps = (xsks[i]->app_stats.opt_polls -
2368c2ecf20Sopenharmony_ci					xsks[i]->app_stats.prev_opt_polls) * 1000000000. / dt;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci		printf("\n%-18s %-14s %-14s\n", "", "calls/s", "count");
2398c2ecf20Sopenharmony_ci		printf(fmt, "rx empty polls", rx_empty_polls_ps, xsks[i]->app_stats.rx_empty_polls);
2408c2ecf20Sopenharmony_ci		printf(fmt, "fill fail polls", fill_fail_polls_ps,
2418c2ecf20Sopenharmony_ci							xsks[i]->app_stats.fill_fail_polls);
2428c2ecf20Sopenharmony_ci		printf(fmt, "copy tx sendtos", copy_tx_sendtos_ps,
2438c2ecf20Sopenharmony_ci							xsks[i]->app_stats.copy_tx_sendtos);
2448c2ecf20Sopenharmony_ci		printf(fmt, "tx wakeup sendtos", tx_wakeup_sendtos_ps,
2458c2ecf20Sopenharmony_ci							xsks[i]->app_stats.tx_wakeup_sendtos);
2468c2ecf20Sopenharmony_ci		printf(fmt, "opt polls", opt_polls_ps, xsks[i]->app_stats.opt_polls);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		xsks[i]->app_stats.prev_rx_empty_polls = xsks[i]->app_stats.rx_empty_polls;
2498c2ecf20Sopenharmony_ci		xsks[i]->app_stats.prev_fill_fail_polls = xsks[i]->app_stats.fill_fail_polls;
2508c2ecf20Sopenharmony_ci		xsks[i]->app_stats.prev_copy_tx_sendtos = xsks[i]->app_stats.copy_tx_sendtos;
2518c2ecf20Sopenharmony_ci		xsks[i]->app_stats.prev_tx_wakeup_sendtos = xsks[i]->app_stats.tx_wakeup_sendtos;
2528c2ecf20Sopenharmony_ci		xsks[i]->app_stats.prev_opt_polls = xsks[i]->app_stats.opt_polls;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic bool get_interrupt_number(void)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	FILE *f_int_proc;
2598c2ecf20Sopenharmony_ci	char line[4096];
2608c2ecf20Sopenharmony_ci	bool found = false;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	f_int_proc = fopen("/proc/interrupts", "r");
2638c2ecf20Sopenharmony_ci	if (f_int_proc == NULL) {
2648c2ecf20Sopenharmony_ci		printf("Failed to open /proc/interrupts.\n");
2658c2ecf20Sopenharmony_ci		return found;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	while (!feof(f_int_proc) && !found) {
2698c2ecf20Sopenharmony_ci		/* Make sure to read a full line at a time */
2708c2ecf20Sopenharmony_ci		if (fgets(line, sizeof(line), f_int_proc) == NULL ||
2718c2ecf20Sopenharmony_ci				line[strlen(line) - 1] != '\n') {
2728c2ecf20Sopenharmony_ci			printf("Error reading from interrupts file\n");
2738c2ecf20Sopenharmony_ci			break;
2748c2ecf20Sopenharmony_ci		}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		/* Extract interrupt number from line */
2778c2ecf20Sopenharmony_ci		if (strstr(line, opt_irq_str) != NULL) {
2788c2ecf20Sopenharmony_ci			irq_no = atoi(line);
2798c2ecf20Sopenharmony_ci			found = true;
2808c2ecf20Sopenharmony_ci			break;
2818c2ecf20Sopenharmony_ci		}
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	fclose(f_int_proc);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	return found;
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic int get_irqs(void)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	char count_path[PATH_MAX];
2928c2ecf20Sopenharmony_ci	int total_intrs = -1;
2938c2ecf20Sopenharmony_ci	FILE *f_count_proc;
2948c2ecf20Sopenharmony_ci	char line[4096];
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	snprintf(count_path, sizeof(count_path),
2978c2ecf20Sopenharmony_ci		"/sys/kernel/irq/%i/per_cpu_count", irq_no);
2988c2ecf20Sopenharmony_ci	f_count_proc = fopen(count_path, "r");
2998c2ecf20Sopenharmony_ci	if (f_count_proc == NULL) {
3008c2ecf20Sopenharmony_ci		printf("Failed to open %s\n", count_path);
3018c2ecf20Sopenharmony_ci		return total_intrs;
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (fgets(line, sizeof(line), f_count_proc) == NULL ||
3058c2ecf20Sopenharmony_ci			line[strlen(line) - 1] != '\n') {
3068c2ecf20Sopenharmony_ci		printf("Error reading from %s\n", count_path);
3078c2ecf20Sopenharmony_ci	} else {
3088c2ecf20Sopenharmony_ci		static const char com[2] = ",";
3098c2ecf20Sopenharmony_ci		char *token;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		total_intrs = 0;
3128c2ecf20Sopenharmony_ci		token = strtok(line, com);
3138c2ecf20Sopenharmony_ci		while (token != NULL) {
3148c2ecf20Sopenharmony_ci			/* sum up interrupts across all cores */
3158c2ecf20Sopenharmony_ci			total_intrs += atoi(token);
3168c2ecf20Sopenharmony_ci			token = strtok(NULL, com);
3178c2ecf20Sopenharmony_ci		}
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	fclose(f_count_proc);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	return total_intrs;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic void dump_driver_stats(long dt)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	int i;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	for (i = 0; i < num_socks && xsks[i]; i++) {
3308c2ecf20Sopenharmony_ci		char *fmt = "%-18s %'-14.0f %'-14lu\n";
3318c2ecf20Sopenharmony_ci		double intrs_ps;
3328c2ecf20Sopenharmony_ci		int n_ints = get_irqs();
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci		if (n_ints < 0) {
3358c2ecf20Sopenharmony_ci			printf("error getting intr info for intr %i\n", irq_no);
3368c2ecf20Sopenharmony_ci			return;
3378c2ecf20Sopenharmony_ci		}
3388c2ecf20Sopenharmony_ci		xsks[i]->drv_stats.intrs = n_ints - irqs_at_init;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci		intrs_ps = (xsks[i]->drv_stats.intrs - xsks[i]->drv_stats.prev_intrs) *
3418c2ecf20Sopenharmony_ci			 1000000000. / dt;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci		printf("\n%-18s %-14s %-14s\n", "", "intrs/s", "count");
3448c2ecf20Sopenharmony_ci		printf(fmt, "irqs", intrs_ps, xsks[i]->drv_stats.intrs);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		xsks[i]->drv_stats.prev_intrs = xsks[i]->drv_stats.intrs;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic void dump_stats(void)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	unsigned long now = get_nsecs();
3538c2ecf20Sopenharmony_ci	long dt = now - prev_time;
3548c2ecf20Sopenharmony_ci	int i;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	prev_time = now;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	for (i = 0; i < num_socks && xsks[i]; i++) {
3598c2ecf20Sopenharmony_ci		char *fmt = "%-18s %'-14.0f %'-14lu\n";
3608c2ecf20Sopenharmony_ci		double rx_pps, tx_pps, dropped_pps, rx_invalid_pps, full_pps, fill_empty_pps,
3618c2ecf20Sopenharmony_ci			tx_invalid_pps, tx_empty_pps;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci		rx_pps = (xsks[i]->ring_stats.rx_npkts - xsks[i]->ring_stats.prev_rx_npkts) *
3648c2ecf20Sopenharmony_ci			 1000000000. / dt;
3658c2ecf20Sopenharmony_ci		tx_pps = (xsks[i]->ring_stats.tx_npkts - xsks[i]->ring_stats.prev_tx_npkts) *
3668c2ecf20Sopenharmony_ci			 1000000000. / dt;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci		printf("\n sock%d@", i);
3698c2ecf20Sopenharmony_ci		print_benchmark(false);
3708c2ecf20Sopenharmony_ci		printf("\n");
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci		printf("%-18s %-14s %-14s %-14.2f\n", "", "pps", "pkts",
3738c2ecf20Sopenharmony_ci		       dt / 1000000000.);
3748c2ecf20Sopenharmony_ci		printf(fmt, "rx", rx_pps, xsks[i]->ring_stats.rx_npkts);
3758c2ecf20Sopenharmony_ci		printf(fmt, "tx", tx_pps, xsks[i]->ring_stats.tx_npkts);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		xsks[i]->ring_stats.prev_rx_npkts = xsks[i]->ring_stats.rx_npkts;
3788c2ecf20Sopenharmony_ci		xsks[i]->ring_stats.prev_tx_npkts = xsks[i]->ring_stats.tx_npkts;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci		if (opt_extra_stats) {
3818c2ecf20Sopenharmony_ci			if (!xsk_get_xdp_stats(xsk_socket__fd(xsks[i]->xsk), xsks[i])) {
3828c2ecf20Sopenharmony_ci				dropped_pps = (xsks[i]->ring_stats.rx_dropped_npkts -
3838c2ecf20Sopenharmony_ci						xsks[i]->ring_stats.prev_rx_dropped_npkts) *
3848c2ecf20Sopenharmony_ci							1000000000. / dt;
3858c2ecf20Sopenharmony_ci				rx_invalid_pps = (xsks[i]->ring_stats.rx_invalid_npkts -
3868c2ecf20Sopenharmony_ci						xsks[i]->ring_stats.prev_rx_invalid_npkts) *
3878c2ecf20Sopenharmony_ci							1000000000. / dt;
3888c2ecf20Sopenharmony_ci				tx_invalid_pps = (xsks[i]->ring_stats.tx_invalid_npkts -
3898c2ecf20Sopenharmony_ci						xsks[i]->ring_stats.prev_tx_invalid_npkts) *
3908c2ecf20Sopenharmony_ci							1000000000. / dt;
3918c2ecf20Sopenharmony_ci				full_pps = (xsks[i]->ring_stats.rx_full_npkts -
3928c2ecf20Sopenharmony_ci						xsks[i]->ring_stats.prev_rx_full_npkts) *
3938c2ecf20Sopenharmony_ci							1000000000. / dt;
3948c2ecf20Sopenharmony_ci				fill_empty_pps = (xsks[i]->ring_stats.rx_fill_empty_npkts -
3958c2ecf20Sopenharmony_ci						xsks[i]->ring_stats.prev_rx_fill_empty_npkts) *
3968c2ecf20Sopenharmony_ci							1000000000. / dt;
3978c2ecf20Sopenharmony_ci				tx_empty_pps = (xsks[i]->ring_stats.tx_empty_npkts -
3988c2ecf20Sopenharmony_ci						xsks[i]->ring_stats.prev_tx_empty_npkts) *
3998c2ecf20Sopenharmony_ci							1000000000. / dt;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci				printf(fmt, "rx dropped", dropped_pps,
4028c2ecf20Sopenharmony_ci				       xsks[i]->ring_stats.rx_dropped_npkts);
4038c2ecf20Sopenharmony_ci				printf(fmt, "rx invalid", rx_invalid_pps,
4048c2ecf20Sopenharmony_ci				       xsks[i]->ring_stats.rx_invalid_npkts);
4058c2ecf20Sopenharmony_ci				printf(fmt, "tx invalid", tx_invalid_pps,
4068c2ecf20Sopenharmony_ci				       xsks[i]->ring_stats.tx_invalid_npkts);
4078c2ecf20Sopenharmony_ci				printf(fmt, "rx queue full", full_pps,
4088c2ecf20Sopenharmony_ci				       xsks[i]->ring_stats.rx_full_npkts);
4098c2ecf20Sopenharmony_ci				printf(fmt, "fill ring empty", fill_empty_pps,
4108c2ecf20Sopenharmony_ci				       xsks[i]->ring_stats.rx_fill_empty_npkts);
4118c2ecf20Sopenharmony_ci				printf(fmt, "tx ring empty", tx_empty_pps,
4128c2ecf20Sopenharmony_ci				       xsks[i]->ring_stats.tx_empty_npkts);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci				xsks[i]->ring_stats.prev_rx_dropped_npkts =
4158c2ecf20Sopenharmony_ci					xsks[i]->ring_stats.rx_dropped_npkts;
4168c2ecf20Sopenharmony_ci				xsks[i]->ring_stats.prev_rx_invalid_npkts =
4178c2ecf20Sopenharmony_ci					xsks[i]->ring_stats.rx_invalid_npkts;
4188c2ecf20Sopenharmony_ci				xsks[i]->ring_stats.prev_tx_invalid_npkts =
4198c2ecf20Sopenharmony_ci					xsks[i]->ring_stats.tx_invalid_npkts;
4208c2ecf20Sopenharmony_ci				xsks[i]->ring_stats.prev_rx_full_npkts =
4218c2ecf20Sopenharmony_ci					xsks[i]->ring_stats.rx_full_npkts;
4228c2ecf20Sopenharmony_ci				xsks[i]->ring_stats.prev_rx_fill_empty_npkts =
4238c2ecf20Sopenharmony_ci					xsks[i]->ring_stats.rx_fill_empty_npkts;
4248c2ecf20Sopenharmony_ci				xsks[i]->ring_stats.prev_tx_empty_npkts =
4258c2ecf20Sopenharmony_ci					xsks[i]->ring_stats.tx_empty_npkts;
4268c2ecf20Sopenharmony_ci			} else {
4278c2ecf20Sopenharmony_ci				printf("%-15s\n", "Error retrieving extra stats");
4288c2ecf20Sopenharmony_ci			}
4298c2ecf20Sopenharmony_ci		}
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	if (opt_app_stats)
4338c2ecf20Sopenharmony_ci		dump_app_stats(dt);
4348c2ecf20Sopenharmony_ci	if (irq_no)
4358c2ecf20Sopenharmony_ci		dump_driver_stats(dt);
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic bool is_benchmark_done(void)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	if (opt_duration > 0) {
4418c2ecf20Sopenharmony_ci		unsigned long dt = (get_nsecs() - start_time);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci		if (dt >= opt_duration)
4448c2ecf20Sopenharmony_ci			benchmark_done = true;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci	return benchmark_done;
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cistatic void *poller(void *arg)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	(void)arg;
4528c2ecf20Sopenharmony_ci	while (!is_benchmark_done()) {
4538c2ecf20Sopenharmony_ci		sleep(opt_interval);
4548c2ecf20Sopenharmony_ci		dump_stats();
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	return NULL;
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic void remove_xdp_program(void)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	u32 curr_prog_id = 0;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	if (bpf_get_link_xdp_id(opt_ifindex, &curr_prog_id, opt_xdp_flags)) {
4658c2ecf20Sopenharmony_ci		printf("bpf_get_link_xdp_id failed\n");
4668c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci	if (prog_id == curr_prog_id)
4698c2ecf20Sopenharmony_ci		bpf_set_link_xdp_fd(opt_ifindex, -1, opt_xdp_flags);
4708c2ecf20Sopenharmony_ci	else if (!curr_prog_id)
4718c2ecf20Sopenharmony_ci		printf("couldn't find a prog id on a given interface\n");
4728c2ecf20Sopenharmony_ci	else
4738c2ecf20Sopenharmony_ci		printf("program on interface changed, not removing\n");
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic void int_exit(int sig)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	benchmark_done = true;
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic void xdpsock_cleanup(void)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	struct xsk_umem *umem = xsks[0]->umem->umem;
4848c2ecf20Sopenharmony_ci	int i;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	dump_stats();
4878c2ecf20Sopenharmony_ci	for (i = 0; i < num_socks; i++)
4888c2ecf20Sopenharmony_ci		xsk_socket__delete(xsks[i]->xsk);
4898c2ecf20Sopenharmony_ci	(void)xsk_umem__delete(umem);
4908c2ecf20Sopenharmony_ci	remove_xdp_program();
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cistatic void __exit_with_error(int error, const char *file, const char *func,
4948c2ecf20Sopenharmony_ci			      int line)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	fprintf(stderr, "%s:%s:%i: errno: %d/\"%s\"\n", file, func,
4978c2ecf20Sopenharmony_ci		line, error, strerror(error));
4988c2ecf20Sopenharmony_ci	dump_stats();
4998c2ecf20Sopenharmony_ci	remove_xdp_program();
5008c2ecf20Sopenharmony_ci	exit(EXIT_FAILURE);
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci#define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, \
5048c2ecf20Sopenharmony_ci						 __LINE__)
5058c2ecf20Sopenharmony_cistatic void swap_mac_addresses(void *data)
5068c2ecf20Sopenharmony_ci{
5078c2ecf20Sopenharmony_ci	struct ether_header *eth = (struct ether_header *)data;
5088c2ecf20Sopenharmony_ci	struct ether_addr *src_addr = (struct ether_addr *)&eth->ether_shost;
5098c2ecf20Sopenharmony_ci	struct ether_addr *dst_addr = (struct ether_addr *)&eth->ether_dhost;
5108c2ecf20Sopenharmony_ci	struct ether_addr tmp;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	tmp = *src_addr;
5138c2ecf20Sopenharmony_ci	*src_addr = *dst_addr;
5148c2ecf20Sopenharmony_ci	*dst_addr = tmp;
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic void hex_dump(void *pkt, size_t length, u64 addr)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	const unsigned char *address = (unsigned char *)pkt;
5208c2ecf20Sopenharmony_ci	const unsigned char *line = address;
5218c2ecf20Sopenharmony_ci	size_t line_size = 32;
5228c2ecf20Sopenharmony_ci	unsigned char c;
5238c2ecf20Sopenharmony_ci	char buf[32];
5248c2ecf20Sopenharmony_ci	int i = 0;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	if (!DEBUG_HEXDUMP)
5278c2ecf20Sopenharmony_ci		return;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	sprintf(buf, "addr=%llu", addr);
5308c2ecf20Sopenharmony_ci	printf("length = %zu\n", length);
5318c2ecf20Sopenharmony_ci	printf("%s | ", buf);
5328c2ecf20Sopenharmony_ci	while (length-- > 0) {
5338c2ecf20Sopenharmony_ci		printf("%02X ", *address++);
5348c2ecf20Sopenharmony_ci		if (!(++i % line_size) || (length == 0 && i % line_size)) {
5358c2ecf20Sopenharmony_ci			if (length == 0) {
5368c2ecf20Sopenharmony_ci				while (i++ % line_size)
5378c2ecf20Sopenharmony_ci					printf("__ ");
5388c2ecf20Sopenharmony_ci			}
5398c2ecf20Sopenharmony_ci			printf(" | ");	/* right close */
5408c2ecf20Sopenharmony_ci			while (line < address) {
5418c2ecf20Sopenharmony_ci				c = *line++;
5428c2ecf20Sopenharmony_ci				printf("%c", (c < 33 || c == 255) ? 0x2E : c);
5438c2ecf20Sopenharmony_ci			}
5448c2ecf20Sopenharmony_ci			printf("\n");
5458c2ecf20Sopenharmony_ci			if (length > 0)
5468c2ecf20Sopenharmony_ci				printf("%s | ", buf);
5478c2ecf20Sopenharmony_ci		}
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci	printf("\n");
5508c2ecf20Sopenharmony_ci}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_cistatic void *memset32_htonl(void *dest, u32 val, u32 size)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	u32 *ptr = (u32 *)dest;
5558c2ecf20Sopenharmony_ci	int i;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	val = htonl(val);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	for (i = 0; i < (size & (~0x3)); i += 4)
5608c2ecf20Sopenharmony_ci		ptr[i >> 2] = val;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	for (; i < size; i++)
5638c2ecf20Sopenharmony_ci		((char *)dest)[i] = ((char *)&val)[i & 3];
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	return dest;
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci/*
5698c2ecf20Sopenharmony_ci * This function code has been taken from
5708c2ecf20Sopenharmony_ci * Linux kernel lib/checksum.c
5718c2ecf20Sopenharmony_ci */
5728c2ecf20Sopenharmony_cistatic inline unsigned short from32to16(unsigned int x)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	/* add up 16-bit and 16-bit for 16+c bit */
5758c2ecf20Sopenharmony_ci	x = (x & 0xffff) + (x >> 16);
5768c2ecf20Sopenharmony_ci	/* add up carry.. */
5778c2ecf20Sopenharmony_ci	x = (x & 0xffff) + (x >> 16);
5788c2ecf20Sopenharmony_ci	return x;
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci/*
5828c2ecf20Sopenharmony_ci * This function code has been taken from
5838c2ecf20Sopenharmony_ci * Linux kernel lib/checksum.c
5848c2ecf20Sopenharmony_ci */
5858c2ecf20Sopenharmony_cistatic unsigned int do_csum(const unsigned char *buff, int len)
5868c2ecf20Sopenharmony_ci{
5878c2ecf20Sopenharmony_ci	unsigned int result = 0;
5888c2ecf20Sopenharmony_ci	int odd;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	if (len <= 0)
5918c2ecf20Sopenharmony_ci		goto out;
5928c2ecf20Sopenharmony_ci	odd = 1 & (unsigned long)buff;
5938c2ecf20Sopenharmony_ci	if (odd) {
5948c2ecf20Sopenharmony_ci#ifdef __LITTLE_ENDIAN
5958c2ecf20Sopenharmony_ci		result += (*buff << 8);
5968c2ecf20Sopenharmony_ci#else
5978c2ecf20Sopenharmony_ci		result = *buff;
5988c2ecf20Sopenharmony_ci#endif
5998c2ecf20Sopenharmony_ci		len--;
6008c2ecf20Sopenharmony_ci		buff++;
6018c2ecf20Sopenharmony_ci	}
6028c2ecf20Sopenharmony_ci	if (len >= 2) {
6038c2ecf20Sopenharmony_ci		if (2 & (unsigned long)buff) {
6048c2ecf20Sopenharmony_ci			result += *(unsigned short *)buff;
6058c2ecf20Sopenharmony_ci			len -= 2;
6068c2ecf20Sopenharmony_ci			buff += 2;
6078c2ecf20Sopenharmony_ci		}
6088c2ecf20Sopenharmony_ci		if (len >= 4) {
6098c2ecf20Sopenharmony_ci			const unsigned char *end = buff +
6108c2ecf20Sopenharmony_ci						   ((unsigned int)len & ~3);
6118c2ecf20Sopenharmony_ci			unsigned int carry = 0;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci			do {
6148c2ecf20Sopenharmony_ci				unsigned int w = *(unsigned int *)buff;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci				buff += 4;
6178c2ecf20Sopenharmony_ci				result += carry;
6188c2ecf20Sopenharmony_ci				result += w;
6198c2ecf20Sopenharmony_ci				carry = (w > result);
6208c2ecf20Sopenharmony_ci			} while (buff < end);
6218c2ecf20Sopenharmony_ci			result += carry;
6228c2ecf20Sopenharmony_ci			result = (result & 0xffff) + (result >> 16);
6238c2ecf20Sopenharmony_ci		}
6248c2ecf20Sopenharmony_ci		if (len & 2) {
6258c2ecf20Sopenharmony_ci			result += *(unsigned short *)buff;
6268c2ecf20Sopenharmony_ci			buff += 2;
6278c2ecf20Sopenharmony_ci		}
6288c2ecf20Sopenharmony_ci	}
6298c2ecf20Sopenharmony_ci	if (len & 1)
6308c2ecf20Sopenharmony_ci#ifdef __LITTLE_ENDIAN
6318c2ecf20Sopenharmony_ci		result += *buff;
6328c2ecf20Sopenharmony_ci#else
6338c2ecf20Sopenharmony_ci		result += (*buff << 8);
6348c2ecf20Sopenharmony_ci#endif
6358c2ecf20Sopenharmony_ci	result = from32to16(result);
6368c2ecf20Sopenharmony_ci	if (odd)
6378c2ecf20Sopenharmony_ci		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
6388c2ecf20Sopenharmony_ciout:
6398c2ecf20Sopenharmony_ci	return result;
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci__sum16 ip_fast_csum(const void *iph, unsigned int ihl);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci/*
6458c2ecf20Sopenharmony_ci *	This is a version of ip_compute_csum() optimized for IP headers,
6468c2ecf20Sopenharmony_ci *	which always checksum on 4 octet boundaries.
6478c2ecf20Sopenharmony_ci *	This function code has been taken from
6488c2ecf20Sopenharmony_ci *	Linux kernel lib/checksum.c
6498c2ecf20Sopenharmony_ci */
6508c2ecf20Sopenharmony_ci__sum16 ip_fast_csum(const void *iph, unsigned int ihl)
6518c2ecf20Sopenharmony_ci{
6528c2ecf20Sopenharmony_ci	return (__force __sum16)~do_csum(iph, ihl * 4);
6538c2ecf20Sopenharmony_ci}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci/*
6568c2ecf20Sopenharmony_ci * Fold a partial checksum
6578c2ecf20Sopenharmony_ci * This function code has been taken from
6588c2ecf20Sopenharmony_ci * Linux kernel include/asm-generic/checksum.h
6598c2ecf20Sopenharmony_ci */
6608c2ecf20Sopenharmony_cistatic inline __sum16 csum_fold(__wsum csum)
6618c2ecf20Sopenharmony_ci{
6628c2ecf20Sopenharmony_ci	u32 sum = (__force u32)csum;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	sum = (sum & 0xffff) + (sum >> 16);
6658c2ecf20Sopenharmony_ci	sum = (sum & 0xffff) + (sum >> 16);
6668c2ecf20Sopenharmony_ci	return (__force __sum16)~sum;
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci/*
6708c2ecf20Sopenharmony_ci * This function code has been taken from
6718c2ecf20Sopenharmony_ci * Linux kernel lib/checksum.c
6728c2ecf20Sopenharmony_ci */
6738c2ecf20Sopenharmony_cistatic inline u32 from64to32(u64 x)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci	/* add up 32-bit and 32-bit for 32+c bit */
6768c2ecf20Sopenharmony_ci	x = (x & 0xffffffff) + (x >> 32);
6778c2ecf20Sopenharmony_ci	/* add up carry.. */
6788c2ecf20Sopenharmony_ci	x = (x & 0xffffffff) + (x >> 32);
6798c2ecf20Sopenharmony_ci	return (u32)x;
6808c2ecf20Sopenharmony_ci}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
6838c2ecf20Sopenharmony_ci			  __u32 len, __u8 proto, __wsum sum);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci/*
6868c2ecf20Sopenharmony_ci * This function code has been taken from
6878c2ecf20Sopenharmony_ci * Linux kernel lib/checksum.c
6888c2ecf20Sopenharmony_ci */
6898c2ecf20Sopenharmony_ci__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
6908c2ecf20Sopenharmony_ci			  __u32 len, __u8 proto, __wsum sum)
6918c2ecf20Sopenharmony_ci{
6928c2ecf20Sopenharmony_ci	unsigned long long s = (__force u32)sum;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	s += (__force u32)saddr;
6958c2ecf20Sopenharmony_ci	s += (__force u32)daddr;
6968c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN__
6978c2ecf20Sopenharmony_ci	s += proto + len;
6988c2ecf20Sopenharmony_ci#else
6998c2ecf20Sopenharmony_ci	s += (proto + len) << 8;
7008c2ecf20Sopenharmony_ci#endif
7018c2ecf20Sopenharmony_ci	return (__force __wsum)from64to32(s);
7028c2ecf20Sopenharmony_ci}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci/*
7058c2ecf20Sopenharmony_ci * This function has been taken from
7068c2ecf20Sopenharmony_ci * Linux kernel include/asm-generic/checksum.h
7078c2ecf20Sopenharmony_ci */
7088c2ecf20Sopenharmony_cistatic inline __sum16
7098c2ecf20Sopenharmony_cicsum_tcpudp_magic(__be32 saddr, __be32 daddr, __u32 len,
7108c2ecf20Sopenharmony_ci		  __u8 proto, __wsum sum)
7118c2ecf20Sopenharmony_ci{
7128c2ecf20Sopenharmony_ci	return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
7138c2ecf20Sopenharmony_ci}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_cistatic inline u16 udp_csum(u32 saddr, u32 daddr, u32 len,
7168c2ecf20Sopenharmony_ci			   u8 proto, u16 *udp_pkt)
7178c2ecf20Sopenharmony_ci{
7188c2ecf20Sopenharmony_ci	u32 csum = 0;
7198c2ecf20Sopenharmony_ci	u32 cnt = 0;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	/* udp hdr and data */
7228c2ecf20Sopenharmony_ci	for (; cnt < len; cnt += 2)
7238c2ecf20Sopenharmony_ci		csum += udp_pkt[cnt >> 1];
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	return csum_tcpudp_magic(saddr, daddr, len, proto, csum);
7268c2ecf20Sopenharmony_ci}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci#define ETH_FCS_SIZE 4
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci#define PKT_HDR_SIZE (sizeof(struct ethhdr) + sizeof(struct iphdr) + \
7318c2ecf20Sopenharmony_ci		      sizeof(struct udphdr))
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci#define PKT_SIZE		(opt_pkt_size - ETH_FCS_SIZE)
7348c2ecf20Sopenharmony_ci#define IP_PKT_SIZE		(PKT_SIZE - sizeof(struct ethhdr))
7358c2ecf20Sopenharmony_ci#define UDP_PKT_SIZE		(IP_PKT_SIZE - sizeof(struct iphdr))
7368c2ecf20Sopenharmony_ci#define UDP_PKT_DATA_SIZE	(UDP_PKT_SIZE - sizeof(struct udphdr))
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_cistatic u8 pkt_data[XSK_UMEM__DEFAULT_FRAME_SIZE];
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cistatic void gen_eth_hdr_data(void)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	struct udphdr *udp_hdr = (struct udphdr *)(pkt_data +
7438c2ecf20Sopenharmony_ci						   sizeof(struct ethhdr) +
7448c2ecf20Sopenharmony_ci						   sizeof(struct iphdr));
7458c2ecf20Sopenharmony_ci	struct iphdr *ip_hdr = (struct iphdr *)(pkt_data +
7468c2ecf20Sopenharmony_ci						sizeof(struct ethhdr));
7478c2ecf20Sopenharmony_ci	struct ethhdr *eth_hdr = (struct ethhdr *)pkt_data;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	/* ethernet header */
7508c2ecf20Sopenharmony_ci	memcpy(eth_hdr->h_dest, "\x3c\xfd\xfe\x9e\x7f\x71", ETH_ALEN);
7518c2ecf20Sopenharmony_ci	memcpy(eth_hdr->h_source, "\xec\xb1\xd7\x98\x3a\xc0", ETH_ALEN);
7528c2ecf20Sopenharmony_ci	eth_hdr->h_proto = htons(ETH_P_IP);
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	/* IP header */
7558c2ecf20Sopenharmony_ci	ip_hdr->version = IPVERSION;
7568c2ecf20Sopenharmony_ci	ip_hdr->ihl = 0x5; /* 20 byte header */
7578c2ecf20Sopenharmony_ci	ip_hdr->tos = 0x0;
7588c2ecf20Sopenharmony_ci	ip_hdr->tot_len = htons(IP_PKT_SIZE);
7598c2ecf20Sopenharmony_ci	ip_hdr->id = 0;
7608c2ecf20Sopenharmony_ci	ip_hdr->frag_off = 0;
7618c2ecf20Sopenharmony_ci	ip_hdr->ttl = IPDEFTTL;
7628c2ecf20Sopenharmony_ci	ip_hdr->protocol = IPPROTO_UDP;
7638c2ecf20Sopenharmony_ci	ip_hdr->saddr = htonl(0x0a0a0a10);
7648c2ecf20Sopenharmony_ci	ip_hdr->daddr = htonl(0x0a0a0a20);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	/* IP header checksum */
7678c2ecf20Sopenharmony_ci	ip_hdr->check = 0;
7688c2ecf20Sopenharmony_ci	ip_hdr->check = ip_fast_csum((const void *)ip_hdr, ip_hdr->ihl);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	/* UDP header */
7718c2ecf20Sopenharmony_ci	udp_hdr->source = htons(0x1000);
7728c2ecf20Sopenharmony_ci	udp_hdr->dest = htons(0x1000);
7738c2ecf20Sopenharmony_ci	udp_hdr->len = htons(UDP_PKT_SIZE);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	/* UDP data */
7768c2ecf20Sopenharmony_ci	memset32_htonl(pkt_data + PKT_HDR_SIZE, opt_pkt_fill_pattern,
7778c2ecf20Sopenharmony_ci		       UDP_PKT_DATA_SIZE);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	/* UDP header checksum */
7808c2ecf20Sopenharmony_ci	udp_hdr->check = 0;
7818c2ecf20Sopenharmony_ci	udp_hdr->check = udp_csum(ip_hdr->saddr, ip_hdr->daddr, UDP_PKT_SIZE,
7828c2ecf20Sopenharmony_ci				  IPPROTO_UDP, (u16 *)udp_hdr);
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_cistatic void gen_eth_frame(struct xsk_umem_info *umem, u64 addr)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	memcpy(xsk_umem__get_data(umem->buffer, addr), pkt_data,
7888c2ecf20Sopenharmony_ci	       PKT_SIZE);
7898c2ecf20Sopenharmony_ci}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_cistatic struct xsk_umem_info *xsk_configure_umem(void *buffer, u64 size)
7928c2ecf20Sopenharmony_ci{
7938c2ecf20Sopenharmony_ci	struct xsk_umem_info *umem;
7948c2ecf20Sopenharmony_ci	struct xsk_umem_config cfg = {
7958c2ecf20Sopenharmony_ci		/* We recommend that you set the fill ring size >= HW RX ring size +
7968c2ecf20Sopenharmony_ci		 * AF_XDP RX ring size. Make sure you fill up the fill ring
7978c2ecf20Sopenharmony_ci		 * with buffers at regular intervals, and you will with this setting
7988c2ecf20Sopenharmony_ci		 * avoid allocation failures in the driver. These are usually quite
7998c2ecf20Sopenharmony_ci		 * expensive since drivers have not been written to assume that
8008c2ecf20Sopenharmony_ci		 * allocation failures are common. For regular sockets, kernel
8018c2ecf20Sopenharmony_ci		 * allocated memory is used that only runs out in OOM situations
8028c2ecf20Sopenharmony_ci		 * that should be rare.
8038c2ecf20Sopenharmony_ci		 */
8048c2ecf20Sopenharmony_ci		.fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS * 2,
8058c2ecf20Sopenharmony_ci		.comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS,
8068c2ecf20Sopenharmony_ci		.frame_size = opt_xsk_frame_size,
8078c2ecf20Sopenharmony_ci		.frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM,
8088c2ecf20Sopenharmony_ci		.flags = opt_umem_flags
8098c2ecf20Sopenharmony_ci	};
8108c2ecf20Sopenharmony_ci	int ret;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	umem = calloc(1, sizeof(*umem));
8138c2ecf20Sopenharmony_ci	if (!umem)
8148c2ecf20Sopenharmony_ci		exit_with_error(errno);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	ret = xsk_umem__create(&umem->umem, buffer, size, &umem->fq, &umem->cq,
8178c2ecf20Sopenharmony_ci			       &cfg);
8188c2ecf20Sopenharmony_ci	if (ret)
8198c2ecf20Sopenharmony_ci		exit_with_error(-ret);
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	umem->buffer = buffer;
8228c2ecf20Sopenharmony_ci	return umem;
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_cistatic void xsk_populate_fill_ring(struct xsk_umem_info *umem)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci	int ret, i;
8288c2ecf20Sopenharmony_ci	u32 idx;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	ret = xsk_ring_prod__reserve(&umem->fq,
8318c2ecf20Sopenharmony_ci				     XSK_RING_PROD__DEFAULT_NUM_DESCS * 2, &idx);
8328c2ecf20Sopenharmony_ci	if (ret != XSK_RING_PROD__DEFAULT_NUM_DESCS * 2)
8338c2ecf20Sopenharmony_ci		exit_with_error(-ret);
8348c2ecf20Sopenharmony_ci	for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS * 2; i++)
8358c2ecf20Sopenharmony_ci		*xsk_ring_prod__fill_addr(&umem->fq, idx++) =
8368c2ecf20Sopenharmony_ci			i * opt_xsk_frame_size;
8378c2ecf20Sopenharmony_ci	xsk_ring_prod__submit(&umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS * 2);
8388c2ecf20Sopenharmony_ci}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_cistatic struct xsk_socket_info *xsk_configure_socket(struct xsk_umem_info *umem,
8418c2ecf20Sopenharmony_ci						    bool rx, bool tx)
8428c2ecf20Sopenharmony_ci{
8438c2ecf20Sopenharmony_ci	struct xsk_socket_config cfg;
8448c2ecf20Sopenharmony_ci	struct xsk_socket_info *xsk;
8458c2ecf20Sopenharmony_ci	struct xsk_ring_cons *rxr;
8468c2ecf20Sopenharmony_ci	struct xsk_ring_prod *txr;
8478c2ecf20Sopenharmony_ci	int ret;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	xsk = calloc(1, sizeof(*xsk));
8508c2ecf20Sopenharmony_ci	if (!xsk)
8518c2ecf20Sopenharmony_ci		exit_with_error(errno);
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	xsk->umem = umem;
8548c2ecf20Sopenharmony_ci	cfg.rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS;
8558c2ecf20Sopenharmony_ci	cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
8568c2ecf20Sopenharmony_ci	if (opt_num_xsks > 1)
8578c2ecf20Sopenharmony_ci		cfg.libbpf_flags = XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD;
8588c2ecf20Sopenharmony_ci	else
8598c2ecf20Sopenharmony_ci		cfg.libbpf_flags = 0;
8608c2ecf20Sopenharmony_ci	cfg.xdp_flags = opt_xdp_flags;
8618c2ecf20Sopenharmony_ci	cfg.bind_flags = opt_xdp_bind_flags;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	rxr = rx ? &xsk->rx : NULL;
8648c2ecf20Sopenharmony_ci	txr = tx ? &xsk->tx : NULL;
8658c2ecf20Sopenharmony_ci	ret = xsk_socket__create(&xsk->xsk, opt_if, opt_queue, umem->umem,
8668c2ecf20Sopenharmony_ci				 rxr, txr, &cfg);
8678c2ecf20Sopenharmony_ci	if (ret)
8688c2ecf20Sopenharmony_ci		exit_with_error(-ret);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	ret = bpf_get_link_xdp_id(opt_ifindex, &prog_id, opt_xdp_flags);
8718c2ecf20Sopenharmony_ci	if (ret)
8728c2ecf20Sopenharmony_ci		exit_with_error(-ret);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	xsk->app_stats.rx_empty_polls = 0;
8758c2ecf20Sopenharmony_ci	xsk->app_stats.fill_fail_polls = 0;
8768c2ecf20Sopenharmony_ci	xsk->app_stats.copy_tx_sendtos = 0;
8778c2ecf20Sopenharmony_ci	xsk->app_stats.tx_wakeup_sendtos = 0;
8788c2ecf20Sopenharmony_ci	xsk->app_stats.opt_polls = 0;
8798c2ecf20Sopenharmony_ci	xsk->app_stats.prev_rx_empty_polls = 0;
8808c2ecf20Sopenharmony_ci	xsk->app_stats.prev_fill_fail_polls = 0;
8818c2ecf20Sopenharmony_ci	xsk->app_stats.prev_copy_tx_sendtos = 0;
8828c2ecf20Sopenharmony_ci	xsk->app_stats.prev_tx_wakeup_sendtos = 0;
8838c2ecf20Sopenharmony_ci	xsk->app_stats.prev_opt_polls = 0;
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	return xsk;
8868c2ecf20Sopenharmony_ci}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_cistatic struct option long_options[] = {
8898c2ecf20Sopenharmony_ci	{"rxdrop", no_argument, 0, 'r'},
8908c2ecf20Sopenharmony_ci	{"txonly", no_argument, 0, 't'},
8918c2ecf20Sopenharmony_ci	{"l2fwd", no_argument, 0, 'l'},
8928c2ecf20Sopenharmony_ci	{"interface", required_argument, 0, 'i'},
8938c2ecf20Sopenharmony_ci	{"queue", required_argument, 0, 'q'},
8948c2ecf20Sopenharmony_ci	{"poll", no_argument, 0, 'p'},
8958c2ecf20Sopenharmony_ci	{"xdp-skb", no_argument, 0, 'S'},
8968c2ecf20Sopenharmony_ci	{"xdp-native", no_argument, 0, 'N'},
8978c2ecf20Sopenharmony_ci	{"interval", required_argument, 0, 'n'},
8988c2ecf20Sopenharmony_ci	{"zero-copy", no_argument, 0, 'z'},
8998c2ecf20Sopenharmony_ci	{"copy", no_argument, 0, 'c'},
9008c2ecf20Sopenharmony_ci	{"frame-size", required_argument, 0, 'f'},
9018c2ecf20Sopenharmony_ci	{"no-need-wakeup", no_argument, 0, 'm'},
9028c2ecf20Sopenharmony_ci	{"unaligned", no_argument, 0, 'u'},
9038c2ecf20Sopenharmony_ci	{"shared-umem", no_argument, 0, 'M'},
9048c2ecf20Sopenharmony_ci	{"force", no_argument, 0, 'F'},
9058c2ecf20Sopenharmony_ci	{"duration", required_argument, 0, 'd'},
9068c2ecf20Sopenharmony_ci	{"batch-size", required_argument, 0, 'b'},
9078c2ecf20Sopenharmony_ci	{"tx-pkt-count", required_argument, 0, 'C'},
9088c2ecf20Sopenharmony_ci	{"tx-pkt-size", required_argument, 0, 's'},
9098c2ecf20Sopenharmony_ci	{"tx-pkt-pattern", required_argument, 0, 'P'},
9108c2ecf20Sopenharmony_ci	{"extra-stats", no_argument, 0, 'x'},
9118c2ecf20Sopenharmony_ci	{"quiet", no_argument, 0, 'Q'},
9128c2ecf20Sopenharmony_ci	{"app-stats", no_argument, 0, 'a'},
9138c2ecf20Sopenharmony_ci	{"irq-string", no_argument, 0, 'I'},
9148c2ecf20Sopenharmony_ci	{0, 0, 0, 0}
9158c2ecf20Sopenharmony_ci};
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_cistatic void usage(const char *prog)
9188c2ecf20Sopenharmony_ci{
9198c2ecf20Sopenharmony_ci	const char *str =
9208c2ecf20Sopenharmony_ci		"  Usage: %s [OPTIONS]\n"
9218c2ecf20Sopenharmony_ci		"  Options:\n"
9228c2ecf20Sopenharmony_ci		"  -r, --rxdrop		Discard all incoming packets (default)\n"
9238c2ecf20Sopenharmony_ci		"  -t, --txonly		Only send packets\n"
9248c2ecf20Sopenharmony_ci		"  -l, --l2fwd		MAC swap L2 forwarding\n"
9258c2ecf20Sopenharmony_ci		"  -i, --interface=n	Run on interface n\n"
9268c2ecf20Sopenharmony_ci		"  -q, --queue=n	Use queue n (default 0)\n"
9278c2ecf20Sopenharmony_ci		"  -p, --poll		Use poll syscall\n"
9288c2ecf20Sopenharmony_ci		"  -S, --xdp-skb=n	Use XDP skb-mod\n"
9298c2ecf20Sopenharmony_ci		"  -N, --xdp-native=n	Enforce XDP native mode\n"
9308c2ecf20Sopenharmony_ci		"  -n, --interval=n	Specify statistics update interval (default 1 sec).\n"
9318c2ecf20Sopenharmony_ci		"  -z, --zero-copy      Force zero-copy mode.\n"
9328c2ecf20Sopenharmony_ci		"  -c, --copy           Force copy mode.\n"
9338c2ecf20Sopenharmony_ci		"  -m, --no-need-wakeup Turn off use of driver need wakeup flag.\n"
9348c2ecf20Sopenharmony_ci		"  -f, --frame-size=n   Set the frame size (must be a power of two in aligned mode, default is %d).\n"
9358c2ecf20Sopenharmony_ci		"  -u, --unaligned	Enable unaligned chunk placement\n"
9368c2ecf20Sopenharmony_ci		"  -M, --shared-umem	Enable XDP_SHARED_UMEM\n"
9378c2ecf20Sopenharmony_ci		"  -F, --force		Force loading the XDP prog\n"
9388c2ecf20Sopenharmony_ci		"  -d, --duration=n	Duration in secs to run command.\n"
9398c2ecf20Sopenharmony_ci		"			Default: forever.\n"
9408c2ecf20Sopenharmony_ci		"  -b, --batch-size=n	Batch size for sending or receiving\n"
9418c2ecf20Sopenharmony_ci		"			packets. Default: %d\n"
9428c2ecf20Sopenharmony_ci		"  -C, --tx-pkt-count=n	Number of packets to send.\n"
9438c2ecf20Sopenharmony_ci		"			Default: Continuous packets.\n"
9448c2ecf20Sopenharmony_ci		"  -s, --tx-pkt-size=n	Transmit packet size.\n"
9458c2ecf20Sopenharmony_ci		"			(Default: %d bytes)\n"
9468c2ecf20Sopenharmony_ci		"			Min size: %d, Max size %d.\n"
9478c2ecf20Sopenharmony_ci		"  -P, --tx-pkt-pattern=nPacket fill pattern. Default: 0x%x\n"
9488c2ecf20Sopenharmony_ci		"  -x, --extra-stats	Display extra statistics.\n"
9498c2ecf20Sopenharmony_ci		"  -Q, --quiet          Do not display any stats.\n"
9508c2ecf20Sopenharmony_ci		"  -a, --app-stats	Display application (syscall) statistics.\n"
9518c2ecf20Sopenharmony_ci		"  -I, --irq-string	Display driver interrupt statistics for interface associated with irq-string.\n"
9528c2ecf20Sopenharmony_ci		"\n";
9538c2ecf20Sopenharmony_ci	fprintf(stderr, str, prog, XSK_UMEM__DEFAULT_FRAME_SIZE,
9548c2ecf20Sopenharmony_ci		opt_batch_size, MIN_PKT_SIZE, MIN_PKT_SIZE,
9558c2ecf20Sopenharmony_ci		XSK_UMEM__DEFAULT_FRAME_SIZE, opt_pkt_fill_pattern);
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	exit(EXIT_FAILURE);
9588c2ecf20Sopenharmony_ci}
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_cistatic void parse_command_line(int argc, char **argv)
9618c2ecf20Sopenharmony_ci{
9628c2ecf20Sopenharmony_ci	int option_index, c;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	opterr = 0;
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	for (;;) {
9678c2ecf20Sopenharmony_ci		c = getopt_long(argc, argv, "Frtli:q:pSNn:czf:muMd:b:C:s:P:xQaI:",
9688c2ecf20Sopenharmony_ci				long_options, &option_index);
9698c2ecf20Sopenharmony_ci		if (c == -1)
9708c2ecf20Sopenharmony_ci			break;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci		switch (c) {
9738c2ecf20Sopenharmony_ci		case 'r':
9748c2ecf20Sopenharmony_ci			opt_bench = BENCH_RXDROP;
9758c2ecf20Sopenharmony_ci			break;
9768c2ecf20Sopenharmony_ci		case 't':
9778c2ecf20Sopenharmony_ci			opt_bench = BENCH_TXONLY;
9788c2ecf20Sopenharmony_ci			break;
9798c2ecf20Sopenharmony_ci		case 'l':
9808c2ecf20Sopenharmony_ci			opt_bench = BENCH_L2FWD;
9818c2ecf20Sopenharmony_ci			break;
9828c2ecf20Sopenharmony_ci		case 'i':
9838c2ecf20Sopenharmony_ci			opt_if = optarg;
9848c2ecf20Sopenharmony_ci			break;
9858c2ecf20Sopenharmony_ci		case 'q':
9868c2ecf20Sopenharmony_ci			opt_queue = atoi(optarg);
9878c2ecf20Sopenharmony_ci			break;
9888c2ecf20Sopenharmony_ci		case 'p':
9898c2ecf20Sopenharmony_ci			opt_poll = 1;
9908c2ecf20Sopenharmony_ci			break;
9918c2ecf20Sopenharmony_ci		case 'S':
9928c2ecf20Sopenharmony_ci			opt_xdp_flags |= XDP_FLAGS_SKB_MODE;
9938c2ecf20Sopenharmony_ci			opt_xdp_bind_flags |= XDP_COPY;
9948c2ecf20Sopenharmony_ci			break;
9958c2ecf20Sopenharmony_ci		case 'N':
9968c2ecf20Sopenharmony_ci			/* default, set below */
9978c2ecf20Sopenharmony_ci			break;
9988c2ecf20Sopenharmony_ci		case 'n':
9998c2ecf20Sopenharmony_ci			opt_interval = atoi(optarg);
10008c2ecf20Sopenharmony_ci			break;
10018c2ecf20Sopenharmony_ci		case 'z':
10028c2ecf20Sopenharmony_ci			opt_xdp_bind_flags |= XDP_ZEROCOPY;
10038c2ecf20Sopenharmony_ci			break;
10048c2ecf20Sopenharmony_ci		case 'c':
10058c2ecf20Sopenharmony_ci			opt_xdp_bind_flags |= XDP_COPY;
10068c2ecf20Sopenharmony_ci			break;
10078c2ecf20Sopenharmony_ci		case 'u':
10088c2ecf20Sopenharmony_ci			opt_umem_flags |= XDP_UMEM_UNALIGNED_CHUNK_FLAG;
10098c2ecf20Sopenharmony_ci			opt_unaligned_chunks = 1;
10108c2ecf20Sopenharmony_ci			opt_mmap_flags = MAP_HUGETLB;
10118c2ecf20Sopenharmony_ci			break;
10128c2ecf20Sopenharmony_ci		case 'F':
10138c2ecf20Sopenharmony_ci			opt_xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
10148c2ecf20Sopenharmony_ci			break;
10158c2ecf20Sopenharmony_ci		case 'f':
10168c2ecf20Sopenharmony_ci			opt_xsk_frame_size = atoi(optarg);
10178c2ecf20Sopenharmony_ci			break;
10188c2ecf20Sopenharmony_ci		case 'm':
10198c2ecf20Sopenharmony_ci			opt_need_wakeup = false;
10208c2ecf20Sopenharmony_ci			opt_xdp_bind_flags &= ~XDP_USE_NEED_WAKEUP;
10218c2ecf20Sopenharmony_ci			break;
10228c2ecf20Sopenharmony_ci		case 'M':
10238c2ecf20Sopenharmony_ci			opt_num_xsks = MAX_SOCKS;
10248c2ecf20Sopenharmony_ci			break;
10258c2ecf20Sopenharmony_ci		case 'd':
10268c2ecf20Sopenharmony_ci			opt_duration = atoi(optarg);
10278c2ecf20Sopenharmony_ci			opt_duration *= 1000000000;
10288c2ecf20Sopenharmony_ci			break;
10298c2ecf20Sopenharmony_ci		case 'b':
10308c2ecf20Sopenharmony_ci			opt_batch_size = atoi(optarg);
10318c2ecf20Sopenharmony_ci			break;
10328c2ecf20Sopenharmony_ci		case 'C':
10338c2ecf20Sopenharmony_ci			opt_pkt_count = atoi(optarg);
10348c2ecf20Sopenharmony_ci			break;
10358c2ecf20Sopenharmony_ci		case 's':
10368c2ecf20Sopenharmony_ci			opt_pkt_size = atoi(optarg);
10378c2ecf20Sopenharmony_ci			if (opt_pkt_size > (XSK_UMEM__DEFAULT_FRAME_SIZE) ||
10388c2ecf20Sopenharmony_ci			    opt_pkt_size < MIN_PKT_SIZE) {
10398c2ecf20Sopenharmony_ci				fprintf(stderr,
10408c2ecf20Sopenharmony_ci					"ERROR: Invalid frame size %d\n",
10418c2ecf20Sopenharmony_ci					opt_pkt_size);
10428c2ecf20Sopenharmony_ci				usage(basename(argv[0]));
10438c2ecf20Sopenharmony_ci			}
10448c2ecf20Sopenharmony_ci			break;
10458c2ecf20Sopenharmony_ci		case 'P':
10468c2ecf20Sopenharmony_ci			opt_pkt_fill_pattern = strtol(optarg, NULL, 16);
10478c2ecf20Sopenharmony_ci			break;
10488c2ecf20Sopenharmony_ci		case 'x':
10498c2ecf20Sopenharmony_ci			opt_extra_stats = 1;
10508c2ecf20Sopenharmony_ci			break;
10518c2ecf20Sopenharmony_ci		case 'Q':
10528c2ecf20Sopenharmony_ci			opt_quiet = 1;
10538c2ecf20Sopenharmony_ci			break;
10548c2ecf20Sopenharmony_ci		case 'a':
10558c2ecf20Sopenharmony_ci			opt_app_stats = 1;
10568c2ecf20Sopenharmony_ci			break;
10578c2ecf20Sopenharmony_ci		case 'I':
10588c2ecf20Sopenharmony_ci			opt_irq_str = optarg;
10598c2ecf20Sopenharmony_ci			if (get_interrupt_number())
10608c2ecf20Sopenharmony_ci				irqs_at_init = get_irqs();
10618c2ecf20Sopenharmony_ci			if (irqs_at_init < 0) {
10628c2ecf20Sopenharmony_ci				fprintf(stderr, "ERROR: Failed to get irqs for %s\n", opt_irq_str);
10638c2ecf20Sopenharmony_ci				usage(basename(argv[0]));
10648c2ecf20Sopenharmony_ci			}
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci			break;
10678c2ecf20Sopenharmony_ci		default:
10688c2ecf20Sopenharmony_ci			usage(basename(argv[0]));
10698c2ecf20Sopenharmony_ci		}
10708c2ecf20Sopenharmony_ci	}
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	if (!(opt_xdp_flags & XDP_FLAGS_SKB_MODE))
10738c2ecf20Sopenharmony_ci		opt_xdp_flags |= XDP_FLAGS_DRV_MODE;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	opt_ifindex = if_nametoindex(opt_if);
10768c2ecf20Sopenharmony_ci	if (!opt_ifindex) {
10778c2ecf20Sopenharmony_ci		fprintf(stderr, "ERROR: interface \"%s\" does not exist\n",
10788c2ecf20Sopenharmony_ci			opt_if);
10798c2ecf20Sopenharmony_ci		usage(basename(argv[0]));
10808c2ecf20Sopenharmony_ci	}
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	if ((opt_xsk_frame_size & (opt_xsk_frame_size - 1)) &&
10838c2ecf20Sopenharmony_ci	    !opt_unaligned_chunks) {
10848c2ecf20Sopenharmony_ci		fprintf(stderr, "--frame-size=%d is not a power of two\n",
10858c2ecf20Sopenharmony_ci			opt_xsk_frame_size);
10868c2ecf20Sopenharmony_ci		usage(basename(argv[0]));
10878c2ecf20Sopenharmony_ci	}
10888c2ecf20Sopenharmony_ci}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_cistatic void kick_tx(struct xsk_socket_info *xsk)
10918c2ecf20Sopenharmony_ci{
10928c2ecf20Sopenharmony_ci	int ret;
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	ret = sendto(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0);
10958c2ecf20Sopenharmony_ci	if (ret >= 0 || errno == ENOBUFS || errno == EAGAIN ||
10968c2ecf20Sopenharmony_ci	    errno == EBUSY || errno == ENETDOWN)
10978c2ecf20Sopenharmony_ci		return;
10988c2ecf20Sopenharmony_ci	exit_with_error(errno);
10998c2ecf20Sopenharmony_ci}
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_cistatic inline void complete_tx_l2fwd(struct xsk_socket_info *xsk,
11028c2ecf20Sopenharmony_ci				     struct pollfd *fds)
11038c2ecf20Sopenharmony_ci{
11048c2ecf20Sopenharmony_ci	struct xsk_umem_info *umem = xsk->umem;
11058c2ecf20Sopenharmony_ci	u32 idx_cq = 0, idx_fq = 0;
11068c2ecf20Sopenharmony_ci	unsigned int rcvd;
11078c2ecf20Sopenharmony_ci	size_t ndescs;
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	if (!xsk->outstanding_tx)
11108c2ecf20Sopenharmony_ci		return;
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	/* In copy mode, Tx is driven by a syscall so we need to use e.g. sendto() to
11138c2ecf20Sopenharmony_ci	 * really send the packets. In zero-copy mode we do not have to do this, since Tx
11148c2ecf20Sopenharmony_ci	 * is driven by the NAPI loop. So as an optimization, we do not have to call
11158c2ecf20Sopenharmony_ci	 * sendto() all the time in zero-copy mode for l2fwd.
11168c2ecf20Sopenharmony_ci	 */
11178c2ecf20Sopenharmony_ci	if (opt_xdp_bind_flags & XDP_COPY) {
11188c2ecf20Sopenharmony_ci		xsk->app_stats.copy_tx_sendtos++;
11198c2ecf20Sopenharmony_ci		kick_tx(xsk);
11208c2ecf20Sopenharmony_ci	}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	ndescs = (xsk->outstanding_tx > opt_batch_size) ? opt_batch_size :
11238c2ecf20Sopenharmony_ci		xsk->outstanding_tx;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	/* re-add completed Tx buffers */
11268c2ecf20Sopenharmony_ci	rcvd = xsk_ring_cons__peek(&umem->cq, ndescs, &idx_cq);
11278c2ecf20Sopenharmony_ci	if (rcvd > 0) {
11288c2ecf20Sopenharmony_ci		unsigned int i;
11298c2ecf20Sopenharmony_ci		int ret;
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci		ret = xsk_ring_prod__reserve(&umem->fq, rcvd, &idx_fq);
11328c2ecf20Sopenharmony_ci		while (ret != rcvd) {
11338c2ecf20Sopenharmony_ci			if (ret < 0)
11348c2ecf20Sopenharmony_ci				exit_with_error(-ret);
11358c2ecf20Sopenharmony_ci			if (xsk_ring_prod__needs_wakeup(&umem->fq)) {
11368c2ecf20Sopenharmony_ci				xsk->app_stats.fill_fail_polls++;
11378c2ecf20Sopenharmony_ci				ret = poll(fds, num_socks, opt_timeout);
11388c2ecf20Sopenharmony_ci			}
11398c2ecf20Sopenharmony_ci			ret = xsk_ring_prod__reserve(&umem->fq, rcvd, &idx_fq);
11408c2ecf20Sopenharmony_ci		}
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci		for (i = 0; i < rcvd; i++)
11438c2ecf20Sopenharmony_ci			*xsk_ring_prod__fill_addr(&umem->fq, idx_fq++) =
11448c2ecf20Sopenharmony_ci				*xsk_ring_cons__comp_addr(&umem->cq, idx_cq++);
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci		xsk_ring_prod__submit(&xsk->umem->fq, rcvd);
11478c2ecf20Sopenharmony_ci		xsk_ring_cons__release(&xsk->umem->cq, rcvd);
11488c2ecf20Sopenharmony_ci		xsk->outstanding_tx -= rcvd;
11498c2ecf20Sopenharmony_ci		xsk->ring_stats.tx_npkts += rcvd;
11508c2ecf20Sopenharmony_ci	}
11518c2ecf20Sopenharmony_ci}
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_cistatic inline void complete_tx_only(struct xsk_socket_info *xsk,
11548c2ecf20Sopenharmony_ci				    int batch_size)
11558c2ecf20Sopenharmony_ci{
11568c2ecf20Sopenharmony_ci	unsigned int rcvd;
11578c2ecf20Sopenharmony_ci	u32 idx;
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	if (!xsk->outstanding_tx)
11608c2ecf20Sopenharmony_ci		return;
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	if (!opt_need_wakeup || xsk_ring_prod__needs_wakeup(&xsk->tx)) {
11638c2ecf20Sopenharmony_ci		xsk->app_stats.tx_wakeup_sendtos++;
11648c2ecf20Sopenharmony_ci		kick_tx(xsk);
11658c2ecf20Sopenharmony_ci	}
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	rcvd = xsk_ring_cons__peek(&xsk->umem->cq, batch_size, &idx);
11688c2ecf20Sopenharmony_ci	if (rcvd > 0) {
11698c2ecf20Sopenharmony_ci		xsk_ring_cons__release(&xsk->umem->cq, rcvd);
11708c2ecf20Sopenharmony_ci		xsk->outstanding_tx -= rcvd;
11718c2ecf20Sopenharmony_ci		xsk->ring_stats.tx_npkts += rcvd;
11728c2ecf20Sopenharmony_ci	}
11738c2ecf20Sopenharmony_ci}
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_cistatic void rx_drop(struct xsk_socket_info *xsk, struct pollfd *fds)
11768c2ecf20Sopenharmony_ci{
11778c2ecf20Sopenharmony_ci	unsigned int rcvd, i;
11788c2ecf20Sopenharmony_ci	u32 idx_rx = 0, idx_fq = 0;
11798c2ecf20Sopenharmony_ci	int ret;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	rcvd = xsk_ring_cons__peek(&xsk->rx, opt_batch_size, &idx_rx);
11828c2ecf20Sopenharmony_ci	if (!rcvd) {
11838c2ecf20Sopenharmony_ci		if (xsk_ring_prod__needs_wakeup(&xsk->umem->fq)) {
11848c2ecf20Sopenharmony_ci			xsk->app_stats.rx_empty_polls++;
11858c2ecf20Sopenharmony_ci			ret = poll(fds, num_socks, opt_timeout);
11868c2ecf20Sopenharmony_ci		}
11878c2ecf20Sopenharmony_ci		return;
11888c2ecf20Sopenharmony_ci	}
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	ret = xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq);
11918c2ecf20Sopenharmony_ci	while (ret != rcvd) {
11928c2ecf20Sopenharmony_ci		if (ret < 0)
11938c2ecf20Sopenharmony_ci			exit_with_error(-ret);
11948c2ecf20Sopenharmony_ci		if (xsk_ring_prod__needs_wakeup(&xsk->umem->fq)) {
11958c2ecf20Sopenharmony_ci			xsk->app_stats.fill_fail_polls++;
11968c2ecf20Sopenharmony_ci			ret = poll(fds, num_socks, opt_timeout);
11978c2ecf20Sopenharmony_ci		}
11988c2ecf20Sopenharmony_ci		ret = xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq);
11998c2ecf20Sopenharmony_ci	}
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	for (i = 0; i < rcvd; i++) {
12028c2ecf20Sopenharmony_ci		u64 addr = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx)->addr;
12038c2ecf20Sopenharmony_ci		u32 len = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++)->len;
12048c2ecf20Sopenharmony_ci		u64 orig = xsk_umem__extract_addr(addr);
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci		addr = xsk_umem__add_offset_to_addr(addr);
12078c2ecf20Sopenharmony_ci		char *pkt = xsk_umem__get_data(xsk->umem->buffer, addr);
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci		hex_dump(pkt, len, addr);
12108c2ecf20Sopenharmony_ci		*xsk_ring_prod__fill_addr(&xsk->umem->fq, idx_fq++) = orig;
12118c2ecf20Sopenharmony_ci	}
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	xsk_ring_prod__submit(&xsk->umem->fq, rcvd);
12148c2ecf20Sopenharmony_ci	xsk_ring_cons__release(&xsk->rx, rcvd);
12158c2ecf20Sopenharmony_ci	xsk->ring_stats.rx_npkts += rcvd;
12168c2ecf20Sopenharmony_ci}
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_cistatic void rx_drop_all(void)
12198c2ecf20Sopenharmony_ci{
12208c2ecf20Sopenharmony_ci	struct pollfd fds[MAX_SOCKS] = {};
12218c2ecf20Sopenharmony_ci	int i, ret;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	for (i = 0; i < num_socks; i++) {
12248c2ecf20Sopenharmony_ci		fds[i].fd = xsk_socket__fd(xsks[i]->xsk);
12258c2ecf20Sopenharmony_ci		fds[i].events = POLLIN;
12268c2ecf20Sopenharmony_ci	}
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	for (;;) {
12298c2ecf20Sopenharmony_ci		if (opt_poll) {
12308c2ecf20Sopenharmony_ci			for (i = 0; i < num_socks; i++)
12318c2ecf20Sopenharmony_ci				xsks[i]->app_stats.opt_polls++;
12328c2ecf20Sopenharmony_ci			ret = poll(fds, num_socks, opt_timeout);
12338c2ecf20Sopenharmony_ci			if (ret <= 0)
12348c2ecf20Sopenharmony_ci				continue;
12358c2ecf20Sopenharmony_ci		}
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci		for (i = 0; i < num_socks; i++)
12388c2ecf20Sopenharmony_ci			rx_drop(xsks[i], fds);
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci		if (benchmark_done)
12418c2ecf20Sopenharmony_ci			break;
12428c2ecf20Sopenharmony_ci	}
12438c2ecf20Sopenharmony_ci}
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_cistatic void tx_only(struct xsk_socket_info *xsk, u32 *frame_nb, int batch_size)
12468c2ecf20Sopenharmony_ci{
12478c2ecf20Sopenharmony_ci	u32 idx;
12488c2ecf20Sopenharmony_ci	unsigned int i;
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	while (xsk_ring_prod__reserve(&xsk->tx, batch_size, &idx) <
12518c2ecf20Sopenharmony_ci				      batch_size) {
12528c2ecf20Sopenharmony_ci		complete_tx_only(xsk, batch_size);
12538c2ecf20Sopenharmony_ci		if (benchmark_done)
12548c2ecf20Sopenharmony_ci			return;
12558c2ecf20Sopenharmony_ci	}
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	for (i = 0; i < batch_size; i++) {
12588c2ecf20Sopenharmony_ci		struct xdp_desc *tx_desc = xsk_ring_prod__tx_desc(&xsk->tx,
12598c2ecf20Sopenharmony_ci								  idx + i);
12608c2ecf20Sopenharmony_ci		tx_desc->addr = (*frame_nb + i) * opt_xsk_frame_size;
12618c2ecf20Sopenharmony_ci		tx_desc->len = PKT_SIZE;
12628c2ecf20Sopenharmony_ci	}
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	xsk_ring_prod__submit(&xsk->tx, batch_size);
12658c2ecf20Sopenharmony_ci	xsk->outstanding_tx += batch_size;
12668c2ecf20Sopenharmony_ci	*frame_nb += batch_size;
12678c2ecf20Sopenharmony_ci	*frame_nb %= NUM_FRAMES;
12688c2ecf20Sopenharmony_ci	complete_tx_only(xsk, batch_size);
12698c2ecf20Sopenharmony_ci}
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_cistatic inline int get_batch_size(int pkt_cnt)
12728c2ecf20Sopenharmony_ci{
12738c2ecf20Sopenharmony_ci	if (!opt_pkt_count)
12748c2ecf20Sopenharmony_ci		return opt_batch_size;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	if (pkt_cnt + opt_batch_size <= opt_pkt_count)
12778c2ecf20Sopenharmony_ci		return opt_batch_size;
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	return opt_pkt_count - pkt_cnt;
12808c2ecf20Sopenharmony_ci}
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_cistatic void complete_tx_only_all(void)
12838c2ecf20Sopenharmony_ci{
12848c2ecf20Sopenharmony_ci	bool pending;
12858c2ecf20Sopenharmony_ci	int i;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	do {
12888c2ecf20Sopenharmony_ci		pending = false;
12898c2ecf20Sopenharmony_ci		for (i = 0; i < num_socks; i++) {
12908c2ecf20Sopenharmony_ci			if (xsks[i]->outstanding_tx) {
12918c2ecf20Sopenharmony_ci				complete_tx_only(xsks[i], opt_batch_size);
12928c2ecf20Sopenharmony_ci				pending = !!xsks[i]->outstanding_tx;
12938c2ecf20Sopenharmony_ci			}
12948c2ecf20Sopenharmony_ci		}
12958c2ecf20Sopenharmony_ci	} while (pending);
12968c2ecf20Sopenharmony_ci}
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_cistatic void tx_only_all(void)
12998c2ecf20Sopenharmony_ci{
13008c2ecf20Sopenharmony_ci	struct pollfd fds[MAX_SOCKS] = {};
13018c2ecf20Sopenharmony_ci	u32 frame_nb[MAX_SOCKS] = {};
13028c2ecf20Sopenharmony_ci	int pkt_cnt = 0;
13038c2ecf20Sopenharmony_ci	int i, ret;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	for (i = 0; i < num_socks; i++) {
13068c2ecf20Sopenharmony_ci		fds[0].fd = xsk_socket__fd(xsks[i]->xsk);
13078c2ecf20Sopenharmony_ci		fds[0].events = POLLOUT;
13088c2ecf20Sopenharmony_ci	}
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	while ((opt_pkt_count && pkt_cnt < opt_pkt_count) || !opt_pkt_count) {
13118c2ecf20Sopenharmony_ci		int batch_size = get_batch_size(pkt_cnt);
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci		if (opt_poll) {
13148c2ecf20Sopenharmony_ci			for (i = 0; i < num_socks; i++)
13158c2ecf20Sopenharmony_ci				xsks[i]->app_stats.opt_polls++;
13168c2ecf20Sopenharmony_ci			ret = poll(fds, num_socks, opt_timeout);
13178c2ecf20Sopenharmony_ci			if (ret <= 0)
13188c2ecf20Sopenharmony_ci				continue;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci			if (!(fds[0].revents & POLLOUT))
13218c2ecf20Sopenharmony_ci				continue;
13228c2ecf20Sopenharmony_ci		}
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci		for (i = 0; i < num_socks; i++)
13258c2ecf20Sopenharmony_ci			tx_only(xsks[i], &frame_nb[i], batch_size);
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci		pkt_cnt += batch_size;
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci		if (benchmark_done)
13308c2ecf20Sopenharmony_ci			break;
13318c2ecf20Sopenharmony_ci	}
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	if (opt_pkt_count)
13348c2ecf20Sopenharmony_ci		complete_tx_only_all();
13358c2ecf20Sopenharmony_ci}
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_cistatic void l2fwd(struct xsk_socket_info *xsk, struct pollfd *fds)
13388c2ecf20Sopenharmony_ci{
13398c2ecf20Sopenharmony_ci	unsigned int rcvd, i;
13408c2ecf20Sopenharmony_ci	u32 idx_rx = 0, idx_tx = 0;
13418c2ecf20Sopenharmony_ci	int ret;
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	complete_tx_l2fwd(xsk, fds);
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	rcvd = xsk_ring_cons__peek(&xsk->rx, opt_batch_size, &idx_rx);
13468c2ecf20Sopenharmony_ci	if (!rcvd) {
13478c2ecf20Sopenharmony_ci		if (xsk_ring_prod__needs_wakeup(&xsk->umem->fq)) {
13488c2ecf20Sopenharmony_ci			xsk->app_stats.rx_empty_polls++;
13498c2ecf20Sopenharmony_ci			ret = poll(fds, num_socks, opt_timeout);
13508c2ecf20Sopenharmony_ci		}
13518c2ecf20Sopenharmony_ci		return;
13528c2ecf20Sopenharmony_ci	}
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	ret = xsk_ring_prod__reserve(&xsk->tx, rcvd, &idx_tx);
13558c2ecf20Sopenharmony_ci	while (ret != rcvd) {
13568c2ecf20Sopenharmony_ci		if (ret < 0)
13578c2ecf20Sopenharmony_ci			exit_with_error(-ret);
13588c2ecf20Sopenharmony_ci		complete_tx_l2fwd(xsk, fds);
13598c2ecf20Sopenharmony_ci		if (xsk_ring_prod__needs_wakeup(&xsk->tx)) {
13608c2ecf20Sopenharmony_ci			xsk->app_stats.tx_wakeup_sendtos++;
13618c2ecf20Sopenharmony_ci			kick_tx(xsk);
13628c2ecf20Sopenharmony_ci		}
13638c2ecf20Sopenharmony_ci		ret = xsk_ring_prod__reserve(&xsk->tx, rcvd, &idx_tx);
13648c2ecf20Sopenharmony_ci	}
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	for (i = 0; i < rcvd; i++) {
13678c2ecf20Sopenharmony_ci		u64 addr = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx)->addr;
13688c2ecf20Sopenharmony_ci		u32 len = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++)->len;
13698c2ecf20Sopenharmony_ci		u64 orig = addr;
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci		addr = xsk_umem__add_offset_to_addr(addr);
13728c2ecf20Sopenharmony_ci		char *pkt = xsk_umem__get_data(xsk->umem->buffer, addr);
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci		swap_mac_addresses(pkt);
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci		hex_dump(pkt, len, addr);
13778c2ecf20Sopenharmony_ci		xsk_ring_prod__tx_desc(&xsk->tx, idx_tx)->addr = orig;
13788c2ecf20Sopenharmony_ci		xsk_ring_prod__tx_desc(&xsk->tx, idx_tx++)->len = len;
13798c2ecf20Sopenharmony_ci	}
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_ci	xsk_ring_prod__submit(&xsk->tx, rcvd);
13828c2ecf20Sopenharmony_ci	xsk_ring_cons__release(&xsk->rx, rcvd);
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	xsk->ring_stats.rx_npkts += rcvd;
13858c2ecf20Sopenharmony_ci	xsk->outstanding_tx += rcvd;
13868c2ecf20Sopenharmony_ci}
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_cistatic void l2fwd_all(void)
13898c2ecf20Sopenharmony_ci{
13908c2ecf20Sopenharmony_ci	struct pollfd fds[MAX_SOCKS] = {};
13918c2ecf20Sopenharmony_ci	int i, ret;
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	for (i = 0; i < num_socks; i++) {
13948c2ecf20Sopenharmony_ci		fds[i].fd = xsk_socket__fd(xsks[i]->xsk);
13958c2ecf20Sopenharmony_ci		fds[i].events = POLLOUT | POLLIN;
13968c2ecf20Sopenharmony_ci	}
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	for (;;) {
13998c2ecf20Sopenharmony_ci		if (opt_poll) {
14008c2ecf20Sopenharmony_ci			for (i = 0; i < num_socks; i++)
14018c2ecf20Sopenharmony_ci				xsks[i]->app_stats.opt_polls++;
14028c2ecf20Sopenharmony_ci			ret = poll(fds, num_socks, opt_timeout);
14038c2ecf20Sopenharmony_ci			if (ret <= 0)
14048c2ecf20Sopenharmony_ci				continue;
14058c2ecf20Sopenharmony_ci		}
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci		for (i = 0; i < num_socks; i++)
14088c2ecf20Sopenharmony_ci			l2fwd(xsks[i], fds);
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci		if (benchmark_done)
14118c2ecf20Sopenharmony_ci			break;
14128c2ecf20Sopenharmony_ci	}
14138c2ecf20Sopenharmony_ci}
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_cistatic void load_xdp_program(char **argv, struct bpf_object **obj)
14168c2ecf20Sopenharmony_ci{
14178c2ecf20Sopenharmony_ci	struct bpf_prog_load_attr prog_load_attr = {
14188c2ecf20Sopenharmony_ci		.prog_type      = BPF_PROG_TYPE_XDP,
14198c2ecf20Sopenharmony_ci	};
14208c2ecf20Sopenharmony_ci	char xdp_filename[256];
14218c2ecf20Sopenharmony_ci	int prog_fd;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	snprintf(xdp_filename, sizeof(xdp_filename), "%s_kern.o", argv[0]);
14248c2ecf20Sopenharmony_ci	prog_load_attr.file = xdp_filename;
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	if (bpf_prog_load_xattr(&prog_load_attr, obj, &prog_fd))
14278c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
14288c2ecf20Sopenharmony_ci	if (prog_fd < 0) {
14298c2ecf20Sopenharmony_ci		fprintf(stderr, "ERROR: no program found: %s\n",
14308c2ecf20Sopenharmony_ci			strerror(prog_fd));
14318c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
14328c2ecf20Sopenharmony_ci	}
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	if (bpf_set_link_xdp_fd(opt_ifindex, prog_fd, opt_xdp_flags) < 0) {
14358c2ecf20Sopenharmony_ci		fprintf(stderr, "ERROR: link set xdp fd failed\n");
14368c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
14378c2ecf20Sopenharmony_ci	}
14388c2ecf20Sopenharmony_ci}
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_cistatic void enter_xsks_into_map(struct bpf_object *obj)
14418c2ecf20Sopenharmony_ci{
14428c2ecf20Sopenharmony_ci	struct bpf_map *map;
14438c2ecf20Sopenharmony_ci	int i, xsks_map;
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	map = bpf_object__find_map_by_name(obj, "xsks_map");
14468c2ecf20Sopenharmony_ci	xsks_map = bpf_map__fd(map);
14478c2ecf20Sopenharmony_ci	if (xsks_map < 0) {
14488c2ecf20Sopenharmony_ci		fprintf(stderr, "ERROR: no xsks map found: %s\n",
14498c2ecf20Sopenharmony_ci			strerror(xsks_map));
14508c2ecf20Sopenharmony_ci			exit(EXIT_FAILURE);
14518c2ecf20Sopenharmony_ci	}
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	for (i = 0; i < num_socks; i++) {
14548c2ecf20Sopenharmony_ci		int fd = xsk_socket__fd(xsks[i]->xsk);
14558c2ecf20Sopenharmony_ci		int key, ret;
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci		key = i;
14588c2ecf20Sopenharmony_ci		ret = bpf_map_update_elem(xsks_map, &key, &fd, 0);
14598c2ecf20Sopenharmony_ci		if (ret) {
14608c2ecf20Sopenharmony_ci			fprintf(stderr, "ERROR: bpf_map_update_elem %d\n", i);
14618c2ecf20Sopenharmony_ci			exit(EXIT_FAILURE);
14628c2ecf20Sopenharmony_ci		}
14638c2ecf20Sopenharmony_ci	}
14648c2ecf20Sopenharmony_ci}
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ciint main(int argc, char **argv)
14678c2ecf20Sopenharmony_ci{
14688c2ecf20Sopenharmony_ci	struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
14698c2ecf20Sopenharmony_ci	bool rx = false, tx = false;
14708c2ecf20Sopenharmony_ci	struct xsk_umem_info *umem;
14718c2ecf20Sopenharmony_ci	struct bpf_object *obj;
14728c2ecf20Sopenharmony_ci	pthread_t pt;
14738c2ecf20Sopenharmony_ci	int i, ret;
14748c2ecf20Sopenharmony_ci	void *bufs;
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	parse_command_line(argc, argv);
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	if (setrlimit(RLIMIT_MEMLOCK, &r)) {
14798c2ecf20Sopenharmony_ci		fprintf(stderr, "ERROR: setrlimit(RLIMIT_MEMLOCK) \"%s\"\n",
14808c2ecf20Sopenharmony_ci			strerror(errno));
14818c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
14828c2ecf20Sopenharmony_ci	}
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	if (opt_num_xsks > 1)
14858c2ecf20Sopenharmony_ci		load_xdp_program(argv, &obj);
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci	/* Reserve memory for the umem. Use hugepages if unaligned chunk mode */
14888c2ecf20Sopenharmony_ci	bufs = mmap(NULL, NUM_FRAMES * opt_xsk_frame_size,
14898c2ecf20Sopenharmony_ci		    PROT_READ | PROT_WRITE,
14908c2ecf20Sopenharmony_ci		    MAP_PRIVATE | MAP_ANONYMOUS | opt_mmap_flags, -1, 0);
14918c2ecf20Sopenharmony_ci	if (bufs == MAP_FAILED) {
14928c2ecf20Sopenharmony_ci		printf("ERROR: mmap failed\n");
14938c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
14948c2ecf20Sopenharmony_ci	}
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	/* Create sockets... */
14978c2ecf20Sopenharmony_ci	umem = xsk_configure_umem(bufs, NUM_FRAMES * opt_xsk_frame_size);
14988c2ecf20Sopenharmony_ci	if (opt_bench == BENCH_RXDROP || opt_bench == BENCH_L2FWD) {
14998c2ecf20Sopenharmony_ci		rx = true;
15008c2ecf20Sopenharmony_ci		xsk_populate_fill_ring(umem);
15018c2ecf20Sopenharmony_ci	}
15028c2ecf20Sopenharmony_ci	if (opt_bench == BENCH_L2FWD || opt_bench == BENCH_TXONLY)
15038c2ecf20Sopenharmony_ci		tx = true;
15048c2ecf20Sopenharmony_ci	for (i = 0; i < opt_num_xsks; i++)
15058c2ecf20Sopenharmony_ci		xsks[num_socks++] = xsk_configure_socket(umem, rx, tx);
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	if (opt_bench == BENCH_TXONLY) {
15088c2ecf20Sopenharmony_ci		gen_eth_hdr_data();
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci		for (i = 0; i < NUM_FRAMES; i++)
15118c2ecf20Sopenharmony_ci			gen_eth_frame(umem, i * opt_xsk_frame_size);
15128c2ecf20Sopenharmony_ci	}
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	if (opt_num_xsks > 1 && opt_bench != BENCH_TXONLY)
15158c2ecf20Sopenharmony_ci		enter_xsks_into_map(obj);
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	signal(SIGINT, int_exit);
15188c2ecf20Sopenharmony_ci	signal(SIGTERM, int_exit);
15198c2ecf20Sopenharmony_ci	signal(SIGABRT, int_exit);
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci	setlocale(LC_ALL, "");
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	prev_time = get_nsecs();
15248c2ecf20Sopenharmony_ci	start_time = prev_time;
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	if (!opt_quiet) {
15278c2ecf20Sopenharmony_ci		ret = pthread_create(&pt, NULL, poller, NULL);
15288c2ecf20Sopenharmony_ci		if (ret)
15298c2ecf20Sopenharmony_ci			exit_with_error(ret);
15308c2ecf20Sopenharmony_ci	}
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci	if (opt_bench == BENCH_RXDROP)
15348c2ecf20Sopenharmony_ci		rx_drop_all();
15358c2ecf20Sopenharmony_ci	else if (opt_bench == BENCH_TXONLY)
15368c2ecf20Sopenharmony_ci		tx_only_all();
15378c2ecf20Sopenharmony_ci	else
15388c2ecf20Sopenharmony_ci		l2fwd_all();
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci	benchmark_done = true;
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	if (!opt_quiet)
15438c2ecf20Sopenharmony_ci		pthread_join(pt, NULL);
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	xdpsock_cleanup();
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_ci	munmap(bufs, NUM_FRAMES * opt_xsk_frame_size);
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	return 0;
15508c2ecf20Sopenharmony_ci}
1551