selftests/bpf: Add bpf_arena_htab test.
authorAlexei Starovoitov <ast@kernel.org>
Fri, 8 Mar 2024 01:08:12 +0000 (17:08 -0800)
committerAndrii Nakryiko <andrii@kernel.org>
Mon, 11 Mar 2024 22:43:43 +0000 (15:43 -0700)
bpf_arena_htab.h - hash table implemented as bpf program

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20240308010812.89848-15-alexei.starovoitov@gmail.com
tools/testing/selftests/bpf/DENYLIST.aarch64
tools/testing/selftests/bpf/DENYLIST.s390x
tools/testing/selftests/bpf/bpf_arena_htab.h [new file with mode: 0644]
tools/testing/selftests/bpf/prog_tests/arena_htab.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/arena_htab.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/arena_htab_asm.c [new file with mode: 0644]

index f9101651747b553cc5601be90252475488fc7b6d..d8ade15e27898961554726158babaa03b3c18f0d 100644 (file)
@@ -11,3 +11,4 @@ fill_link_info/kretprobe_multi_link_info         # bpf_program__attach_kprobe_mu
 fill_link_info/kprobe_multi_invalid_ubuff        # bpf_program__attach_kprobe_multi_opts unexpected error: -95
 missed/kprobe_recursion                          # missed_kprobe_recursion__attach unexpected error: -95 (errno 95)
 verifier_arena                                   # JIT does not support arena
+arena_htab                                       # JIT does not support arena
index aa8a620f3318d282df2519e516a63d12e1856686..f4a2f66a683ddbd91d7f81ae5264335be09f067e 100644 (file)
@@ -5,3 +5,4 @@ get_stack_raw_tp                         # user_stack corrupted user stack
 stacktrace_build_id                      # compare_map_keys stackid_hmap vs. stackmap err -2 errno 2                   (?)
 verifier_iterating_callbacks
 verifier_arena                           # JIT does not support arena
