diff --git a/runtime/src/bpf_map/userspace/per_cpu_array_map.cpp b/runtime/src/bpf_map/userspace/per_cpu_array_map.cpp index b4114f9a..d6ca2447 100644 --- a/runtime/src/bpf_map/userspace/per_cpu_array_map.cpp +++ b/runtime/src/bpf_map/userspace/per_cpu_array_map.cpp @@ -4,6 +4,7 @@ * All rights reserved. */ #include "bpf_map/map_common_def.hpp" +#include "linux/bpf.h" #include "spdlog/spdlog.h" #include #include @@ -39,7 +40,7 @@ void *per_cpu_array_map_impl::elem_lookup(const void *key) } uint32_t key_val = *(uint32_t *)key; if (key_val >= max_ent) { - errno = E2BIG; + errno = ENOENT; return nullptr; } return data_at(key_val, cpu); @@ -49,6 +50,8 @@ void *per_cpu_array_map_impl::elem_lookup(const void *key) long per_cpu_array_map_impl::elem_update(const void *key, const void *value, uint64_t flags) { + if (!check_update_flags(flags)) + return -1; return ensure_on_current_cpu([&](int cpu) -> long { // return impl[cpu].elem_update(key, value, flags); if (key == nullptr) { @@ -56,6 +59,10 @@ long per_cpu_array_map_impl::elem_update(const void *key, const void *value, return -1; } uint32_t key_val = *(uint32_t *)key; + if (key_val < max_ent && flags == BPF_NOEXIST) { + errno = EEXIST; + return -1; + } if (key_val >= max_ent) { errno = E2BIG; return -1; @@ -68,13 +75,12 @@ long per_cpu_array_map_impl::elem_update(const void *key, const void *value, long per_cpu_array_map_impl::elem_delete(const void *key) { - errno = ENOTSUP; + errno = EINVAL; SPDLOG_DEBUG("Deleting of per cpu array is not supported"); return -1; } -int per_cpu_array_map_impl::map_get_next_key(const void *key, - void *next_key) +int per_cpu_array_map_impl::map_get_next_key(const void *key, void *next_key) { // Not found if (key == nullptr || *(uint32_t *)key >= max_ent) { @@ -100,7 +106,7 @@ void *per_cpu_array_map_impl::elem_lookup_userspace(const void *key) } uint32_t key_val = *(uint32_t *)key; if (key_val >= max_ent) { - errno = E2BIG; + errno = ENOENT; return nullptr; } return data_at(key_val, 0); @@ -110,11 +116,17 @@ long per_cpu_array_map_impl::elem_update_userspace(const void *key, const void *value, uint64_t flags) { + if (!check_update_flags(flags)) + return -1; if (key == nullptr) { errno = ENOENT; return -1; } uint32_t key_val = *(uint32_t *)key; + if (key_val < max_ent && flags == BPF_NOEXIST) { + errno = EEXIST; + return -1; + } if (key_val >= max_ent) { errno = E2BIG; return -1; @@ -126,7 +138,7 @@ long per_cpu_array_map_impl::elem_update_userspace(const void *key, long per_cpu_array_map_impl::elem_delete_userspace(const void *key) { - errno = ENOTSUP; + errno = EINVAL; SPDLOG_ERROR("Element delete is not supported by per cpu array"); return -1; } diff --git a/runtime/unit-test/maps/kernel_unit_tests.cpp b/runtime/unit-test/maps/kernel_unit_tests.cpp index 9c010438..e2189ff5 100644 --- a/runtime/unit-test/maps/kernel_unit_tests.cpp +++ b/runtime/unit-test/maps/kernel_unit_tests.cpp @@ -1,4 +1,5 @@ #include "bpf_map/userspace/array_map.hpp" +#include "bpf_map/userspace/per_cpu_array_map.hpp" #include "bpf_map/userspace/per_cpu_hash_map.hpp" #include "catch2/catch_test_macros.hpp" #include "linux/bpf.h" @@ -375,3 +376,62 @@ TEST_CASE("test_arraymap (kernel)") key = 1; REQUIRE((map.elem_delete(&key) < 0 && errno == EINVAL)); } + +TEST_CASE("test_arraymap_percpu (kernel)") +{ + shm_remove remover(SHM_NAME); + managed_shared_memory mem(boost::interprocess::create_only, SHM_NAME, + 20 << 20); + unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); + BPF_DECLARE_PERCPU(long, values); + int key, next_key, i; + per_cpu_array_map_impl map(mem, sizeof(bpf_percpu(values, 0)), 2); + const auto lookup_helper = [&](const void *key, void *value) -> long { + auto returned_value = map.elem_lookup_userspace(key); + if (!returned_value) + return -1; + memcpy(value, returned_value, sizeof(values)); + return 0; + }; + + for (i = 0; i < (int)nr_cpus; i++) + bpf_percpu(values, i) = i + 100; + + key = 1; + /* Insert key=1 element. */ + REQUIRE(map.elem_update_userspace(&key, values, BPF_ANY) == 0); + + bpf_percpu(values, 0) = 0; + REQUIRE((map.elem_update_userspace(&key, values, BPF_NOEXIST) < 0 && + errno == EEXIST)); + + /* Check that key=1 can be found. */ + REQUIRE((lookup_helper(&key, values) == 0 && + bpf_percpu(values, 0) == 100)); + + key = 0; + /* Check that key=0 is also found and zero initialized. */ + REQUIRE((lookup_helper(&key, values) == 0 && + bpf_percpu(values, 0) == 0 && + bpf_percpu(values, nr_cpus - 1) == 0)); + + /* Check that key=2 cannot be inserted due to max_entries limit. */ + key = 2; + REQUIRE((map.elem_update_userspace(&key, values, BPF_EXIST) < 0 && + errno == E2BIG)); + + /* Check that key = 2 doesn't exist. */ + REQUIRE((lookup_helper(&key, values) < 0 && errno == ENOENT)); + + /* Iterate over two elements. */ + REQUIRE((map.map_get_next_key(NULL, &next_key) == 0 && next_key == 0)); + REQUIRE((map.map_get_next_key(&key, &next_key) == 0 && next_key == 0)); + REQUIRE((map.map_get_next_key(&next_key, &next_key) == 0 && + next_key == 1)); + REQUIRE((map.map_get_next_key(&next_key, &next_key) < 0 && + errno == ENOENT)); + + /* Delete shouldn't succeed. */ + key = 1; + REQUIRE((map.elem_delete_userspace(&key) < 0 && errno == EINVAL)); +}