Skip to content

Commit f7f055b

Browse files
lmbti-mo
authored andcommitted
map: build BTF on the fly
Use btf.Encoder to write out MapSpec.Key and MapSpec.Value if necessary. MapSpec.BTF is now unused, and therefore deprecated like ProgramSpec.BTF.
1 parent ad9f96e commit f7f055b

10 files changed

+90
-81
lines changed

btf/marshal.go

+29
Original file line numberDiff line numberDiff line change
@@ -348,3 +348,32 @@ func (e *encoder) deflateVarSecinfos(vars []VarSecinfo) []btfVarSecinfo {
348348
}
349349
return vsis
350350
}
351+
352+
// MarshalMapKV creates a BTF object containing a map key and value.
353+
//
354+
// The function is intended for the use of the ebpf package and may be removed
355+
// at any point in time.
356+
func MarshalMapKV(key, value Type) (_ *Handle, keyID, valueID TypeID, _ error) {
357+
enc := nativeEncoderPool.Get().(*encoder)
358+
defer nativeEncoderPool.Put(enc)
359+
360+
enc.Reset()
361+
362+
keyID, err := enc.Add(key)
363+
if err != nil {
364+
return nil, 0, 0, err
365+
}
366+
367+
valueID, err = enc.Add(value)
368+
if err != nil {
369+
return nil, 0, 0, err
370+
}
371+
372+
btf, err := enc.Encode()
373+
if err != nil {
374+
return nil, 0, 0, fmt.Errorf("marshal BTF: %w", err)
375+
}
376+
377+
handle, err := newHandleFromRawBTF(btf)
378+
return handle, keyID, valueID, err
379+
}

collection.go

+1-34
Original file line numberDiff line numberDiff line change
@@ -386,42 +386,11 @@ func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Co
386386
}, nil
387387
}
388388

389-
type handleCache struct {
390-
btfHandles map[*btf.Spec]*btf.Handle
391-
}
392-
393-
func newHandleCache() *handleCache {
394-
return &handleCache{
395-
btfHandles: make(map[*btf.Spec]*btf.Handle),
396-
}
397-
}
398-
399-
func (hc handleCache) btfHandle(spec *btf.Spec) (*btf.Handle, error) {
400-
if hc.btfHandles[spec] != nil {
401-
return hc.btfHandles[spec], nil
402-
}
403-
404-
handle, err := btf.NewHandle(spec)
405-
if err != nil {
406-
return nil, err
407-
}
408-
409-
hc.btfHandles[spec] = handle
410-
return handle, nil
411-
}
412-
413-
func (hc handleCache) close() {
414-
for _, handle := range hc.btfHandles {
415-
handle.Close()
416-
}
417-
}
418-
419389
type collectionLoader struct {
420390
coll *CollectionSpec
421391
opts *CollectionOptions
422392
maps map[string]*Map
423393
programs map[string]*Program
424-
handles *handleCache
425394
}
426395

427396
func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collectionLoader, error) {
@@ -446,13 +415,11 @@ func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collec
446415
opts,
447416
make(map[string]*Map),
448417
make(map[string]*Program),
449-
newHandleCache(),
450418
}, nil
451419
}
452420

453421
// close all resources left over in the collectionLoader.
454422
func (cl *collectionLoader) close() {
455-
cl.handles.close()
456423
for _, m := range cl.maps {
457424
m.Close()
458425
}
@@ -486,7 +453,7 @@ func (cl *collectionLoader) loadMap(mapName string) (*Map, error) {
486453
return m, nil
487454
}
488455

489-
m, err := newMapWithOptions(mapSpec, cl.opts.Maps, cl.handles)
456+
m, err := newMapWithOptions(mapSpec, cl.opts.Maps)
490457
if err != nil {
491458
return nil, fmt.Errorf("map %s: %w", mapName, err)
492459
}

elf_reader.go

-7
Original file line numberDiff line numberDiff line change
@@ -897,13 +897,6 @@ func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *b
897897
}
898898
}
899899

