Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.17.
  1/*
  2 * Testsuite for eBPF maps
  3 *
  4 * Copyright (c) 2014 PLUMgrid, http://plumgrid.com
  5 * Copyright (c) 2016 Facebook
  6 *
  7 * This program is free software; you can redistribute it and/or
  8 * modify it under the terms of version 2 of the GNU General Public
  9 * License as published by the Free Software Foundation.
 10 */
 11#include <stdio.h>
 12#include <unistd.h>
 13#include <linux/bpf.h>
 14#include <errno.h>
 15#include <string.h>
 16#include <assert.h>
 17#include <sys/wait.h>
 18#include <stdlib.h>
 19#include "libbpf.h"
 20
 21static int map_flags;
 22
 23/* sanity tests for map API */
 24static void test_hashmap_sanity(int i, void *data)
 25{
 26	long long key, next_key, value;
 27	int map_fd;
 28
 29	map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
 30				2, map_flags);
 31	if (map_fd < 0) {
 32		printf("failed to create hashmap '%s'\n", strerror(errno));
 33		exit(1);
 34	}
 35
 36	key = 1;
 37	value = 1234;
 38	/* insert key=1 element */
 39	assert(bpf_update_elem(map_fd, &key, &value, BPF_ANY) == 0);
 40
 41	value = 0;
 42	/* BPF_NOEXIST means: add new element if it doesn't exist */
 43	assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
 44	       /* key=1 already exists */
 45	       errno == EEXIST);
 46
 47	assert(bpf_update_elem(map_fd, &key, &value, -1) == -1 && errno == EINVAL);
 48
 49	/* check that key=1 can be found */
 50	assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 1234);
 51
 52	key = 2;
 53	/* check that key=2 is not found */
 54	assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT);
 55
 56	/* BPF_EXIST means: update existing element */
 57	assert(bpf_update_elem(map_fd, &key, &value, BPF_EXIST) == -1 &&
 58	       /* key=2 is not there */
 59	       errno == ENOENT);
 60
 61	/* insert key=2 element */
 62	assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0);
 63
 64	/* key=1 and key=2 were inserted, check that key=0 cannot be inserted
 65	 * due to max_entries limit
 66	 */
 67	key = 0;
 68	assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
 69	       errno == E2BIG);
 70
 71	/* check that key = 0 doesn't exist */
 72	assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT);
 73
 74	/* iterate over two elements */
 75	assert(bpf_get_next_key(map_fd, &key, &next_key) == 0 &&
 76	       (next_key == 1 || next_key == 2));
 77	assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 &&
 78	       (next_key == 1 || next_key == 2));
 79	assert(bpf_get_next_key(map_fd, &next_key, &next_key) == -1 &&
 80	       errno == ENOENT);
 81
 82	/* delete both elements */
 83	key = 1;
 84	assert(bpf_delete_elem(map_fd, &key) == 0);
 85	key = 2;
 86	assert(bpf_delete_elem(map_fd, &key) == 0);
 87	assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT);
 88
 89	key = 0;
 90	/* check that map is empty */
 91	assert(bpf_get_next_key(map_fd, &key, &next_key) == -1 &&
 92	       errno == ENOENT);
 93	close(map_fd);
 94}
 95
 96/* sanity tests for percpu map API */
 97static void test_percpu_hashmap_sanity(int task, void *data)
 98{
 99	long long key, next_key;
100	int expected_key_mask = 0;
101	unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
102	long long value[nr_cpus];
103	int map_fd, i;
104
105	map_fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_HASH, sizeof(key),
106				sizeof(value[0]), 2, map_flags);
107	if (map_fd < 0) {
108		printf("failed to create hashmap '%s'\n", strerror(errno));
109		exit(1);
110	}
111
112	for (i = 0; i < nr_cpus; i++)
113		value[i] = i + 100;
114	key = 1;
115	/* insert key=1 element */
116	assert(!(expected_key_mask & key));
117	assert(bpf_update_elem(map_fd, &key, value, BPF_ANY) == 0);
118	expected_key_mask |= key;
119
120	/* BPF_NOEXIST means: add new element if it doesn't exist */
121	assert(bpf_update_elem(map_fd, &key, value, BPF_NOEXIST) == -1 &&
122	       /* key=1 already exists */
123	       errno == EEXIST);
124
125	/* -1 is an invalid flag */
126	assert(bpf_update_elem(map_fd, &key, value, -1) == -1 &&
127	       errno == EINVAL);
128
129	/* check that key=1 can be found. value could be 0 if the lookup
130	 * was run from a different cpu.
131	 */
132	value[0] = 1;
133	assert(bpf_lookup_elem(map_fd, &key, value) == 0 && value[0] == 100);
134
135	key = 2;
136	/* check that key=2 is not found */
137	assert(bpf_lookup_elem(map_fd, &key, value) == -1 && errno == ENOENT);
138
139	/* BPF_EXIST means: update existing element */
140	assert(bpf_update_elem(map_fd, &key, value, BPF_EXIST) == -1 &&
141	       /* key=2 is not there */
142	       errno == ENOENT);
143
144	/* insert key=2 element */
145	assert(!(expected_key_mask & key));
146	assert(bpf_update_elem(map_fd, &key, value, BPF_NOEXIST) == 0);
147	expected_key_mask |= key;
148
149	/* key=1 and key=2 were inserted, check that key=0 cannot be inserted
150	 * due to max_entries limit
151	 */
152	key = 0;
153	assert(bpf_update_elem(map_fd, &key, value, BPF_NOEXIST) == -1 &&
154	       errno == E2BIG);
155
156	/* check that key = 0 doesn't exist */
157	assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT);
158
159	/* iterate over two elements */
160	while (!bpf_get_next_key(map_fd, &key, &next_key)) {
161		assert((expected_key_mask & next_key) == next_key);
162		expected_key_mask &= ~next_key;
163
164		assert(bpf_lookup_elem(map_fd, &next_key, value) == 0);
165		for (i = 0; i < nr_cpus; i++)
166			assert(value[i] == i + 100);
167
168		key = next_key;
169	}
170	assert(errno == ENOENT);
171
172	/* Update with BPF_EXIST */
173	key = 1;
174	assert(bpf_update_elem(map_fd, &key, value, BPF_EXIST) == 0);
175
176	/* delete both elements */
177	key = 1;
178	assert(bpf_delete_elem(map_fd, &key) == 0);
179	key = 2;
180	assert(bpf_delete_elem(map_fd, &key) == 0);
181	assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT);
182
183	key = 0;
184	/* check that map is empty */
185	assert(bpf_get_next_key(map_fd, &key, &next_key) == -1 &&
186	       errno == ENOENT);
187	close(map_fd);
188}
189
190static void test_arraymap_sanity(int i, void *data)
191{
192	int key, next_key, map_fd;
193	long long value;
194
195	map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value),
196				2, 0);
197	if (map_fd < 0) {
198		printf("failed to create arraymap '%s'\n", strerror(errno));
199		exit(1);
200	}
201
202	key = 1;
203	value = 1234;
204	/* insert key=1 element */
205	assert(bpf_update_elem(map_fd, &key, &value, BPF_ANY) == 0);
206
207	value = 0;
208	assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
209	       errno == EEXIST);
210
211	/* check that key=1 can be found */
212	assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 1234);
213
214	key = 0;
215	/* check that key=0 is also found and zero initialized */
216	assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 0);
217
218
219	/* key=0 and key=1 were inserted, check that key=2 cannot be inserted
220	 * due to max_entries limit
221	 */
222	key = 2;
223	assert(bpf_update_elem(map_fd, &key, &value, BPF_EXIST) == -1 &&
224	       errno == E2BIG);
225
226	/* check that key = 2 doesn't exist */
227	assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT);
228
229	/* iterate over two elements */
230	assert(bpf_get_next_key(map_fd, &key, &next_key) == 0 &&
231	       next_key == 0);
232	assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 &&
233	       next_key == 1);
234	assert(bpf_get_next_key(map_fd, &next_key, &next_key) == -1 &&
235	       errno == ENOENT);
236
237	/* delete shouldn't succeed */
238	key = 1;
239	assert(bpf_delete_elem(map_fd, &key) == -1 && errno == EINVAL);
240
241	close(map_fd);
242}
243
244static void test_percpu_arraymap_many_keys(void)
245{
246	unsigned nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
247	unsigned nr_keys = 20000;
248	long values[nr_cpus];
249	int key, map_fd, i;
250
251	map_fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key),
252				sizeof(values[0]), nr_keys, 0);
253	if (map_fd < 0) {
254		printf("failed to create per-cpu arraymap '%s'\n",
255		       strerror(errno));
256		exit(1);
257	}
258
259	for (i = 0; i < nr_cpus; i++)
260		values[i] = i + 10;
261
262	for (key = 0; key < nr_keys; key++)
263		assert(bpf_update_elem(map_fd, &key, values, BPF_ANY) == 0);
264
265	for (key = 0; key < nr_keys; key++) {
266		for (i = 0; i < nr_cpus; i++)
267			values[i] = 0;
268		assert(bpf_lookup_elem(map_fd, &key, values) == 0);
269		for (i = 0; i < nr_cpus; i++)
270			assert(values[i] == i + 10);
271	}
272
273	close(map_fd);
274}
275
276static void test_percpu_arraymap_sanity(int i, void *data)
277{
278	unsigned nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
279	long values[nr_cpus];
280	int key, next_key, map_fd;
281
282	map_fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key),
283				sizeof(values[0]), 2, 0);
284	if (map_fd < 0) {
285		printf("failed to create arraymap '%s'\n", strerror(errno));
286		exit(1);
287	}
288
289	for (i = 0; i < nr_cpus; i++)
290		values[i] = i + 100;
291
292	key = 1;
293	/* insert key=1 element */
294	assert(bpf_update_elem(map_fd, &key, values, BPF_ANY) == 0);
295
296	values[0] = 0;
297	assert(bpf_update_elem(map_fd, &key, values, BPF_NOEXIST) == -1 &&
298	       errno == EEXIST);
299
300	/* check that key=1 can be found */
301	assert(bpf_lookup_elem(map_fd, &key, values) == 0 && values[0] == 100);
302
303	key = 0;
304	/* check that key=0 is also found and zero initialized */
305	assert(bpf_lookup_elem(map_fd, &key, values) == 0 &&
306	       values[0] == 0 && values[nr_cpus - 1] == 0);
307
308
309	/* check that key=2 cannot be inserted due to max_entries limit */
310	key = 2;
311	assert(bpf_update_elem(map_fd, &key, values, BPF_EXIST) == -1 &&
312	       errno == E2BIG);
313
314	/* check that key = 2 doesn't exist */
315	assert(bpf_lookup_elem(map_fd, &key, values) == -1 && errno == ENOENT);
316
317	/* iterate over two elements */
318	assert(bpf_get_next_key(map_fd, &key, &next_key) == 0 &&
319	       next_key == 0);
320	assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 &&
321	       next_key == 1);
322	assert(bpf_get_next_key(map_fd, &next_key, &next_key) == -1 &&
323	       errno == ENOENT);
324
325	/* delete shouldn't succeed */
326	key = 1;
327	assert(bpf_delete_elem(map_fd, &key) == -1 && errno == EINVAL);
328
329	close(map_fd);
330}
331
332#define MAP_SIZE (32 * 1024)
333static void test_map_large(void)
334{
335	struct bigkey {
336		int a;
337		char b[116];
338		long long c;
339	} key;
340	int map_fd, i, value;
341
342	/* allocate 4Mbyte of memory */
343	map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
344				MAP_SIZE, map_flags);
345	if (map_fd < 0) {
346		printf("failed to create large map '%s'\n", strerror(errno));
347		exit(1);
348	}
349
350	for (i = 0; i < MAP_SIZE; i++) {
351		key = (struct bigkey) {.c = i};
352		value = i;
353		assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0);
354	}
355	key.c = -1;
356	assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
357	       errno == E2BIG);
358
359	/* iterate through all elements */
360	for (i = 0; i < MAP_SIZE; i++)
361		assert(bpf_get_next_key(map_fd, &key, &key) == 0);
362	assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT);
363
364	key.c = 0;
365	assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 0);
366	key.a = 1;
367	assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT);
368
369	close(map_fd);
370}
371
372/* fork N children and wait for them to complete */
373static void run_parallel(int tasks, void (*fn)(int i, void *data), void *data)
374{
375	pid_t pid[tasks];
376	int i;
377
378	for (i = 0; i < tasks; i++) {
379		pid[i] = fork();
380		if (pid[i] == 0) {
381			fn(i, data);
382			exit(0);
383		} else if (pid[i] == -1) {
384			printf("couldn't spawn #%d process\n", i);
385			exit(1);
386		}
387	}
388	for (i = 0; i < tasks; i++) {
389		int status;
390
391		assert(waitpid(pid[i], &status, 0) == pid[i]);
392		assert(status == 0);
393	}
394}
395
396static void test_map_stress(void)
397{
398	run_parallel(100, test_hashmap_sanity, NULL);
399	run_parallel(100, test_percpu_hashmap_sanity, NULL);
400	run_parallel(100, test_arraymap_sanity, NULL);
401	run_parallel(100, test_percpu_arraymap_sanity, NULL);
402}
403
404#define TASKS 1024
405#define DO_UPDATE 1
406#define DO_DELETE 0
407static void do_work(int fn, void *data)
408{
409	int map_fd = ((int *)data)[0];
410	int do_update = ((int *)data)[1];
411	int i;
412	int key, value;
413
414	for (i = fn; i < MAP_SIZE; i += TASKS) {
415		key = value = i;
416		if (do_update)
417			assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0);
418		else
419			assert(bpf_delete_elem(map_fd, &key) == 0);
420	}
421}
422
423static void test_map_parallel(void)
424{
425	int i, map_fd, key = 0, value = 0;
426	int data[2];
427
428	map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
429				MAP_SIZE, map_flags);
430	if (map_fd < 0) {
431		printf("failed to create map for parallel test '%s'\n",
432		       strerror(errno));
433		exit(1);
434	}
435
436	data[0] = map_fd;
437	data[1] = DO_UPDATE;
438	/* use the same map_fd in children to add elements to this map
439	 * child_0 adds key=0, key=1024, key=2048, ...
440	 * child_1 adds key=1, key=1025, key=2049, ...
441	 * child_1023 adds key=1023, ...
442	 */
443	run_parallel(TASKS, do_work, data);
444
445	/* check that key=0 is already there */
446	assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
447	       errno == EEXIST);
448
449	/* check that all elements were inserted */
450	key = -1;
451	for (i = 0; i < MAP_SIZE; i++)
452		assert(bpf_get_next_key(map_fd, &key, &key) == 0);
453	assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT);
454
455	/* another check for all elements */
456	for (i = 0; i < MAP_SIZE; i++) {
457		key = MAP_SIZE - i - 1;
458		assert(bpf_lookup_elem(map_fd, &key, &value) == 0 &&
459		       value == key);
460	}
461
462	/* now let's delete all elemenets in parallel */
463	data[1] = DO_DELETE;
464	run_parallel(TASKS, do_work, data);
465
466	/* nothing should be left */
467	key = -1;
468	assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT);
469}
470
471static void run_all_tests(void)
472{
473	test_hashmap_sanity(0, NULL);
474	test_percpu_hashmap_sanity(0, NULL);
475	test_arraymap_sanity(0, NULL);
476	test_percpu_arraymap_sanity(0, NULL);
477	test_percpu_arraymap_many_keys();
478
479	test_map_large();
480	test_map_parallel();
481	test_map_stress();
482}
483
484int main(void)
485{
486	map_flags = 0;
487	run_all_tests();
488	map_flags = BPF_F_NO_PREALLOC;
489	run_all_tests();
490	printf("test_maps: OK\n");
491	return 0;
492}