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 */
21static 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
92static 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)
146static 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 */
186static 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
209static 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
218static 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
234static 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
282int 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