xref: /linux/tools/testing/selftests/bpf/prog_tests/lru_lock_nmi.c (revision b2128290c29902315e632ea59e0504d6bc9e9b42)
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