test_maps.c 16.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Testsuite for eBPF maps
 *
 * Copyright (c) 2014 PLUMgrid, http://plumgrid.com
 * Copyright (c) 2016 Facebook
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General Public
 * License as published by the Free Software Foundation.
 */

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>

#include <sys/wait.h>
#include <sys/resource.h>

#include <linux/bpf.h>

24
#include <bpf/bpf.h>
25
#include "bpf_util.h"
26 27 28 29 30

static int map_flags;

static void test_hashmap(int task, void *data)
{
31
	long long key, next_key, first_key, value;
32 33
	int fd;

34
	fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
35 36 37 38 39 40 41 42 43
			    2, map_flags);
	if (fd < 0) {
		printf("Failed to create hashmap '%s'!\n", strerror(errno));
		exit(1);
	}

	key = 1;
	value = 1234;
	/* Insert key=1 element. */
44
	assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0);
45 46 47

	value = 0;
	/* BPF_NOEXIST means add new element if it doesn't exist. */
48
	assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 &&
49 50 51 52
	       /* key=1 already exists. */
	       errno == EEXIST);

	/* -1 is an invalid flag. */
53 54
	assert(bpf_map_update_elem(fd, &key, &value, -1) == -1 &&
	       errno == EINVAL);
55 56

	/* Check that key=1 can be found. */
57
	assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 1234);
58 59 60

	key = 2;
	/* Check that key=2 is not found. */
61
	assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT);
62 63

	/* BPF_EXIST means update existing element. */
64
	assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) == -1 &&
65 66 67 68
	       /* key=2 is not there. */
	       errno == ENOENT);

	/* Insert key=2 element. */
69
	assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == 0);
70 71 72 73 74

	/* key=1 and key=2 were inserted, check that key=0 cannot be
	 * inserted due to max_entries limit.
	 */
	key = 0;
75
	assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 &&
76 77 78 79
	       errno == E2BIG);

	/* Update existing element, though the map is full. */
	key = 1;
80
	assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) == 0);
81
	key = 2;
82
	assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0);
83 84 85
	key = 3;
	assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 &&
	       errno == E2BIG);
86 87 88

	/* Check that key = 0 doesn't exist. */
	key = 0;
89
	assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT);
90 91

	/* Iterate over two elements. */
92 93
	assert(bpf_map_get_next_key(fd, NULL, &first_key) == 0 &&
	       (first_key == 1 || first_key == 2));
94
	assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 &&
95
	       (next_key == first_key));
96
	assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 &&
97 98
	       (next_key == 1 || next_key == 2) &&
	       (next_key != first_key));
99
	assert(bpf_map_get_next_key(fd, &next_key, &next_key) == -1 &&
100 101 102 103
	       errno == ENOENT);

	/* Delete both elements. */
	key = 1;
104
	assert(bpf_map_delete_elem(fd, &key) == 0);
105
	key = 2;
106 107
	assert(bpf_map_delete_elem(fd, &key) == 0);
	assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT);
108 109 110

	key = 0;
	/* Check that map is empty. */
111 112
	assert(bpf_map_get_next_key(fd, NULL, &next_key) == -1 &&
	       errno == ENOENT);
113
	assert(bpf_map_get_next_key(fd, &key, &next_key) == -1 &&
114 115 116 117 118
	       errno == ENOENT);

	close(fd);
}

119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
static void test_hashmap_sizes(int task, void *data)
{
	int fd, i, j;

	for (i = 1; i <= 512; i <<= 1)
		for (j = 1; j <= 1 << 18; j <<= 1) {
			fd = bpf_create_map(BPF_MAP_TYPE_HASH, i, j,
					    2, map_flags);
			if (fd < 0) {
				printf("Failed to create hashmap key=%d value=%d '%s'\n",
				       i, j, strerror(errno));
				exit(1);
			}
			close(fd);
			usleep(10); /* give kernel time to destroy */
		}
}

