From: Mykyta Yatsenko <mykyta.yatsenko5@gmail.com>
To: bpf@vger.kernel.org, ast@kernel.org, andrii@kernel.org,
daniel@iogearbox.net, kafai@meta.com, kernel-team@meta.com,
eddyz87@gmail.com, memxor@gmail.com,
herbert@gondor.apana.org.au
Cc: Mykyta Yatsenko <yatsenko@meta.com>
Subject: [PATCH bpf-next v4 08/11] selftests/bpf: Add basic tests for resizable hash map
Date: Wed, 13 May 2026 15:36:11 -0700 [thread overview]
Message-ID: <20260513-rhash-v4-8-dd3d541ccb0b@meta.com> (raw)
In-Reply-To: <20260513-rhash-v4-0-dd3d541ccb0b@meta.com>
From: Mykyta Yatsenko <yatsenko@meta.com>
Test basic map operations (lookup, update, delete) for
BPF_MAP_TYPE_RHASH including boundary conditions like duplicate
key insertion and deletion of nonexistent keys.
Signed-off-by: Mykyta Yatsenko <yatsenko@meta.com>
---
tools/testing/selftests/bpf/prog_tests/rhash.c | 120 ++++++++++++
tools/testing/selftests/bpf/progs/rhash.c | 248 +++++++++++++++++++++++++
2 files changed, 368 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/rhash.c b/tools/testing/selftests/bpf/prog_tests/rhash.c
new file mode 100644
index 000000000000..69686bf69ba5
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/rhash.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <test_progs.h>
+#include <string.h>
+#include <stdio.h>
+#include "rhash.skel.h"
+#include <linux/bpf.h>
+#include <linux/perf_event.h>
+#include <sys/syscall.h>
+
+static void rhash_run(const char *prog_name)
+{
+ struct rhash *skel;
+ struct bpf_program *prog;
+ LIBBPF_OPTS(bpf_test_run_opts, opts);
+ int err;
+
+ skel = rhash__open();
+ if (!ASSERT_OK_PTR(skel, "rhash__open"))
+ return;
+
+ prog = bpf_object__find_program_by_name(skel->obj, prog_name);
+ if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name"))
+ goto cleanup;
+ bpf_program__set_autoload(prog, true);
+
+ err = rhash__load(skel);
+ if (!ASSERT_OK(err, "skel_load"))
+ goto cleanup;
+
+ err = bpf_prog_test_run_opts(bpf_program__fd(prog), &opts);
+ if (!ASSERT_OK(err, "prog run"))
+ goto cleanup;
+
+ if (!ASSERT_OK(opts.retval, "prog retval"))
+ goto cleanup;
+
+ if (!ASSERT_OK(skel->bss->err, "bss->err"))
+ goto cleanup;
+
+cleanup:
+ rhash__destroy(skel);
+}
+
+static int rhash_map_create(__u32 max_entries, __u64 map_extra)
+{
+ LIBBPF_OPTS(bpf_map_create_opts, opts,
+ .map_flags = BPF_F_NO_PREALLOC,
+ .map_extra = map_extra);
+
+ return bpf_map_create(BPF_MAP_TYPE_RHASH, "rhash_extra",
+ sizeof(__u32), sizeof(__u64), max_entries, &opts);
+}
+
+static void rhash_map_extra_presize(void)
+{
+ const __u32 max_entries = 1024;
+ const __u32 nelem_hint = 256;
+ struct bpf_map_info info = {};
+ __u32 info_len = sizeof(info);
+ __u64 val = 0;
+ __u32 key;
+ int fd, i;
+
+ fd = rhash_map_create(max_entries, nelem_hint);
+ if (!ASSERT_GE(fd, 0, "rhash_map_create presize"))
+ return;
+
+ if (!ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &info_len), "info"))
+ goto close;
+ ASSERT_EQ(info.map_extra, nelem_hint, "info.map_extra");
+
+ for (i = 0; i < (int)nelem_hint; i++) {
+ key = i;
+ if (!ASSERT_OK(bpf_map_update_elem(fd, &key, &val, BPF_NOEXIST),
+ "update"))
+ goto close;
+ }
+close:
+ close(fd);
+}
+
+static void rhash_map_extra_too_big(void)
+{
+ int fd;
+
+ fd = rhash_map_create(1U << 20, 0x10000);
+ if (!ASSERT_LT(fd, 0, "rhash_map_create hint > U16_MAX"))
+ close(fd);
+}
+
+void test_rhash(void)
+{
+ if (test__start_subtest("test_rhash_lookup_update"))
+ rhash_run("test_rhash_lookup_update");
+
+ if (test__start_subtest("test_rhash_update_delete"))
+ rhash_run("test_rhash_update_delete");
+
+ if (test__start_subtest("test_rhash_update_elements"))
+ rhash_run("test_rhash_update_elements");
+
+ if (test__start_subtest("test_rhash_update_exist"))
+ rhash_run("test_rhash_update_exist");
+
+ if (test__start_subtest("test_rhash_update_any"))
+ rhash_run("test_rhash_update_any");
+
+ if (test__start_subtest("test_rhash_noexist_duplicate"))
+ rhash_run("test_rhash_noexist_duplicate");
+
+ if (test__start_subtest("test_rhash_delete_nonexistent"))
+ rhash_run("test_rhash_delete_nonexistent");
+
+ if (test__start_subtest("test_rhash_map_extra_presize"))
+ rhash_map_extra_presize();
+
+ if (test__start_subtest("test_rhash_map_extra_too_big"))
+ rhash_map_extra_too_big();
+}
diff --git a/tools/testing/selftests/bpf/progs/rhash.c b/tools/testing/selftests/bpf/progs/rhash.c
new file mode 100644
index 000000000000..fc2dac3a719e
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/rhash.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+
+#include <vmlinux.h>
+#include <stdbool.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include "bpf_misc.h"
+
+#define ENOENT 2
+#define EEXIST 17
+
+char _license[] SEC("license") = "GPL";
+
+int err;
+
+struct elem {
+ char arr[128];
+ int val;
+};
+
+struct {
+ __uint(type, BPF_MAP_TYPE_RHASH);
+ __uint(map_flags, BPF_F_NO_PREALLOC);
+ __uint(max_entries, 128);
+ __type(key, int);
+ __type(value, struct elem);
+} rhmap SEC(".maps");
+
+SEC("syscall")
+int test_rhash_lookup_update(void *ctx)
+{
+ int key = 5;
+ struct elem empty = {.val = 3, .arr = {0}};
+ struct elem *e;
+
+ err = 1;
+ e = bpf_map_lookup_elem(&rhmap, &key);
+ if (e)
+ return 1;
+
+ err = bpf_map_update_elem(&rhmap, &key, &empty, BPF_NOEXIST);
+ if (err)
+ return 1;
+
+ e = bpf_map_lookup_elem(&rhmap, &key);
+ if (!e || e->val != empty.val) {
+ err = 2;
+ return 2;
+ }
+
+ err = 0;
+ return 0;
+}
+
+SEC("syscall")
+int test_rhash_update_delete(void *ctx)
+{
+ int key = 6;
+ struct elem empty = {.val = 4, .arr = {0}};
+ struct elem *e;
+
+ err = 1;
+ e = bpf_map_lookup_elem(&rhmap, &key);
+ if (e)
+ return 1;
+
+ err = bpf_map_update_elem(&rhmap, &key, &empty, BPF_NOEXIST);
+ if (err)
+ return 2;
+
+ err = bpf_map_delete_elem(&rhmap, &key);
+ if (err)
+ return 3;
+
+ e = bpf_map_lookup_elem(&rhmap, &key);
+ if (e) {
+ err = 4;
+ return 4;
+ }
+
+ err = 0;
+ return 0;
+}
+
+SEC("syscall")
+int test_rhash_update_elements(void *ctx)
+{
+ int key = 0;
+ struct elem empty = {.val = 4, .arr = {0}};
+ struct elem *e;
+ int i;
+
+ err = 1;
+
+ for (i = 0; i < 128; ++i) {
+ key = i;
+ e = bpf_map_lookup_elem(&rhmap, &key);
+ if (e)
+ return 1;
+
+ empty.val = key;
+ err = bpf_map_update_elem(&rhmap, &key, &empty, BPF_NOEXIST);
+ if (err)
+ return 2;
+
+ e = bpf_map_lookup_elem(&rhmap, &key);
+ if (!e || e->val != key) {
+ err = 4;
+ return 4;
+ }
+ }
+
+ for (i = 0; i < 128; ++i) {
+ key = i;
+ err = bpf_map_delete_elem(&rhmap, &key);
+ if (err)
+ return 3;
+
+ e = bpf_map_lookup_elem(&rhmap, &key);
+ if (e) {
+ err = 5;
+ return 5;
+ }
+ }
+
+ err = 0;
+ return 0;
+}
+
+SEC("syscall")
+int test_rhash_update_exist(void *ctx)
+{
+ int key = 10;
+ struct elem val1 = {.val = 100, .arr = {0}};
+ struct elem val2 = {.val = 200, .arr = {0}};
+ struct elem *e;
+ int ret;
+
+ err = 1;
+
+ /* BPF_EXIST on non-existent key should fail with -ENOENT */
+ ret = bpf_map_update_elem(&rhmap, &key, &val1, BPF_EXIST);
+ if (ret != -ENOENT)
+ return 1;
+
+ /* Insert element first */
+ ret = bpf_map_update_elem(&rhmap, &key, &val1, BPF_NOEXIST);
+ if (ret)
+ return 2;
+
+ /* Verify initial value */
+ e = bpf_map_lookup_elem(&rhmap, &key);
+ if (!e || e->val != 100)
+ return 3;
+
+ /* BPF_EXIST on existing key should succeed and update value */
+ ret = bpf_map_update_elem(&rhmap, &key, &val2, BPF_EXIST);
+ if (ret)
+ return 4;
+
+ /* Verify value was updated */
+ e = bpf_map_lookup_elem(&rhmap, &key);
+ if (!e || e->val != 200)
+ return 5;
+
+ /* Cleanup */
+ bpf_map_delete_elem(&rhmap, &key);
+ err = 0;
+ return 0;
+}
+
+SEC("syscall")
+int test_rhash_update_any(void *ctx)
+{
+ int key = 11;
+ struct elem val1 = {.val = 111, .arr = {0}};
+ struct elem val2 = {.val = 222, .arr = {0}};
+ struct elem *e;
+ int ret;
+
+ err = 1;
+
+ /* BPF_ANY on non-existent key should insert */
+ ret = bpf_map_update_elem(&rhmap, &key, &val1, BPF_ANY);
+ if (ret)
+ return 1;
+
+ e = bpf_map_lookup_elem(&rhmap, &key);
+ if (!e || e->val != 111)
+ return 2;
+
+ /* BPF_ANY on existing key should update */
+ ret = bpf_map_update_elem(&rhmap, &key, &val2, BPF_ANY);
+ if (ret)
+ return 3;
+
+ e = bpf_map_lookup_elem(&rhmap, &key);
+ if (!e || e->val != 222)
+ return 4;
+
+ /* Cleanup */
+ bpf_map_delete_elem(&rhmap, &key);
+ err = 0;
+ return 0;
+}
+
+SEC("syscall")
+int test_rhash_noexist_duplicate(void *ctx)
+{
+ int key = 12;
+ struct elem val = {.val = 600, .arr = {0}};
+ int ret;
+
+ err = 1;
+
+ /* Insert element */
+ ret = bpf_map_update_elem(&rhmap, &key, &val, BPF_NOEXIST);
+ if (ret)
+ return 1;
+
+ /* Try to insert again with BPF_NOEXIST - should fail with -EEXIST */
+ ret = bpf_map_update_elem(&rhmap, &key, &val, BPF_NOEXIST);
+ if (ret != -EEXIST)
+ return 2;
+
+ /* Cleanup */
+ bpf_map_delete_elem(&rhmap, &key);
+ err = 0;
+ return 0;
+}
+
+SEC("syscall")
+int test_rhash_delete_nonexistent(void *ctx)
+{
+ int key = 99999;
+ int ret;
+
+ err = 1;
+
+ /* Delete non-existent key should return -ENOENT */
+ ret = bpf_map_delete_elem(&rhmap, &key);
+ if (ret != -ENOENT)
+ return 1;
+
+ err = 0;
+ return 0;
+}
--
2.53.0-Meta
next prev parent reply other threads:[~2026-05-13 22:37 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-13 22:36 [PATCH bpf-next v4 00/11] bpf: Introduce resizable hash map Mykyta Yatsenko
2026-05-13 22:36 ` [PATCH bpf-next v4 01/11] rhashtable: Add rhashtable_next_key() API Mykyta Yatsenko
2026-05-13 23:21 ` bot+bpf-ci
2026-05-13 22:36 ` [PATCH bpf-next v4 02/11] rhashtable: Add selftest for rhashtable_next_key() Mykyta Yatsenko
2026-05-13 22:36 ` [PATCH bpf-next v4 03/11] bpf: Implement resizable hashmap basic functions Mykyta Yatsenko
2026-05-13 23:21 ` bot+bpf-ci
2026-05-14 10:59 ` Mykyta Yatsenko
2026-05-13 22:36 ` [PATCH bpf-next v4 04/11] bpf: Implement iteration ops for resizable hashtab Mykyta Yatsenko
2026-05-13 22:36 ` [PATCH bpf-next v4 05/11] bpf: Allow special fields in " Mykyta Yatsenko
2026-05-13 23:21 ` bot+bpf-ci
2026-05-14 11:00 ` Mykyta Yatsenko
2026-05-13 22:36 ` [PATCH bpf-next v4 06/11] bpf: Optimize word-sized keys for resizable hashtable Mykyta Yatsenko
2026-05-13 22:36 ` [PATCH bpf-next v4 07/11] libbpf: Support " Mykyta Yatsenko
2026-05-13 22:36 ` Mykyta Yatsenko [this message]
2026-05-13 22:36 ` [PATCH bpf-next v4 09/11] selftests/bpf: Add BPF iterator tests for resizable hash map Mykyta Yatsenko
2026-05-13 22:36 ` [PATCH bpf-next v4 10/11] bpftool: Add rhash map documentation Mykyta Yatsenko
2026-05-13 22:36 ` [PATCH bpf-next v4 11/11] selftests/bpf: Add resizable hashmap to benchmarks Mykyta Yatsenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260513-rhash-v4-8-dd3d541ccb0b@meta.com \
--to=mykyta.yatsenko5@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=herbert@gondor.apana.org.au \
--cc=kafai@meta.com \
--cc=kernel-team@meta.com \
--cc=memxor@gmail.com \
--cc=yatsenko@meta.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.