root/tools/testing/selftests/bpf/progs/rhash.c
// 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;
}