Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
9struct htab_op_ctx {
10 int fd;
11 int loop;
12 bool stop;
13};
14
15struct htab_val {
16 unsigned int lock;
17 unsigned int data;
18};
19
20static 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
37static 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
62static 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
94reap:
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
111struct htab_val_large {
112 struct bpf_spin_lock lock;
113 __u32 seq;
114 __u64 data[256];
115};
116
117struct consistency_ctx {
118 int fd;
119 int start_fd;
120 int loop;
121 volatile bool torn_write;
122};
123
124static void wait_for_start(int fd)
125{
126 char buf;
127
128 read(fd, &buf, 1);
129}
130
131static 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. */
151static 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
171static 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
195static 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
245stop:
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
254close_pipe:
255 if (pipefd[1] >= 0)
256 close(pipefd[1]);
257 close(pipefd[0]);
258out:
259 htab_reuse__destroy(skel);
260}
261
262void 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}