From c26e4a034138f24a132efe1ff83353ed2539a204 Mon Sep 17 00:00:00 2001 From: Lorenz Bauer Date: Fri, 1 Nov 2024 22:59:38 +0000 Subject: [PATCH] bpf(): allow creating nested maps (#3968) * bpf(): allow creating nested maps Add support for creating nested maps via the bpf() syscall wrapper. Updates #3729 * Address feedback from shpalani --- include/linux/bpf.h | 3 +- libs/api/bpf_syscall.cpp | 7 ++++- tests/unit/libbpf_test.cpp | 58 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 63ae172f1..eefd2d375 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -92,7 +92,8 @@ typedef struct uint32_t key_size; ///< Size in bytes of keys. uint32_t value_size; ///< Size in bytes of values. uint32_t max_entries; ///< Maximum number of entries in the map. - uint32_t map_flags; ///< Flags (currently 0). + uint32_t map_flags; ///< Not supported, must be zero. + uint32_t inner_map_fd; ///< File descriptor of inner map. } sys_bpf_map_create_attr_t; typedef struct diff --git a/libs/api/bpf_syscall.cpp b/libs/api/bpf_syscall.cpp index abd7a6026..a49238e7b 100644 --- a/libs/api/bpf_syscall.cpp +++ b/libs/api/bpf_syscall.cpp @@ -93,7 +93,12 @@ bpf(int cmd, union bpf_attr* attr, unsigned int size) } case BPF_MAP_CREATE: { ExtensibleStruct map_create_attr((void*)attr, (size_t)size); - struct bpf_map_create_opts opts = {.map_flags = map_create_attr->map_flags}; + + struct bpf_map_create_opts opts = { + .inner_map_fd = map_create_attr->inner_map_fd, + .map_flags = map_create_attr->map_flags, + }; + return bpf_map_create( map_create_attr->map_type, nullptr, diff --git a/tests/unit/libbpf_test.cpp b/tests/unit/libbpf_test.cpp index f69822a4d..4811a4adc 100644 --- a/tests/unit/libbpf_test.cpp +++ b/tests/unit/libbpf_test.cpp @@ -3006,6 +3006,64 @@ TEST_CASE("BPF_PROG_BIND_MAP etc.", "[libbpf]") attr.prog_bind_map.flags = 0; REQUIRE(bpf(BPF_PROG_BIND_MAP, &attr, sizeof(attr)) == 0); + // Verify we can create a nested array map. + uint32_t key = 0; + fd_t value = map_fd; + attr.map_create = {}; + attr.map_create.map_type = BPF_MAP_TYPE_ARRAY_OF_MAPS; + attr.map_create.key_size = sizeof(key); + attr.map_create.value_size = sizeof(value); + attr.map_create.max_entries = 1; + attr.map_create.inner_map_fd = map_fd; + int nested_map_fd = bpf(BPF_MAP_CREATE, &attr, sizeof(attr.map_create)); + REQUIRE(nested_map_fd >= 0); + + // Ensure we can insert map_fd into the outer map. + attr.map_update = {}; + attr.map_update.map_fd = nested_map_fd; + attr.map_update.key = (uintptr_t)&key; + attr.map_update.value = (uintptr_t)&value; + REQUIRE(bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr.map_update)) == 0); + + // Ensure that looking up the same key gives the ID of the inner map. + ebpf_id_t value_id = 0; + attr.map_lookup = {}; + attr.map_lookup.map_fd = nested_map_fd; + attr.map_lookup.key = (uintptr_t)&key; + attr.map_lookup.value = (uintptr_t)&value_id; + REQUIRE(bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr.map_lookup)) == 0); + REQUIRE(value_id == map_id); + + Platform::_close(nested_map_fd); + + // Verify we can create a nested hash map. + attr.map_create = {}; + attr.map_create.map_type = BPF_MAP_TYPE_HASH_OF_MAPS; + attr.map_create.key_size = sizeof(key); + attr.map_create.value_size = sizeof(value); + attr.map_create.max_entries = 1; + attr.map_create.inner_map_fd = map_fd; + nested_map_fd = bpf(BPF_MAP_CREATE, &attr, sizeof(attr.map_create)); + REQUIRE(nested_map_fd >= 0); + + // Ensure we can insert map_fd into the outer map. + attr.map_update = {}; + attr.map_update.map_fd = nested_map_fd; + attr.map_update.key = (uintptr_t)&key; + attr.map_update.value = (uintptr_t)&value; + REQUIRE(bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr.map_update)) == 0); + + // Ensure that looking up the same key gives the ID of the inner map. + value_id = 0; + attr.map_lookup = {}; + attr.map_lookup.map_fd = nested_map_fd; + attr.map_lookup.key = (uintptr_t)&key; + attr.map_lookup.value = (uintptr_t)&value_id; + REQUIRE(bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr.map_lookup)) == 0); + REQUIRE(value_id == map_id); + + Platform::_close(nested_map_fd); + // Release our own references on the map and program. Platform::_close(map_fd); Platform::_close(program_fd);