Skip to content

Commit 2026709

Browse files
committed
selftests/bpf: Add test case for atomic update of fd htab
JIRA: https://issues.redhat.com/browse/RHEL-78203 commit 7c6fb1c Author: Hou Tao <[email protected]> Date: Tue Apr 1 14:22:50 2025 +0800 selftests/bpf: Add test case for atomic update of fd htab Add a test case to verify the atomic update of existing elements in the htab of maps. The test proceeds in three steps: 1) fill the outer map with keys in the range [0, 8] For each inner array map, the value of its first element is set as the key used to lookup the inner map. 2) create 16 threads to lookup these keys concurrently Each lookup thread first lookups the inner map, then it checks whether the first value of the inner array map is the same as the key used to lookup the inner map. 3) create 8 threads to overwrite these keys concurrently Each update thread first creates an inner array, it sets the first value of the array to the key used to update the outer map, then it uses the key and the inner map to update the outer map. Without atomic update support, the lookup operation may return -ENOENT during the lookup of outer map, or return -EINVAL during the comparison of the first value in the inner map and the key used for inner map, and the test will fail. After the atomic update change, both the lookup and the comparison will succeed. Given that the update of outer map is slow, the test case sets the loop number for each thread as 5 to reduce the total running time. However, the loop number could also be adjusted through FD_HTAB_LOOP_NR environment variable. Acked-by: Andrii Nakryiko <[email protected]> Signed-off-by: Hou Tao <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]> Signed-off-by: Viktor Malik <[email protected]>
1 parent 88a88f7 commit 2026709

File tree

2 files changed

+217
-0
lines changed

2 files changed

