| // 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; |
| } |