1*6e1e4a9dSMykyta Yatsenko // SPDX-License-Identifier: GPL-2.0 2*6e1e4a9dSMykyta Yatsenko /* 3*6e1e4a9dSMykyta Yatsenko * Stress every LRU lock-failure and orphan-recovery. 4*6e1e4a9dSMykyta Yatsenko * perf_event NMI BPF on every online CPU does 5*6e1e4a9dSMykyta Yatsenko * update+delete on a small LRU map; userspace threads on every CPU do 6*6e1e4a9dSMykyta Yatsenko * the same from syscall context. 7*6e1e4a9dSMykyta Yatsenko */ 8*6e1e4a9dSMykyta Yatsenko #define _GNU_SOURCE 9*6e1e4a9dSMykyta Yatsenko #include <pthread.h> 10*6e1e4a9dSMykyta Yatsenko #include <sched.h> 11*6e1e4a9dSMykyta Yatsenko #include <sys/syscall.h> 12*6e1e4a9dSMykyta Yatsenko #include <linux/perf_event.h> 13*6e1e4a9dSMykyta Yatsenko #include <test_progs.h> 14*6e1e4a9dSMykyta Yatsenko #include "testing_helpers.h" 15*6e1e4a9dSMykyta Yatsenko #include "lru_lock_nmi.skel.h" 16*6e1e4a9dSMykyta Yatsenko 17*6e1e4a9dSMykyta Yatsenko #define MAP_ENTRIES 64 18*6e1e4a9dSMykyta Yatsenko #define KEY_RANGE (MAP_ENTRIES * 2) 19*6e1e4a9dSMykyta Yatsenko #define STRESS_NS (500 * 1000 * 1000ULL) 20*6e1e4a9dSMykyta Yatsenko 21*6e1e4a9dSMykyta Yatsenko struct hammer_arg { 22*6e1e4a9dSMykyta Yatsenko int map_fd; 23*6e1e4a9dSMykyta Yatsenko int cpu; 24*6e1e4a9dSMykyta Yatsenko __u64 deadline_ns; 25*6e1e4a9dSMykyta Yatsenko }; 26*6e1e4a9dSMykyta Yatsenko 27*6e1e4a9dSMykyta Yatsenko struct refill_arg { 28*6e1e4a9dSMykyta Yatsenko int map_fd; 29*6e1e4a9dSMykyta Yatsenko int cpu; 30*6e1e4a9dSMykyta Yatsenko int per_cpu_quota; 31*6e1e4a9dSMykyta Yatsenko int update_errors; 32*6e1e4a9dSMykyta Yatsenko }; 33*6e1e4a9dSMykyta Yatsenko 34*6e1e4a9dSMykyta Yatsenko /* 35*6e1e4a9dSMykyta Yatsenko * Pin the calling thread to @cpu. Uses dynamically-allocated CPU sets so 36*6e1e4a9dSMykyta Yatsenko * we stay correct on hosts with @cpu >= CPU_SETSIZE (default 1024). 37*6e1e4a9dSMykyta Yatsenko */ 38*6e1e4a9dSMykyta Yatsenko static int pin_to_cpu(int cpu) 39*6e1e4a9dSMykyta Yatsenko { 40*6e1e4a9dSMykyta Yatsenko cpu_set_t *cs; 41*6e1e4a9dSMykyta Yatsenko size_t cs_size; 42*6e1e4a9dSMykyta Yatsenko int err; 43*6e1e4a9dSMykyta Yatsenko 44*6e1e4a9dSMykyta Yatsenko cs = CPU_ALLOC(cpu + 1); 45*6e1e4a9dSMykyta Yatsenko if (!cs) 46*6e1e4a9dSMykyta Yatsenko return -ENOMEM; 47*6e1e4a9dSMykyta Yatsenko cs_size = CPU_ALLOC_SIZE(cpu + 1); 48*6e1e4a9dSMykyta Yatsenko 49*6e1e4a9dSMykyta Yatsenko CPU_ZERO_S(cs_size, cs); 50*6e1e4a9dSMykyta Yatsenko CPU_SET_S(cpu, cs_size, cs); 51*6e1e4a9dSMykyta Yatsenko err = pthread_setaffinity_np(pthread_self(), cs_size, cs); 52*6e1e4a9dSMykyta Yatsenko CPU_FREE(cs); 53*6e1e4a9dSMykyta Yatsenko return err; 54*6e1e4a9dSMykyta Yatsenko } 55*6e1e4a9dSMykyta Yatsenko 56*6e1e4a9dSMykyta Yatsenko static void *hammer_thread(void *p) 57*6e1e4a9dSMykyta Yatsenko { 58*6e1e4a9dSMykyta Yatsenko struct hammer_arg *a = p; 59*6e1e4a9dSMykyta Yatsenko int nr_possible_cpus = libbpf_num_possible_cpus(); 60*6e1e4a9dSMykyta Yatsenko __u64 val[nr_possible_cpus]; 61*6e1e4a9dSMykyta Yatsenko unsigned int seed; 62*6e1e4a9dSMykyta Yatsenko __u32 key; 63*6e1e4a9dSMykyta Yatsenko 64*6e1e4a9dSMykyta Yatsenko memset(val, 0, sizeof(val)); 65*6e1e4a9dSMykyta Yatsenko pin_to_cpu(a->cpu); 66*6e1e4a9dSMykyta Yatsenko 67*6e1e4a9dSMykyta Yatsenko seed = (unsigned int)a->cpu ^ (unsigned int)(uintptr_t)pthread_self(); 68*6e1e4a9dSMykyta Yatsenko 69*6e1e4a9dSMykyta Yatsenko while (get_time_ns() < a->deadline_ns) { 70*6e1e4a9dSMykyta Yatsenko bool do_update = rand_r(&seed) & 1; 71*6e1e4a9dSMykyta Yatsenko 72*6e1e4a9dSMykyta Yatsenko key = rand_r(&seed) % KEY_RANGE; 73*6e1e4a9dSMykyta Yatsenko if (do_update) 74*6e1e4a9dSMykyta Yatsenko bpf_map_update_elem(a->map_fd, &key, val, BPF_ANY); 75*6e1e4a9dSMykyta Yatsenko else 76*6e1e4a9dSMykyta Yatsenko bpf_map_delete_elem(a->map_fd, &key); 77*6e1e4a9dSMykyta Yatsenko } 78*6e1e4a9dSMykyta Yatsenko return NULL; 79*6e1e4a9dSMykyta Yatsenko } 80*6e1e4a9dSMykyta Yatsenko 81*6e1e4a9dSMykyta Yatsenko static void *refill_thread(void *p) 82*6e1e4a9dSMykyta Yatsenko { 83*6e1e4a9dSMykyta Yatsenko struct refill_arg *a = p; 84*6e1e4a9dSMykyta Yatsenko int nr_possible_cpus = libbpf_num_possible_cpus(); 85*6e1e4a9dSMykyta Yatsenko __u64 val[nr_possible_cpus]; 86*6e1e4a9dSMykyta Yatsenko __u32 start, end, key; 87*6e1e4a9dSMykyta Yatsenko 88*6e1e4a9dSMykyta Yatsenko memset(val, 0, sizeof(val)); 89*6e1e4a9dSMykyta Yatsenko pin_to_cpu(a->cpu); 90*6e1e4a9dSMykyta Yatsenko 91*6e1e4a9dSMykyta Yatsenko start = (__u32)a->cpu * (__u32)a->per_cpu_quota; 92*6e1e4a9dSMykyta Yatsenko end = start + (__u32)a->per_cpu_quota; 93*6e1e4a9dSMykyta Yatsenko for (key = start; key < end; key++) 94*6e1e4a9dSMykyta Yatsenko if (bpf_map_update_elem(a->map_fd, &key, val, BPF_ANY)) 95*6e1e4a9dSMykyta Yatsenko a->update_errors++; 96*6e1e4a9dSMykyta Yatsenko return NULL; 97*6e1e4a9dSMykyta Yatsenko } 98*6e1e4a9dSMykyta Yatsenko 99*6e1e4a9dSMykyta Yatsenko /* 100*6e1e4a9dSMykyta Yatsenko * Drain the map, then refill it with each CPU inserting only its own 101*6e1e4a9dSMykyta Yatsenko * quota of keys. 102*6e1e4a9dSMykyta Yatsenko * After refill, lookup every key we inserted - a stranded node on any 103*6e1e4a9dSMykyta Yatsenko * CPU's pool would have forced eviction. 104*6e1e4a9dSMykyta Yatsenko */ 105*6e1e4a9dSMykyta Yatsenko static int drain_then_verify_capacity(int map_fd, int nr_cpus) 106*6e1e4a9dSMykyta Yatsenko { 107*6e1e4a9dSMykyta Yatsenko int per_cpu_quota = MAP_ENTRIES / nr_cpus; 108*6e1e4a9dSMykyta Yatsenko int total = per_cpu_quota * nr_cpus; 109*6e1e4a9dSMykyta Yatsenko int nr_possible_cpus = libbpf_num_possible_cpus(); 110*6e1e4a9dSMykyta Yatsenko pthread_t threads[nr_cpus]; 111*6e1e4a9dSMykyta Yatsenko struct refill_arg args[nr_cpus]; 112*6e1e4a9dSMykyta Yatsenko __u64 val[nr_possible_cpus]; 113*6e1e4a9dSMykyta Yatsenko int i, hits = 0, nthreads = 0; 114*6e1e4a9dSMykyta Yatsenko __u32 key; 115*6e1e4a9dSMykyta Yatsenko 116*6e1e4a9dSMykyta Yatsenko memset(val, 0, sizeof(val)); 117*6e1e4a9dSMykyta Yatsenko 118*6e1e4a9dSMykyta Yatsenko for (key = 0; key < KEY_RANGE; key++) 119*6e1e4a9dSMykyta Yatsenko bpf_map_delete_elem(map_fd, &key); 120*6e1e4a9dSMykyta Yatsenko 121*6e1e4a9dSMykyta Yatsenko for (i = 0; i < nr_cpus; i++) { 122*6e1e4a9dSMykyta Yatsenko args[i] = (struct refill_arg){ 123*6e1e4a9dSMykyta Yatsenko .map_fd = map_fd, 124*6e1e4a9dSMykyta Yatsenko .cpu = i, 125*6e1e4a9dSMykyta Yatsenko .per_cpu_quota = per_cpu_quota, 126*6e1e4a9dSMykyta Yatsenko }; 127*6e1e4a9dSMykyta Yatsenko if (pthread_create(&threads[nthreads], NULL, refill_thread, &args[i]) == 0) 128*6e1e4a9dSMykyta Yatsenko nthreads++; 129*6e1e4a9dSMykyta Yatsenko } 130*6e1e4a9dSMykyta Yatsenko for (i = 0; i < nthreads; i++) 131*6e1e4a9dSMykyta Yatsenko pthread_join(threads[i], NULL); 132*6e1e4a9dSMykyta Yatsenko 133*6e1e4a9dSMykyta Yatsenko for (i = 0; i < nr_cpus; i++) 134*6e1e4a9dSMykyta Yatsenko if (args[i].update_errors) 135*6e1e4a9dSMykyta Yatsenko return -ENOMEM; 136*6e1e4a9dSMykyta Yatsenko 137*6e1e4a9dSMykyta Yatsenko for (key = 0; key < (__u32)total; key++) 138*6e1e4a9dSMykyta Yatsenko if (bpf_map_lookup_elem(map_fd, &key, val) == 0) 139*6e1e4a9dSMykyta Yatsenko hits++; 140*6e1e4a9dSMykyta Yatsenko 141*6e1e4a9dSMykyta Yatsenko return hits == total ? 0 : -EIO; 142*6e1e4a9dSMykyta Yatsenko } 143*6e1e4a9dSMykyta Yatsenko 144*6e1e4a9dSMykyta Yatsenko static void run_variant(enum bpf_map_type type, __u32 map_flags, const char *name) 145*6e1e4a9dSMykyta Yatsenko { 146*6e1e4a9dSMykyta Yatsenko struct perf_event_attr attr = { 147*6e1e4a9dSMykyta Yatsenko .size = sizeof(attr), 148*6e1e4a9dSMykyta Yatsenko .type = PERF_TYPE_HARDWARE, 149*6e1e4a9dSMykyta Yatsenko .config = PERF_COUNT_HW_CPU_CYCLES, 150*6e1e4a9dSMykyta Yatsenko .freq = 1, 151*6e1e4a9dSMykyta Yatsenko }; 152*6e1e4a9dSMykyta Yatsenko int nr_cpus, max_cpus = 64; 153*6e1e4a9dSMykyta Yatsenko struct bpf_link *links[max_cpus]; 154*6e1e4a9dSMykyta Yatsenko pthread_t threads[max_cpus]; 155*6e1e4a9dSMykyta Yatsenko struct hammer_arg args[max_cpus]; 156*6e1e4a9dSMykyta Yatsenko struct lru_lock_nmi *skel = NULL; 157*6e1e4a9dSMykyta Yatsenko int map_fd, i, err, nr_threads = 0, pmu_fd = -1; 158*6e1e4a9dSMykyta Yatsenko __u64 deadline; 159*6e1e4a9dSMykyta Yatsenko 160*6e1e4a9dSMykyta Yatsenko nr_cpus = libbpf_num_possible_cpus(); 161*6e1e4a9dSMykyta Yatsenko if (!ASSERT_GT(nr_cpus, 0, "num_cpus")) 162*6e1e4a9dSMykyta Yatsenko return; 163*6e1e4a9dSMykyta Yatsenko 164*6e1e4a9dSMykyta Yatsenko if (nr_cpus > max_cpus) 165*6e1e4a9dSMykyta Yatsenko nr_cpus = max_cpus; 166*6e1e4a9dSMykyta Yatsenko 167*6e1e4a9dSMykyta Yatsenko if (!test__start_subtest(name)) 168*6e1e4a9dSMykyta Yatsenko return; 169*6e1e4a9dSMykyta Yatsenko 170*6e1e4a9dSMykyta Yatsenko memset(links, 0, sizeof(links)); 171*6e1e4a9dSMykyta Yatsenko skel = lru_lock_nmi__open(); 172*6e1e4a9dSMykyta Yatsenko if (!ASSERT_OK_PTR(skel, "skel_open")) 173*6e1e4a9dSMykyta Yatsenko goto cleanup; 174*6e1e4a9dSMykyta Yatsenko 175*6e1e4a9dSMykyta Yatsenko err = bpf_map__set_type(skel->maps.lru_map, type); 176*6e1e4a9dSMykyta Yatsenko if (!ASSERT_OK(err, "set_type")) 177*6e1e4a9dSMykyta Yatsenko goto cleanup; 178*6e1e4a9dSMykyta Yatsenko err = bpf_map__set_map_flags(skel->maps.lru_map, map_flags); 179*6e1e4a9dSMykyta Yatsenko if (!ASSERT_OK(err, "set_flags")) 180*6e1e4a9dSMykyta Yatsenko goto cleanup; 181*6e1e4a9dSMykyta Yatsenko err = bpf_map__set_max_entries(skel->maps.lru_map, MAP_ENTRIES); 182*6e1e4a9dSMykyta Yatsenko if (!ASSERT_OK(err, "set_max_entries")) 183*6e1e4a9dSMykyta Yatsenko goto cleanup; 184*6e1e4a9dSMykyta Yatsenko 185*6e1e4a9dSMykyta Yatsenko err = lru_lock_nmi__load(skel); 186*6e1e4a9dSMykyta Yatsenko if (!ASSERT_OK(err, "skel_load")) 187*6e1e4a9dSMykyta Yatsenko goto cleanup; 188*6e1e4a9dSMykyta Yatsenko 189*6e1e4a9dSMykyta Yatsenko skel->bss->hits = 0; 190*6e1e4a9dSMykyta Yatsenko map_fd = bpf_map__fd(skel->maps.lru_map); 191*6e1e4a9dSMykyta Yatsenko attr.sample_freq = read_perf_max_sample_freq(); 192*6e1e4a9dSMykyta Yatsenko 193*6e1e4a9dSMykyta Yatsenko for (i = 0; i < nr_cpus; i++) { 194*6e1e4a9dSMykyta Yatsenko pmu_fd = syscall(__NR_perf_event_open, &attr, -1, i, -1, 0); 195*6e1e4a9dSMykyta Yatsenko if (pmu_fd < 0) { 196*6e1e4a9dSMykyta Yatsenko if (i == 0 && 197*6e1e4a9dSMykyta Yatsenko (errno == ENOENT || errno == EOPNOTSUPP)) { 198*6e1e4a9dSMykyta Yatsenko test__skip(); 199*6e1e4a9dSMykyta Yatsenko goto cleanup; 200*6e1e4a9dSMykyta Yatsenko } 201*6e1e4a9dSMykyta Yatsenko continue; 202*6e1e4a9dSMykyta Yatsenko } 203*6e1e4a9dSMykyta Yatsenko /* libbpf takes ownership of pfd on success */ 204*6e1e4a9dSMykyta Yatsenko links[i] = bpf_program__attach_perf_event(skel->progs.oncpu, pmu_fd); 205*6e1e4a9dSMykyta Yatsenko if (!links[i]) 206*6e1e4a9dSMykyta Yatsenko close(pmu_fd); 207*6e1e4a9dSMykyta Yatsenko } 208*6e1e4a9dSMykyta Yatsenko 209*6e1e4a9dSMykyta Yatsenko deadline = get_time_ns() + STRESS_NS; 210*6e1e4a9dSMykyta Yatsenko for (i = 0; i < nr_cpus; i++) { 211*6e1e4a9dSMykyta Yatsenko args[i].map_fd = map_fd; 212*6e1e4a9dSMykyta Yatsenko args[i].cpu = i; 213*6e1e4a9dSMykyta Yatsenko args[i].deadline_ns = deadline; 214*6e1e4a9dSMykyta Yatsenko if (pthread_create(&threads[nr_threads], NULL, hammer_thread, &args[i]) == 0) 215*6e1e4a9dSMykyta Yatsenko nr_threads++; 216*6e1e4a9dSMykyta Yatsenko } 217*6e1e4a9dSMykyta Yatsenko for (i = 0; i < nr_threads; i++) 218*6e1e4a9dSMykyta Yatsenko pthread_join(threads[i], NULL); 219*6e1e4a9dSMykyta Yatsenko 220*6e1e4a9dSMykyta Yatsenko for (i = 0; i < nr_cpus; i++) { 221*6e1e4a9dSMykyta Yatsenko if (links[i]) { 222*6e1e4a9dSMykyta Yatsenko bpf_link__destroy(links[i]); 223*6e1e4a9dSMykyta Yatsenko links[i] = NULL; 224*6e1e4a9dSMykyta Yatsenko } 225*6e1e4a9dSMykyta Yatsenko } 226*6e1e4a9dSMykyta Yatsenko 227*6e1e4a9dSMykyta Yatsenko ASSERT_GT(skel->bss->hits, 0, "nmi_bpf_ran"); 228*6e1e4a9dSMykyta Yatsenko ASSERT_OK(drain_then_verify_capacity(map_fd, nr_cpus), "drain_then_verify_capacity"); 229*6e1e4a9dSMykyta Yatsenko 230*6e1e4a9dSMykyta Yatsenko cleanup: 231*6e1e4a9dSMykyta Yatsenko for (i = 0; i < nr_cpus; i++) { 232*6e1e4a9dSMykyta Yatsenko if (links[i]) 233*6e1e4a9dSMykyta Yatsenko bpf_link__destroy(links[i]); 234*6e1e4a9dSMykyta Yatsenko } 235*6e1e4a9dSMykyta Yatsenko lru_lock_nmi__destroy(skel); 236*6e1e4a9dSMykyta Yatsenko } 237*6e1e4a9dSMykyta Yatsenko 238*6e1e4a9dSMykyta Yatsenko void serial_test_lru_lock_nmi(void) 239*6e1e4a9dSMykyta Yatsenko { 240*6e1e4a9dSMykyta Yatsenko run_variant(BPF_MAP_TYPE_LRU_HASH, 0, "common_lru"); 241*6e1e4a9dSMykyta Yatsenko run_variant(BPF_MAP_TYPE_LRU_HASH, BPF_F_NO_COMMON_LRU, "no_common_lru"); 242*6e1e4a9dSMykyta Yatsenko run_variant(BPF_MAP_TYPE_LRU_PERCPU_HASH, 0, "percpu_lru"); 243*6e1e4a9dSMykyta Yatsenko } 244