900-
if key == nil {
901-
key = &btf.Void{}
902-
}
903-
if value == nil {
904-
value = &btf.Void{}
905-
}
906-
907900
return &MapSpec{
908901
Name: SanitizeName(name, -1),
909902
Type: MapType(mapType),

elf_reader_test.go

+29
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,19 @@ func TestLoadInitializedBTFMap(t *testing.T) {
361361
t.Fatal(err)
362362
}
363363

364+
t.Run("NewCollection", func(t *testing.T) {
365+
if coll.ByteOrder != internal.NativeEndian {
366+
t.Skipf("Skipping %s collection", coll.ByteOrder)
367+
}
368+
369+
tmp, err := NewCollection(coll)
370+
testutils.SkipIfNotSupported(t, err)
371+
if err != nil {
372+
t.Fatal("NewCollection failed:", err)
373+
}
374+
tmp.Close()
375+
})
376+
364377
t.Run("prog_array", func(t *testing.T) {
365378
m, ok := coll.Maps["prog_array_init"]
366379
if !ok {
@@ -395,6 +408,22 @@ func TestLoadInitializedBTFMap(t *testing.T) {
395408
t.Error("expecting exactly 1 item in MapSpec contents")
396409
}
397410

411+
if m.Key == nil {
412+
t.Error("Expected non-nil key")
413+
}
414+
415+
if m.Value == nil {
416+
t.Error("Expected non-nil value")
417+
}
418+
419+
if m.InnerMap.Key == nil {
420+
t.Error("Expected non-nil InnerMap key")
421+
}
422+
423+
if m.InnerMap.Value == nil {
424+
t.Error("Expected non-nil InnerMap value")
425+
}
426+
398427
p := m.Contents[0]
399428
if cmp.Equal(p.Key, 1) {
400429
t.Errorf("expecting MapSpec entry Key to equal 1, got %v", p.Key)

map.go

+16-30
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ type MapSpec struct {
7979
Key, Value btf.Type
8080

8181
// The BTF associated with this map.
82+
//
83+
// Deprecated: use [CollectionSpec.Types] instead.
8284
BTF *btf.Spec
8385
}
8486

@@ -104,12 +106,6 @@ func (ms *MapSpec) Copy() *MapSpec {
104106
return &cpy
105107
}
106108

107-
// hasBTF returns true if the MapSpec has a valid BTF spec and if its
108-
// map type supports associated BTF metadata in the kernel.
109-
func (ms *MapSpec) hasBTF() bool {
110-
return ms.BTF != nil && ms.Type.hasBTF()
111-
}
112-
113109
func (ms *MapSpec) clampPerfEventArraySize() error {
114110
if ms.Type != PerfEventArray {
115111
return nil
@@ -241,10 +237,7 @@ func NewMap(spec *MapSpec) (*Map, error) {
241237
//
242238
// May return an error wrapping ErrMapIncompatible.
243239
func NewMapWithOptions(spec *MapSpec, opts MapOptions) (*Map, error) {
244-
handles := newHandleCache()
245-
defer handles.close()
246-
247-
m, err := newMapWithOptions(spec, opts, handles)
240+
m, err := newMapWithOptions(spec, opts)
248241
if err != nil {
249242
return nil, fmt.Errorf("creating map: %w", err)
250243
}
@@ -257,7 +250,7 @@ func NewMapWithOptions(spec *MapSpec, opts MapOptions) (*Map, error) {
257250
return m, nil
258251
}
259252

260-
func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_ *Map, err error) {
253+
func newMapWithOptions(spec *MapSpec, opts MapOptions) (_ *Map, err error) {
261254
closeOnError := func(c io.Closer) {
262255
if err != nil {
263256
c.Close()
@@ -307,7 +300,7 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_
307300
return nil, errors.New("inner maps cannot be pinned")
308301
}
309302

310-
template, err := spec.InnerMap.createMap(nil, opts, handles)
303+
template, err := spec.InnerMap.createMap(nil, opts)
311304
if err != nil {
312305
return nil, fmt.Errorf("inner map: %w", err)
313306
}
@@ -319,7 +312,7 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_
319312
innerFd = template.fd
320313
}
321314

322-
m, err := spec.createMap(innerFd, opts, handles)
315+
m, err := spec.createMap(innerFd, opts)
323316
if err != nil {
324317
return nil, err
325318
}
@@ -337,7 +330,7 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_
337330

338331
// createMap validates the spec's properties and creates the map in the kernel
339332
// using the given opts. It does not populate or freeze the map.
340-
func (spec *MapSpec) createMap(inner *sys.FD, opts MapOptions, handles *handleCache) (_ *Map, err error) {
333+
func (spec *MapSpec) createMap(inner *sys.FD, opts MapOptions) (_ *Map, err error) {
341334
closeOnError := func(closer io.Closer) {
342335
if err != nil {
343336
closer.Close()
@@ -425,22 +418,15 @@ func (spec *MapSpec) createMap(inner *sys.FD, opts MapOptions, handles *handleCa
425418
attr.MapName = sys.NewObjName(spec.Name)
426419
}
427420

428-
if spec.hasBTF() {
429-
handle, err := handles.btfHandle(spec.BTF)
430-
if err != nil && !errors.Is(err, btf.ErrNotSupported) {
421+
btfDisabled := false
422+
if spec.Type.supportsBTF() && (spec.Key != nil || spec.Value != nil) {
423+
handle, keyTypeID, valueTypeID, err := btf.MarshalMapKV(spec.Key, spec.Value)
424+
btfDisabled = errors.Is(err, btf.ErrNotSupported)
425+
if err != nil && !btfDisabled {
431426
return nil, fmt.Errorf("load BTF: %w", err)
432427
}
433-
434428
if handle != nil {
435-
keyTypeID, err := spec.BTF.TypeID(spec.Key)
436-
if err != nil {
437-
return nil, err
438-
}
439-
440-
valueTypeID, err := spec.BTF.TypeID(spec.Value)
441-
if err != nil {
442-
return nil, err
443-
}
429+
defer handle.Close()
444430

445431
attr.BtfFd = uint32(handle.FD())
446432
attr.BtfKeyTypeId = uint32(keyTypeID)
@@ -453,12 +439,12 @@ func (spec *MapSpec) createMap(inner *sys.FD, opts MapOptions, handles *handleCa
453439
if errors.Is(err, unix.EPERM) {
454440
return nil, fmt.Errorf("map create: %w (MEMLOCK may be too low, consider rlimit.RemoveMemlock)", err)
455441
}
456-
if !spec.hasBTF() {
457-
return nil, fmt.Errorf("map create without BTF: %w", err)
458-
}
459442
if errors.Is(err, unix.EINVAL) && attr.MaxEntries == 0 {
460443
return nil, fmt.Errorf("map create: %w (MaxEntries may be incorrectly set to zero)", err)
461444
}
445+
if btfDisabled {
446+
return nil, fmt.Errorf("map create: %w (kernel without BTF support)", err)
447+
}
462448
return nil, fmt.Errorf("map create: %w", err)
463449
}
464450
defer closeOnError(fd)

map_test.go

+11-6
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"unsafe"
1313

1414
"github.com/cilium/ebpf/asm"
15-
"github.com/cilium/ebpf/btf"
1615
"github.com/cilium/ebpf/internal"
1716
"github.com/cilium/ebpf/internal/sys"
1817
"github.com/cilium/ebpf/internal/testutils"
@@ -1655,22 +1654,28 @@ func TestMapPinning(t *testing.T) {
16551654
pinned := m1.IsPinned()
16561655
c.Assert(pinned, qt.IsTrue)
16571656

1657+
m1Info, err := m1.Info()
1658+
c.Assert(err, qt.IsNil)
1659+
16581660
if err := m1.Put(uint32(0), uint32(42)); err != nil {
16591661
t.Fatal("Can't write value:", err)
16601662
}
16611663

1662-
// This is a terrible hack: if loading a pinned map tries to load BTF,
1663-
// it will get a nil *btf.Spec from this *btf.Map. This is turn will make
1664-
// btf.NewHandle fail.
1665-
spec.BTF = new(btf.Spec)
1666-
16671664
m2, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp})
16681665
testutils.SkipIfNotSupported(t, err)
16691666
if err != nil {
16701667
t.Fatal("Can't create map:", err)
16711668
}
16721669
defer m2.Close()
16731670

1671+
m2Info, err := m2.Info()
1672+
c.Assert(err, qt.IsNil)
1673+
1674+
if m1ID, ok := m1Info.ID(); ok {
1675+
m2ID, _ := m2Info.ID()
1676+
c.Assert(m2ID, qt.Equals, m1ID)
1677+
}
1678+
16741679
var value uint32
16751680
if err := m2.Lookup(uint32(0), &value); err != nil {
16761681
t.Fatal("Can't read from map:", err)

testdata/btf_map_init-eb.elf

-8 Bytes
Binary file not shown.

testdata/btf_map_init-el.elf

-8 Bytes
Binary file not shown.

testdata/btf_map_init.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ struct {
4343
struct {
4444
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
4545
__uint(max_entries, 2);
46-
__uint(key_size, sizeof(uint32_t));
47-
__uint(value_size, sizeof(uint32_t));
46+
__type(key, uint32_t);
47+
__type(value, uint32_t);
4848
__array(values, typeof(inner_map));
4949
} outer_map_init __section(".maps") = {
5050
.values =

types.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ func (mt MapType) canStoreProgram() bool {
121121
return mt == ProgramArray
122122
}
123123

124-
// hasBTF returns true if the map type supports BTF key/value metadata.
125-
func (mt MapType) hasBTF() bool {
124+
// supportsBTF returns true if the map type supports BTF key/value metadata.
125+
func (mt MapType) supportsBTF() bool {
126126
switch mt {
127127
case PerfEventArray, CGroupArray, StackTrace, ArrayOfMaps, HashOfMaps, DevMap,
128128
DevMapHash, CPUMap, XSKMap, SockMap, SockHash, Queue, Stack, RingBuf:

0 commit comments

Comments
 (0)