]> git.feebdaed.xyz Git - 0xmirror/ebpf.git/commitdiff
map: preliminary support for arenas
authorTimo Beckers <timo@isovalent.com>
Tue, 9 Sep 2025 12:12:33 +0000 (14:12 +0200)
committerTimo Beckers <ti-mo@users.noreply.github.com>
Tue, 9 Sep 2025 12:50:16 +0000 (14:50 +0200)
This commit teaches ebpf-go to understand the __ulong BTF map definition macro
and uses it to specify a 64-bit value for map_extra in an arena map, defining
the start of the mmap region for the Collection's arena.

Signed-off-by: Timo Beckers <timo@isovalent.com>
Makefile
elf_reader.go
elf_reader_test.go
testdata/arena-eb.elf [new file with mode: 0644]
testdata/arena-el.elf [new file with mode: 0644]
testdata/arena.c [new file with mode: 0644]
testdata/common.h

index 45462e8d5a8924c557ce865b10c77c5cd96da0ba..0ffc844ebc170a816910e1b8512638e03f050073 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -49,6 +49,7 @@ TARGETS := \
        testdata/constants \
        testdata/errors \
        testdata/variables \
+       testdata/arena \
        btf/testdata/relocs \
        btf/testdata/relocs_read \
        btf/testdata/relocs_read_tgt \
index 1e02ef8ee198e444dfcd1ae62cfb07096a5f38b1..f2c9196b72160b97dc59005df685abbd1f1d165a 100644 (file)
@@ -873,9 +873,9 @@ func (ec *elfCode) loadBTFMaps() error {
 func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *btf.Spec, name string, inner bool) (*MapSpec, error) {
        var (
                key, value         btf.Type
-               keySize, valueSize uint32
+               keySize, valueSize uint64
                mapType            MapType
-               flags, maxEntries  uint32
+               flags, maxEntries  uint64
                pinType            PinType
                mapExtra           uint64
                innerMapSpec       *MapSpec
@@ -921,7 +921,7 @@ func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *b
                                return nil, fmt.Errorf("can't get size of BTF key: %w", err)
                        }
 
-                       keySize = uint32(size)
+                       keySize = uint64(size)
 
                case "value":
                        if valueSize != 0 {
@@ -940,7 +940,7 @@ func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *b
                                return nil, fmt.Errorf("can't get size of BTF value: %w", err)
                        }
 
-                       valueSize = uint32(size)
+                       valueSize = uint64(size)
 
                case "key_size":
                        // Key needs to be nil and keySize needs to be 0 for key_size to be
@@ -1061,10 +1061,10 @@ func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *b
        return &MapSpec{
                Name:       sanitizeName(name, -1),
                Type:       MapType(mapType),
-               KeySize:    keySize,
-               ValueSize:  valueSize,
-               MaxEntries: maxEntries,
-               Flags:      flags,
+               KeySize:    uint32(keySize),
+               ValueSize:  uint32(valueSize),
+               MaxEntries: uint32(maxEntries),
+               Flags:      uint32(flags),
                Key:        key,
                Value:      value,
                Pinning:    pinType,
@@ -1075,20 +1075,31 @@ func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *b
        }, nil
 }
 