+217
-0
lines changed
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (C) 2025. Huawei Technologies Co., Ltd */
3+
#define _GNU_SOURCE
4+
#include <stdbool.h>
5+
#include <test_progs.h>
6+
#include "fd_htab_lookup.skel.h"
7+
8+
struct htab_op_ctx {
9+
int fd;
10+
int loop;
11+
unsigned int entries;
12+
bool stop;
13+
};
14+
15+
#define ERR_TO_RETVAL(where, err) ((void *)(long)(((where) << 12) | (-err)))
16+
17+
static void *htab_lookup_fn(void *arg)
18+
{
19+
struct htab_op_ctx *ctx = arg;
20+
int i = 0;
21+
22+
while (i++ < ctx->loop && !ctx->stop) {
23+
unsigned int j;
24+
25+
for (j = 0; j < ctx->entries; j++) {
26+
unsigned int key = j, zero = 0, value;
27+
int inner_fd, err;
28+
29+
err = bpf_map_lookup_elem(ctx->fd, &key, &value);
30+
if (err) {
31+
ctx->stop = true;
32+
return ERR_TO_RETVAL(1, err);
33+
}
34+
35+
inner_fd = bpf_map_get_fd_by_id(value);
36+
if (inner_fd < 0) {
37+
/* The old map has been freed */
38+
if (inner_fd == -ENOENT)
39+
continue;
40+
ctx->stop = true;
41+
return ERR_TO_RETVAL(2, inner_fd);
42+
}
43+
44+
err = bpf_map_lookup_elem(inner_fd, &zero, &value);
45+
if (err) {
46+
close(inner_fd);
47+
ctx->stop = true;
48+
return ERR_TO_RETVAL(3, err);
49+
}
50+
close(inner_fd);
51+
52+
if (value != key) {
53+
ctx->stop = true;
54+
return ERR_TO_RETVAL(4, -EINVAL);
55+
}
56+
}
57+
}
58+
59+
return NULL;
60+
}
61+
62+
static void *htab_update_fn(void *arg)
63+
{
64+
struct htab_op_ctx *ctx = arg;
65+
int i = 0;
66+
67+
while (i++ < ctx->loop && !ctx->stop) {
68+
unsigned int j;
69+
70+
for (j = 0; j < ctx->entries; j++) {
71+
unsigned int key = j, zero = 0;
72+
int inner_fd, err;
73+
74+
inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, 4, 4, 1, NULL);
75+
if (inner_fd < 0) {
76+
ctx->stop = true;
77+
return ERR_TO_RETVAL(1, inner_fd);
78+
}
79+
80+
err = bpf_map_update_elem(inner_fd, &zero, &key, 0);
81+
if (err) {
82+
close(inner_fd);
83+
ctx->stop = true;
84+
return ERR_TO_RETVAL(2, err);
85+
}
86+
87+
err = bpf_map_update_elem(ctx->fd, &key, &inner_fd, BPF_EXIST);
88+
if (err) {
89+
close(inner_fd);
90+
ctx->stop = true;
91+
return ERR_TO_RETVAL(3, err);
92+
}
93+
close(inner_fd);
94+
}
95+
}
96+
97+
return NULL;
98+
}
99+
100+
static int setup_htab(int fd, unsigned int entries)
101+
{
102+
unsigned int i;
103+
104+
for (i = 0; i < entries; i++) {
105+
unsigned int key = i, zero = 0;
106+
int inner_fd, err;
107+
108+
inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, 4, 4, 1, NULL);
109+
if (!ASSERT_OK_FD(inner_fd, "new array"))
110+
return -1;
111+
112+
err = bpf_map_update_elem(inner_fd, &zero, &key, 0);
113+
if (!ASSERT_OK(err, "init array")) {
114+
close(inner_fd);
115+
return -1;
116+
}
117+
118+
err = bpf_map_update_elem(fd, &key, &inner_fd, 0);
119+
if (!ASSERT_OK(err, "init outer")) {
120+
close(inner_fd);
121+
return -1;
122+
}
123+
close(inner_fd);
124+
}
125+
126+
return 0;
127+
}
128+
129+
static int get_int_from_env(const char *name, int dft)
130+
{
131+
const char *value;
132+
133+
value = getenv(name);
134+
if (!value)
135+
return dft;
136+
137+
return atoi(value);
138+
}
139+
140+
void test_fd_htab_lookup(void)
141+
{
142+
unsigned int i, wr_nr = 8, rd_nr = 16;
143+
pthread_t tids[wr_nr + rd_nr];
144+
struct fd_htab_lookup *skel;
145+
struct htab_op_ctx ctx;
146+
int err;
147+
148+
skel = fd_htab_lookup__open_and_load();
149+
if (!ASSERT_OK_PTR(skel, "fd_htab_lookup__open_and_load"))
150+
return;
151+
152+
ctx.fd = bpf_map__fd(skel->maps.outer_map);
153+
ctx.loop = get_int_from_env("FD_HTAB_LOOP_NR", 5);
154+
ctx.stop = false;
155+
ctx.entries = 8;
156+
157+
err = setup_htab(ctx.fd, ctx.entries);
158+
if (err)
159+
goto destroy;
160+
161+
memset(tids, 0, sizeof(tids));
162+
for (i = 0; i < wr_nr; i++) {
163+
err = pthread_create(&tids[i], NULL, htab_update_fn, &ctx);
164+
if (!ASSERT_OK(err, "pthread_create")) {
165+
ctx.stop = true;
166+
goto reap;
167+
}
168+
}
169+
for (i = 0; i < rd_nr; i++) {
170+
err = pthread_create(&tids[i + wr_nr], NULL, htab_lookup_fn, &ctx);
171+
if (!ASSERT_OK(err, "pthread_create")) {
172+
ctx.stop = true;
173+
goto reap;
174+
}
175+
}
176+
177+
reap:
178+
for (i = 0; i < wr_nr + rd_nr; i++) {
179+
void *ret = NULL;
180+
char desc[32];
181+
182+
if (!tids[i])
183+
continue;
184+
185+
snprintf(desc, sizeof(desc), "thread %u", i + 1);
186+
err = pthread_join(tids[i], &ret);
187+
ASSERT_OK(err, desc);
188+
ASSERT_EQ(ret, NULL, desc);
189+
}
190+
destroy:
191+
fd_htab_lookup__destroy(skel);
192+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (C) 2025. Huawei Technologies Co., Ltd */
3+
#include <linux/bpf.h>
4+
#include <bpf/bpf_helpers.h>
5+
6+
char _license[] SEC("license") = "GPL";
7+
8+
struct inner_map_type {
9+
__uint(type, BPF_MAP_TYPE_ARRAY);
10+
__uint(key_size, 4);
11+
__uint(value_size, 4);
12+
__uint(max_entries, 1);
13+
} inner_map SEC(".maps");
14+
15+
struct {
16+
__uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
17+
__uint(max_entries, 64);
18+
__type(key, int);
19+
__type(value, int);
20+
__array(values, struct inner_map_type);
21+
} outer_map SEC(".maps") = {
22+
.values = {
23+
[0] = &inner_map,
24+
},
25+
};

0 commit comments

Comments
 (0)