1 /*
2  * Testsuite for eBPF maps
3  *
4  * Copyright (c) 2014 PLUMgrid, http://plumgrid.com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2 of the GNU General Public
8  * License as published by the Free Software Foundation.
9  */
10 #include <stdio.h>
11 #include <unistd.h>
12 #include <linux/bpf.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <assert.h>
16 #include <sys/wait.h>
17 #include <stdlib.h>
18 #include "libbpf.h"
19 
20 /* sanity tests for map API */
test_hashmap_sanity(int i,void * data)21 static void test_hashmap_sanity(int i, void *data)
22 {
23 	long long key, next_key, value;
24 	int map_fd;
25 
26 	map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), 2);
27 	if (map_fd < 0) {
28 		printf("failed to create hashmap '%s'\n", strerror(errno));
29 		exit(1);
30 	}
31 
32 	key = 1;
33 	value = 1234;
34 	/* insert key=1 element */
35 	assert(bpf_update_elem(map_fd, &key, &value, BPF_ANY) == 0);
36 
37 	value = 0;
38 	/* BPF_NOEXIST means: add new element if it doesn't exist */
39 	assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
40 	       /* key=1 already exists */
41 	       errno == EEXIST);
42 
43 	assert(bpf_update_elem(map_fd, &key, &value, -1) == -1 && errno == EINVAL);
44 
45 	/* check that key=1 can be found */
46 	assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 1234);
47 
48 	key = 2;
49 	/* check that key=2 is not found */
50 	assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT);
51 
52 	/* BPF_EXIST means: update existing element */
53 	assert(bpf_update_elem(map_fd, &key, &value, BPF_EXIST) == -1 &&
54 	       /* key=2 is not there */
55 	       errno == ENOENT);
56 
57 	/* insert key=2 element */
58 	assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0);
59 
60 	/* key=1 and key=2 were inserted, check that key=0 cannot be inserted
61 	 * due to max_entries limit
62 	 */
63 	key = 0;
64 	assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
65 	       errno == E2BIG);
66 
67 	/* check that key = 0 doesn't exist */
68 	assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT);
69 
70 	/* iterate over two elements */
71 	assert(bpf_get_next_key(map_fd, &key, &next_key) == 0 &&
72 	       (next_key == 1 || next_key == 2));
73 	assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 &&
74 	       (next_key == 1 || next_key == 2));
75 	assert(bpf_get_next_key(map_fd, &next_key, &next_key) == -1 &&
76 	       errno == ENOENT);
77 
78 	/* delete both elements */
79 	key = 1;
80 	assert(bpf_delete_elem(map_fd, &key) == 0);
81 	key = 2;
82 	assert(bpf_delete_elem(map_fd, &key) == 0);
83 	assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT);
84 
85 	key = 0;
86 	/* check that map is empty */
87 	assert(bpf_get_next_key(map_fd, &key, &next_key) == -1 &&
88 	       errno == ENOENT);
89 	close(map_fd);
90 }
91 
test_arraymap_sanity(int i,void * data)92 static void test_arraymap_sanity(int i, void *data)
93 {
94 	int key, next_key, map_fd;
95 	long long value;
96 
97 	map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value), 2);
98 	if (map_fd < 0) {
99 		printf("failed to create arraymap '%s'\n", strerror(errno));
100 		exit(1);
101 	}
102 
103 	key = 1;
104 	value = 1234;
105 	/* insert key=1 element */
106 	assert(bpf_update_elem(map_fd, &key, &value, BPF_ANY) == 0);
107 
108 	value = 0;
109 	assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
110 	       errno == EEXIST);
111 
112 	/* check that key=1 can be found */
113 	assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 1234);
114 
115 	key = 0;
116 	/* check that key=0 is also found and zero initialized */
117 	assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 0);
118 
119 
120 	/* key=0 and key=1 were inserted, check that key=2 cannot be inserted
121 	 * due to max_entries limit
122 	 */
123 	key = 2;
124 	assert(bpf_update_elem(map_fd, &key, &value, BPF_EXIST) == -1 &&
125 	       errno == E2BIG);
126 
127 	/* check that key = 2 doesn't exist */
128 	assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT);
129 
130 	/* iterate over two elements */
131 	assert(bpf_get_next_key(map_fd, &key, &next_key) == 0 &&
132 	       next_key == 0);
133 	assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 &&
134 	       next_key == 1);
135 	assert(bpf_get_next_key(map_fd, &next_key, &next_key) == -1 &&
136 	       errno == ENOENT);
137 
138 	/* delete shouldn't succeed */
139 	key = 1;
140 	assert(bpf_delete_elem(map_fd, &key) == -1 && errno == EINVAL);
141 
142 	close(map_fd);
143 }
144 
145 #define MAP_SIZE (32 * 1024)
test_map_large(void)146 static void test_map_large(void)
147 {
148 	struct bigkey {
149 		int a;
150 		char b[116];
151 		long long c;
152 	} key;
153 	int map_fd, i, value;
154 
155 	/* allocate 4Mbyte of memory */
156 	map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
157 				MAP_SIZE);
158 	if (map_fd < 0) {
159 		printf("failed to create large map '%s'\n", strerror(errno));
160 		exit(1);
161 	}
162 
163 	for (i = 0; i < MAP_SIZE; i++) {
164 		key = (struct bigkey) {.c = i};
165 		value = i;
166 		assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0);
167 	}
168 	key.c = -1;
169 	assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
170 	       errno == E2BIG);
171 
172 	/* iterate through all elements */
173 	for (i = 0; i < MAP_SIZE; i++)
174 		assert(bpf_get_next_key(map_fd, &key, &key) == 0);
175 	assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT);
176 
177 	key.c = 0;
178 	assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 0);
179 	key.a = 1;
180 	assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT);
181 
182 	close(map_fd);
183 }
184 
185 /* fork N children and wait for them to complete */
run_parallel(int tasks,void (* fn)(int i,void * data),void * data)186 static void run_parallel(int tasks, void (*fn)(int i, void *data), void *data)
187 {
188 	pid_t pid[tasks];
189 	int i;
190 
191 	for (i = 0; i < tasks; i++) {
192 		pid[i] = fork();
193 		if (pid[i] == 0) {
194 			fn(i, data);
195 			exit(0);
196 		} else if (pid[i] == -1) {
197 			printf("couldn't spawn #%d process\n", i);
198 			exit(1);
199 		}
200 	}
201 	for (i = 0; i < tasks; i++) {
202 		int status;
203 
204 		assert(waitpid(pid[i], &status, 0) == pid[i]);
205 		assert(status == 0);
206 	}
207 }
208 
test_map_stress(void)209 static void test_map_stress(void)
210 {
211 	run_parallel(100, test_hashmap_sanity, NULL);
212 	run_parallel(100, test_arraymap_sanity, NULL);
213 }
214 
215 #define TASKS 1024
216 #define DO_UPDATE 1
217 #define DO_DELETE 0
do_work(int fn,void * data)218 static void do_work(int fn, void *data)
219 {
220 	int map_fd = ((int *)data)[0];
221 	int do_update = ((int *)data)[1];
222 	int i;
223 	int key, value;
224 
225 	for (i = fn; i < MAP_SIZE; i += TASKS) {
226 		key = value = i;
227 		if (do_update)
228 			assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0);
229 		else
230 			assert(bpf_delete_elem(map_fd, &key) == 0);
231 	}
232 }
233 
test_map_parallel(void)234 static void test_map_parallel(void)
235 {
236 	int i, map_fd, key = 0, value = 0;
237 	int data[2];
238 
239 	map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
240 				MAP_SIZE);
241 	if (map_fd < 0) {
242 		printf("failed to create map for parallel test '%s'\n",
243 		       strerror(errno));
244 		exit(1);
245 	}
246 
247 	data[0] = map_fd;
248 	data[1] = DO_UPDATE;
249 	/* use the same map_fd in children to add elements to this map
250 	 * child_0 adds key=0, key=1024, key=2048, ...
251 	 * child_1 adds key=1, key=1025, key=2049, ...
252 	 * child_1023 adds key=1023, ...
253 	 */
254 	run_parallel(TASKS, do_work, data);
255 
256 	/* check that key=0 is already there */
257 	assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
258 	       errno == EEXIST);
259 
260 	/* check that all elements were inserted */
261 	key = -1;
262 	for (i = 0; i < MAP_SIZE; i++)
263 		assert(bpf_get_next_key(map_fd, &key, &key) == 0);
264 	assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT);
265 
266 	/* another check for all elements */
267 	for (i = 0; i < MAP_SIZE; i++) {
268 		key = MAP_SIZE - i - 1;
269 		assert(bpf_lookup_elem(map_fd, &key, &value) == 0 &&
270 		       value == key);
271 	}
272 
273 	/* now let's delete all elemenets in parallel */
274 	data[1] = DO_DELETE;
275 	run_parallel(TASKS, do_work, data);
276 
277 	/* nothing should be left */
278 	key = -1;
279 	assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT);
280 }
281 
main(void)282 int main(void)
283 {
284 	test_hashmap_sanity(0, NULL);
285 	test_arraymap_sanity(0, NULL);
286 	test_map_large();
287 	test_map_parallel();
288 	test_map_stress();
289 	printf("test_maps: OK\n");
290 	return 0;
291 }
292