1939 lines · 50.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Testsuite for eBPF maps * * Copyright (c) 2014 PLUMgrid, http://plumgrid.com * Copyright (c) 2016 Facebook */ #include <stdio.h>#include <unistd.h>#include <errno.h>#include <string.h>#include <assert.h>#include <stdlib.h>#include <time.h> #include <sys/wait.h>#include <sys/socket.h>#include <netinet/in.h>#include <linux/bpf.h> #include <bpf/bpf.h>#include <bpf/libbpf.h> #include "bpf_util.h"#include "test_maps.h"#include "testing_helpers.h" int skips; static struct bpf_map_create_opts map_opts = { .sz = sizeof(map_opts) }; static void test_hashmap(unsigned int task, void *data){	long long key, next_key, first_key, value;	int fd; 	fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(key), sizeof(value), 2, &map_opts);	if (fd < 0) {		printf("Failed to create hashmap '%s'!\n", strerror(errno));		exit(1);	} 	key = 1;	value = 1234;	/* Insert key=1 element. */	assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0); 	value = 0;	/* BPF_NOEXIST means add new element if it doesn't exist. */	assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) < 0 &&	       /* key=1 already exists. */	       errno == EEXIST); 	/* -1 is an invalid flag. */	assert(bpf_map_update_elem(fd, &key, &value, -1) < 0 &&	       errno == EINVAL); 	/* Check that key=1 can be found. */	assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 1234); 	key = 2;	value = 1234;	/* Insert key=2 element. */	assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0); 	/* Check that key=2 matches the value and delete it */	assert(bpf_map_lookup_and_delete_elem(fd, &key, &value) == 0 && value == 1234); 	/* Check that key=2 is not found. */	assert(bpf_map_lookup_elem(fd, &key, &value) < 0 && errno == ENOENT); 	/* BPF_EXIST means update existing element. */	assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) < 0 &&	       /* key=2 is not there. */	       errno == ENOENT); 	/* Insert key=2 element. */	assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == 0); 	/* key=1 and key=2 were inserted, check that key=0 cannot be	 * inserted due to max_entries limit.	 */	key = 0;	assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) < 0 &&	       errno == E2BIG); 	/* Update existing element, though the map is full. */	key = 1;	assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) == 0);	key = 2;	assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0);	key = 3;	assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) < 0 &&	       errno == E2BIG); 	/* Check that key = 0 doesn't exist. */	key = 0;	assert(bpf_map_delete_elem(fd, &key) < 0 && errno == ENOENT); 	/* Iterate over two elements. */	assert(bpf_map_get_next_key(fd, NULL, &first_key) == 0 &&	       (first_key == 1 || first_key == 2));	assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 &&	       (next_key == first_key));	assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 &&	       (next_key == 1 || next_key == 2) &&	       (next_key != first_key));	assert(bpf_map_get_next_key(fd, &next_key, &next_key) < 0 &&	       errno == ENOENT); 	/* Delete both elements. */	key = 1;	assert(bpf_map_delete_elem(fd, &key) == 0);	key = 2;	assert(bpf_map_delete_elem(fd, &key) == 0);	assert(bpf_map_delete_elem(fd, &key) < 0 && errno == ENOENT); 	key = 0;	/* Check that map is empty. */	assert(bpf_map_get_next_key(fd, NULL, &next_key) < 0 &&	       errno == ENOENT);	assert(bpf_map_get_next_key(fd, &key, &next_key) < 0 &&	       errno == ENOENT); 	close(fd);} static void test_hashmap_sizes(unsigned 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_map_create(BPF_MAP_TYPE_HASH, NULL, i, j, 2, &map_opts);			if (fd < 0) {				if (errno == ENOMEM)					return;				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 */		}} static void test_hashmap_percpu(unsigned int task, void *data){	unsigned int nr_cpus = bpf_num_possible_cpus();	BPF_DECLARE_PERCPU(long, value);	long long key, next_key, first_key;	int expected_key_mask = 0;	int fd, i; 	fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_HASH, NULL, sizeof(key),			    sizeof(bpf_percpu(value, 0)), 2, &map_opts);	if (fd < 0) {		printf("Failed to create hashmap '%s'!\n", strerror(errno));		exit(1);	} 	for (i = 0; i < nr_cpus; i++)		bpf_percpu(value, i) = i + 100; 	key = 1;	/* Insert key=1 element. */	assert(!(expected_key_mask & key));	assert(bpf_map_update_elem(fd, &key, value, BPF_ANY) == 0); 	/* Lookup and delete elem key=1 and check value. */	assert(bpf_map_lookup_and_delete_elem(fd, &key, value) == 0 &&	       bpf_percpu(value,0) == 100); 	for (i = 0; i < nr_cpus; i++)		bpf_percpu(value,i) = i + 100; 	/* Insert key=1 element which should not exist. */	assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == 0);	expected_key_mask |= key; 	/* BPF_NOEXIST means add new element if it doesn't exist. */	assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) < 0 &&	       /* key=1 already exists. */	       errno == EEXIST); 	/* -1 is an invalid flag. */	assert(bpf_map_update_elem(fd, &key, value, -1) < 0 &&	       errno == EINVAL); 	/* Check that key=1 can be found. Value could be 0 if the lookup	 * was run from a different CPU.	 */	bpf_percpu(value, 0) = 1;	assert(bpf_map_lookup_elem(fd, &key, value) == 0 &&	       bpf_percpu(value, 0) == 100); 	key = 2;	/* Check that key=2 is not found. */	assert(bpf_map_lookup_elem(fd, &key, value) < 0 && errno == ENOENT); 	/* BPF_EXIST means update existing element. */	assert(bpf_map_update_elem(fd, &key, value, BPF_EXIST) < 0 &&	       /* key=2 is not there. */	       errno == ENOENT); 	/* Insert key=2 element. */	assert(!(expected_key_mask & key));	assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == 0);	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;	assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) < 0 &&	       errno == E2BIG); 	/* Check that key = 0 doesn't exist. */	assert(bpf_map_delete_elem(fd, &key) < 0 && errno == ENOENT); 	/* Iterate over two elements. */	assert(bpf_map_get_next_key(fd, NULL, &first_key) == 0 &&	       ((expected_key_mask & first_key) == first_key));	while (!bpf_map_get_next_key(fd, &key, &next_key)) {		if (first_key) {			assert(next_key == first_key);			first_key = 0;		}		assert((expected_key_mask & next_key) == next_key);		expected_key_mask &= ~next_key; 		assert(bpf_map_lookup_elem(fd, &next_key, value) == 0); 		for (i = 0; i < nr_cpus; i++)			assert(bpf_percpu(value, i) == i + 100); 		key = next_key;	}	assert(errno == ENOENT); 	/* Update with BPF_EXIST. */	key = 1;	assert(bpf_map_update_elem(fd, &key, value, BPF_EXIST) == 0); 	/* Delete both elements. */	key = 1;	assert(bpf_map_delete_elem(fd, &key) == 0);	key = 2;	assert(bpf_map_delete_elem(fd, &key) == 0);	assert(bpf_map_delete_elem(fd, &key) < 0 && errno == ENOENT); 	key = 0;	/* Check that map is empty. */	assert(bpf_map_get_next_key(fd, NULL, &next_key) < 0 &&	       errno == ENOENT);	assert(bpf_map_get_next_key(fd, &key, &next_key) < 0 &&	       errno == ENOENT); 	close(fd);} #define MAP_RETRIES 20 static bool can_retry(int err){	return (err == EAGAIN || err == EBUSY ||		((err == ENOMEM || err == E2BIG) &&		 map_opts.map_flags == BPF_F_NO_PREALLOC));}  #define VALUE_SIZE 3static int helper_fill_hashmap(int max_entries){	int i, fd, ret;	long long key, value[VALUE_SIZE] = {}; 	fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(key), sizeof(value),			    max_entries, &map_opts);	CHECK(fd < 0,	      "failed to create hashmap",	      "err: %s, flags: 0x%x\n", strerror(errno), map_opts.map_flags); 	for (i = 0; i < max_entries; i++) {		key = i; value[0] = key;		ret = map_update_retriable(fd, &key, value, BPF_NOEXIST,					   MAP_RETRIES, can_retry);		CHECK(ret != 0,		      "can't update hashmap",		      "err: %s\n", strerror(-ret));	} 	return fd;} static void test_hashmap_walk(unsigned int task, void *data){	int fd, i, max_entries = 10000;	long long key, value[VALUE_SIZE], next_key;	bool next_key_valid = true; 	fd = helper_fill_hashmap(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(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[0]++;		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[0] - 1 == key);	} 	assert(i == max_entries);	close(fd);} static void test_hashmap_zero_seed(void){	int i, first, second, old_flags;	long long key, next_first, next_second; 	old_flags = map_opts.map_flags;	map_opts.map_flags |= BPF_F_ZERO_SEED; 	first = helper_fill_hashmap(3);	second = helper_fill_hashmap(3); 	for (i = 0; ; i++) {		void *key_ptr = !i ? NULL : &key; 		if (bpf_map_get_next_key(first, key_ptr, &next_first) != 0)			break; 		CHECK(bpf_map_get_next_key(second, key_ptr, &next_second) != 0,		      "next_key for second map must succeed",		      "key_ptr: %p", key_ptr);		CHECK(next_first != next_second,		      "keys must match",		      "i: %d first: %lld second: %lld\n", i,		      next_first, next_second); 		key = next_first;	} 	map_opts.map_flags = old_flags;	close(first);	close(second);} static void test_arraymap(unsigned int task, void *data){	int key, next_key, fd;	long long value; 	fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, sizeof(key), sizeof(value), 2, NULL);	if (fd < 0) {		printf("Failed to create arraymap '%s'!\n", strerror(errno));		exit(1);	} 	key = 1;	value = 1234;	/* Insert key=1 element. */	assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0); 	value = 0;	assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) < 0 &&	       errno == EEXIST); 	/* Check that key=1 can be found. */	assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 1234); 	key = 0;	/* Check that key=0 is also found and zero initialized. */	assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 0); 	/* key=0 and key=1 were inserted, check that key=2 cannot be inserted	 * due to max_entries limit.	 */	key = 2;	assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) < 0 &&	       errno == E2BIG); 	/* Check that key = 2 doesn't exist. */	assert(bpf_map_lookup_elem(fd, &key, &value) < 0 && errno == ENOENT); 	/* Iterate over two elements. */	assert(bpf_map_get_next_key(fd, NULL, &next_key) == 0 &&	       next_key == 0);	assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 &&	       next_key == 0);	assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 &&	       next_key == 1);	assert(bpf_map_get_next_key(fd, &next_key, &next_key) < 0 &&	       errno == ENOENT); 	/* Delete shouldn't succeed. */	key = 1;	assert(bpf_map_delete_elem(fd, &key) < 0 && errno == EINVAL); 	close(fd);} static void test_arraymap_percpu(unsigned int task, void *data){	unsigned int nr_cpus = bpf_num_possible_cpus();	BPF_DECLARE_PERCPU(long, values);	int key, next_key, fd, i; 	fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, NULL, sizeof(key),			    sizeof(bpf_percpu(values, 0)), 2, NULL);	if (fd < 0) {		printf("Failed to create arraymap '%s'!\n", strerror(errno));		exit(1);	} 	for (i = 0; i < nr_cpus; i++)		bpf_percpu(values, i) = i + 100; 	key = 1;	/* Insert key=1 element. */	assert(bpf_map_update_elem(fd, &key, values, BPF_ANY) == 0); 	bpf_percpu(values, 0) = 0;	assert(bpf_map_update_elem(fd, &key, values, BPF_NOEXIST) < 0 &&	       errno == EEXIST); 	/* Check that key=1 can be found. */	assert(bpf_map_lookup_elem(fd, &key, values) == 0 &&	       bpf_percpu(values, 0) == 100); 	key = 0;	/* Check that key=0 is also found and zero initialized. */	assert(bpf_map_lookup_elem(fd, &key, values) == 0 &&	       bpf_percpu(values, 0) == 0 &&	       bpf_percpu(values, nr_cpus - 1) == 0); 	/* Check that key=2 cannot be inserted due to max_entries limit. */	key = 2;	assert(bpf_map_update_elem(fd, &key, values, BPF_EXIST) < 0 &&	       errno == E2BIG); 	/* Check that key = 2 doesn't exist. */	assert(bpf_map_lookup_elem(fd, &key, values) < 0 && errno == ENOENT); 	/* Iterate over two elements. */	assert(bpf_map_get_next_key(fd, NULL, &next_key) == 0 &&	       next_key == 0);	assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 &&	       next_key == 0);	assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 &&	       next_key == 1);	assert(bpf_map_get_next_key(fd, &next_key, &next_key) < 0 &&	       errno == ENOENT); 	/* Delete shouldn't succeed. */	key = 1;	assert(bpf_map_delete_elem(fd, &key) < 0 && errno == EINVAL); 	close(fd);} static void test_arraymap_percpu_many_keys(void){	unsigned int nr_cpus = bpf_num_possible_cpus();	BPF_DECLARE_PERCPU(long, values);	/* nr_keys is not too large otherwise the test stresses percpu	 * allocator more than anything else	 */	unsigned int nr_keys = 2000;	int key, fd, i; 	fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, NULL, sizeof(key),			    sizeof(bpf_percpu(values, 0)), nr_keys, NULL);	if (fd < 0) {		printf("Failed to create per-cpu arraymap '%s'!\n",		       strerror(errno));		exit(1);	} 	for (i = 0; i < nr_cpus; i++)		bpf_percpu(values, i) = i + 10;

Lines 1–500 of 1939.