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, ð, 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, ð, 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