xref: /linux/tools/testing/selftests/bpf/prog_tests/htab_reuse.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (C) 2023. Huawei Technologies Co., Ltd */
3 #define _GNU_SOURCE
4 #include <sched.h>
5 #include <stdbool.h>
6 #include <test_progs.h>
7 #include "htab_reuse.skel.h"
8 
9 struct htab_op_ctx {
10 	int fd;
11 	int loop;
12 	bool stop;
13 };
14 
15 struct htab_val {
16 	unsigned int lock;
17 	unsigned int data;
18 };
19 
20 static void *htab_lookup_fn(void *arg)
21 {
22 	struct htab_op_ctx *ctx = arg;
23 	int i = 0;
24 
25 	while (i++ < ctx->loop && !ctx->stop) {
26 		struct htab_val value;
27 		unsigned int key;
28 
29 		/* Use BPF_F_LOCK to use spin-lock in map value. */
30 		key = 7;
31 		bpf_map_lookup_elem_flags(ctx->fd, &key, &value, BPF_F_LOCK);
32 	}
33 
34 	return NULL;
35 }
36 
37 static void *htab_update_fn(void *arg)
38 {
39 	struct htab_op_ctx *ctx = arg;
40 	int i = 0;
41 
42 	while (i++ < ctx->loop && !ctx->stop) {
43 		struct htab_val value;
44 		unsigned int key;
45 
46 		key = 7;
47 		value.lock = 0;
48 		value.data = key;
49 		bpf_map_update_elem(ctx->fd, &key, &value, BPF_F_LOCK);
50 		bpf_map_delete_elem(ctx->fd, &key);
51 
52 		key = 24;
53 		value.lock = 0;
54 		value.data = key;
55 		bpf_map_update_elem(ctx->fd, &key, &value, BPF_F_LOCK);
56 		bpf_map_delete_elem(ctx->fd, &key);
57 	}
58 
59 	return NULL;
60 }
61 
62 static void test_htab_reuse_basic(void)
63 {
64 	unsigned int i, wr_nr = 1, rd_nr = 4;
65 	pthread_t tids[wr_nr + rd_nr];
66 	struct htab_reuse *skel;
67 	struct htab_op_ctx ctx;
68 	int err;
69 
70 	skel = htab_reuse__open_and_load();
71 	if (!ASSERT_OK_PTR(skel, "htab_reuse__open_and_load"))
72 		return;
73 
74 	ctx.fd = bpf_map__fd(skel->maps.htab);
75 	ctx.loop = 500;
76 	ctx.stop = false;
77 
78 	memset(tids, 0, sizeof(tids));
79 	for (i = 0; i < wr_nr; i++) {
80 		err = pthread_create(&tids[i], NULL, htab_update_fn, &ctx);
81 		if (!ASSERT_OK(err, "pthread_create")) {
82 			ctx.stop = true;
83 			goto reap;
84 		}
85 	}
86 	for (i = 0; i < rd_nr; i++) {
87 		err = pthread_create(&tids[i + wr_nr], NULL, htab_lookup_fn, &ctx);
88 		if (!ASSERT_OK(err, "pthread_create")) {
89 			ctx.stop = true;
90 			goto reap;
91 		}
92 	}
93 
94 reap:
95 	for (i = 0; i < wr_nr + rd_nr; i++) {
96 		if (!tids[i])
97 			continue;
98 		pthread_join(tids[i], NULL);
99 	}
100 	htab_reuse__destroy(skel);
101 }
102 
103 /*
104  * Writes consistency test for BPF_F_LOCK update
105  *
106  * The race:
107  *   1. Thread A: BPF_F_LOCK|BPF_EXIST update
108  *   2. Thread B: delete element then update it with BPF_ANY
109  */
110 
111 struct htab_val_large {
112 	struct bpf_spin_lock lock;
113 	__u32 seq;
114 	__u64 data[256];
115 };
116 
117 struct consistency_ctx {
118 	int fd;
119 	int start_fd;
120 	int loop;
121 	volatile bool torn_write;
122 };
123 
124 static void wait_for_start(int fd)
125 {
126 	char buf;
127 
128 	read(fd, &buf, 1);
129 }
130 
131 static void *locked_update_fn(void *arg)
132 {
133 	struct consistency_ctx *ctx = arg;
134 	struct htab_val_large value;
135 	unsigned int key = 1;
136 	int i;
137 
138 	memset(&value, 0xAA, sizeof(value));
139 	wait_for_start(ctx->start_fd);
140 
141 	for (i = 0; i < ctx->loop; i++) {
142 		value.seq = i;
143 		bpf_map_update_elem(ctx->fd, &key, &value,
144 				    BPF_F_LOCK | BPF_EXIST);
145 	}
146 
147 	return NULL;
148 }
149 
150 /* Delete + update: removes the element then re-creates it with BPF_ANY. */
151 static void *delete_update_fn(void *arg)
152 {
153 	struct consistency_ctx *ctx = arg;
154 	struct htab_val_large value;
155 	unsigned int key = 1;
156 	int i;
157 
158 	memset(&value, 0xBB, sizeof(value));
159 
160 	wait_for_start(ctx->start_fd);
161 
162 	for (i = 0; i < ctx->loop; i++) {
163 		value.seq = i;
164 		bpf_map_delete_elem(ctx->fd, &key);
165 		bpf_map_update_elem(ctx->fd, &key, &value, BPF_ANY | BPF_F_LOCK);
166 	}
167 
168 	return NULL;
169 }
170 
171 static void *locked_lookup_fn(void *arg)
172 {
173 	struct consistency_ctx *ctx = arg;
174 	struct htab_val_large value;
175 	unsigned int key = 1;
176 	int i, j;
177 
178 	wait_for_start(ctx->start_fd);
179 
180 	for (i = 0; i < ctx->loop && !ctx->torn_write; i++) {
181 		if (bpf_map_lookup_elem_flags(ctx->fd, &key, &value, BPF_F_LOCK))
182 			continue;
183 
184 		for (j = 0; j < 256; j++) {
185 			if (value.data[j] != value.data[0]) {
186 				ctx->torn_write = true;
187 				return NULL;
188 			}
189 		}
190 	}
191 
192 	return NULL;
193 }
194 
195 static void test_htab_reuse_consistency(void)
196 {
197 	int threads_total = 6, threads = 2;
198 	pthread_t tids[threads_total];
199 	struct consistency_ctx ctx;
200 	struct htab_val_large seed;
201 	struct htab_reuse *skel;
202 	unsigned int key = 1, i;
203 	int pipefd[2];
204 	int err;
205 
206 	skel = htab_reuse__open_and_load();
207 	if (!ASSERT_OK_PTR(skel, "htab_reuse__open_and_load"))
208 		return;
209 
210 	if (!ASSERT_OK(pipe(pipefd), "pipe"))
211 		goto out;
212 
213 	ctx.fd = bpf_map__fd(skel->maps.htab_lock_consistency);
214 	ctx.start_fd = pipefd[0];
215 	ctx.loop = 100000;
216 	ctx.torn_write = false;
217 
218 	/* Seed the element so locked updaters have something to find */
219 	memset(&seed, 0xBB, sizeof(seed));
220 	err = bpf_map_update_elem(ctx.fd, &key, &seed, BPF_ANY);
221 	if (!ASSERT_OK(err, "seed_element"))
222 		goto close_pipe;
223 
224 	memset(tids, 0, sizeof(tids));
225 	for (i = 0; i < threads; i++) {
226 		err = pthread_create(&tids[i], NULL, locked_update_fn, &ctx);
227 		if (!ASSERT_OK(err, "pthread_create"))
228 			goto stop;
229 	}
230 	for (i = 0; i < threads; i++) {
231 		err = pthread_create(&tids[threads + i], NULL, delete_update_fn, &ctx);
232 		if (!ASSERT_OK(err, "pthread_create"))
233 			goto stop;
234 	}
235 	for (i = 0; i < threads; i++) {
236 		err = pthread_create(&tids[threads * 2 + i], NULL, locked_lookup_fn, &ctx);
237 		if (!ASSERT_OK(err, "pthread_create"))
238 			goto stop;
239 	}
240 
241 	/* Release all threads simultaneously */
242 	close(pipefd[1]);
243 	pipefd[1] = -1;
244 
245 stop:
246 	for (i = 0; i < threads_total; i++) {
247 		if (!tids[i])
248 			continue;
249 		pthread_join(tids[i], NULL);
250 	}
251 
252 	ASSERT_FALSE(ctx.torn_write, "no torn writes detected");
253 
254 close_pipe:
255 	if (pipefd[1] >= 0)
256 		close(pipefd[1]);
257 	close(pipefd[0]);
258 out:
259 	htab_reuse__destroy(skel);
260 }
261 
262 void test_htab_reuse(void)
263 {
264 	if (test__start_subtest("basic"))
265 		test_htab_reuse_basic();
266 	if (test__start_subtest("consistency"))
267 		test_htab_reuse_consistency();
268 }
269