+arena_htab                               # JIT does not support arena
diff --git a/tools/testing/selftests/bpf/bpf_arena_htab.h b/tools/testing/selftests/bpf/bpf_arena_htab.h
new file mode 100644 (file)
index 0000000..acc01a8
--- /dev/null
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
+#pragma once
+#include <errno.h>
+#include "bpf_arena_alloc.h"
+#include "bpf_arena_list.h"
+
+struct htab_bucket {
+       struct arena_list_head head;
+};
+typedef struct htab_bucket __arena htab_bucket_t;
+
+struct htab {
+       htab_bucket_t *buckets;
+       int n_buckets;
+};
+typedef struct htab __arena htab_t;
+
+static inline htab_bucket_t *__select_bucket(htab_t *htab, __u32 hash)
+{
+       htab_bucket_t *b = htab->buckets;
+
+       cast_kern(b);
+       return &b[hash & (htab->n_buckets - 1)];
+}
+
+static inline arena_list_head_t *select_bucket(htab_t *htab, __u32 hash)
+{
+       return &__select_bucket(htab, hash)->head;
+}
+
+struct hashtab_elem {
+       int hash;
+       int key;
+       int value;
+       struct arena_list_node hash_node;
+};
+typedef struct hashtab_elem __arena hashtab_elem_t;
+
+static hashtab_elem_t *lookup_elem_raw(arena_list_head_t *head, __u32 hash, int key)
+{
+       hashtab_elem_t *l;
+
+       list_for_each_entry(l, head, hash_node)
+               if (l->hash == hash && l->key == key)
+                       return l;
+
+       return NULL;
+}
+
+static int htab_hash(int key)
+{
+       return key;
+}
+
+__weak int htab_lookup_elem(htab_t *htab __arg_arena, int key)
+{
+       hashtab_elem_t *l_old;
+       arena_list_head_t *head;
+
+       cast_kern(htab);
+       head = select_bucket(htab, key);
+       l_old = lookup_elem_raw(head, htab_hash(key), key);
+       if (l_old)
+               return l_old->value;
+       return 0;
+}
+
+__weak int htab_update_elem(htab_t *htab __arg_arena, int key, int value)
+{
+       hashtab_elem_t *l_new = NULL, *l_old;
+       arena_list_head_t *head;
+
+       cast_kern(htab);
+       head = select_bucket(htab, key);
+       l_old = lookup_elem_raw(head, htab_hash(key), key);
+
+       l_new = bpf_alloc(sizeof(*l_new));
+       if (!l_new)
+               return -ENOMEM;
+       l_new->key = key;
+       l_new->hash = htab_hash(key);
+       l_new->value = value;
+
+       list_add_head(&l_new->hash_node, head);
+       if (l_old) {
+               list_del(&l_old->hash_node);
+               bpf_free(l_old);
+       }
+       return 0;
+}
+
+void htab_init(htab_t *htab)
+{
+       void __arena *buckets = bpf_arena_alloc_pages(&arena, NULL, 2, NUMA_NO_NODE, 0);
+
+       cast_user(buckets);
+       htab->buckets = buckets;
+       htab->n_buckets = 2 * PAGE_SIZE / sizeof(struct htab_bucket);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/arena_htab.c b/tools/testing/selftests/bpf/prog_tests/arena_htab.c
new file mode 100644 (file)
index 0000000..0766702
--- /dev/null
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
+#include <test_progs.h>
+#include <sys/mman.h>
+#include <network_helpers.h>
+
+#include "arena_htab_asm.skel.h"
+#include "arena_htab.skel.h"
+
+#define PAGE_SIZE 4096
+
+#include "bpf_arena_htab.h"
+
+static void test_arena_htab_common(struct htab *htab)
+{
+       int i;
+
+       printf("htab %p buckets %p n_buckets %d\n", htab, htab->buckets, htab->n_buckets);
+       ASSERT_OK_PTR(htab->buckets, "htab->buckets shouldn't be NULL");
+       for (i = 0; htab->buckets && i < 16; i += 4) {
+               /*
+                * Walk htab buckets and link lists since all pointers are correct,
+                * though they were written by bpf program.
+                */
+               int val = htab_lookup_elem(htab, i);
+
+               ASSERT_EQ(i, val, "key == value");
+       }
+}
+
+static void test_arena_htab_llvm(void)
+{
+       LIBBPF_OPTS(bpf_test_run_opts, opts);
+       struct arena_htab *skel;
+       struct htab *htab;
+       size_t arena_sz;
+       void *area;
+       int ret;
+
+       skel = arena_htab__open_and_load();
+       if (!ASSERT_OK_PTR(skel, "arena_htab__open_and_load"))
+               return;
+
+       area = bpf_map__initial_value(skel->maps.arena, &arena_sz);
+       /* fault-in a page with pgoff == 0 as sanity check */
+       *(volatile int *)area = 0x55aa;
+
+       /* bpf prog will allocate more pages */
+       ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.arena_htab_llvm), &opts);
+       ASSERT_OK(ret, "ret");
+       ASSERT_OK(opts.retval, "retval");
+       if (skel->bss->skip) {
+               printf("%s:SKIP:compiler doesn't support arena_cast\n", __func__);
+               test__skip();
+               goto out;
+       }
+       htab = skel->bss->htab_for_user;
+       test_arena_htab_common(htab);
+out:
+       arena_htab__destroy(skel);
+}
+
+static void test_arena_htab_asm(void)
+{
+       LIBBPF_OPTS(bpf_test_run_opts, opts);
+       struct arena_htab_asm *skel;
+       struct htab *htab;
+       int ret;
+
+       skel = arena_htab_asm__open_and_load();
+       if (!ASSERT_OK_PTR(skel, "arena_htab_asm__open_and_load"))
+               return;
+
+       ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.arena_htab_asm), &opts);
+       ASSERT_OK(ret, "ret");
+       ASSERT_OK(opts.retval, "retval");
+       htab = skel->bss->htab_for_user;
+       test_arena_htab_common(htab);
+       arena_htab_asm__destroy(skel);
+}
+
+void test_arena_htab(void)
+{
+       if (test__start_subtest("arena_htab_llvm"))
+               test_arena_htab_llvm();
+       if (test__start_subtest("arena_htab_asm"))
+               test_arena_htab_asm();
+}
diff --git a/tools/testing/selftests/bpf/progs/arena_htab.c b/tools/testing/selftests/bpf/progs/arena_htab.c
new file mode 100644 (file)
index 0000000..b7bb712
--- /dev/null
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+#include "bpf_experimental.h"
+
+struct {
+       __uint(type, BPF_MAP_TYPE_ARENA);
+       __uint(map_flags, BPF_F_MMAPABLE);
+       __uint(max_entries, 100); /* number of pages */
+} arena SEC(".maps");
+
+#include "bpf_arena_htab.h"
+
+void __arena *htab_for_user;
+bool skip = false;
+
+int zero = 0;
+
+SEC("syscall")
+int arena_htab_llvm(void *ctx)
+{
+#if defined(__BPF_FEATURE_ARENA_CAST) || defined(BPF_ARENA_FORCE_ASM)
+       struct htab __arena *htab;
+       __u64 i;
+
+       htab = bpf_alloc(sizeof(*htab));
+       cast_kern(htab);
+       htab_init(htab);
+
+       /* first run. No old elems in the table */
+       for (i = zero; i < 1000; i++)
+               htab_update_elem(htab, i, i);
+
+       /* should replace all elems with new ones */
+       for (i = zero; i < 1000; i++)
+               htab_update_elem(htab, i, i);
+       cast_user(htab);
+       htab_for_user = htab;
+#else
+       skip = true;
+#endif
+       return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/arena_htab_asm.c b/tools/testing/selftests/bpf/progs/arena_htab_asm.c
new file mode 100644 (file)
index 0000000..6cd70ea
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
+#define BPF_ARENA_FORCE_ASM
+#define arena_htab_llvm arena_htab_asm
+#include "arena_htab.c"