137 138
static void test_hashmap_percpu(int task, void *data)
{
139
	unsigned int nr_cpus = bpf_num_possible_cpus();
140
	BPF_DECLARE_PERCPU(long, value);
141
	long long key, next_key, first_key;
142 143 144
	int expected_key_mask = 0;
	int fd, i;

145
	fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_HASH, sizeof(key),
146
			    sizeof(bpf_percpu(value, 0)), 2, map_flags);
147 148 149 150 151 152
	if (fd < 0) {
		printf("Failed to create hashmap '%s'!\n", strerror(errno));
		exit(1);
	}

	for (i = 0; i < nr_cpus; i++)
153
		bpf_percpu(value, i) = i + 100;
154 155 156 157

	key = 1;
	/* Insert key=1 element. */
	assert(!(expected_key_mask & key));
158
	assert(bpf_map_update_elem(fd, &key, value, BPF_ANY) == 0);
159 160 161
	expected_key_mask |= key;

	/* BPF_NOEXIST means add new element if it doesn't exist. */
162
	assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == -1 &&
163 164 165 166
	       /* key=1 already exists. */
	       errno == EEXIST);

	/* -1 is an invalid flag. */
167 168
	assert(bpf_map_update_elem(fd, &key, value, -1) == -1 &&
	       errno == EINVAL);
169 170 171 172

	/* Check that key=1 can be found. Value could be 0 if the lookup
	 * was run from a different CPU.
	 */
173 174 175
	bpf_percpu(value, 0) = 1;
	assert(bpf_map_lookup_elem(fd, &key, value) == 0 &&
	       bpf_percpu(value, 0) == 100);
176 177 178

	key = 2;
	/* Check that key=2 is not found. */
179
	assert(bpf_map_lookup_elem(fd, &key, value) == -1 && errno == ENOENT);
180 181

	/* BPF_EXIST means update existing element. */
182
	assert(bpf_map_update_elem(fd, &key, value, BPF_EXIST) == -1 &&
183 184 185 186 187
	       /* key=2 is not there. */
	       errno == ENOENT);

	/* Insert key=2 element. */
	assert(!(expected_key_mask & key));
188
	assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == 0);
189 190 191 192 193 194
	expected_key_mask |= key;

	/* key=1 and key=2 were inserted, check that key=0 cannot be
	 * inserted due to max_entries limit.
	 */
	key = 0;
195
	assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == -1 &&
196 197 198
	       errno == E2BIG);

	/* Check that key = 0 doesn't exist. */
199
	assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT);
200 201

	/* Iterate over two elements. */
202 203
	assert(bpf_map_get_next_key(fd, NULL, &first_key) == 0 &&
	       ((expected_key_mask & first_key) == first_key));
204
	while (!bpf_map_get_next_key(fd, &key, &next_key)) {
205 206 207 208
		if (first_key) {
			assert(next_key == first_key);
			first_key = 0;
		}
209 210 211
		assert((expected_key_mask & next_key) == next_key);
		expected_key_mask &= ~next_key;

212
		assert(bpf_map_lookup_elem(fd, &next_key, value) == 0);
213 214

		for (i = 0; i < nr_cpus; i++)
215
			assert(bpf_percpu(value, i) == i + 100);
216 217 218 219 220 221 222

		key = next_key;
	}
	assert(errno == ENOENT);

	/* Update with BPF_EXIST. */
	key = 1;
223
	assert(bpf_map_update_elem(fd, &key, value, BPF_EXIST) == 0);
224 225 226

	/* Delete both elements. */
	key = 1;
227
	assert(bpf_map_delete_elem(fd, &key) == 0);
228
	key = 2;
229 230
	assert(bpf_map_delete_elem(fd, &key) == 0);
	assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT);
231 232 233

	key = 0;
	/* Check that map is empty. */
234 235
	assert(bpf_map_get_next_key(fd, NULL, &next_key) == -1 &&
	       errno == ENOENT);
