xref: /linux/tools/testing/selftests/bpf/prog_tests/fd_htab_lookup.c (revision 25489a4f556414445d342951615178368ee45cde)
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 }
193