Skip to content

Commit 624a8b7

Browse files
lmbti-mo
authored andcommitted
Revert "btf: avoid copies of vmlinux BTF for internal callers"
This reverts commit c3a89fc. I've come to the conclusion that a separate package isn't the right approach. Parsing vmlinux is still too expensive and will stay this way as long as we unmarshal the entire type graph when parsing BTF. I think that the real solution will involve breaking API once more, probably in the next release. I'm backing out the change to avoid this churn. The downside is that kfunc, CO-RE and attaching programs like fentry get slower again. Since they were already slow in the last release I'm hoping that this doesn't cause too many problems. Signed-off-by: Lorenz Bauer <[email protected]>
1 parent 4267cbc commit 624a8b7

File tree

8 files changed

+142
-179
lines changed

8 files changed

+142
-179
lines changed

btf/btf.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"math"
1111
"os"
1212
"reflect"
13+
"sync"
1314

1415
"github.com/cilium/ebpf/internal"
1516
"github.com/cilium/ebpf/internal/sys"
@@ -297,6 +298,107 @@ func indexTypes(types []Type, firstTypeID TypeID) (map[Type]TypeID, map[essentia
297298
return typeIDs, typesByName
298299
}
299300

301+
// LoadKernelSpec returns the current kernel's BTF information.
302+
//
303+
// Defaults to /sys/kernel/btf/vmlinux and falls back to scanning the file system
304+
// for vmlinux ELFs. Returns an error wrapping ErrNotSupported if BTF is not enabled.
305+
func LoadKernelSpec() (*Spec, error) {
306+
spec, _, err := kernelSpec()
307+
if err != nil {
308+
return nil, err
309+
}
310+
return spec.Copy(), nil
311+
}
312+
313+
var kernelBTF struct {
314+
sync.RWMutex
315+
spec *Spec
316+
// True if the spec was read from an ELF instead of raw BTF in /sys.
317+
fallback bool
318+
}
319+
320+
// FlushKernelSpec removes any cached kernel type information.
321+
func FlushKernelSpec() {
322+
kernelBTF.Lock()
323+
defer kernelBTF.Unlock()
324+
325+
kernelBTF.spec, kernelBTF.fallback = nil, false
326+
}
327+
328+
func kernelSpec() (*Spec, bool, error) {
329+
kernelBTF.RLock()
330+
spec, fallback := kernelBTF.spec, kernelBTF.fallback
331+
kernelBTF.RUnlock()
332+
333+
if spec == nil {
334+
kernelBTF.Lock()
335+
defer kernelBTF.Unlock()
336+
337+
spec, fallback = kernelBTF.spec, kernelBTF.fallback
338+
}
339+
340+
if spec != nil {
341+
return spec, fallback, nil
342+
}
343+
344+
spec, fallback, err := loadKernelSpec()
345+
if err != nil {
346+
return nil, false, err
347+
}
348+
349+
kernelBTF.spec, kernelBTF.fallback = spec, fallback
350+
return spec, fallback, nil
351+
}
352+
353+
func loadKernelSpec() (_ *Spec, fallback bool, _ error) {
354+
fh, err := os.Open("/sys/kernel/btf/vmlinux")
355+
if err == nil {
356+
defer fh.Close()
357+
358+
spec, err := loadRawSpec(fh, internal.NativeEndian, nil)
359+
return spec, false, err
360+
}
361+
362+
file, err := findVMLinux()
363+
if err != nil {
364+
return nil, false, err
365+
}
366+
defer file.Close()
367+
368+
spec, err := loadSpecFromELF(file)
369+
return spec, true, err
370+
}
371+
372+
// findVMLinux scans multiple well-known paths for vmlinux kernel images.
373+
func findVMLinux() (*internal.SafeELFFile, error) {
374+
release, err := internal.KernelRelease()
375+
if err != nil {
376+
return nil, err
377+
}
378+
379+
// use same list of locations as libbpf
380+
// https://github.com/libbpf/libbpf/blob/9a3a42608dbe3731256a5682a125ac1e23bced8f/src/btf.c#L3114-L3122
381+
locations := []string{
382+
"/boot/vmlinux-%s",
383+
"/lib/modules/%s/vmlinux-%[1]s",
384+
"/lib/modules/%s/build/vmlinux",
385+
"/usr/lib/modules/%s/kernel/vmlinux",
386+
"/usr/lib/debug/boot/vmlinux-%s",
387+
"/usr/lib/debug/boot/vmlinux-%s.debug",
388+
"/usr/lib/debug/lib/modules/%s/vmlinux",
389+
}
390+
391+
for _, loc := range locations {
392+
file, err := internal.OpenSafeELFFile(fmt.Sprintf(loc, release))
393+
if errors.Is(err, os.ErrNotExist) {
394+
continue
395+
}
396+
return file, err
397+
}
398+
399+
return nil, fmt.Errorf("no BTF found for kernel version %s: %w", release, internal.ErrNotSupported)
400+
}
401+
300402
// parseBTFHeader parses the header of the .BTF section.
301403
func parseBTFHeader(r io.Reader, bo binary.ByteOrder) (*btfHeader, error) {
302404
var header btfHeader

btf/btf_test.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@ func vmlinuxSpec(tb testing.TB) *Spec {
2121
// through sysfs"), which shipped in Linux 5.4.
2222
testutils.SkipOnOldKernel(tb, "5.4", "vmlinux BTF in sysfs")
2323

24-
spec, err := LoadSpec("/sys/kernel/btf/vmlinux")
24+
spec, fallback, err := kernelSpec()
2525
if err != nil {
2626
tb.Fatal(err)
2727
}
28+
if fallback {
29+
tb.Fatal("/sys/kernel/btf/vmlinux is not available")
30+
}
2831
return spec.Copy()
2932
}
3033

@@ -240,6 +243,24 @@ func TestParseCurrentKernelBTF(t *testing.T) {
240243
t.Logf("Average string size: %.0f", float64(totalBytes)/float64(len(spec.strings.strings)))
241244
}
242245

246+
func TestFindVMLinux(t *testing.T) {
247+
file, err := findVMLinux()
248+
testutils.SkipIfNotSupported(t, err)
249+
if err != nil {
250+
t.Fatal("Can't find vmlinux:", err)
251+
}
252+
defer file.Close()
253+
254+
spec, err := loadSpecFromELF(file)
255+
if err != nil {
256+
t.Fatal("Can't load BTF:", err)
257+
}
258+
259+
if len(spec.namedTypes) == 0 {
260+
t.Fatal("Empty kernel BTF")
261+
}
262+
}
263+
243264
func TestLoadSpecFromElf(t *testing.T) {
244265
testutils.Files(t, testutils.Glob(t, "../testdata/loader-e*.elf"), func(t *testing.T, file string) {
245266
spec := parseELFBTF(t, file)
@@ -295,6 +316,17 @@ func TestVerifierError(t *testing.T) {
295316
}
296317
}
297318

319+
func TestLoadKernelSpec(t *testing.T) {
320+
if _, err := os.Stat("/sys/kernel/btf/vmlinux"); os.IsNotExist(err) {
321+
t.Skip("/sys/kernel/btf/vmlinux not present")
322+
}
323+
324+
_, err := LoadKernelSpec()
325+
if err != nil {
326+
t.Fatal("Can't load kernel spec:", err)
327+
}
328+
}
329+
298330
func TestGuessBTFByteOrder(t *testing.T) {
299331
bo := guessRawBTFByteOrder(vmlinuxTestdataReader(t))
300332
if bo != binary.LittleEndian {

btf/core.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,11 @@ func (k coreKind) String() string {
166166
// for relos[i].
167167
func CORERelocate(relos []*CORERelocation, target *Spec, bo binary.ByteOrder) ([]COREFixup, error) {
168168
if target == nil {
169-
// Explicitly check for nil here since the argument used to be optional.
170-
return nil, fmt.Errorf("target must be provided")
169+
var err error
170+
target, _, err = kernelSpec()
171+
if err != nil {
172+
return nil, fmt.Errorf("load kernel spec: %w", err)
173+
}
171174
}
172175

173176
if bo != target.byteOrder {

internal/linux/types.go

Lines changed: 0 additions & 107 deletions
This file was deleted.

internal/linux/types_test.go

Lines changed: 0 additions & 33 deletions
This file was deleted.

linker.go

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"github.com/cilium/ebpf/asm"
1111
"github.com/cilium/ebpf/btf"
1212
"github.com/cilium/ebpf/internal"
13-
"github.com/cilium/ebpf/internal/linux"
1413
)
1514

1615
// handles stores handle objects to avoid gc cleanup
@@ -124,14 +123,6 @@ func applyRelocations(insns asm.Instructions, target *btf.Spec, bo binary.ByteOr
124123
bo = internal.NativeEndian
125124
}
126125

127-
if target == nil {
128-
var err error
129-
target, err = linux.TypesNoCopy()
130-
if err != nil {
131-
return err
132-
}
133-
}
134-
135126
fixups, err := btf.CORERelocate(relos, target, bo)
136127
if err != nil {
137128
return err
@@ -253,7 +244,7 @@ func fixupKfuncs(insns asm.Instructions) (handles, error) {
253244

254245
fixups:
255246
// only load the kernel spec if we found at least one kfunc call
256-
kernelSpec, err := linux.TypesNoCopy()
247+
kernelSpec, err := btf.LoadKernelSpec()
257248
if err != nil {
258249
return nil, err
259250
}

linux/types.go

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)