236
	assert(bpf_map_get_next_key(fd, &key, &next_key) == -1 &&
237 238 239 240 241
	       errno == ENOENT);

	close(fd);
}

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
static void test_hashmap_walk(int task, void *data)
{
	int fd, i, max_entries = 100000;
	long long key, value, next_key;
	bool next_key_valid = true;

	fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
			    max_entries, map_flags);
	if (fd < 0) {
		printf("Failed to create hashmap '%s'!\n", strerror(errno));
		exit(1);
	}

	for (i = 0; i < max_entries; i++) {
		key = i; value = key;
		assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == 0);
	}

	for (i = 0; bpf_map_get_next_key(fd, !i ? NULL : &key,
					 &next_key) == 0; i++) {
		key = next_key;
		assert(bpf_map_lookup_elem(fd, &key, &value) == 0);
	}

	assert(i == max_entries);

	assert(bpf_map_get_next_key(fd, NULL, &key) == 0);
	for (i = 0; next_key_valid; i++) {
		next_key_valid = bpf_map_get_next_key(fd, &key, &next_key) == 0;
		assert(bpf_map_lookup_elem(fd, &key, &value) == 0);
		value++;
		assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) == 0);
		key = next_key;
	}

	assert(i == max_entries);

	for (i = 0; bpf_map_get_next_key(fd, !i ? NULL : &key,
					 &next_key) == 0; i++) {
		key = next_key;
		assert(bpf_map_lookup_elem(fd, &key, &value) == 0);
		assert(value - 1 == key);
	}

	assert(i == max_entries);
	close(fd);
}

290 291 292 293 294
static void test_arraymap(int task, void *data)
{
	int key, next_key, fd;
	long long value;

295
	fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value),
296 297 298 299 300 301 302 303 304
			    2, 0);
	if (fd < 0) {
		printf("Failed to create arraymap '%s'!\n", strerror(errno));
		exit(1);
	}

	key = 1;
	value = 1234;
	/* Insert key=1 element. */
305
	assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0);
306 307

	value = 0;
308
	assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 &&
309 310 311
	       errno == EEXIST);

	/* Check that key=1 can be found. */
312
	assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 1234);
313 314 315

	key = 0;
	/* Check that key=0 is also found and zero initialized. */
316
	assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 0);
317 318 319 320 321

	/* key=0 and key=1 were inserted, check that key=2 cannot be inserted
	 * due to max_entries limit.
	 */
	key = 2;
322
	assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) == -1 &&
323 324 325
	       errno == E2BIG);

	/* Check that key = 2 doesn't exist. */
326
	assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT);
327 328

	/* Iterate over two elements. */
329 330
	assert(bpf_map_get_next_key(fd, NULL, &next_key) == 0 &&
	       next_key == 0);
331
	assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 &&
332
	       next_key == 0);
333
	assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 &&
334
	       next_key == 1);
335
	assert(bpf_map_get_next_key(fd, &next_key, &next_key) == -1 &&
336 337 338 339
	       errno == ENOENT);

	/* Delete shouldn't succeed. */
	key = 1;
340
	assert(bpf_map_delete_elem(fd, &key) == -1 && errno == EINVAL);
341 342 343 344 345 346

	close(fd);
}

static void test_arraymap_percpu(int task, void *data)
{
347
	unsigned int nr_cpus = bpf_num_possible_cpus();
348
	BPF_DECLARE_PERCPU(long, values);
349 350
	int key, next_key, fd, i;

351
	fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key),
352
			    sizeof(bpf_percpu(values, 0)), 2, 0);
353 354 355 356 357 358
	if (fd < 0) {
		printf("Failed to create arraymap '%s'!\n", strerror(errno));
		exit(1);
	}

	for (i = 0; i < nr_cpus; i++)
359
		bpf_percpu(values, i) = i + 100;
360 361 362

	key = 1;
	/* Insert key=1 element. */
363
	assert(bpf_map_update_elem(fd, &key, values, BPF_ANY) == 0);
364

365
	bpf_percpu(values, 0) = 0;
366
	assert(bpf_map_update_elem(fd, &key, values, BPF_NOEXIST) == -1 &&
367 368 369
	       errno == EEXIST);

	/* Check that key=1 can be found. */
