Skip to content

Commit 7c861fd

Browse files
committed
program: use cached BTF for fixups and target search
Extend the BTF cache to fixups and target search. This means that each collection load will load each BTF blob at most once. Unexport fields in btf.Cache since we don't need to short-circuit BTF loading from package ebpf anymore. Signed-off-by: Lorenz Bauer <[email protected]>
1 parent 5ba3156 commit 7c861fd

File tree

5 files changed

+67
-74
lines changed

5 files changed

+67
-74
lines changed

btf/kernel.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,9 @@ func findVMLinux() (*os.File, error) {
225225
//
226226
// It is not safe for concurrent use.
227227
type Cache struct {
228-
KernelTypes *Spec
229-
ModuleTypes map[string]*Spec
230-
LoadedModules []string
228+
kernelTypes *Spec
229+
moduleTypes map[string]*Spec
230+
loadedModules []string
231231
}
232232

233233
// NewCache creates a new Cache.
@@ -262,26 +262,26 @@ func NewCache() *Cache {
262262
// Kernel is equivalent to [LoadKernelSpec], except that repeated calls do
263263
// not copy the Spec.
264264
func (c *Cache) Kernel() (*Spec, error) {
265-
if c.KernelTypes != nil {
266-
return c.KernelTypes, nil
265+
if c.kernelTypes != nil {
266+
return c.kernelTypes, nil
267267
}
268268

269269
var err error
270-
c.KernelTypes, err = LoadKernelSpec()
271-
return c.KernelTypes, err
270+
c.kernelTypes, err = LoadKernelSpec()
271+
return c.kernelTypes, err
272272
}
273273

274274
// Module is equivalent to [LoadKernelModuleSpec], except that repeated calls do
275275
// not copy the spec.
276276
//
277277
// All modules also share the return value of [Kernel] as their base.
278278
func (c *Cache) Module(name string) (*Spec, error) {
279-
if spec := c.ModuleTypes[name]; spec != nil {
279+
if spec := c.moduleTypes[name]; spec != nil {
280280
return spec, nil
281281
}
282282

283-
if c.ModuleTypes == nil {
284-
c.ModuleTypes = make(map[string]*Spec)
283+
if c.moduleTypes == nil {
284+
c.moduleTypes = make(map[string]*Spec)
285285
}
286286

287287
base, err := c.Kernel()
@@ -302,14 +302,14 @@ func (c *Cache) Module(name string) (*Spec, error) {
302302
}
303303

304304
spec = &Spec{decoder: decoder}
305-
c.ModuleTypes[name] = spec
305+
c.moduleTypes[name] = spec
306306
return spec, err
307307
}
308308

309309
// Modules returns a sorted list of all loaded modules.
310310
func (c *Cache) Modules() ([]string, error) {
311-
if c.LoadedModules != nil {
312-
return c.LoadedModules, nil
311+
if c.loadedModules != nil {
312+
return c.loadedModules, nil
313313
}
314314

315315
btfDir, err := os.Open("/sys/kernel/btf")
@@ -328,6 +328,6 @@ func (c *Cache) Modules() ([]string, error) {
328328
})
329329

330330
sort.Strings(entries)
331-
c.LoadedModules = entries
331+
c.loadedModules = entries
332332
return entries, nil
333333
}

btf/kernel_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ func TestCache(t *testing.T) {
4343
FlushKernelSpec()
4444
c := NewCache()
4545

46-
qt.Assert(t, qt.IsNil(c.KernelTypes))
47-
qt.Assert(t, qt.HasLen(c.ModuleTypes, 0))
48-
qt.Assert(t, qt.IsNil(c.LoadedModules))
46+
qt.Assert(t, qt.IsNil(c.kernelTypes))
47+
qt.Assert(t, qt.HasLen(c.moduleTypes, 0))
48+
qt.Assert(t, qt.IsNil(c.loadedModules))
4949

5050
// Test that Kernel() creates only one copy
5151
spec1, err := c.Kernel()
@@ -83,15 +83,15 @@ func TestCache(t *testing.T) {
8383

8484
// Test that NewCache populates from global cache
8585
c = NewCache()
86-
qt.Assert(t, qt.IsNotNil(c.KernelTypes))
87-
qt.Assert(t, qt.Not(qt.Equals(c.KernelTypes, vmlinux)))
86+
qt.Assert(t, qt.IsNotNil(c.kernelTypes))
87+
qt.Assert(t, qt.Not(qt.Equals(c.kernelTypes, vmlinux)))
8888
if testmod != nil {
89-
qt.Assert(t, qt.IsNotNil(c.ModuleTypes["bpf_testmod"]))
90-
qt.Assert(t, qt.Not(qt.Equals(c.ModuleTypes["bpf_testmod"], testmod)))
89+
qt.Assert(t, qt.IsNotNil(c.moduleTypes["bpf_testmod"]))
90+
qt.Assert(t, qt.Not(qt.Equals(c.moduleTypes["bpf_testmod"], testmod)))
9191
}
9292

9393
// Test that Modules only reads modules once.
9494
_, err = c.Modules()
9595
qt.Assert(t, qt.IsNil(err))
96-
qt.Assert(t, qt.IsNotNil(c.LoadedModules))
96+
qt.Assert(t, qt.IsNotNil(c.loadedModules))
9797
}

collection.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collec
437437
make(map[string]*Map),
438438
make(map[string]*Program),
439439
make(map[string]*Variable),
440-
newBTFCache(&opts.Programs),
440+
btf.NewCache(),
441441
}, nil
442442
}
443443

linker.go

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,8 @@ func hasFunctionReferences(insns asm.Instructions) bool {
122122

123123
// applyRelocations collects and applies any CO-RE relocations in insns.
124124
//
125-
// Passing a nil target will relocate against the running kernel. insns are
126-
// modified in place.
127-
func applyRelocations(insns asm.Instructions, bo binary.ByteOrder, b *btf.Builder, c *btf.Cache, extraTargets []*btf.Spec) error {
125+
// insns are modified in place.
126+
func applyRelocations(insns asm.Instructions, bo binary.ByteOrder, b *btf.Builder, c *btf.Cache, kernelOverride *btf.Spec, extraTargets []*btf.Spec) error {
128127
var relos []*btf.CORERelocation
129128
var reloInsns []*asm.Instruction
130129
iter := insns.Iterate()
@@ -143,28 +142,35 @@ func applyRelocations(insns asm.Instructions, bo binary.ByteOrder, b *btf.Builde
143142
bo = internal.NativeEndian
144143
}
145144

146-
kernelTarget, err := c.Kernel()
147-
if err != nil {
148-
return fmt.Errorf("load kernel spec: %w", err)
149-
}
145+
var targets []*btf.Spec
146+
if kernelOverride == nil {
147+
kernel, err := c.Kernel()
148+
if err != nil {
149+
return fmt.Errorf("load kernel spec: %w", err)
150+
}
150151

151-
modules, err := c.Modules()
152-
// Ignore ErrNotExists to cater to kernels which have CONFIG_DEBUG_INFO_BTF_MODULES
153-
// or CONFIG_DEBUG_INFO_BTF disabled.
154-
if err != nil && !errors.Is(err, fs.ErrNotExist) {
155-
return err
156-
}
152+
modules, err := c.Modules()
153+
// Ignore ErrNotExists to cater to kernels which have CONFIG_DEBUG_INFO_BTF_MODULES
154+
// or CONFIG_DEBUG_INFO_BTF disabled.
155+
if err != nil && !errors.Is(err, fs.ErrNotExist) {
156+
return err
157+
}
157158

158-
targets := make([]*btf.Spec, 0, 1+len(modules)+len(extraTargets))
159-
targets = append(targets, kernelTarget)
159+
targets = make([]*btf.Spec, 0, 1+len(modules)+len(extraTargets))
160+
targets = append(targets, kernel)
160161

161-
for _, kmod := range modules {
162-
spec, err := c.Module(kmod)
163-
if err != nil {
164-
return fmt.Errorf("load BTF for kmod %s: %w", kmod, err)
165-
}
162+
for _, kmod := range modules {
163+
spec, err := c.Module(kmod)
164+
if err != nil {
165+
return fmt.Errorf("load BTF for kmod %s: %w", kmod, err)
166+
}
166167

167-
targets = append(targets, spec)
168+
targets = append(targets, spec)
169+
}
170+
} else {
171+
// We expect kernelOverride to contain the merged types
172+
// of vmlinux and kernel modules, as distributed by btfhub.
173+
targets = []*btf.Spec{kernelOverride}
168174
}
169175

170176
targets = append(targets, extraTargets...)
@@ -289,7 +295,7 @@ const kfuncCallPoisonBase = 0xdedc0de
289295
// fixupKfuncs loops over all instructions in search for kfunc calls.
290296
// If at least one is found, the current kernels BTF and module BTFis are searched to set Instruction.Constant
291297
// and Instruction.Offset to the correct values.
292-
func fixupKfuncs(insns asm.Instructions) (_ handles, err error) {
298+
func fixupKfuncs(insns asm.Instructions, cache *btf.Cache) (_ handles, err error) {
293299
closeOnError := func(c io.Closer) {
294300
if err != nil {
295301
c.Close()
@@ -310,7 +316,7 @@ fixups:
310316
// Only load kernel BTF if we found at least one kfunc call. kernelSpec can be
311317
// nil if the kernel does not have BTF, in which case we poison all kfunc
312318
// calls.
313-
kernelSpec, err := btf.LoadKernelSpec()
319+
_, err = cache.Kernel()
314320
// ErrNotSupportedOnOS wraps ErrNotSupported, check for it first.
315321
if errors.Is(err, internal.ErrNotSupportedOnOS) {
316322
return nil, fmt.Errorf("kfuncs are not supported on this platform: %w", err)
@@ -342,7 +348,7 @@ fixups:
342348

343349
// findTargetInKernel returns btf.ErrNotFound if the input btf.Spec is nil.
344350
target := btf.Type((*btf.Func)(nil))
345-
spec, module, err := findTargetInKernel(kernelSpec, kfm.Func.Name, &target)
351+
spec, module, err := findTargetInKernel(kfm.Func.Name, &target, cache)
346352
if errors.Is(err, btf.ErrNotFound) {
347353
if kfm.Binding == elf.STB_WEAK {
348354
if ins.IsKfuncCall() {

prog.go

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er
236236
return nil, errors.New("can't load a program from a nil spec")
237237
}
238238

239-
prog, err := newProgramWithOptions(spec, opts, newBTFCache(&opts))
239+
prog, err := newProgramWithOptions(spec, opts, btf.NewCache())
240240
if errors.Is(err, asm.ErrUnsatisfiedMapReference) {
241241
return nil, fmt.Errorf("cannot load program without loading its whole collection: %w", err)
242242
}
@@ -297,7 +297,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache)
297297
copy(insns, spec.Instructions)
298298

299299
var b btf.Builder
300-
if err := applyRelocations(insns, spec.ByteOrder, &b, c, opts.ExtraRelocationTargets); err != nil {
300+
if err := applyRelocations(insns, spec.ByteOrder, &b, c, opts.KernelTypes, opts.ExtraRelocationTargets); err != nil {
301301
return nil, fmt.Errorf("apply CO-RE relocations: %w", err)
302302
}
303303

@@ -350,7 +350,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache)
350350
return nil, err
351351
}
352352

353-
handles, err := fixupKfuncs(insns)
353+
handles, err := fixupKfuncs(insns, c)
354354
if err != nil {
355355
return nil, fmt.Errorf("fixing up kfuncs: %w", err)
356356
}
@@ -381,7 +381,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache)
381381
attr.AttachBtfObjFd = uint32(spec.AttachTarget.FD())
382382
defer runtime.KeepAlive(spec.AttachTarget)
383383
} else if spec.AttachTo != "" {
384-
module, targetID, err := findProgramTargetInKernel(spec.AttachTo, spec.Type, spec.AttachType)
384+
module, targetID, err := findProgramTargetInKernel(spec.AttachTo, spec.Type, spec.AttachType, c)
385385
if err != nil && !errors.Is(err, errUnrecognizedAttachType) {
386386
// We ignore errUnrecognizedAttachType since AttachTo may be non-empty
387387
// for programs that don't attach anywhere.
@@ -1027,7 +1027,7 @@ var errUnrecognizedAttachType = errors.New("unrecognized attach type")
10271027
//
10281028
// Returns errUnrecognizedAttachType if the combination of progType and attachType
10291029
// is not recognised.
1030-
func findProgramTargetInKernel(name string, progType ProgramType, attachType AttachType) (*btf.Handle, btf.TypeID, error) {
1030+
func findProgramTargetInKernel(name string, progType ProgramType, attachType AttachType, cache *btf.Cache) (*btf.Handle, btf.TypeID, error) {
10311031
type match struct {
10321032
p ProgramType
10331033
a AttachType
@@ -1067,12 +1067,7 @@ func findProgramTargetInKernel(name string, progType ProgramType, attachType Att
10671067
return nil, 0, errUnrecognizedAttachType
10681068
}
10691069

1070-
spec, err := btf.LoadKernelSpec()
1071-
if err != nil {
1072-
return nil, 0, fmt.Errorf("load kernel spec: %w", err)
1073-
}
1074-
1075-
spec, module, err := findTargetInKernel(spec, typeName, &target)
1070+
spec, module, err := findTargetInKernel(typeName, &target, cache)
10761071
if errors.Is(err, btf.ErrNotFound) {
10771072
return nil, 0, &internal.UnsupportedFeatureError{Name: featureName}
10781073
}
@@ -1102,14 +1097,15 @@ func findProgramTargetInKernel(name string, progType ProgramType, attachType Att
11021097
//
11031098
// Returns a non-nil handle if the type was found in a module, or btf.ErrNotFound
11041099
// if the type wasn't found at all.
1105-
func findTargetInKernel(kernelSpec *btf.Spec, typeName string, target *btf.Type) (*btf.Spec, *btf.Handle, error) {
1106-
if kernelSpec == nil {
1107-
return nil, nil, fmt.Errorf("nil kernelSpec: %w", btf.ErrNotFound)
1100+
func findTargetInKernel(typeName string, target *btf.Type, cache *btf.Cache) (*btf.Spec, *btf.Handle, error) {
1101+
kernelSpec, err := cache.Kernel()
1102+
if err != nil {
1103+
return nil, nil, err
11081104
}
11091105

1110-
err := kernelSpec.TypeByName(typeName, target)
1106+
err = kernelSpec.TypeByName(typeName, target)
11111107
if errors.Is(err, btf.ErrNotFound) {
1112-
spec, module, err := findTargetInModule(kernelSpec, typeName, target)
1108+
spec, module, err := findTargetInModule(typeName, target, cache)
11131109
if err != nil {
11141110
return nil, nil, fmt.Errorf("find target in modules: %w", err)
11151111
}
@@ -1127,7 +1123,7 @@ func findTargetInKernel(kernelSpec *btf.Spec, typeName string, target *btf.Type)
11271123
// are searched in the order they were loaded.
11281124
//
11291125
// Returns btf.ErrNotFound if the target can't be found in any module.
1130-
func findTargetInModule(base *btf.Spec, typeName string, target *btf.Type) (*btf.Spec, *btf.Handle, error) {
1126+
func findTargetInModule(typeName string, target *btf.Type, cache *btf.Cache) (*btf.Spec, *btf.Handle, error) {
11311127
it := new(btf.HandleIterator)
11321128
defer it.Handle.Close()
11331129

@@ -1141,7 +1137,7 @@ func findTargetInModule(base *btf.Spec, typeName string, target *btf.Type) (*btf
11411137
continue
11421138
}
11431139

1144-
spec, err := it.Handle.Spec(base)
1140+
spec, err := cache.Module(info.Name)
11451141
if err != nil {
11461142
return nil, nil, fmt.Errorf("parse types for module %s: %w", info.Name, err)
11471143
}
@@ -1201,12 +1197,3 @@ func findTargetInProgram(prog *Program, name string, progType ProgramType, attac
12011197

12021198
return spec.TypeID(targetFunc)
12031199
}
1204-
1205-
func newBTFCache(opts *ProgramOptions) *btf.Cache {
1206-
c := btf.NewCache()
1207-
if opts.KernelTypes != nil {
1208-
c.KernelTypes = opts.KernelTypes
1209-
c.LoadedModules = []string{}
1210-
}
1211-
return c
1212-
}

0 commit comments

Comments
 (0)