xref: /linux/tools/testing/selftests/bpf/benchs/bench_xdp_lb.c (revision 12e896b9794bbd88f56aeac2a5807ae8d4bb5ad8)
1a4b5ba81SPuranjay Mohan // SPDX-License-Identifier: GPL-2.0
2a4b5ba81SPuranjay Mohan /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
3a4b5ba81SPuranjay Mohan 
4a4b5ba81SPuranjay Mohan #include <argp.h>
5a4b5ba81SPuranjay Mohan #include <string.h>
6a4b5ba81SPuranjay Mohan #include <arpa/inet.h>
7a4b5ba81SPuranjay Mohan #include <linux/if_ether.h>
8a4b5ba81SPuranjay Mohan #include <linux/ip.h>
9a4b5ba81SPuranjay Mohan #include <linux/ipv6.h>
10a4b5ba81SPuranjay Mohan #include <linux/in.h>
11a4b5ba81SPuranjay Mohan #include <linux/tcp.h>
12a4b5ba81SPuranjay Mohan #include <linux/udp.h>
13a4b5ba81SPuranjay Mohan #include "bench.h"
14a4b5ba81SPuranjay Mohan #include "bench_bpf_timing.h"
15a4b5ba81SPuranjay Mohan #include "xdp_lb_bench.skel.h"
16a4b5ba81SPuranjay Mohan #include "xdp_lb_bench_common.h"
17a4b5ba81SPuranjay Mohan #include "bpf_util.h"
18a4b5ba81SPuranjay Mohan 
19a4b5ba81SPuranjay Mohan #define IP4(a, b, c, d) (((__u32)(a) << 24) | ((__u32)(b) << 16) | ((__u32)(c) << 8) | (__u32)(d))
20a4b5ba81SPuranjay Mohan 
21a4b5ba81SPuranjay Mohan #define IP6(a, b, c, d)  { (__u32)(a), (__u32)(b), (__u32)(c), (__u32)(d) }
22a4b5ba81SPuranjay Mohan 
23a4b5ba81SPuranjay Mohan #define TNL_DST		IP4(192, 168, 1, 2)
24a4b5ba81SPuranjay Mohan #define REAL_INDEX	1
25a4b5ba81SPuranjay Mohan #define REAL_INDEX_V6	2
26a4b5ba81SPuranjay Mohan #define MAX_PKT_SIZE	256
27a4b5ba81SPuranjay Mohan #define IP_MF		0x2000
28a4b5ba81SPuranjay Mohan 
29a4b5ba81SPuranjay Mohan static const __u32 tnl_dst_v6[4] = { 0xfd000000, 0, 0, 2 };
30a4b5ba81SPuranjay Mohan 
31a4b5ba81SPuranjay Mohan static const __u8 lb_mac[ETH_ALEN]	= {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
32a4b5ba81SPuranjay Mohan static const __u8 client_mac[ETH_ALEN]	= {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
33a4b5ba81SPuranjay Mohan static const __u8 router_mac[ETH_ALEN]	= {0xde, 0xad, 0xbe, 0xef, 0x00, 0x01};
34a4b5ba81SPuranjay Mohan 
35a4b5ba81SPuranjay Mohan enum scenario_id {
36a4b5ba81SPuranjay Mohan 	S_TCP_V4_LRU_HIT,
37a4b5ba81SPuranjay Mohan 	S_TCP_V4_CH,
38a4b5ba81SPuranjay Mohan 	S_TCP_V6_LRU_HIT,
39a4b5ba81SPuranjay Mohan 	S_TCP_V6_CH,
40a4b5ba81SPuranjay Mohan 	S_UDP_V4_LRU_HIT,
41a4b5ba81SPuranjay Mohan 	S_UDP_V6_LRU_HIT,
42a4b5ba81SPuranjay Mohan 	S_TCP_V4V6_LRU_HIT,
43a4b5ba81SPuranjay Mohan 	S_TCP_V4_LRU_DIVERSE,
44a4b5ba81SPuranjay Mohan 	S_TCP_V4_CH_DIVERSE,
45a4b5ba81SPuranjay Mohan 	S_TCP_V6_LRU_DIVERSE,
46a4b5ba81SPuranjay Mohan 	S_TCP_V6_CH_DIVERSE,
47a4b5ba81SPuranjay Mohan 	S_UDP_V4_LRU_DIVERSE,
48a4b5ba81SPuranjay Mohan 	S_TCP_V4_LRU_MISS,
49a4b5ba81SPuranjay Mohan 	S_UDP_V4_LRU_MISS,
50a4b5ba81SPuranjay Mohan 	S_TCP_V4_LRU_WARMUP,
51a4b5ba81SPuranjay Mohan 	S_TCP_V4_SYN,
52a4b5ba81SPuranjay Mohan 	S_TCP_V4_RST_MISS,
53a4b5ba81SPuranjay Mohan 	S_PASS_V4_NO_VIP,
54a4b5ba81SPuranjay Mohan 	S_PASS_V6_NO_VIP,
55a4b5ba81SPuranjay Mohan 	S_PASS_V4_ICMP,
56a4b5ba81SPuranjay Mohan 	S_PASS_NON_IP,
57a4b5ba81SPuranjay Mohan 	S_DROP_V4_FRAG,
58a4b5ba81SPuranjay Mohan 	S_DROP_V4_OPTIONS,
59a4b5ba81SPuranjay Mohan 	S_DROP_V6_FRAG,
60a4b5ba81SPuranjay Mohan 	NUM_SCENARIOS,
61a4b5ba81SPuranjay Mohan };
62a4b5ba81SPuranjay Mohan 
63a4b5ba81SPuranjay Mohan enum lru_miss_type {
64a4b5ba81SPuranjay Mohan 	LRU_MISS_AUTO = 0,	/* compute from scenario flags (default) */
65a4b5ba81SPuranjay Mohan 	LRU_MISS_NONE,		/* 0 misses (all LRU hits) */
66a4b5ba81SPuranjay Mohan 	LRU_MISS_ALL,		/* batch_iters+1 misses (every op misses) */
67a4b5ba81SPuranjay Mohan 	LRU_MISS_FIRST,		/* 1 miss (first miss, then hits) */
68a4b5ba81SPuranjay Mohan };
69a4b5ba81SPuranjay Mohan 
70a4b5ba81SPuranjay Mohan #define S_BASE_ENCAP_V4							\
71a4b5ba81SPuranjay Mohan 	.expected_retval = XDP_TX, .expect_encap = true,		\
72a4b5ba81SPuranjay Mohan 	.tunnel_dst = TNL_DST
73a4b5ba81SPuranjay Mohan 
74a4b5ba81SPuranjay Mohan #define S_BASE_ENCAP_V6							\
75a4b5ba81SPuranjay Mohan 	.expected_retval = XDP_TX, .expect_encap = true,		\
76a4b5ba81SPuranjay Mohan 	.is_v6 = true, .encap_v6_outer = true,				\
77a4b5ba81SPuranjay Mohan 	.tunnel_dst_v6 = { 0xfd000000, 0, 0, 2 }
78a4b5ba81SPuranjay Mohan 
79a4b5ba81SPuranjay Mohan #define S_BASE_ENCAP_V4V6						\
80a4b5ba81SPuranjay Mohan 	.expected_retval = XDP_TX, .expect_encap = true,		\
81a4b5ba81SPuranjay Mohan 	.encap_v6_outer = true,						\
82a4b5ba81SPuranjay Mohan 	.tunnel_dst_v6 = { 0xfd000000, 0, 0, 2 }
83a4b5ba81SPuranjay Mohan 
84a4b5ba81SPuranjay Mohan struct test_scenario {
85a4b5ba81SPuranjay Mohan 	const char *name;
86a4b5ba81SPuranjay Mohan 	const char *description;
87a4b5ba81SPuranjay Mohan 	int         expected_retval;
88a4b5ba81SPuranjay Mohan 	bool        expect_encap;
89a4b5ba81SPuranjay Mohan 	bool        is_v6;
90a4b5ba81SPuranjay Mohan 	__u32       vip_addr;
91a4b5ba81SPuranjay Mohan 	__u32       src_addr;
92a4b5ba81SPuranjay Mohan 	__u32       tunnel_dst;
93a4b5ba81SPuranjay Mohan 	__u32       vip_addr_v6[4];
94a4b5ba81SPuranjay Mohan 	__u32       src_addr_v6[4];
95a4b5ba81SPuranjay Mohan 	__u32       tunnel_dst_v6[4];
96a4b5ba81SPuranjay Mohan 	__u16       dst_port;
97a4b5ba81SPuranjay Mohan 	__u16       src_port;
98a4b5ba81SPuranjay Mohan 	__u8        ip_proto;
99a4b5ba81SPuranjay Mohan 	__u32       vip_flags;
100a4b5ba81SPuranjay Mohan 	__u32       vip_num;
101a4b5ba81SPuranjay Mohan 	bool        prepopulate_lru;
102a4b5ba81SPuranjay Mohan 	bool        set_frag;
103a4b5ba81SPuranjay Mohan 	__u16       eth_proto;
104a4b5ba81SPuranjay Mohan 	bool        encap_v6_outer;
105a4b5ba81SPuranjay Mohan 	__u32       flow_mask;
106a4b5ba81SPuranjay Mohan 	bool        cold_lru;
107a4b5ba81SPuranjay Mohan 	bool        set_syn;
108a4b5ba81SPuranjay Mohan 	bool        set_rst;
109a4b5ba81SPuranjay Mohan 	bool        set_ip_options;
110a4b5ba81SPuranjay Mohan 	__u32       fixed_batch_iters;	/* 0 = auto-calibrate, >0 = use this value */
111a4b5ba81SPuranjay Mohan 	enum lru_miss_type lru_miss;	/* expected LRU miss pattern */
112a4b5ba81SPuranjay Mohan };
113a4b5ba81SPuranjay Mohan 
114a4b5ba81SPuranjay Mohan static const struct test_scenario scenarios[NUM_SCENARIOS] = {
115a4b5ba81SPuranjay Mohan 	/* Single-flow baseline */
116a4b5ba81SPuranjay Mohan 	[S_TCP_V4_LRU_HIT] = {
117a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V4, .ip_proto = IPPROTO_TCP,
118a4b5ba81SPuranjay Mohan 		.name        = "tcp-v4-lru-hit",
119a4b5ba81SPuranjay Mohan 		.description = "IPv4 TCP, LRU hit, IPIP encap",
120a4b5ba81SPuranjay Mohan 		.vip_addr    = IP4(10, 10, 1, 1), .dst_port = 80,
121a4b5ba81SPuranjay Mohan 		.src_addr    = IP4(10, 10, 2, 1), .src_port = 12345,
122a4b5ba81SPuranjay Mohan 		.prepopulate_lru = true, .lru_miss = LRU_MISS_NONE,
123a4b5ba81SPuranjay Mohan 	},
124a4b5ba81SPuranjay Mohan 	[S_TCP_V4_CH] = {
125a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V4, .ip_proto = IPPROTO_TCP,
126a4b5ba81SPuranjay Mohan 		.name        = "tcp-v4-ch",
127a4b5ba81SPuranjay Mohan 		.description = "IPv4 TCP, CH (LRU bypass), IPIP encap",
128a4b5ba81SPuranjay Mohan 		.vip_addr    = IP4(10, 10, 1, 2), .dst_port = 80,
129a4b5ba81SPuranjay Mohan 		.src_addr    = IP4(10, 10, 2, 2), .src_port = 54321,
130a4b5ba81SPuranjay Mohan 		.vip_flags   = F_LRU_BYPASS, .vip_num = 1,
131a4b5ba81SPuranjay Mohan 		.lru_miss    = LRU_MISS_ALL,
132a4b5ba81SPuranjay Mohan 	},
133a4b5ba81SPuranjay Mohan 	[S_TCP_V6_LRU_HIT] = {
134a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V6, .ip_proto = IPPROTO_TCP,
135a4b5ba81SPuranjay Mohan 		.name        = "tcp-v6-lru-hit",
136a4b5ba81SPuranjay Mohan 		.description = "IPv6 TCP, LRU hit, IP6IP6 encap",
137a4b5ba81SPuranjay Mohan 		.vip_addr_v6 = IP6(0xfd000100, 0, 0, 1), .dst_port = 80,
138a4b5ba81SPuranjay Mohan 		.src_addr_v6 = IP6(0xfd000200, 0, 0, 1), .src_port = 12345,
139a4b5ba81SPuranjay Mohan 		.vip_num     = 10,
140a4b5ba81SPuranjay Mohan 		.prepopulate_lru = true, .lru_miss = LRU_MISS_NONE,
141a4b5ba81SPuranjay Mohan 	},
142a4b5ba81SPuranjay Mohan 	[S_TCP_V6_CH] = {
143a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V6, .ip_proto = IPPROTO_TCP,
144a4b5ba81SPuranjay Mohan 		.name        = "tcp-v6-ch",
145a4b5ba81SPuranjay Mohan 		.description = "IPv6 TCP, CH (LRU bypass), IP6IP6 encap",
146a4b5ba81SPuranjay Mohan 		.vip_addr_v6 = IP6(0xfd000100, 0, 0, 2), .dst_port = 80,
147a4b5ba81SPuranjay Mohan 		.src_addr_v6 = IP6(0xfd000200, 0, 0, 2), .src_port = 54321,
148a4b5ba81SPuranjay Mohan 		.vip_flags   = F_LRU_BYPASS, .vip_num = 12,
149a4b5ba81SPuranjay Mohan 		.lru_miss    = LRU_MISS_ALL,
150a4b5ba81SPuranjay Mohan 	},
151a4b5ba81SPuranjay Mohan 	[S_UDP_V4_LRU_HIT] = {
152a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V4, .ip_proto = IPPROTO_UDP,
153a4b5ba81SPuranjay Mohan 		.name        = "udp-v4-lru-hit",
154a4b5ba81SPuranjay Mohan 		.description = "IPv4 UDP, LRU hit, IPIP encap",
155a4b5ba81SPuranjay Mohan 		.vip_addr    = IP4(10, 10, 1, 1), .dst_port = 443,
156a4b5ba81SPuranjay Mohan 		.src_addr    = IP4(10, 10, 3, 1), .src_port = 11111,
157a4b5ba81SPuranjay Mohan 		.vip_num     = 2,
158a4b5ba81SPuranjay Mohan 		.prepopulate_lru = true, .lru_miss = LRU_MISS_NONE,
159a4b5ba81SPuranjay Mohan 	},
160a4b5ba81SPuranjay Mohan 	[S_UDP_V6_LRU_HIT] = {
161a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V6, .ip_proto = IPPROTO_UDP,
162a4b5ba81SPuranjay Mohan 		.name        = "udp-v6-lru-hit",
163a4b5ba81SPuranjay Mohan 		.description = "IPv6 UDP, LRU hit, IP6IP6 encap",
164a4b5ba81SPuranjay Mohan 		.vip_addr_v6 = IP6(0xfd000100, 0, 0, 1), .dst_port = 443,
165a4b5ba81SPuranjay Mohan 		.src_addr_v6 = IP6(0xfd000200, 0, 0, 3), .src_port = 22222,
166a4b5ba81SPuranjay Mohan 		.vip_num     = 14,
167a4b5ba81SPuranjay Mohan 		.prepopulate_lru = true, .lru_miss = LRU_MISS_NONE,
168a4b5ba81SPuranjay Mohan 	},
169a4b5ba81SPuranjay Mohan 	[S_TCP_V4V6_LRU_HIT] = {
170a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V4V6, .ip_proto = IPPROTO_TCP,
171a4b5ba81SPuranjay Mohan 		.name        = "tcp-v4v6-lru-hit",
172a4b5ba81SPuranjay Mohan 		.description = "IPv4 TCP, LRU hit, IPv4-in-IPv6 encap",
173a4b5ba81SPuranjay Mohan 		.vip_addr    = IP4(10, 10, 1, 4), .dst_port = 80,
174a4b5ba81SPuranjay Mohan 		.src_addr    = IP4(10, 10, 2, 4), .src_port = 12347,
175a4b5ba81SPuranjay Mohan 		.vip_num     = 13,
176a4b5ba81SPuranjay Mohan 		.prepopulate_lru = true, .lru_miss = LRU_MISS_NONE,
177a4b5ba81SPuranjay Mohan 	},
178a4b5ba81SPuranjay Mohan 
179a4b5ba81SPuranjay Mohan 	/* Diverse flows (4K src addrs) */
180a4b5ba81SPuranjay Mohan 	[S_TCP_V4_LRU_DIVERSE] = {
181a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V4, .ip_proto = IPPROTO_TCP,
182a4b5ba81SPuranjay Mohan 		.name        = "tcp-v4-lru-diverse",
183a4b5ba81SPuranjay Mohan 		.description = "IPv4 TCP, diverse flows, warm LRU",
184a4b5ba81SPuranjay Mohan 		.vip_addr    = IP4(10, 10, 1, 1), .dst_port = 80,
185a4b5ba81SPuranjay Mohan 		.src_addr    = IP4(10, 10, 2, 1), .src_port = 12345,
186a4b5ba81SPuranjay Mohan 		.prepopulate_lru = true, .flow_mask = 0xFFF,
187a4b5ba81SPuranjay Mohan 		.lru_miss    = LRU_MISS_NONE,
188a4b5ba81SPuranjay Mohan 	},
189a4b5ba81SPuranjay Mohan 	[S_TCP_V4_CH_DIVERSE] = {
190a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V4, .ip_proto = IPPROTO_TCP,
191a4b5ba81SPuranjay Mohan 		.name        = "tcp-v4-ch-diverse",
192a4b5ba81SPuranjay Mohan 		.description = "IPv4 TCP, diverse flows, CH (LRU bypass)",
193a4b5ba81SPuranjay Mohan 		.vip_addr    = IP4(10, 10, 1, 2), .dst_port = 80,
194a4b5ba81SPuranjay Mohan 		.src_addr    = IP4(10, 10, 2, 2), .src_port = 54321,
195a4b5ba81SPuranjay Mohan 		.vip_flags   = F_LRU_BYPASS, .vip_num = 1,
196a4b5ba81SPuranjay Mohan 		.flow_mask   = 0xFFF, .lru_miss = LRU_MISS_ALL,
197a4b5ba81SPuranjay Mohan 	},
198a4b5ba81SPuranjay Mohan 	[S_TCP_V6_LRU_DIVERSE] = {
199a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V6, .ip_proto = IPPROTO_TCP,
200a4b5ba81SPuranjay Mohan 		.name        = "tcp-v6-lru-diverse",
201a4b5ba81SPuranjay Mohan 		.description = "IPv6 TCP, diverse flows, warm LRU",
202a4b5ba81SPuranjay Mohan 		.vip_addr_v6 = IP6(0xfd000100, 0, 0, 1), .dst_port = 80,
203a4b5ba81SPuranjay Mohan 		.src_addr_v6 = IP6(0xfd000200, 0, 0, 1), .src_port = 12345,
204a4b5ba81SPuranjay Mohan 		.vip_num     = 10,
205a4b5ba81SPuranjay Mohan 		.prepopulate_lru = true, .flow_mask = 0xFFF,
206a4b5ba81SPuranjay Mohan 		.lru_miss    = LRU_MISS_NONE,
207a4b5ba81SPuranjay Mohan 	},
208a4b5ba81SPuranjay Mohan 	[S_TCP_V6_CH_DIVERSE] = {
209a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V6, .ip_proto = IPPROTO_TCP,
210a4b5ba81SPuranjay Mohan 		.name        = "tcp-v6-ch-diverse",
211a4b5ba81SPuranjay Mohan 		.description = "IPv6 TCP, diverse flows, CH (LRU bypass)",
212a4b5ba81SPuranjay Mohan 		.vip_addr_v6 = IP6(0xfd000100, 0, 0, 2), .dst_port = 80,
213a4b5ba81SPuranjay Mohan 		.src_addr_v6 = IP6(0xfd000200, 0, 0, 2), .src_port = 54321,
214a4b5ba81SPuranjay Mohan 		.vip_flags   = F_LRU_BYPASS, .vip_num = 12,
215a4b5ba81SPuranjay Mohan 		.flow_mask   = 0xFFF, .lru_miss = LRU_MISS_ALL,
216a4b5ba81SPuranjay Mohan 	},
217a4b5ba81SPuranjay Mohan 	[S_UDP_V4_LRU_DIVERSE] = {
218a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V4, .ip_proto = IPPROTO_UDP,
219a4b5ba81SPuranjay Mohan 		.name        = "udp-v4-lru-diverse",
220a4b5ba81SPuranjay Mohan 		.description = "IPv4 UDP, diverse flows, warm LRU",
221a4b5ba81SPuranjay Mohan 		.vip_addr    = IP4(10, 10, 1, 1), .dst_port = 443,
222a4b5ba81SPuranjay Mohan 		.src_addr    = IP4(10, 10, 3, 1), .src_port = 11111,
223a4b5ba81SPuranjay Mohan 		.vip_num     = 2,
224a4b5ba81SPuranjay Mohan 		.prepopulate_lru = true, .flow_mask = 0xFFF,
225a4b5ba81SPuranjay Mohan 		.lru_miss    = LRU_MISS_NONE,
226a4b5ba81SPuranjay Mohan 	},
227a4b5ba81SPuranjay Mohan 
228a4b5ba81SPuranjay Mohan 	/* LRU stress */
229a4b5ba81SPuranjay Mohan 	[S_TCP_V4_LRU_MISS] = {
230a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V4, .ip_proto = IPPROTO_TCP,
231a4b5ba81SPuranjay Mohan 		.name        = "tcp-v4-lru-miss",
232a4b5ba81SPuranjay Mohan 		.description = "IPv4 TCP, LRU miss (16M flow space), CH lookup",
233a4b5ba81SPuranjay Mohan 		.vip_addr    = IP4(10, 10, 1, 1), .dst_port = 80,
234a4b5ba81SPuranjay Mohan 		.src_addr    = IP4(10, 10, 2, 1), .src_port = 12345,
235a4b5ba81SPuranjay Mohan 		.flow_mask   = 0xFFFFFF, .cold_lru = true,
236a4b5ba81SPuranjay Mohan 		.lru_miss    = LRU_MISS_FIRST,
237a4b5ba81SPuranjay Mohan 	},
238a4b5ba81SPuranjay Mohan 	[S_UDP_V4_LRU_MISS] = {
239a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V4, .ip_proto = IPPROTO_UDP,
240a4b5ba81SPuranjay Mohan 		.name        = "udp-v4-lru-miss",
241a4b5ba81SPuranjay Mohan 		.description = "IPv4 UDP, LRU miss (16M flow space), CH lookup",
242a4b5ba81SPuranjay Mohan 		.vip_addr    = IP4(10, 10, 1, 1), .dst_port = 443,
243a4b5ba81SPuranjay Mohan 		.src_addr    = IP4(10, 10, 3, 1), .src_port = 11111,
244a4b5ba81SPuranjay Mohan 		.vip_num     = 2,
245a4b5ba81SPuranjay Mohan 		.flow_mask   = 0xFFFFFF, .cold_lru = true,
246a4b5ba81SPuranjay Mohan 		.lru_miss    = LRU_MISS_FIRST,
247a4b5ba81SPuranjay Mohan 	},
248a4b5ba81SPuranjay Mohan 	[S_TCP_V4_LRU_WARMUP] = {
249a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V4, .ip_proto = IPPROTO_TCP,
250a4b5ba81SPuranjay Mohan 		.name        = "tcp-v4-lru-warmup",
251a4b5ba81SPuranjay Mohan 		.description = "IPv4 TCP, 4K flows, ~50% LRU miss",
252a4b5ba81SPuranjay Mohan 		.vip_addr    = IP4(10, 10, 1, 1), .dst_port = 80,
253a4b5ba81SPuranjay Mohan 		.src_addr    = IP4(10, 10, 2, 1), .src_port = 12345,
254a4b5ba81SPuranjay Mohan 		.flow_mask   = 0xFFF, .cold_lru = true,
255a4b5ba81SPuranjay Mohan 		.fixed_batch_iters = 6500,
256a4b5ba81SPuranjay Mohan 		.lru_miss    = LRU_MISS_FIRST,
257a4b5ba81SPuranjay Mohan 	},
258a4b5ba81SPuranjay Mohan 
259a4b5ba81SPuranjay Mohan 	/* TCP flags */
260a4b5ba81SPuranjay Mohan 	[S_TCP_V4_SYN] = {
261a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V4, .ip_proto = IPPROTO_TCP,
262a4b5ba81SPuranjay Mohan 		.name        = "tcp-v4-syn",
263a4b5ba81SPuranjay Mohan 		.description = "IPv4 TCP SYN, skip LRU, CH + LRU insert",
264a4b5ba81SPuranjay Mohan 		.vip_addr    = IP4(10, 10, 1, 1), .dst_port = 80,
265a4b5ba81SPuranjay Mohan 		.src_addr    = IP4(10, 10, 8, 2), .src_port = 60001,
266a4b5ba81SPuranjay Mohan 		.set_syn     = true, .lru_miss = LRU_MISS_ALL,
267a4b5ba81SPuranjay Mohan 	},
268a4b5ba81SPuranjay Mohan 	[S_TCP_V4_RST_MISS] = {
269a4b5ba81SPuranjay Mohan 		S_BASE_ENCAP_V4, .ip_proto = IPPROTO_TCP,
270a4b5ba81SPuranjay Mohan 		.name        = "tcp-v4-rst-miss",
271a4b5ba81SPuranjay Mohan 		.description = "IPv4 TCP RST, CH lookup, no LRU insert",
272a4b5ba81SPuranjay Mohan 		.vip_addr    = IP4(10, 10, 1, 1), .dst_port = 80,
273a4b5ba81SPuranjay Mohan 		.src_addr    = IP4(10, 10, 8, 1), .src_port = 60000,
274a4b5ba81SPuranjay Mohan 		.flow_mask   = 0xFFFFFF, .cold_lru = true,
275a4b5ba81SPuranjay Mohan 		.set_rst     = true, .lru_miss = LRU_MISS_ALL,
276a4b5ba81SPuranjay Mohan 	},
277a4b5ba81SPuranjay Mohan 
278a4b5ba81SPuranjay Mohan 	/* Early exits */
279a4b5ba81SPuranjay Mohan 	[S_PASS_V4_NO_VIP] = {
280a4b5ba81SPuranjay Mohan 		.name            = "pass-v4-no-vip",
281a4b5ba81SPuranjay Mohan 		.description     = "IPv4 TCP, unknown VIP, XDP_PASS",
282a4b5ba81SPuranjay Mohan 		.expected_retval = XDP_PASS,
283a4b5ba81SPuranjay Mohan 		.ip_proto        = IPPROTO_TCP,
284a4b5ba81SPuranjay Mohan 		.vip_addr        = IP4(10, 10, 9, 9), .dst_port = 80,
285a4b5ba81SPuranjay Mohan 		.src_addr        = IP4(10, 10, 4, 1), .src_port = 33333,
286a4b5ba81SPuranjay Mohan 	},
287a4b5ba81SPuranjay Mohan 	[S_PASS_V6_NO_VIP] = {
288a4b5ba81SPuranjay Mohan 		.name            = "pass-v6-no-vip",
289a4b5ba81SPuranjay Mohan 		.description     = "IPv6 TCP, unknown VIP, XDP_PASS",
290a4b5ba81SPuranjay Mohan 		.expected_retval = XDP_PASS, .is_v6 = true,
291a4b5ba81SPuranjay Mohan 		.ip_proto        = IPPROTO_TCP,
292a4b5ba81SPuranjay Mohan 		.vip_addr_v6     = IP6(0xfd009900, 0, 0, 1), .dst_port = 80,
293a4b5ba81SPuranjay Mohan 		.src_addr_v6     = IP6(0xfd000400, 0, 0, 1), .src_port = 33333,
294a4b5ba81SPuranjay Mohan 	},
295a4b5ba81SPuranjay Mohan 	[S_PASS_V4_ICMP] = {
296a4b5ba81SPuranjay Mohan 		.name            = "pass-v4-icmp",
297a4b5ba81SPuranjay Mohan 		.description     = "IPv4 ICMP, non-TCP/UDP protocol, XDP_PASS",
298a4b5ba81SPuranjay Mohan 		.expected_retval = XDP_PASS,
299a4b5ba81SPuranjay Mohan 		.ip_proto        = IPPROTO_ICMP,
300a4b5ba81SPuranjay Mohan 		.vip_addr        = IP4(10, 10, 1, 1),
301a4b5ba81SPuranjay Mohan 		.src_addr        = IP4(10, 10, 6, 1),
302a4b5ba81SPuranjay Mohan 	},
303a4b5ba81SPuranjay Mohan 	[S_PASS_NON_IP] = {
304a4b5ba81SPuranjay Mohan 		.name            = "pass-non-ip",
305a4b5ba81SPuranjay Mohan 		.description     = "Non-IP (ARP), earliest XDP_PASS exit",
306a4b5ba81SPuranjay Mohan 		.expected_retval = XDP_PASS,
307a4b5ba81SPuranjay Mohan 		.eth_proto       = ETH_P_ARP,
308a4b5ba81SPuranjay Mohan 	},
309a4b5ba81SPuranjay Mohan 	[S_DROP_V4_FRAG] = {
310a4b5ba81SPuranjay Mohan 		.name            = "drop-v4-frag",
311a4b5ba81SPuranjay Mohan 		.description     = "IPv4 fragmented, XDP_DROP",
312a4b5ba81SPuranjay Mohan 		.expected_retval = XDP_DROP, .ip_proto = IPPROTO_TCP,
313a4b5ba81SPuranjay Mohan 		.vip_addr        = IP4(10, 10, 1, 1), .dst_port = 80,
314a4b5ba81SPuranjay Mohan 		.src_addr        = IP4(10, 10, 5, 1), .src_port = 44444,
315a4b5ba81SPuranjay Mohan 		.set_frag        = true,
316a4b5ba81SPuranjay Mohan 	},
317a4b5ba81SPuranjay Mohan 	[S_DROP_V4_OPTIONS] = {
318a4b5ba81SPuranjay Mohan 		.name            = "drop-v4-options",
319a4b5ba81SPuranjay Mohan 		.description     = "IPv4 with IP options (ihl>5), XDP_DROP",
320a4b5ba81SPuranjay Mohan 		.expected_retval = XDP_DROP, .ip_proto = IPPROTO_TCP,
321a4b5ba81SPuranjay Mohan 		.vip_addr        = IP4(10, 10, 1, 1), .dst_port = 80,
322a4b5ba81SPuranjay Mohan 		.src_addr        = IP4(10, 10, 7, 1), .src_port = 55555,
323a4b5ba81SPuranjay Mohan 		.set_ip_options  = true,
324a4b5ba81SPuranjay Mohan 	},
325a4b5ba81SPuranjay Mohan 	[S_DROP_V6_FRAG] = {
326a4b5ba81SPuranjay Mohan 		.name            = "drop-v6-frag",
327a4b5ba81SPuranjay Mohan 		.description     = "IPv6 fragment extension header, XDP_DROP",
328a4b5ba81SPuranjay Mohan 		.expected_retval = XDP_DROP, .is_v6 = true,
329a4b5ba81SPuranjay Mohan 		.ip_proto        = IPPROTO_TCP,
330a4b5ba81SPuranjay Mohan 		.vip_addr_v6     = IP6(0xfd000100, 0, 0, 1), .dst_port = 80,
331a4b5ba81SPuranjay Mohan 		.src_addr_v6     = IP6(0xfd000500, 0, 0, 1), .src_port = 44444,
332a4b5ba81SPuranjay Mohan 		.set_frag        = true,
333a4b5ba81SPuranjay Mohan 	},
334a4b5ba81SPuranjay Mohan };
335a4b5ba81SPuranjay Mohan 
336a4b5ba81SPuranjay Mohan #define MAX_ENCAP_SIZE	(MAX_PKT_SIZE + sizeof(struct ipv6hdr))
337a4b5ba81SPuranjay Mohan 
338a4b5ba81SPuranjay Mohan static __u8  pkt_buf[NUM_SCENARIOS][MAX_PKT_SIZE];
339a4b5ba81SPuranjay Mohan static __u32 pkt_len[NUM_SCENARIOS];
340a4b5ba81SPuranjay Mohan static __u8  expected_buf[NUM_SCENARIOS][MAX_ENCAP_SIZE];
341a4b5ba81SPuranjay Mohan static __u32 expected_len[NUM_SCENARIOS];
342a4b5ba81SPuranjay Mohan 
343a4b5ba81SPuranjay Mohan static int lru_inner_fds[BENCH_NR_CPUS];
344a4b5ba81SPuranjay Mohan static int nr_inner_maps;
345a4b5ba81SPuranjay Mohan 
346a4b5ba81SPuranjay Mohan static struct ctx {
347a4b5ba81SPuranjay Mohan 	struct xdp_lb_bench *skel;
348a4b5ba81SPuranjay Mohan 	struct bpf_bench_timing timing;
349a4b5ba81SPuranjay Mohan 	int prog_fd;
350a4b5ba81SPuranjay Mohan } ctx;
351a4b5ba81SPuranjay Mohan 
352a4b5ba81SPuranjay Mohan static struct {
353a4b5ba81SPuranjay Mohan 	int   scenario;
354a4b5ba81SPuranjay Mohan 	bool  machine_readable;
355a4b5ba81SPuranjay Mohan } args = {
356a4b5ba81SPuranjay Mohan 	.scenario = -1,
357a4b5ba81SPuranjay Mohan };
358a4b5ba81SPuranjay Mohan 
359a4b5ba81SPuranjay Mohan static __u16 ip_checksum(const void *hdr, int len)
360a4b5ba81SPuranjay Mohan {
361a4b5ba81SPuranjay Mohan 	const __u16 *p = hdr;
362a4b5ba81SPuranjay Mohan 	__u32 csum = 0;
363a4b5ba81SPuranjay Mohan 	int i;
364a4b5ba81SPuranjay Mohan 
365a4b5ba81SPuranjay Mohan 	for (i = 0; i < len / 2; i++)
366a4b5ba81SPuranjay Mohan 		csum += p[i];
367a4b5ba81SPuranjay Mohan 
368a4b5ba81SPuranjay Mohan 	while (csum >> 16)
369a4b5ba81SPuranjay Mohan 		csum = (csum & 0xffff) + (csum >> 16);
370a4b5ba81SPuranjay Mohan 
371a4b5ba81SPuranjay Mohan 	return ~csum;
372a4b5ba81SPuranjay Mohan }
373a4b5ba81SPuranjay Mohan 
374a4b5ba81SPuranjay Mohan static void htonl_v6(__be32 dst[4], const __u32 src[4])
375a4b5ba81SPuranjay Mohan {
376a4b5ba81SPuranjay Mohan 	int i;
377a4b5ba81SPuranjay Mohan 
378a4b5ba81SPuranjay Mohan 	for (i = 0; i < 4; i++)
379a4b5ba81SPuranjay Mohan 		dst[i] = htonl(src[i]);
380a4b5ba81SPuranjay Mohan }
381a4b5ba81SPuranjay Mohan 
382a4b5ba81SPuranjay Mohan static void build_flow_key(struct flow_key *fk, const struct test_scenario *sc)
383a4b5ba81SPuranjay Mohan {
384a4b5ba81SPuranjay Mohan 	memset(fk, 0, sizeof(*fk));
385a4b5ba81SPuranjay Mohan 	if (sc->is_v6) {
386a4b5ba81SPuranjay Mohan 		htonl_v6(fk->srcv6, sc->src_addr_v6);
387a4b5ba81SPuranjay Mohan 		htonl_v6(fk->dstv6, sc->vip_addr_v6);
388a4b5ba81SPuranjay Mohan 	} else {
389a4b5ba81SPuranjay Mohan 		fk->src = htonl(sc->src_addr);
390a4b5ba81SPuranjay Mohan 		fk->dst = htonl(sc->vip_addr);
391a4b5ba81SPuranjay Mohan 	}
392a4b5ba81SPuranjay Mohan 	fk->proto = sc->ip_proto;
393a4b5ba81SPuranjay Mohan 	fk->port16[0] = htons(sc->src_port);
394a4b5ba81SPuranjay Mohan 	fk->port16[1] = htons(sc->dst_port);
395a4b5ba81SPuranjay Mohan }
396a4b5ba81SPuranjay Mohan 
397a4b5ba81SPuranjay Mohan static void build_l4(const struct test_scenario *sc, __u8 *p, __u32 *off)
398a4b5ba81SPuranjay Mohan {
399a4b5ba81SPuranjay Mohan 	if (sc->ip_proto == IPPROTO_TCP) {
400a4b5ba81SPuranjay Mohan 		struct tcphdr tcp = {};
401a4b5ba81SPuranjay Mohan 
402a4b5ba81SPuranjay Mohan 		tcp.source = htons(sc->src_port);
403a4b5ba81SPuranjay Mohan 		tcp.dest   = htons(sc->dst_port);
404a4b5ba81SPuranjay Mohan 		tcp.doff   = 5;
405a4b5ba81SPuranjay Mohan 		tcp.syn    = sc->set_syn ? 1 : 0;
406a4b5ba81SPuranjay Mohan 		tcp.rst    = sc->set_rst ? 1 : 0;
407a4b5ba81SPuranjay Mohan 		tcp.window = htons(8192);
408a4b5ba81SPuranjay Mohan 		memcpy(p + *off, &tcp, sizeof(tcp));
409a4b5ba81SPuranjay Mohan 		*off += sizeof(tcp);
410a4b5ba81SPuranjay Mohan 	} else if (sc->ip_proto == IPPROTO_UDP) {
411a4b5ba81SPuranjay Mohan 		struct udphdr udp = {};
412a4b5ba81SPuranjay Mohan 
413a4b5ba81SPuranjay Mohan 		udp.source = htons(sc->src_port);
414a4b5ba81SPuranjay Mohan 		udp.dest   = htons(sc->dst_port);
415a4b5ba81SPuranjay Mohan 		udp.len    = htons(sizeof(udp) + 16);
416a4b5ba81SPuranjay Mohan 		memcpy(p + *off, &udp, sizeof(udp));
417a4b5ba81SPuranjay Mohan 		*off += sizeof(udp);
418a4b5ba81SPuranjay Mohan 	}
419a4b5ba81SPuranjay Mohan }
420a4b5ba81SPuranjay Mohan 
421a4b5ba81SPuranjay Mohan static void build_packet(int idx)
422a4b5ba81SPuranjay Mohan {
423a4b5ba81SPuranjay Mohan 	const struct test_scenario *sc = &scenarios[idx];
424a4b5ba81SPuranjay Mohan 	__u8 *p = pkt_buf[idx];
425a4b5ba81SPuranjay Mohan 	struct ethhdr eth = {};
426a4b5ba81SPuranjay Mohan 	__u16 proto;
427a4b5ba81SPuranjay Mohan 	__u32 off = 0;
428a4b5ba81SPuranjay Mohan 
429a4b5ba81SPuranjay Mohan 	memcpy(eth.h_dest, lb_mac, ETH_ALEN);
430a4b5ba81SPuranjay Mohan 	memcpy(eth.h_source, client_mac, ETH_ALEN);
431a4b5ba81SPuranjay Mohan 
432a4b5ba81SPuranjay Mohan 	if (sc->eth_proto)
433a4b5ba81SPuranjay Mohan 		proto = sc->eth_proto;
434a4b5ba81SPuranjay Mohan 	else if (sc->is_v6)
435a4b5ba81SPuranjay Mohan 		proto = ETH_P_IPV6;
436a4b5ba81SPuranjay Mohan 	else
437a4b5ba81SPuranjay Mohan 		proto = ETH_P_IP;
438a4b5ba81SPuranjay Mohan 
439a4b5ba81SPuranjay Mohan 	eth.h_proto = htons(proto);
440a4b5ba81SPuranjay Mohan 	memcpy(p, &eth, sizeof(eth));
441a4b5ba81SPuranjay Mohan 	off += sizeof(eth);
442a4b5ba81SPuranjay Mohan 
443a4b5ba81SPuranjay Mohan 	if (proto != ETH_P_IP && proto != ETH_P_IPV6) {
444a4b5ba81SPuranjay Mohan 		memcpy(p + off, "bench___payload!", 16);
445a4b5ba81SPuranjay Mohan 		off += 16;
446a4b5ba81SPuranjay Mohan 		pkt_len[idx] = off;
447a4b5ba81SPuranjay Mohan 		return;
448a4b5ba81SPuranjay Mohan 	}
449a4b5ba81SPuranjay Mohan 
450a4b5ba81SPuranjay Mohan 	if (sc->is_v6) {
451a4b5ba81SPuranjay Mohan 		struct ipv6hdr ip6h = {};
452a4b5ba81SPuranjay Mohan 		__u32 ip6_off = off;
453a4b5ba81SPuranjay Mohan 
454a4b5ba81SPuranjay Mohan 		ip6h.version  = 6;
455a4b5ba81SPuranjay Mohan 		ip6h.nexthdr  = sc->set_frag ? 44 : sc->ip_proto;
456a4b5ba81SPuranjay Mohan 		ip6h.hop_limit = 64;
457a4b5ba81SPuranjay Mohan 		htonl_v6((__be32 *)&ip6h.saddr, sc->src_addr_v6);
458a4b5ba81SPuranjay Mohan 		htonl_v6((__be32 *)&ip6h.daddr, sc->vip_addr_v6);
459a4b5ba81SPuranjay Mohan 		off += sizeof(ip6h);
460a4b5ba81SPuranjay Mohan 
461a4b5ba81SPuranjay Mohan 		if (sc->set_frag) {
462a4b5ba81SPuranjay Mohan 			memset(p + off, 0, 8);
463a4b5ba81SPuranjay Mohan 			p[off] = sc->ip_proto;
464a4b5ba81SPuranjay Mohan 			off += 8;
465a4b5ba81SPuranjay Mohan 		}
466a4b5ba81SPuranjay Mohan 
467a4b5ba81SPuranjay Mohan 		build_l4(sc, p, &off);
468a4b5ba81SPuranjay Mohan 
469a4b5ba81SPuranjay Mohan 		memcpy(p + off, "bench___payload!", 16);
470a4b5ba81SPuranjay Mohan 		off += 16;
471a4b5ba81SPuranjay Mohan 
472a4b5ba81SPuranjay Mohan 		ip6h.payload_len = htons(off - ip6_off - sizeof(ip6h));
473a4b5ba81SPuranjay Mohan 		memcpy(p + ip6_off, &ip6h, sizeof(ip6h));
474a4b5ba81SPuranjay Mohan 	} else {
475a4b5ba81SPuranjay Mohan 		struct iphdr iph = {};
476a4b5ba81SPuranjay Mohan 		__u32 ip_off = off;
477a4b5ba81SPuranjay Mohan 
478a4b5ba81SPuranjay Mohan 		iph.version  = 4;
479a4b5ba81SPuranjay Mohan 		iph.ihl      = sc->set_ip_options ? 6 : 5;
480a4b5ba81SPuranjay Mohan 		iph.ttl      = 64;
481a4b5ba81SPuranjay Mohan 		iph.protocol = sc->ip_proto;
482a4b5ba81SPuranjay Mohan 		iph.saddr    = htonl(sc->src_addr);
483a4b5ba81SPuranjay Mohan 		iph.daddr    = htonl(sc->vip_addr);
484a4b5ba81SPuranjay Mohan 		iph.frag_off = sc->set_frag ? htons(IP_MF) : 0;
485a4b5ba81SPuranjay Mohan 		off += sizeof(iph);
486a4b5ba81SPuranjay Mohan 
487a4b5ba81SPuranjay Mohan 		if (sc->set_ip_options) {
488a4b5ba81SPuranjay Mohan 			/* NOP option padding (4 bytes = 1 word) */
489a4b5ba81SPuranjay Mohan 			__u32 nop = htonl(0x01010101);
490a4b5ba81SPuranjay Mohan 
491a4b5ba81SPuranjay Mohan 			memcpy(p + off, &nop, sizeof(nop));
492a4b5ba81SPuranjay Mohan 			off += sizeof(nop);
493a4b5ba81SPuranjay Mohan 		}
494a4b5ba81SPuranjay Mohan 
495a4b5ba81SPuranjay Mohan 		build_l4(sc, p, &off);
496a4b5ba81SPuranjay Mohan 
497a4b5ba81SPuranjay Mohan 		memcpy(p + off, "bench___payload!", 16);
498a4b5ba81SPuranjay Mohan 		off += 16;
499a4b5ba81SPuranjay Mohan 
500a4b5ba81SPuranjay Mohan 		iph.tot_len = htons(off - ip_off);
501a4b5ba81SPuranjay Mohan 		iph.check   = ip_checksum(&iph, sizeof(iph));
502a4b5ba81SPuranjay Mohan 		memcpy(p + ip_off, &iph, sizeof(iph));
503a4b5ba81SPuranjay Mohan 	}
504a4b5ba81SPuranjay Mohan 
505a4b5ba81SPuranjay Mohan 	pkt_len[idx] = off;
506a4b5ba81SPuranjay Mohan }
507a4b5ba81SPuranjay Mohan 
508a4b5ba81SPuranjay Mohan static void populate_vip(struct xdp_lb_bench *skel, const struct test_scenario *sc)
509a4b5ba81SPuranjay Mohan {
510a4b5ba81SPuranjay Mohan 	struct vip_definition key = {};
511a4b5ba81SPuranjay Mohan 	struct vip_meta val = {};
512a4b5ba81SPuranjay Mohan 	int err;
513a4b5ba81SPuranjay Mohan 
514a4b5ba81SPuranjay Mohan 	if (sc->is_v6)
515a4b5ba81SPuranjay Mohan 		htonl_v6(key.vipv6, sc->vip_addr_v6);
516a4b5ba81SPuranjay Mohan 	else
517a4b5ba81SPuranjay Mohan 		key.vip = htonl(sc->vip_addr);
518a4b5ba81SPuranjay Mohan 	key.port  = htons(sc->dst_port);
519a4b5ba81SPuranjay Mohan 	key.proto = sc->ip_proto;
520a4b5ba81SPuranjay Mohan 	val.flags   = sc->vip_flags;
521a4b5ba81SPuranjay Mohan 	val.vip_num = sc->vip_num;
522a4b5ba81SPuranjay Mohan 
523a4b5ba81SPuranjay Mohan 	err = bpf_map_update_elem(bpf_map__fd(skel->maps.vip_map), &key, &val, BPF_ANY);
524a4b5ba81SPuranjay Mohan 	if (err) {
525a4b5ba81SPuranjay Mohan 		fprintf(stderr, "vip_map [%s]: %s\n", sc->name, strerror(errno));
526a4b5ba81SPuranjay Mohan 		exit(1);
527a4b5ba81SPuranjay Mohan 	}
528a4b5ba81SPuranjay Mohan }
529a4b5ba81SPuranjay Mohan 
530a4b5ba81SPuranjay Mohan static void create_per_cpu_lru_maps(struct xdp_lb_bench *skel)
531a4b5ba81SPuranjay Mohan {
532a4b5ba81SPuranjay Mohan 	int outer_fd = bpf_map__fd(skel->maps.lru_mapping);
533a4b5ba81SPuranjay Mohan 	unsigned int nr_cpus = bpf_num_possible_cpus();
534a4b5ba81SPuranjay Mohan 	int i, inner_fd, err;
535a4b5ba81SPuranjay Mohan 	__u32 cpu;
536a4b5ba81SPuranjay Mohan 
537a4b5ba81SPuranjay Mohan 	if (nr_cpus > BENCH_NR_CPUS)
538a4b5ba81SPuranjay Mohan 		nr_cpus = BENCH_NR_CPUS;
539a4b5ba81SPuranjay Mohan 
540a4b5ba81SPuranjay Mohan 	for (i = 0; i < (int)nr_cpus; i++) {
541a4b5ba81SPuranjay Mohan 		LIBBPF_OPTS(bpf_map_create_opts, opts);
542a4b5ba81SPuranjay Mohan 
543a4b5ba81SPuranjay Mohan 		inner_fd = bpf_map_create(BPF_MAP_TYPE_LRU_HASH, "lru_inner",
544a4b5ba81SPuranjay Mohan 					  sizeof(struct flow_key),
545a4b5ba81SPuranjay Mohan 					  sizeof(struct real_pos_lru),
546a4b5ba81SPuranjay Mohan 					  DEFAULT_LRU_SIZE, &opts);
547a4b5ba81SPuranjay Mohan 		if (inner_fd < 0) {
548a4b5ba81SPuranjay Mohan 			fprintf(stderr, "lru_inner[%d]: %s\n", i, strerror(errno));
549a4b5ba81SPuranjay Mohan 			exit(1);
550a4b5ba81SPuranjay Mohan 		}
551a4b5ba81SPuranjay Mohan 
552a4b5ba81SPuranjay Mohan 		cpu = i;
553a4b5ba81SPuranjay Mohan 		err = bpf_map_update_elem(outer_fd, &cpu, &inner_fd, BPF_ANY);
554a4b5ba81SPuranjay Mohan 		if (err) {
555a4b5ba81SPuranjay Mohan 			fprintf(stderr, "lru_mapping[%d]: %s\n", i, strerror(errno));
556a4b5ba81SPuranjay Mohan 			close(inner_fd);
557a4b5ba81SPuranjay Mohan 			exit(1);
558a4b5ba81SPuranjay Mohan 		}
559a4b5ba81SPuranjay Mohan 
560a4b5ba81SPuranjay Mohan 		lru_inner_fds[i] = inner_fd;
561a4b5ba81SPuranjay Mohan 	}
562a4b5ba81SPuranjay Mohan 
563a4b5ba81SPuranjay Mohan 	nr_inner_maps = nr_cpus;
564a4b5ba81SPuranjay Mohan }
565a4b5ba81SPuranjay Mohan 
566*12e896b9SPuranjay Mohan static __u64 ktime_get_ns(void)
567*12e896b9SPuranjay Mohan {
568*12e896b9SPuranjay Mohan 	struct timespec ts;
569*12e896b9SPuranjay Mohan 
570*12e896b9SPuranjay Mohan 	clock_gettime(CLOCK_MONOTONIC, &ts);
571*12e896b9SPuranjay Mohan 	return (__u64)ts.tv_sec * 1000000000ULL + ts.tv_nsec;
572*12e896b9SPuranjay Mohan }
573*12e896b9SPuranjay Mohan 
574a4b5ba81SPuranjay Mohan static void populate_lru(const struct test_scenario *sc, __u32 real_idx)
575a4b5ba81SPuranjay Mohan {
576a4b5ba81SPuranjay Mohan 	struct real_pos_lru lru = { .pos = real_idx };
577a4b5ba81SPuranjay Mohan 	struct flow_key fk;
578a4b5ba81SPuranjay Mohan 	int i, err;
579a4b5ba81SPuranjay Mohan 
580*12e896b9SPuranjay Mohan 	if (sc->ip_proto == IPPROTO_UDP)
581*12e896b9SPuranjay Mohan 		lru.atime = ktime_get_ns();
582*12e896b9SPuranjay Mohan 
583a4b5ba81SPuranjay Mohan 	build_flow_key(&fk, sc);
584a4b5ba81SPuranjay Mohan 
585a4b5ba81SPuranjay Mohan 	/* Insert into every per-CPU inner LRU so the entry is found
586a4b5ba81SPuranjay Mohan 	 * regardless of which CPU runs the BPF program.
587a4b5ba81SPuranjay Mohan 	 */
588a4b5ba81SPuranjay Mohan 	for (i = 0; i < nr_inner_maps; i++) {
589a4b5ba81SPuranjay Mohan 		err = bpf_map_update_elem(lru_inner_fds[i], &fk, &lru, BPF_ANY);
590a4b5ba81SPuranjay Mohan 		if (err) {
591a4b5ba81SPuranjay Mohan 			fprintf(stderr, "lru_inner[%d] [%s]: %s\n", i, sc->name,
592a4b5ba81SPuranjay Mohan 				strerror(errno));
593a4b5ba81SPuranjay Mohan 			exit(1);
594a4b5ba81SPuranjay Mohan 		}
595a4b5ba81SPuranjay Mohan 	}
596a4b5ba81SPuranjay Mohan }
597a4b5ba81SPuranjay Mohan 
598a4b5ba81SPuranjay Mohan static void populate_maps(struct xdp_lb_bench *skel)
599a4b5ba81SPuranjay Mohan {
600a4b5ba81SPuranjay Mohan 	struct real_definition real_v4 = {};
601a4b5ba81SPuranjay Mohan 	struct real_definition real_v6 = {};
602a4b5ba81SPuranjay Mohan 	struct ctl_value cval = {};
603a4b5ba81SPuranjay Mohan 	__u32 key, real_idx = REAL_INDEX;
604a4b5ba81SPuranjay Mohan 	int ch_fd, err, i;
605a4b5ba81SPuranjay Mohan 
606a4b5ba81SPuranjay Mohan 	if (scenarios[args.scenario].expect_encap)
607a4b5ba81SPuranjay Mohan 		populate_vip(skel, &scenarios[args.scenario]);
608a4b5ba81SPuranjay Mohan 
609a4b5ba81SPuranjay Mohan 	ch_fd = bpf_map__fd(skel->maps.ch_rings);
610a4b5ba81SPuranjay Mohan 	for (i = 0; i < CH_RINGS_SIZE; i++) {
611a4b5ba81SPuranjay Mohan 		__u32 k = i;
612a4b5ba81SPuranjay Mohan 
613a4b5ba81SPuranjay Mohan 		err = bpf_map_update_elem(ch_fd, &k, &real_idx, BPF_ANY);
614a4b5ba81SPuranjay Mohan 		if (err) {
615a4b5ba81SPuranjay Mohan 			fprintf(stderr, "ch_rings[%d]: %s\n", i, strerror(errno));
616a4b5ba81SPuranjay Mohan 			exit(1);
617a4b5ba81SPuranjay Mohan 		}
618a4b5ba81SPuranjay Mohan 	}
619a4b5ba81SPuranjay Mohan 
620a4b5ba81SPuranjay Mohan 	memcpy(cval.mac, router_mac, ETH_ALEN);
621a4b5ba81SPuranjay Mohan 	key = 0;
622a4b5ba81SPuranjay Mohan 	err = bpf_map_update_elem(bpf_map__fd(skel->maps.ctl_array), &key, &cval, BPF_ANY);
623a4b5ba81SPuranjay Mohan 	if (err) {
624a4b5ba81SPuranjay Mohan 		fprintf(stderr, "ctl_array: %s\n", strerror(errno));
625a4b5ba81SPuranjay Mohan 		exit(1);
626a4b5ba81SPuranjay Mohan 	}
627a4b5ba81SPuranjay Mohan 
628a4b5ba81SPuranjay Mohan 	key = REAL_INDEX;
629a4b5ba81SPuranjay Mohan 	real_v4.dst = htonl(TNL_DST);
630a4b5ba81SPuranjay Mohan 	htonl_v6(real_v4.dstv6, tnl_dst_v6);
631a4b5ba81SPuranjay Mohan 	err = bpf_map_update_elem(bpf_map__fd(skel->maps.reals), &key, &real_v4, BPF_ANY);
632a4b5ba81SPuranjay Mohan 	if (err) {
633a4b5ba81SPuranjay Mohan 		fprintf(stderr, "reals[%d]: %s\n", REAL_INDEX, strerror(errno));
634a4b5ba81SPuranjay Mohan 		exit(1);
635a4b5ba81SPuranjay Mohan 	}
636a4b5ba81SPuranjay Mohan 
637a4b5ba81SPuranjay Mohan 	key = REAL_INDEX_V6;
638a4b5ba81SPuranjay Mohan 	htonl_v6(real_v6.dstv6, tnl_dst_v6);
639a4b5ba81SPuranjay Mohan 	real_v6.flags = F_IPV6;
640a4b5ba81SPuranjay Mohan 	err = bpf_map_update_elem(bpf_map__fd(skel->maps.reals), &key, &real_v6, BPF_ANY);
641a4b5ba81SPuranjay Mohan 	if (err) {
642a4b5ba81SPuranjay Mohan 		fprintf(stderr, "reals[%d]: %s\n", REAL_INDEX_V6, strerror(errno));
643a4b5ba81SPuranjay Mohan 		exit(1);
644a4b5ba81SPuranjay Mohan 	}
645a4b5ba81SPuranjay Mohan 
646a4b5ba81SPuranjay Mohan 	create_per_cpu_lru_maps(skel);
647a4b5ba81SPuranjay Mohan 
648a4b5ba81SPuranjay Mohan 	if (scenarios[args.scenario].prepopulate_lru) {
649a4b5ba81SPuranjay Mohan 		const struct test_scenario *sc = &scenarios[args.scenario];
650a4b5ba81SPuranjay Mohan 		__u32 ridx = sc->encap_v6_outer ? REAL_INDEX_V6 : REAL_INDEX;
651a4b5ba81SPuranjay Mohan 
652a4b5ba81SPuranjay Mohan 		populate_lru(sc, ridx);
653a4b5ba81SPuranjay Mohan 	}
654a4b5ba81SPuranjay Mohan 
655a4b5ba81SPuranjay Mohan 	if (scenarios[args.scenario].expect_encap) {
656a4b5ba81SPuranjay Mohan 		const struct test_scenario *sc = &scenarios[args.scenario];
657a4b5ba81SPuranjay Mohan 		struct vip_definition miss_vip = {};
658a4b5ba81SPuranjay Mohan 
659a4b5ba81SPuranjay Mohan 		if (sc->is_v6)
660a4b5ba81SPuranjay Mohan 			htonl_v6(miss_vip.vipv6, sc->vip_addr_v6);
661a4b5ba81SPuranjay Mohan 		else
662a4b5ba81SPuranjay Mohan 			miss_vip.vip = htonl(sc->vip_addr);
663a4b5ba81SPuranjay Mohan 		miss_vip.port = htons(sc->dst_port);
664a4b5ba81SPuranjay Mohan 		miss_vip.proto = sc->ip_proto;
665a4b5ba81SPuranjay Mohan 
666a4b5ba81SPuranjay Mohan 		key = 0;
667a4b5ba81SPuranjay Mohan 		err = bpf_map_update_elem(bpf_map__fd(skel->maps.vip_miss_stats),
668a4b5ba81SPuranjay Mohan 					  &key, &miss_vip, BPF_ANY);
669a4b5ba81SPuranjay Mohan 		if (err) {
670a4b5ba81SPuranjay Mohan 			fprintf(stderr, "vip_miss_stats: %s\n", strerror(errno));
671a4b5ba81SPuranjay Mohan 			exit(1);
672a4b5ba81SPuranjay Mohan 		}
673a4b5ba81SPuranjay Mohan 	}
674a4b5ba81SPuranjay Mohan }
675a4b5ba81SPuranjay Mohan 
676a4b5ba81SPuranjay Mohan static void build_expected_packet(int idx)
677a4b5ba81SPuranjay Mohan {
678a4b5ba81SPuranjay Mohan 	const struct test_scenario *sc = &scenarios[idx];
679a4b5ba81SPuranjay Mohan 	__u8 *p = expected_buf[idx];
680a4b5ba81SPuranjay Mohan 	struct ethhdr eth = {};
681a4b5ba81SPuranjay Mohan 	const __u8 *in = pkt_buf[idx];
682a4b5ba81SPuranjay Mohan 	__u32 in_len = pkt_len[idx];
683a4b5ba81SPuranjay Mohan 	__u32 off = 0;
684a4b5ba81SPuranjay Mohan 	__u32 inner_len = in_len - sizeof(struct ethhdr);
685a4b5ba81SPuranjay Mohan 
686a4b5ba81SPuranjay Mohan 	if (sc->expected_retval == XDP_DROP) {
687a4b5ba81SPuranjay Mohan 		expected_len[idx] = 0;
688a4b5ba81SPuranjay Mohan 		return;
689a4b5ba81SPuranjay Mohan 	}
690a4b5ba81SPuranjay Mohan 
691a4b5ba81SPuranjay Mohan 	if (sc->expected_retval == XDP_PASS) {
692a4b5ba81SPuranjay Mohan 		memcpy(p, in, in_len);
693a4b5ba81SPuranjay Mohan 		expected_len[idx] = in_len;
694a4b5ba81SPuranjay Mohan 		return;
695a4b5ba81SPuranjay Mohan 	}
696a4b5ba81SPuranjay Mohan 
697a4b5ba81SPuranjay Mohan 	memcpy(eth.h_dest, router_mac, ETH_ALEN);
698a4b5ba81SPuranjay Mohan 	memcpy(eth.h_source, lb_mac, ETH_ALEN);
699a4b5ba81SPuranjay Mohan 	eth.h_proto = htons(sc->encap_v6_outer ? ETH_P_IPV6 : ETH_P_IP);
700a4b5ba81SPuranjay Mohan 	memcpy(p, &eth, sizeof(eth));
701a4b5ba81SPuranjay Mohan 	off += sizeof(eth);
702a4b5ba81SPuranjay Mohan 
703a4b5ba81SPuranjay Mohan 	if (sc->encap_v6_outer) {
704a4b5ba81SPuranjay Mohan 		struct ipv6hdr ip6h = {};
705a4b5ba81SPuranjay Mohan 		__u8 nexthdr = sc->is_v6 ? IPPROTO_IPV6 : IPPROTO_IPIP;
706a4b5ba81SPuranjay Mohan 
707a4b5ba81SPuranjay Mohan 		ip6h.version     = 6;
708a4b5ba81SPuranjay Mohan 		ip6h.nexthdr     = nexthdr;
709a4b5ba81SPuranjay Mohan 		ip6h.payload_len = htons(inner_len);
710a4b5ba81SPuranjay Mohan 		ip6h.hop_limit   = 64;
711a4b5ba81SPuranjay Mohan 
712a4b5ba81SPuranjay Mohan 		create_encap_ipv6_src(htons(sc->src_port),
713a4b5ba81SPuranjay Mohan 				      sc->is_v6 ? htonl(sc->src_addr_v6[0])
714a4b5ba81SPuranjay Mohan 						: htonl(sc->src_addr),
715a4b5ba81SPuranjay Mohan 				      (__be32 *)&ip6h.saddr);
716a4b5ba81SPuranjay Mohan 		htonl_v6((__be32 *)&ip6h.daddr, sc->tunnel_dst_v6);
717a4b5ba81SPuranjay Mohan 
718a4b5ba81SPuranjay Mohan 		memcpy(p + off, &ip6h, sizeof(ip6h));
719a4b5ba81SPuranjay Mohan 		off += sizeof(ip6h);
720a4b5ba81SPuranjay Mohan 	} else {
721a4b5ba81SPuranjay Mohan 		struct iphdr iph = {};
722a4b5ba81SPuranjay Mohan 
723a4b5ba81SPuranjay Mohan 		iph.version  = 4;
724a4b5ba81SPuranjay Mohan 		iph.ihl      = sizeof(iph) >> 2;
725a4b5ba81SPuranjay Mohan 		iph.protocol = IPPROTO_IPIP;
726a4b5ba81SPuranjay Mohan 		iph.tot_len  = htons(inner_len + sizeof(iph));
727a4b5ba81SPuranjay Mohan 		iph.ttl      = 64;
728a4b5ba81SPuranjay Mohan 		iph.saddr    = create_encap_ipv4_src(htons(sc->src_port),
729a4b5ba81SPuranjay Mohan 						     htonl(sc->src_addr));
730a4b5ba81SPuranjay Mohan 		iph.daddr    = htonl(sc->tunnel_dst);
731a4b5ba81SPuranjay Mohan 		iph.check    = ip_checksum(&iph, sizeof(iph));
732a4b5ba81SPuranjay Mohan 
733a4b5ba81SPuranjay Mohan 		memcpy(p + off, &iph, sizeof(iph));
734a4b5ba81SPuranjay Mohan 		off += sizeof(iph);
735a4b5ba81SPuranjay Mohan 	}
736a4b5ba81SPuranjay Mohan 
737a4b5ba81SPuranjay Mohan 	memcpy(p + off, in + sizeof(struct ethhdr), inner_len);
738a4b5ba81SPuranjay Mohan 	off += inner_len;
739a4b5ba81SPuranjay Mohan 
740a4b5ba81SPuranjay Mohan 	expected_len[idx] = off;
741a4b5ba81SPuranjay Mohan }
742a4b5ba81SPuranjay Mohan 
743a4b5ba81SPuranjay Mohan static void print_hex_diff(const char *name, const __u8 *got, __u32 got_len, const __u8 *exp,
744a4b5ba81SPuranjay Mohan 			   __u32 exp_len)
745a4b5ba81SPuranjay Mohan {
746a4b5ba81SPuranjay Mohan 	__u32 max_len = got_len > exp_len ? got_len : exp_len;
747a4b5ba81SPuranjay Mohan 	__u32 i, ndiffs = 0;
748a4b5ba81SPuranjay Mohan 
749a4b5ba81SPuranjay Mohan 	fprintf(stderr, "  [%s] got %u bytes, expected %u bytes\n",
750a4b5ba81SPuranjay Mohan 		name, got_len, exp_len);
751a4b5ba81SPuranjay Mohan 
752a4b5ba81SPuranjay Mohan 	for (i = 0; i < max_len && ndiffs < 8; i++) {
753a4b5ba81SPuranjay Mohan 		__u8 g = i < got_len ? got[i] : 0;
754a4b5ba81SPuranjay Mohan 		__u8 e = i < exp_len ? exp[i] : 0;
755a4b5ba81SPuranjay Mohan 
756a4b5ba81SPuranjay Mohan 		if (g != e || i >= got_len || i >= exp_len) {
757a4b5ba81SPuranjay Mohan 			fprintf(stderr, "    offset 0x%03x: got 0x%02x  expected 0x%02x\n",
758a4b5ba81SPuranjay Mohan 				i, g, e);
759a4b5ba81SPuranjay Mohan 			ndiffs++;
760a4b5ba81SPuranjay Mohan 		}
761a4b5ba81SPuranjay Mohan 	}
762a4b5ba81SPuranjay Mohan 
763a4b5ba81SPuranjay Mohan 	if (ndiffs >= 8 && i < max_len)
764a4b5ba81SPuranjay Mohan 		fprintf(stderr, "    ... (more differences)\n");
765a4b5ba81SPuranjay Mohan }
766a4b5ba81SPuranjay Mohan 
767a4b5ba81SPuranjay Mohan static void read_stat(int stats_fd, __u32 key, __u64 *v1_out, __u64 *v2_out)
768a4b5ba81SPuranjay Mohan {
769a4b5ba81SPuranjay Mohan 	struct lb_stats values[BENCH_NR_CPUS];
770a4b5ba81SPuranjay Mohan 	unsigned int nr_cpus = bpf_num_possible_cpus();
771a4b5ba81SPuranjay Mohan 	__u64 v1 = 0, v2 = 0;
772a4b5ba81SPuranjay Mohan 	unsigned int i;
773a4b5ba81SPuranjay Mohan 
774a4b5ba81SPuranjay Mohan 	if (nr_cpus > BENCH_NR_CPUS)
775a4b5ba81SPuranjay Mohan 		nr_cpus = BENCH_NR_CPUS;
776a4b5ba81SPuranjay Mohan 
777a4b5ba81SPuranjay Mohan 	if (bpf_map_lookup_elem(stats_fd, &key, values) == 0) {
778a4b5ba81SPuranjay Mohan 		for (i = 0; i < nr_cpus; i++) {
779a4b5ba81SPuranjay Mohan 			v1 += values[i].v1;
780a4b5ba81SPuranjay Mohan 			v2 += values[i].v2;
781a4b5ba81SPuranjay Mohan 		}
782a4b5ba81SPuranjay Mohan 	}
783a4b5ba81SPuranjay Mohan 
784a4b5ba81SPuranjay Mohan 	*v1_out = v1;
785a4b5ba81SPuranjay Mohan 	*v2_out = v2;
786a4b5ba81SPuranjay Mohan }
787a4b5ba81SPuranjay Mohan 
788a4b5ba81SPuranjay Mohan static void reset_stats(int stats_fd)
789a4b5ba81SPuranjay Mohan {
790a4b5ba81SPuranjay Mohan 	struct lb_stats zeros[BENCH_NR_CPUS];
791a4b5ba81SPuranjay Mohan 	__u32 key;
792a4b5ba81SPuranjay Mohan 
793a4b5ba81SPuranjay Mohan 	memset(zeros, 0, sizeof(zeros));
794a4b5ba81SPuranjay Mohan 	for (key = 0; key < STATS_SIZE; key++)
795a4b5ba81SPuranjay Mohan 		bpf_map_update_elem(stats_fd, &key, zeros, BPF_ANY);
796a4b5ba81SPuranjay Mohan }
797a4b5ba81SPuranjay Mohan 
798a4b5ba81SPuranjay Mohan static bool validate_counters(int idx)
799a4b5ba81SPuranjay Mohan {
800a4b5ba81SPuranjay Mohan 	const struct test_scenario *sc = &scenarios[idx];
801a4b5ba81SPuranjay Mohan 	int stats_fd = bpf_map__fd(ctx.skel->maps.stats);
802a4b5ba81SPuranjay Mohan 	__u64 xdp_tx, xdp_pass, xdp_drop, lru_pkts, lru_misses, tcp_misses;
803a4b5ba81SPuranjay Mohan 	__u64 expected_misses;
804a4b5ba81SPuranjay Mohan 	__u64 dummy;
805a4b5ba81SPuranjay Mohan 	/*
806a4b5ba81SPuranjay Mohan 	 * BENCH_BPF_LOOP runs batch_iters timed + 1 untimed iteration.
807a4b5ba81SPuranjay Mohan 	 * Each iteration calls process_packet -> count_action, so all
808a4b5ba81SPuranjay Mohan 	 * counters are incremented (batch_iters + 1) times.
809a4b5ba81SPuranjay Mohan 	 */
810a4b5ba81SPuranjay Mohan 	__u64 n = ctx.timing.batch_iters + 1;
811a4b5ba81SPuranjay Mohan 	bool pass = true;
812a4b5ba81SPuranjay Mohan 
813a4b5ba81SPuranjay Mohan 	read_stat(stats_fd, STATS_XDP_TX, &xdp_tx, &dummy);
814a4b5ba81SPuranjay Mohan 	read_stat(stats_fd, STATS_XDP_PASS, &xdp_pass, &dummy);
815a4b5ba81SPuranjay Mohan 	read_stat(stats_fd, STATS_XDP_DROP, &xdp_drop, &dummy);
816a4b5ba81SPuranjay Mohan 	read_stat(stats_fd, STATS_LRU, &lru_pkts, &lru_misses);
817a4b5ba81SPuranjay Mohan 	read_stat(stats_fd, STATS_LRU_MISS, &tcp_misses, &dummy);
818a4b5ba81SPuranjay Mohan 
819a4b5ba81SPuranjay Mohan 	if (sc->expected_retval == XDP_TX && xdp_tx != n) {
820a4b5ba81SPuranjay Mohan 		fprintf(stderr, "  [%s] COUNTER FAIL: STATS_XDP_TX=%llu, expected %llu\n", sc->name,
821a4b5ba81SPuranjay Mohan 			(unsigned long long)xdp_tx, (unsigned long long)n);
822a4b5ba81SPuranjay Mohan 		pass = false;
823a4b5ba81SPuranjay Mohan 	}
824a4b5ba81SPuranjay Mohan 	if (sc->expected_retval == XDP_PASS && xdp_pass != n) {
825a4b5ba81SPuranjay Mohan 		fprintf(stderr, "  [%s] COUNTER FAIL: STATS_XDP_PASS=%llu, expected %llu\n",
826a4b5ba81SPuranjay Mohan 			sc->name, (unsigned long long)xdp_pass, (unsigned long long)n);
827a4b5ba81SPuranjay Mohan 		pass = false;
828a4b5ba81SPuranjay Mohan 	}
829a4b5ba81SPuranjay Mohan 	if (sc->expected_retval == XDP_DROP && xdp_drop != n) {
830a4b5ba81SPuranjay Mohan 		fprintf(stderr, "  [%s] COUNTER FAIL: STATS_XDP_DROP=%llu, expected %llu\n",
831a4b5ba81SPuranjay Mohan 			sc->name, (unsigned long long)xdp_drop, (unsigned long long)n);
832a4b5ba81SPuranjay Mohan 		pass = false;
833a4b5ba81SPuranjay Mohan 	}
834a4b5ba81SPuranjay Mohan 
835a4b5ba81SPuranjay Mohan 	if (!sc->expect_encap)
836a4b5ba81SPuranjay Mohan 		goto out;
837a4b5ba81SPuranjay Mohan 
838a4b5ba81SPuranjay Mohan 	if (lru_pkts != n) {
839a4b5ba81SPuranjay Mohan 		fprintf(stderr, "  [%s] COUNTER FAIL: STATS_LRU.v1=%llu, expected %llu\n",
840a4b5ba81SPuranjay Mohan 			sc->name, (unsigned long long)lru_pkts, (unsigned long long)n);
841a4b5ba81SPuranjay Mohan 		pass = false;
842a4b5ba81SPuranjay Mohan 	}
843a4b5ba81SPuranjay Mohan 
844a4b5ba81SPuranjay Mohan 	switch (sc->lru_miss) {
845a4b5ba81SPuranjay Mohan 	case LRU_MISS_NONE:
846a4b5ba81SPuranjay Mohan 		expected_misses = 0;
847a4b5ba81SPuranjay Mohan 		break;
848a4b5ba81SPuranjay Mohan 	case LRU_MISS_ALL:
849a4b5ba81SPuranjay Mohan 		expected_misses = n;
850a4b5ba81SPuranjay Mohan 		break;
851a4b5ba81SPuranjay Mohan 	case LRU_MISS_FIRST:
852a4b5ba81SPuranjay Mohan 		expected_misses = 1;
853a4b5ba81SPuranjay Mohan 		break;
854a4b5ba81SPuranjay Mohan 	default:
855a4b5ba81SPuranjay Mohan 		/* LRU_MISS_AUTO: compute from scenario flags */
856a4b5ba81SPuranjay Mohan 		if (sc->prepopulate_lru && !sc->set_syn)
857a4b5ba81SPuranjay Mohan 			expected_misses = 0;
858a4b5ba81SPuranjay Mohan 		else if (sc->set_syn || sc->set_rst ||
859a4b5ba81SPuranjay Mohan 			 (sc->vip_flags & F_LRU_BYPASS))
860a4b5ba81SPuranjay Mohan 			expected_misses = n;
861a4b5ba81SPuranjay Mohan 		else if (sc->cold_lru)
862a4b5ba81SPuranjay Mohan 			expected_misses = 1;
863a4b5ba81SPuranjay Mohan 		else
864a4b5ba81SPuranjay Mohan 			expected_misses = n;
865a4b5ba81SPuranjay Mohan 		break;
866a4b5ba81SPuranjay Mohan 	}
867a4b5ba81SPuranjay Mohan 
868a4b5ba81SPuranjay Mohan 	if (lru_misses != expected_misses) {
869a4b5ba81SPuranjay Mohan 		fprintf(stderr, "  [%s] COUNTER FAIL: LRU misses=%llu, expected %llu\n",
870a4b5ba81SPuranjay Mohan 			sc->name, (unsigned long long)lru_misses,
871a4b5ba81SPuranjay Mohan 			(unsigned long long)expected_misses);
872a4b5ba81SPuranjay Mohan 		pass = false;
873a4b5ba81SPuranjay Mohan 	}
874a4b5ba81SPuranjay Mohan 
875a4b5ba81SPuranjay Mohan 	if (sc->ip_proto == IPPROTO_TCP && lru_misses > 0) {
876a4b5ba81SPuranjay Mohan 		if (tcp_misses != lru_misses) {
877a4b5ba81SPuranjay Mohan 			fprintf(stderr, "  [%s] COUNTER FAIL: TCP LRU misses=%llu, expected %llu\n",
878a4b5ba81SPuranjay Mohan 				sc->name, (unsigned long long)tcp_misses,
879a4b5ba81SPuranjay Mohan 				(unsigned long long)lru_misses);
880a4b5ba81SPuranjay Mohan 			pass = false;
881a4b5ba81SPuranjay Mohan 		}
882a4b5ba81SPuranjay Mohan 	}
883a4b5ba81SPuranjay Mohan 
884a4b5ba81SPuranjay Mohan out:
885a4b5ba81SPuranjay Mohan 	reset_stats(stats_fd);
886a4b5ba81SPuranjay Mohan 	return pass;
887a4b5ba81SPuranjay Mohan }
888a4b5ba81SPuranjay Mohan 
889a4b5ba81SPuranjay Mohan static const char *xdp_action_str(int action)
890a4b5ba81SPuranjay Mohan {
891a4b5ba81SPuranjay Mohan 	switch (action) {
892a4b5ba81SPuranjay Mohan 	case XDP_DROP:	return "XDP_DROP";
893a4b5ba81SPuranjay Mohan 	case XDP_PASS:	return "XDP_PASS";
894a4b5ba81SPuranjay Mohan 	case XDP_TX:	return "XDP_TX";
895a4b5ba81SPuranjay Mohan 	default:	return "UNKNOWN";
896a4b5ba81SPuranjay Mohan 	}
897a4b5ba81SPuranjay Mohan }
898a4b5ba81SPuranjay Mohan 
899a4b5ba81SPuranjay Mohan static bool validate_scenario(int idx)
900a4b5ba81SPuranjay Mohan {
901a4b5ba81SPuranjay Mohan 	LIBBPF_OPTS(bpf_test_run_opts, topts);
902a4b5ba81SPuranjay Mohan 	const struct test_scenario *sc = &scenarios[idx];
903a4b5ba81SPuranjay Mohan 	__u8 out[MAX_ENCAP_SIZE];
904a4b5ba81SPuranjay Mohan 	int err;
905a4b5ba81SPuranjay Mohan 
906a4b5ba81SPuranjay Mohan 	topts.data_in = pkt_buf[idx];
907a4b5ba81SPuranjay Mohan 	topts.data_size_in = pkt_len[idx];
908a4b5ba81SPuranjay Mohan 	topts.data_out = out;
909a4b5ba81SPuranjay Mohan 	topts.data_size_out = sizeof(out);
910a4b5ba81SPuranjay Mohan 	topts.repeat = 1;
911a4b5ba81SPuranjay Mohan 
912a4b5ba81SPuranjay Mohan 	err = bpf_prog_test_run_opts(ctx.prog_fd, &topts);
913a4b5ba81SPuranjay Mohan 	if (err) {
914a4b5ba81SPuranjay Mohan 		fprintf(stderr, "  [%s] FAIL: test_run: %s\n", sc->name, strerror(errno));
915a4b5ba81SPuranjay Mohan 		return false;
916a4b5ba81SPuranjay Mohan 	}
917a4b5ba81SPuranjay Mohan 
918a4b5ba81SPuranjay Mohan 	if ((int)topts.retval != sc->expected_retval) {
919a4b5ba81SPuranjay Mohan 		fprintf(stderr, "  [%s] FAIL: retval %s, expected %s\n", sc->name,
920a4b5ba81SPuranjay Mohan 			xdp_action_str(topts.retval), xdp_action_str(sc->expected_retval));
921a4b5ba81SPuranjay Mohan 		return false;
922a4b5ba81SPuranjay Mohan 	}
923a4b5ba81SPuranjay Mohan 
924a4b5ba81SPuranjay Mohan 	/*
925a4b5ba81SPuranjay Mohan 	 * Compare output packet when it's deterministic.
926a4b5ba81SPuranjay Mohan 	 * Skip for XDP_DROP (no output) and cold_lru (source IP poisoned).
927a4b5ba81SPuranjay Mohan 	 */
928a4b5ba81SPuranjay Mohan 	if (sc->expected_retval != XDP_DROP && !sc->cold_lru) {
929a4b5ba81SPuranjay Mohan 		if (topts.data_size_out != expected_len[idx] ||
930a4b5ba81SPuranjay Mohan 		    memcmp(out, expected_buf[idx], expected_len[idx]) != 0) {
931a4b5ba81SPuranjay Mohan 			fprintf(stderr, "  [%s] FAIL: output packet mismatch\n", sc->name);
932a4b5ba81SPuranjay Mohan 			print_hex_diff(sc->name, out, topts.data_size_out, expected_buf[idx],
933a4b5ba81SPuranjay Mohan 				       expected_len[idx]);
934a4b5ba81SPuranjay Mohan 			return false;
935a4b5ba81SPuranjay Mohan 		}
936a4b5ba81SPuranjay Mohan 	}
937a4b5ba81SPuranjay Mohan 
938a4b5ba81SPuranjay Mohan 	if (!validate_counters(idx))
939a4b5ba81SPuranjay Mohan 		return false;
940a4b5ba81SPuranjay Mohan 	return true;
941a4b5ba81SPuranjay Mohan }
942a4b5ba81SPuranjay Mohan 
943a4b5ba81SPuranjay Mohan static int find_scenario(const char *name)
944a4b5ba81SPuranjay Mohan {
945a4b5ba81SPuranjay Mohan 	int i;
946a4b5ba81SPuranjay Mohan 
947a4b5ba81SPuranjay Mohan 	for (i = 0; i < NUM_SCENARIOS; i++) {
948a4b5ba81SPuranjay Mohan 		if (strcmp(scenarios[i].name, name) == 0)
949a4b5ba81SPuranjay Mohan 			return i;
950a4b5ba81SPuranjay Mohan 	}
951a4b5ba81SPuranjay Mohan 	return -1;
952a4b5ba81SPuranjay Mohan }
953a4b5ba81SPuranjay Mohan 
954a4b5ba81SPuranjay Mohan static void xdp_lb_validate(void)
955a4b5ba81SPuranjay Mohan {
956a4b5ba81SPuranjay Mohan 	if (env.consumer_cnt != 0) {
957a4b5ba81SPuranjay Mohan 		fprintf(stderr, "benchmark doesn't support consumers\n");
958a4b5ba81SPuranjay Mohan 		exit(1);
959a4b5ba81SPuranjay Mohan 	}
960a4b5ba81SPuranjay Mohan 	if (bpf_num_possible_cpus() > BENCH_NR_CPUS) {
961a4b5ba81SPuranjay Mohan 		fprintf(stderr, "too many CPUs (%d > %d), increase BENCH_NR_CPUS\n",
962a4b5ba81SPuranjay Mohan 			bpf_num_possible_cpus(), BENCH_NR_CPUS);
963a4b5ba81SPuranjay Mohan 		exit(1);
964a4b5ba81SPuranjay Mohan 	}
965a4b5ba81SPuranjay Mohan }
966a4b5ba81SPuranjay Mohan 
967a4b5ba81SPuranjay Mohan static void xdp_lb_run_once(void *unused __always_unused)
968a4b5ba81SPuranjay Mohan {
969a4b5ba81SPuranjay Mohan 	int idx = args.scenario;
970a4b5ba81SPuranjay Mohan 
971a4b5ba81SPuranjay Mohan 	LIBBPF_OPTS(bpf_test_run_opts, topts,
972a4b5ba81SPuranjay Mohan 		.data_in      = pkt_buf[idx],
973a4b5ba81SPuranjay Mohan 		.data_size_in = pkt_len[idx],
974a4b5ba81SPuranjay Mohan 		.repeat       = 1,
975a4b5ba81SPuranjay Mohan 	);
976a4b5ba81SPuranjay Mohan 
977a4b5ba81SPuranjay Mohan 	bpf_prog_test_run_opts(ctx.prog_fd, &topts);
978a4b5ba81SPuranjay Mohan }
979a4b5ba81SPuranjay Mohan 
980a4b5ba81SPuranjay Mohan static void xdp_lb_setup(void)
981a4b5ba81SPuranjay Mohan {
982a4b5ba81SPuranjay Mohan 	struct xdp_lb_bench *skel;
983a4b5ba81SPuranjay Mohan 	int err;
984a4b5ba81SPuranjay Mohan 
985a4b5ba81SPuranjay Mohan 	if (args.scenario < 0) {
986a4b5ba81SPuranjay Mohan 		fprintf(stderr, "--scenario is required. Use --list-scenarios to see options.\n");
987a4b5ba81SPuranjay Mohan 		exit(1);
988a4b5ba81SPuranjay Mohan 	}
989a4b5ba81SPuranjay Mohan 
990a4b5ba81SPuranjay Mohan 	setup_libbpf();
991a4b5ba81SPuranjay Mohan 
992a4b5ba81SPuranjay Mohan 	skel = xdp_lb_bench__open();
993a4b5ba81SPuranjay Mohan 	if (!skel) {
994a4b5ba81SPuranjay Mohan 		fprintf(stderr, "failed to open skeleton\n");
995a4b5ba81SPuranjay Mohan 		exit(1);
996a4b5ba81SPuranjay Mohan 	}
997a4b5ba81SPuranjay Mohan 
998a4b5ba81SPuranjay Mohan 	err = xdp_lb_bench__load(skel);
999a4b5ba81SPuranjay Mohan 	if (err) {
1000a4b5ba81SPuranjay Mohan 		fprintf(stderr, "failed to load skeleton: %s\n", strerror(-err));
1001a4b5ba81SPuranjay Mohan 		xdp_lb_bench__destroy(skel);
1002a4b5ba81SPuranjay Mohan 		exit(1);
1003a4b5ba81SPuranjay Mohan 	}
1004a4b5ba81SPuranjay Mohan 
1005a4b5ba81SPuranjay Mohan 	ctx.skel    = skel;
1006a4b5ba81SPuranjay Mohan 	ctx.prog_fd = bpf_program__fd(skel->progs.xdp_lb_bench);
1007a4b5ba81SPuranjay Mohan 
1008a4b5ba81SPuranjay Mohan 	build_packet(args.scenario);
1009a4b5ba81SPuranjay Mohan 	build_expected_packet(args.scenario);
1010a4b5ba81SPuranjay Mohan 
1011a4b5ba81SPuranjay Mohan 	populate_maps(skel);
1012a4b5ba81SPuranjay Mohan 
1013a4b5ba81SPuranjay Mohan 	BENCH_TIMING_INIT(&ctx.timing, skel, 0);
1014a4b5ba81SPuranjay Mohan 	ctx.timing.machine_readable = args.machine_readable;
1015a4b5ba81SPuranjay Mohan 
1016a4b5ba81SPuranjay Mohan 	if (scenarios[args.scenario].fixed_batch_iters) {
1017a4b5ba81SPuranjay Mohan 		ctx.timing.batch_iters = scenarios[args.scenario].fixed_batch_iters;
1018a4b5ba81SPuranjay Mohan 		skel->bss->batch_iters = ctx.timing.batch_iters;
1019a4b5ba81SPuranjay Mohan 	} else {
1020a4b5ba81SPuranjay Mohan 		bpf_bench_calibrate(&ctx.timing, xdp_lb_run_once, NULL);
1021a4b5ba81SPuranjay Mohan 	}
1022a4b5ba81SPuranjay Mohan 
1023a4b5ba81SPuranjay Mohan 	env.duration_sec = 600;
1024a4b5ba81SPuranjay Mohan 
1025a4b5ba81SPuranjay Mohan 	/*
1026a4b5ba81SPuranjay Mohan 	 * Enable cold_lru before validation so LRU miss counters are
1027a4b5ba81SPuranjay Mohan 	 * correct.  Seed the LRU with one run so the original flow is
1028a4b5ba81SPuranjay Mohan 	 * present; validation then sees exactly 1 miss (the poisoned
1029a4b5ba81SPuranjay Mohan 	 * flow) regardless of whether calibration ran.
1030a4b5ba81SPuranjay Mohan 	 */
1031a4b5ba81SPuranjay Mohan 	if (scenarios[args.scenario].cold_lru) {
1032a4b5ba81SPuranjay Mohan 		skel->bss->cold_lru = 1;
1033a4b5ba81SPuranjay Mohan 		xdp_lb_run_once(NULL);
1034a4b5ba81SPuranjay Mohan 	}
1035a4b5ba81SPuranjay Mohan 
1036a4b5ba81SPuranjay Mohan 	reset_stats(bpf_map__fd(skel->maps.stats));
1037a4b5ba81SPuranjay Mohan 
1038a4b5ba81SPuranjay Mohan 	if (!validate_scenario(args.scenario)) {
1039a4b5ba81SPuranjay Mohan 		fprintf(stderr, "Validation FAILED - aborting benchmark\n");
1040a4b5ba81SPuranjay Mohan 		exit(1);
1041a4b5ba81SPuranjay Mohan 	}
1042a4b5ba81SPuranjay Mohan 
1043a4b5ba81SPuranjay Mohan 	if (scenarios[args.scenario].flow_mask)
1044a4b5ba81SPuranjay Mohan 		skel->bss->flow_mask = scenarios[args.scenario].flow_mask;
1045a4b5ba81SPuranjay Mohan }
1046a4b5ba81SPuranjay Mohan 
1047a4b5ba81SPuranjay Mohan static void *xdp_lb_producer(void *input)
1048a4b5ba81SPuranjay Mohan {
1049a4b5ba81SPuranjay Mohan 	while (true)
1050a4b5ba81SPuranjay Mohan 		xdp_lb_run_once(NULL);
1051a4b5ba81SPuranjay Mohan 
1052a4b5ba81SPuranjay Mohan 	return NULL;
1053a4b5ba81SPuranjay Mohan }
1054a4b5ba81SPuranjay Mohan 
1055a4b5ba81SPuranjay Mohan static void xdp_lb_measure(struct bench_res *res)
1056a4b5ba81SPuranjay Mohan {
1057a4b5ba81SPuranjay Mohan 	bpf_bench_timing_measure(&ctx.timing, res);
1058a4b5ba81SPuranjay Mohan }
1059a4b5ba81SPuranjay Mohan 
1060a4b5ba81SPuranjay Mohan static void xdp_lb_report_final(struct bench_res res[], int res_cnt)
1061a4b5ba81SPuranjay Mohan {
1062a4b5ba81SPuranjay Mohan 	bpf_bench_timing_report(&ctx.timing, scenarios[args.scenario].name,
1063a4b5ba81SPuranjay Mohan 				scenarios[args.scenario].description);
1064a4b5ba81SPuranjay Mohan }
1065a4b5ba81SPuranjay Mohan 
1066a4b5ba81SPuranjay Mohan enum {
1067a4b5ba81SPuranjay Mohan 	ARG_SCENARIO         = 9001,
1068a4b5ba81SPuranjay Mohan 	ARG_LIST_SCENARIOS   = 9002,
1069a4b5ba81SPuranjay Mohan 	ARG_MACHINE_READABLE = 9003,
1070a4b5ba81SPuranjay Mohan };
1071a4b5ba81SPuranjay Mohan 
1072a4b5ba81SPuranjay Mohan static const struct argp_option opts[] = {
1073a4b5ba81SPuranjay Mohan 	{ "scenario", ARG_SCENARIO, "NAME", 0,
1074a4b5ba81SPuranjay Mohan 	  "Scenario to benchmark (required)" },
1075a4b5ba81SPuranjay Mohan 	{ "list-scenarios", ARG_LIST_SCENARIOS, NULL, 0,
1076a4b5ba81SPuranjay Mohan 	  "List available scenarios and exit" },
1077a4b5ba81SPuranjay Mohan 	{ "machine-readable", ARG_MACHINE_READABLE, NULL, 0,
1078a4b5ba81SPuranjay Mohan 	  "Print only a machine-readable RESULT line" },
1079a4b5ba81SPuranjay Mohan 	{},
1080a4b5ba81SPuranjay Mohan };
1081a4b5ba81SPuranjay Mohan 
1082a4b5ba81SPuranjay Mohan static error_t parse_arg(int key, char *arg, struct argp_state *state)
1083a4b5ba81SPuranjay Mohan {
1084a4b5ba81SPuranjay Mohan 	int i;
1085a4b5ba81SPuranjay Mohan 
1086a4b5ba81SPuranjay Mohan 	switch (key) {
1087a4b5ba81SPuranjay Mohan 	case ARG_SCENARIO:
1088a4b5ba81SPuranjay Mohan 		args.scenario = find_scenario(arg);
1089a4b5ba81SPuranjay Mohan 		if (args.scenario < 0) {
1090a4b5ba81SPuranjay Mohan 			fprintf(stderr, "unknown scenario: '%s'\n", arg);
1091a4b5ba81SPuranjay Mohan 			fprintf(stderr, "use --list-scenarios to see options\n");
1092a4b5ba81SPuranjay Mohan 			argp_usage(state);
1093a4b5ba81SPuranjay Mohan 		}
1094a4b5ba81SPuranjay Mohan 		break;
1095a4b5ba81SPuranjay Mohan 	case ARG_LIST_SCENARIOS:
1096a4b5ba81SPuranjay Mohan 		printf("Available scenarios:\n");
1097a4b5ba81SPuranjay Mohan 		for (i = 0; i < NUM_SCENARIOS; i++)
1098a4b5ba81SPuranjay Mohan 			printf("  %-20s  %s\n", scenarios[i].name, scenarios[i].description);
1099a4b5ba81SPuranjay Mohan 		exit(0);
1100a4b5ba81SPuranjay Mohan 	case ARG_MACHINE_READABLE:
1101a4b5ba81SPuranjay Mohan 		args.machine_readable = true;
1102a4b5ba81SPuranjay Mohan 		env.quiet = true;
1103a4b5ba81SPuranjay Mohan 		break;
1104a4b5ba81SPuranjay Mohan 	default:
1105a4b5ba81SPuranjay Mohan 		return ARGP_ERR_UNKNOWN;
1106a4b5ba81SPuranjay Mohan 	}
1107a4b5ba81SPuranjay Mohan 
1108a4b5ba81SPuranjay Mohan 	return 0;
1109a4b5ba81SPuranjay Mohan }
1110a4b5ba81SPuranjay Mohan 
1111a4b5ba81SPuranjay Mohan const struct argp bench_xdp_lb_argp = {
1112a4b5ba81SPuranjay Mohan 	.options = opts,
1113a4b5ba81SPuranjay Mohan 	.parser  = parse_arg,
1114a4b5ba81SPuranjay Mohan };
1115a4b5ba81SPuranjay Mohan 
1116a4b5ba81SPuranjay Mohan const struct bench bench_xdp_lb = {
1117a4b5ba81SPuranjay Mohan 	.name            = "xdp-lb",
1118a4b5ba81SPuranjay Mohan 	.argp            = &bench_xdp_lb_argp,
1119a4b5ba81SPuranjay Mohan 	.validate        = xdp_lb_validate,
1120a4b5ba81SPuranjay Mohan 	.setup           = xdp_lb_setup,
1121a4b5ba81SPuranjay Mohan 	.producer_thread = xdp_lb_producer,
1122a4b5ba81SPuranjay Mohan 	.measure         = xdp_lb_measure,
1123a4b5ba81SPuranjay Mohan 	.report_final    = xdp_lb_report_final,
1124a4b5ba81SPuranjay Mohan };
1125