370 371
	assert(bpf_map_lookup_elem(fd, &key, values) == 0 &&
	       bpf_percpu(values, 0) == 100);
372 373 374

	key = 0;
	/* Check that key=0 is also found and zero initialized. */
375
	assert(bpf_map_lookup_elem(fd, &key, values) == 0 &&
376 377
	       bpf_percpu(values, 0) == 0 &&
	       bpf_percpu(values, nr_cpus - 1) == 0);
378 379 380

	/* Check that key=2 cannot be inserted due to max_entries limit. */
	key = 2;
381
	assert(bpf_map_update_elem(fd, &key, values, BPF_EXIST) == -1 &&
382 383 384
	       errno == E2BIG);

	/* Check that key = 2 doesn't exist. */
385
	assert(bpf_map_lookup_elem(fd, &key, values) == -1 && errno == ENOENT);
386 387

	/* Iterate over two elements. */
388 389
	assert(bpf_map_get_next_key(fd, NULL, &next_key) == 0 &&
	       next_key == 0);
390
	assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 &&
391
	       next_key == 0);
392
	assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 &&
393
	       next_key == 1);
394
	assert(bpf_map_get_next_key(fd, &next_key, &next_key) == -1 &&
395 396 397 398
	       errno == ENOENT);

	/* Delete shouldn't succeed. */
	key = 1;
399
	assert(bpf_map_delete_elem(fd, &key) == -1 && errno == EINVAL);
400 401 402 403 404 405

	close(fd);
}

static void test_arraymap_percpu_many_keys(void)
{
406
	unsigned int nr_cpus = bpf_num_possible_cpus();
407
	BPF_DECLARE_PERCPU(long, values);
408 409 410 411
	/* nr_keys is not too large otherwise the test stresses percpu
	 * allocator more than anything else
	 */
	unsigned int nr_keys = 2000;
412 413
	int key, fd, i;

414
	fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key),
415
			    sizeof(bpf_percpu(values, 0)), nr_keys, 0);
416 417 418 419 420 421 422
	if (fd < 0) {
		printf("Failed to create per-cpu arraymap '%s'!\n",
		       strerror(errno));
		exit(1);
	}

	for (i = 0; i < nr_cpus; i++)
423
		bpf_percpu(values, i) = i + 10;
424 425

	for (key = 0; key < nr_keys; key++)
426
		assert(bpf_map_update_elem(fd, &key, values, BPF_ANY) == 0);
427 428 429

	for (key = 0; key < nr_keys; key++) {
		for (i = 0; i < nr_cpus; i++)
430
			bpf_percpu(values, i) = 0;
431

432
		assert(bpf_map_lookup_elem(fd, &key, values) == 0);
433 434

		for (i = 0; i < nr_cpus; i++)
435
			assert(bpf_percpu(values, i) == i + 10);
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
	}

	close(fd);
}

#define MAP_SIZE (32 * 1024)

static void test_map_large(void)
{
	struct bigkey {
		int a;
		char b[116];
		long long c;
	} key;
	int fd, i, value;

452
	fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
453 454 455 456 457 458 459 460 461 462
			    MAP_SIZE, map_flags);
	if (fd < 0) {
		printf("Failed to create large map '%s'!\n", strerror(errno));
		exit(1);
	}

	for (i = 0; i < MAP_SIZE; i++) {
		key = (struct bigkey) { .c = i };
		value = i;

463
		assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == 0);
464 465 466
	}

	key.c = -1;
467
	assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 &&
468 469 470
	       errno == E2BIG);

	/* Iterate through all elements. */
471 472
	assert(bpf_map_get_next_key(fd, NULL, &key) == 0);
	key.c = -1;
473
	for (i = 0; i < MAP_SIZE; i++)
474 475
		assert(bpf_map_get_next_key(fd, &key, &key) == 0);
	assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT);
476 477

	key.c = 0;
478
	assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 0);
479
	key.a = 1;
480
	assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT);
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513

	close(fd);
}

