mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-03 07:38:10 +00:00
Merge branch 'bpf-zero-hash-seed'
Lorenz Bauer says: ==================== Allow forcing the seed of a hash table to zero, for deterministic execution during benchmarking and testing. Changes from v2: * Change ordering of BPF_F_ZERO_SEED in linux/bpf.h Comments adressed from v1: * Add comment to discourage production use to linux/bpf.h * Require CAP_SYS_ADMIN ==================== Acked-by: Song Liu <songliubraving@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
This commit is contained in:
commit
bbe5d311be
4 changed files with 84 additions and 19 deletions
|
@ -257,9 +257,6 @@ enum bpf_attach_type {
|
||||||
/* Specify numa node during map creation */
|
/* Specify numa node during map creation */
|
||||||
#define BPF_F_NUMA_NODE (1U << 2)
|
#define BPF_F_NUMA_NODE (1U << 2)
|
||||||
|
|
||||||
/* flags for BPF_PROG_QUERY */
|
|
||||||
#define BPF_F_QUERY_EFFECTIVE (1U << 0)
|
|
||||||
|
|
||||||
#define BPF_OBJ_NAME_LEN 16U
|
#define BPF_OBJ_NAME_LEN 16U
|
||||||
|
|
||||||
/* Flags for accessing BPF object */
|
/* Flags for accessing BPF object */
|
||||||
|
@ -269,6 +266,12 @@ enum bpf_attach_type {
|
||||||
/* Flag for stack_map, store build_id+offset instead of pointer */
|
/* Flag for stack_map, store build_id+offset instead of pointer */
|
||||||
#define BPF_F_STACK_BUILD_ID (1U << 5)
|
#define BPF_F_STACK_BUILD_ID (1U << 5)
|
||||||
|
|
||||||
|
/* Zero-initialize hash function seed. This should only be used for testing. */
|
||||||
|
#define BPF_F_ZERO_SEED (1U << 6)
|
||||||
|
|
||||||
|
/* flags for BPF_PROG_QUERY */
|
||||||
|
#define BPF_F_QUERY_EFFECTIVE (1U << 0)
|
||||||
|
|
||||||
enum bpf_stack_build_id_status {
|
enum bpf_stack_build_id_status {
|
||||||
/* user space need an empty entry to identify end of a trace */
|
/* user space need an empty entry to identify end of a trace */
|
||||||
BPF_STACK_BUILD_ID_EMPTY = 0,
|
BPF_STACK_BUILD_ID_EMPTY = 0,
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
#define HTAB_CREATE_FLAG_MASK \
|
#define HTAB_CREATE_FLAG_MASK \
|
||||||
(BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE | \
|
(BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE | \
|
||||||
BPF_F_RDONLY | BPF_F_WRONLY)
|
BPF_F_RDONLY | BPF_F_WRONLY | BPF_F_ZERO_SEED)
|
||||||
|
|
||||||
struct bucket {
|
struct bucket {
|
||||||
struct hlist_nulls_head head;
|
struct hlist_nulls_head head;
|
||||||
|
@ -244,6 +244,7 @@ static int htab_map_alloc_check(union bpf_attr *attr)
|
||||||
*/
|
*/
|
||||||
bool percpu_lru = (attr->map_flags & BPF_F_NO_COMMON_LRU);
|
bool percpu_lru = (attr->map_flags & BPF_F_NO_COMMON_LRU);
|
||||||
bool prealloc = !(attr->map_flags & BPF_F_NO_PREALLOC);
|
bool prealloc = !(attr->map_flags & BPF_F_NO_PREALLOC);
|
||||||
|
bool zero_seed = (attr->map_flags & BPF_F_ZERO_SEED);
|
||||||
int numa_node = bpf_map_attr_numa_node(attr);
|
int numa_node = bpf_map_attr_numa_node(attr);
|
||||||
|
|
||||||
BUILD_BUG_ON(offsetof(struct htab_elem, htab) !=
|
BUILD_BUG_ON(offsetof(struct htab_elem, htab) !=
|
||||||
|
@ -257,6 +258,10 @@ static int htab_map_alloc_check(union bpf_attr *attr)
|
||||||
*/
|
*/
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
|
if (zero_seed && !capable(CAP_SYS_ADMIN))
|
||||||
|
/* Guard against local DoS, and discourage production use. */
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
if (attr->map_flags & ~HTAB_CREATE_FLAG_MASK)
|
if (attr->map_flags & ~HTAB_CREATE_FLAG_MASK)
|
||||||
/* reserved bits should not be used */
|
/* reserved bits should not be used */
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -373,7 +378,11 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
|
||||||
if (!htab->buckets)
|
if (!htab->buckets)
|
||||||
goto free_htab;
|
goto free_htab;
|
||||||
|
|
||||||
|
if (htab->map.map_flags & BPF_F_ZERO_SEED)
|
||||||
|
htab->hashrnd = 0;
|
||||||
|
else
|
||||||
htab->hashrnd = get_random_int();
|
htab->hashrnd = get_random_int();
|
||||||
|
|
||||||
for (i = 0; i < htab->n_buckets; i++) {
|
for (i = 0; i < htab->n_buckets; i++) {
|
||||||
INIT_HLIST_NULLS_HEAD(&htab->buckets[i].head, i);
|
INIT_HLIST_NULLS_HEAD(&htab->buckets[i].head, i);
|
||||||
raw_spin_lock_init(&htab->buckets[i].lock);
|
raw_spin_lock_init(&htab->buckets[i].lock);
|
||||||
|
|
|
@ -257,9 +257,6 @@ enum bpf_attach_type {
|
||||||
/* Specify numa node during map creation */
|
/* Specify numa node during map creation */
|
||||||
#define BPF_F_NUMA_NODE (1U << 2)
|
#define BPF_F_NUMA_NODE (1U << 2)
|
||||||
|
|
||||||
/* flags for BPF_PROG_QUERY */
|
|
||||||
#define BPF_F_QUERY_EFFECTIVE (1U << 0)
|
|
||||||
|
|
||||||
#define BPF_OBJ_NAME_LEN 16U
|
#define BPF_OBJ_NAME_LEN 16U
|
||||||
|
|
||||||
/* Flags for accessing BPF object */
|
/* Flags for accessing BPF object */
|
||||||
|
@ -269,6 +266,12 @@ enum bpf_attach_type {
|
||||||
/* Flag for stack_map, store build_id+offset instead of pointer */
|
/* Flag for stack_map, store build_id+offset instead of pointer */
|
||||||
#define BPF_F_STACK_BUILD_ID (1U << 5)
|
#define BPF_F_STACK_BUILD_ID (1U << 5)
|
||||||
|
|
||||||
|
/* Zero-initialize hash function seed. This should only be used for testing. */
|
||||||
|
#define BPF_F_ZERO_SEED (1U << 6)
|
||||||
|
|
||||||
|
/* flags for BPF_PROG_QUERY */
|
||||||
|
#define BPF_F_QUERY_EFFECTIVE (1U << 0)
|
||||||
|
|
||||||
enum bpf_stack_build_id_status {
|
enum bpf_stack_build_id_status {
|
||||||
/* user space need an empty entry to identify end of a trace */
|
/* user space need an empty entry to identify end of a trace */
|
||||||
BPF_STACK_BUILD_ID_EMPTY = 0,
|
BPF_STACK_BUILD_ID_EMPTY = 0,
|
||||||
|
@ -2201,6 +2204,8 @@ union bpf_attr {
|
||||||
* **CONFIG_NET** configuration option.
|
* **CONFIG_NET** configuration option.
|
||||||
* Return
|
* Return
|
||||||
* Pointer to *struct bpf_sock*, or NULL in case of failure.
|
* Pointer to *struct bpf_sock*, or NULL in case of failure.
|
||||||
|
* For sockets with reuseport option, *struct bpf_sock*
|
||||||
|
* return is from reuse->socks[] using hash of the packet.
|
||||||
*
|
*
|
||||||
* struct bpf_sock *bpf_sk_lookup_udp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u32 netns, u64 flags)
|
* struct bpf_sock *bpf_sk_lookup_udp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u32 netns, u64 flags)
|
||||||
* Description
|
* Description
|
||||||
|
@ -2233,6 +2238,8 @@ union bpf_attr {
|
||||||
* **CONFIG_NET** configuration option.
|
* **CONFIG_NET** configuration option.
|
||||||
* Return
|
* Return
|
||||||
* Pointer to *struct bpf_sock*, or NULL in case of failure.
|
* Pointer to *struct bpf_sock*, or NULL in case of failure.
|
||||||
|
* For sockets with reuseport option, *struct bpf_sock*
|
||||||
|
* return is from reuse->socks[] using hash of the packet.
|
||||||
*
|
*
|
||||||
* int bpf_sk_release(struct bpf_sock *sk)
|
* int bpf_sk_release(struct bpf_sock *sk)
|
||||||
* Description
|
* Description
|
||||||
|
|
|
@ -258,23 +258,35 @@ static void test_hashmap_percpu(int task, void *data)
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int helper_fill_hashmap(int max_entries)
|
||||||
|
{
|
||||||
|
int i, fd, ret;
|
||||||
|
long long key, value;
|
||||||
|
|
||||||
|
fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
|
||||||
|
max_entries, map_flags);
|
||||||
|
CHECK(fd < 0,
|
||||||
|
"failed to create hashmap",
|
||||||
|
"err: %s, flags: 0x%x\n", strerror(errno), map_flags);
|
||||||
|
|
||||||
|
for (i = 0; i < max_entries; i++) {
|
||||||
|
key = i; value = key;
|
||||||
|
ret = bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST);
|
||||||
|
CHECK(ret != 0,
|
||||||
|
"can't update hashmap",
|
||||||
|
"err: %s\n", strerror(ret));
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
static void test_hashmap_walk(int task, void *data)
|
static void test_hashmap_walk(int task, void *data)
|
||||||
{
|
{
|
||||||
int fd, i, max_entries = 1000;
|
int fd, i, max_entries = 1000;
|
||||||
long long key, value, next_key;
|
long long key, value, next_key;
|
||||||
bool next_key_valid = true;
|
bool next_key_valid = true;
|
||||||
|
|
||||||
fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
|
fd = helper_fill_hashmap(max_entries);
|
||||||
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,
|
for (i = 0; bpf_map_get_next_key(fd, !i ? NULL : &key,
|
||||||
&next_key) == 0; i++) {
|
&next_key) == 0; i++) {
|
||||||
|
@ -306,6 +318,39 @@ static void test_hashmap_walk(int task, void *data)
|
||||||
close(fd);
|
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_flags;
|
||||||
|
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_flags = old_flags;
|
||||||
|
close(first);
|
||||||
|
close(second);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_arraymap(int task, void *data)
|
static void test_arraymap(int task, void *data)
|
||||||
{
|
{
|
||||||
int key, next_key, fd;
|
int key, next_key, fd;
|
||||||
|
@ -1534,6 +1579,7 @@ static void run_all_tests(void)
|
||||||
test_hashmap(0, NULL);
|
test_hashmap(0, NULL);
|
||||||
test_hashmap_percpu(0, NULL);
|
test_hashmap_percpu(0, NULL);
|
||||||
test_hashmap_walk(0, NULL);
|
test_hashmap_walk(0, NULL);
|
||||||
|
test_hashmap_zero_seed();
|
||||||
|
|
||||||
test_arraymap(0, NULL);
|
test_arraymap(0, NULL);
|
||||||
test_arraymap_percpu(0, NULL);
|
test_arraymap_percpu(0, NULL);
|
||||||
|
|
Loading…
Reference in a new issue