-// uintFromBTF resolves the __uint macro, which is a pointer to a sized
-// array, e.g. for int (*foo)[10], this function will return 10.
-func uintFromBTF(typ btf.Type) (uint32, error) {
-       ptr, ok := typ.(*btf.Pointer)
-       if !ok {
-               return 0, fmt.Errorf("not a pointer: %v", typ)
-       }
+// uintFromBTF resolves the __uint and __ulong macros.
+//
+// __uint emits a pointer to a sized array. For int (*foo)[10], this function
+// will return 10.
+//
+// __ulong emits an enum with a single value that can represent a 64-bit
+// integer. The first (and only) enum value is returned.
+func uintFromBTF(typ btf.Type) (uint64, error) {
+       switch t := typ.(type) {
+       case *btf.Pointer:
+               arr, ok := t.Target.(*btf.Array)
+               if !ok {
+                       return 0, fmt.Errorf("not a pointer to array: %v", typ)
+               }
+               return uint64(arr.Nelems), nil
 
-       arr, ok := ptr.Target.(*btf.Array)
-       if !ok {
-               return 0, fmt.Errorf("not a pointer to array: %v", typ)
-       }
+       case *btf.Enum:
+               if len(t.Values) == 0 {
+                       return 0, errors.New("enum has no values")
+               }
+               return t.Values[0].Value, nil
 
-       return arr.Nelems, nil
+       default:
+               return 0, fmt.Errorf("not a pointer or enum: %v", typ)
+       }
 }
 
 // resolveBTFArrayMacro resolves the __array macro, which declares an array
index 1d0a52aaf9380a1ab2065d311e182ce93b2253c0..d7909c8643e7ab5abfd747ab0534cab13c56f478 100644 (file)
@@ -943,6 +943,30 @@ func TestIPRoute2Compat(t *testing.T) {
        coll.Close()
 }
 
+func TestArena(t *testing.T) {
+       file := testutils.NativeFile(t, "testdata/arena-%s.elf")
+       coll, err := LoadCollectionSpec(file)
+       qt.Assert(t, qt.IsNil(err))
+
+       want := &CollectionSpec{
+               Maps: map[string]*MapSpec{
+                       "arena": {
+                               Name:       "arena",
+                               Type:       Arena,
+                               MaxEntries: 100,
+                               Flags:      sys.BPF_F_MMAPABLE,
+                               MapExtra:   1 << 44,
+                       },
+               },
+               Programs:  map[string]*ProgramSpec{},
+               Variables: map[string]*VariableSpec{},
+       }
+       qt.Assert(t, qt.CmpEquals(coll, want, csCmpOpts))
+
+       testutils.SkipOnOldKernel(t, "6.9", "arena maps")
+       mustNewCollection(t, coll, nil)
+}
+
 var (
        elfPath    = flag.String("elfs", os.Getenv("CI_KERNEL_SELFTESTS"), "`Path` containing libbpf-compatible ELFs (defaults to $CI_KERNEL_SELFTESTS)")
        elfPattern = flag.String("elf-pattern", "*.o", "Glob `pattern` for object files that should be tested")
@@ -997,8 +1021,6 @@ func TestLibBPFCompat(t *testing.T) {
                        t.Skip("Skipping since the test generates dynamic BTF")
                case "test_static_linked":
                        t.Skip("Skipping since .text contains 'subprog' twice")
-               case "bloom_filter_map", "bloom_filter_bench":
-                       t.Skip("Skipping due to missing MapExtra field in MapSpec")
                case "netif_receive_skb",
                        "local_kptr_stash",
                        "local_kptr_stash_fail",
diff --git a/testdata/arena-eb.elf b/testdata/arena-eb.elf
new file mode 100644 (file)
index 0000000..e6e0522
Binary files /dev/null and b/testdata/arena-eb.elf differ
diff --git a/testdata/arena-el.elf b/testdata/arena-el.elf
new file mode 100644 (file)
index 0000000..a04fdd7
Binary files /dev/null and b/testdata/arena-el.elf differ
diff --git a/testdata/arena.c b/testdata/arena.c
new file mode 100644 (file)
index 0000000..85d6555
--- /dev/null
@@ -0,0 +1,10 @@
+/* This file excercises the ELF loader. It is not a valid BPF program. */
+
+#include "common.h"
+
+struct {
+       __uint(type, BPF_MAP_TYPE_ARENA);
+       __uint(map_flags, BPF_F_MMAPABLE);
+       __uint(max_entries, 100); /* number of pages */
+       __ulong(map_extra, 0x1ull << 44); /* start of mmap region */
+} arena __section(".maps");
index a8c81a0cb050a8b169fb15336a4ff7c6d0e128ea..ffe55c115a67cdff67f977e85e3c6cb529892ffc 100644 (file)
@@ -13,10 +13,14 @@ enum libbpf_tristate {
        TRI_MODULE = 2,
 };
 
+#define ___bpf_concat(a, b) ____bpf_concat(a, b)
+#define ____bpf_concat(a, b) a ## b
+
 #define __section(NAME) __attribute__((section(NAME), used))
 #define __uint(name, val) int(*name)[val]
 #define __type(name, val) typeof(val) *name
 #define __array(name, val) typeof(val) *name[]
+#define __ulong(name, val) enum { ___bpf_concat(__unique_value, __COUNTER__) = val } name
 
 #define __kconfig __attribute__((section(".kconfig")))
 #define __ksym __attribute__((section(".ksyms")))
@@ -40,8 +44,10 @@ enum libbpf_tristate {
 #define BPF_MAP_TYPE_PERF_EVENT_ARRAY (4)
 #define BPF_MAP_TYPE_ARRAY_OF_MAPS (12)
 #define BPF_MAP_TYPE_HASH_OF_MAPS (13)
+#define BPF_MAP_TYPE_ARENA (33)
 
 #define BPF_F_NO_PREALLOC (1U << 0)
+#define BPF_F_MMAPABLE (1U << 10)
 #define BPF_F_CURRENT_CPU (0xffffffffULL)
 
 /* From tools/lib/bpf/libbpf.h */