static void run_parallel(int tasks, void (*fn)(int task, void *data),
			 void *data)
{
	pid_t pid[tasks];
	int i;

	for (i = 0; i < tasks; i++) {
		pid[i] = fork();
		if (pid[i] == 0) {
			fn(i, data);
			exit(0);
		} else if (pid[i] == -1) {
			printf("Couldn't spawn #%d process!\n", i);
			exit(1);
		}
	}

	for (i = 0; i < tasks; i++) {
		int status;

		assert(waitpid(pid[i], &status, 0) == pid[i]);
		assert(status == 0);
	}
}

static void test_map_stress(void)
{
	run_parallel(100, test_hashmap, NULL);
	run_parallel(100, test_hashmap_percpu, NULL);
514
	run_parallel(100, test_hashmap_sizes, NULL);
515
	run_parallel(100, test_hashmap_walk, NULL);
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535

	run_parallel(100, test_arraymap, NULL);
	run_parallel(100, test_arraymap_percpu, NULL);
}

#define TASKS 1024

#define DO_UPDATE 1
#define DO_DELETE 0

static void do_work(int fn, void *data)
{
	int do_update = ((int *)data)[1];
	int fd = ((int *)data)[0];
	int i, key, value;

	for (i = fn; i < MAP_SIZE; i += TASKS) {
		key = value = i;

		if (do_update) {
536 537 538 539
			assert(bpf_map_update_elem(fd, &key, &value,
						   BPF_NOEXIST) == 0);
			assert(bpf_map_update_elem(fd, &key, &value,
						   BPF_EXIST) == 0);
540
		} else {
541
			assert(bpf_map_delete_elem(fd, &key) == 0);
542 543 544 545 546 547 548 549 550
		}
	}
}

static void test_map_parallel(void)
{
	int i, fd, key = 0, value = 0;
	int data[2];

551
	fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
			    MAP_SIZE, map_flags);
	if (fd < 0) {
		printf("Failed to create map for parallel test '%s'!\n",
		       strerror(errno));
		exit(1);
	}

	/* Use the same fd in children to add elements to this map:
	 * child_0 adds key=0, key=1024, key=2048, ...
	 * child_1 adds key=1, key=1025, key=2049, ...
	 * child_1023 adds key=1023, ...
	 */
	data[0] = fd;
	data[1] = DO_UPDATE;
	run_parallel(TASKS, do_work, data);

	/* Check that key=0 is already there. */
569
	assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 &&
570 571 572
	       errno == EEXIST);

	/* Check that all elements were inserted. */
573
	assert(bpf_map_get_next_key(fd, NULL, &key) == 0);
574 575
	key = -1;
	for (i = 0; i < MAP_SIZE; i++)
576 577
		assert(bpf_map_get_next_key(fd, &key, &key) == 0);
	assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT);
578 579 580 581 582

	/* Another check for all elements */
	for (i = 0; i < MAP_SIZE; i++) {
		key = MAP_SIZE - i - 1;

583
		assert(bpf_map_lookup_elem(fd, &key, &value) == 0 &&
584 585 586 587 588 589 590 591 592
		       value == key);
	}

	/* Now let's delete all elemenets in parallel. */
	data[1] = DO_DELETE;
	run_parallel(TASKS, do_work, data);

	/* Nothing should be left. */
	key = -1;
593
	assert(bpf_map_get_next_key(fd, NULL, &key) == -1 && errno == ENOENT);
594
	assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT);
595 596 597 598 599 600
}

static void run_all_tests(void)
{
	test_hashmap(0, NULL);
	test_hashmap_percpu(0, NULL);
601
	test_hashmap_walk(0, NULL);
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627

	test_arraymap(0, NULL);
	test_arraymap_percpu(0, NULL);

	test_arraymap_percpu_many_keys();

	test_map_large();
	test_map_parallel();
	test_map_stress();
}

int main(void)
{
	struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };

	setrlimit(RLIMIT_MEMLOCK, &rinf);

	map_flags = 0;
	run_all_tests();

	map_flags = BPF_F_NO_PREALLOC;
	run_all_tests();

	printf("test_maps: OK\n");
	return 0;
}