Skip to content

Commit 86f162b

Browse files
committed
struct_ops: Add struct_ops loader
- Add a smoke test for LoadAndAssign with a StructOpsMap (skips on unsupported kernels) - Call stOps.preLoad / .onProgramLoaded / .postLoad from LoadAndAssign - postLoad: no-op for now; value population will follow Signed-off-by: shun159 <[email protected]>
1 parent a8776c2 commit 86f162b

File tree

3 files changed

+94
-0
lines changed

3 files changed

+94
-0
lines changed

collection.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,11 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
268268
}
269269
defer loader.close()
270270

271+
// initialize struct_ops maps
272+
if err := loader.stOps.preLoad(loader.coll); err != nil {
273+
return err
274+
}
275+
271276
// Support assigning Programs and Maps, lazy-loading the required objects.
272277
assignedMaps := make(map[string]bool)
273278
assignedProgs := make(map[string]bool)
@@ -303,6 +308,11 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
303308
return err
304309
}
305310

311+
// load programs to struct_ops maps
312+
if err := loader.stOps.postLoad(loader.maps); err != nil {
313+
return err
314+
}
315+
306316
// Evaluate the loader's objects after all (lazy)loading has taken place.
307317
for n, m := range loader.maps {
308318
if m.typ.canStoreProgram() {
@@ -413,6 +423,7 @@ type collectionLoader struct {
413423
programs map[string]*Program
414424
vars map[string]*Variable
415425
types *btf.Cache
426+
stOps *structOpsLoader
416427
}
417428

418429
func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collectionLoader, error) {
@@ -438,6 +449,7 @@ func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collec
438449
make(map[string]*Program),
439450
make(map[string]*Variable),
440451
newBTFCache(&opts.Programs),
452+
newStructOpsLoader(),
441453
}, nil
442454
}
443455

@@ -581,6 +593,13 @@ func (cl *collectionLoader) loadProgram(progName string) (*Program, error) {
581593
}
582594

583595
cl.programs[progName] = prog
596+
597+
if prog.Type() == StructOps {
598+
if err := cl.stOps.onProgramLoaded(prog, progSpec, cl.coll); err != nil {
599+
return nil, err
600+
}
601+
}
602+
584603
return prog, nil
585604
}
586605

collection_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,3 +771,40 @@ func ExampleCollectionSpec_LoadAndAssign() {
771771
defer objs.Program.Close()
772772
defer objs.Map.Close()
773773
}
774+
775+
func TestStructOpsMapSpecSimpleLoadAndAssign(t *testing.T) {
776+
requireStructOpsDummy(t)
777+
778+
spec := &CollectionSpec{
779+
Maps: map[string]*MapSpec{
780+
"dummy_ops": {
781+
Name: "dummy_ops",
782+
Type: StructOpsMap,
783+
KeySize: 4,
784+
ValueSize: 128,
785+
MaxEntries: 1,
786+
Contents: []MapKV{
787+
{Key: uint32(0), Value: structOpsMeta{
788+
userTypeName: "bpf_dummy_ops",
789+
kernTypeName: "bpf_struct_ops_bpf_dummy_ops",
790+
}},
791+
},
792+
},
793+
},
794+
}
795+
796+
var obj struct {
797+
DummyOps *Map `ebpf:"dummy_ops"`
798+
}
799+
800+
err := spec.LoadAndAssign(&obj, nil)
801+
testutils.SkipIfNotSupported(t, err)
802+
if err != nil {
803+
t.Fatalf("LoadAndAssign failed: %v", err)
804+
}
805+
t.Cleanup(func() { _ = obj.DummyOps.Close() })
806+
807+
if obj.DummyOps == nil {
808+
t.Fatal("DummyOps not assigned")
809+
}
810+
}

struct_ops.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,41 @@ func extractStructOpsMeta(contents []MapKV) (*structOpsMeta, error) {
3232

3333
return &meta, nil
3434
}
35+
36+
type structOpsLoader struct {
37+
metas map[string]*structOpsMeta
38+
specCopy map[string]*MapSpec
39+
progsByName map[string]*Program
40+
}
41+
42+
func newStructOpsLoader() *structOpsLoader {
43+
return &structOpsLoader{
44+
metas: make(map[string]*structOpsMeta),
45+
specCopy: make(map[string]*MapSpec),
46+
progsByName: make(map[string]*Program),
47+
}
48+
}
49+
50+
// preLoad collects typed metadata for struct_ops maps from the CollectionSpec.
51+
// It does not modify specs nor create kernel objects. Value population happens in a follow-up PR.
52+
func (sl *structOpsLoader) preLoad(cs *CollectionSpec) error {
53+
return nil
54+
}
55+
56+
// onProgramLoaded is called right after a Program has been successfully
57+
// loaded by collectionLoader.loadProgram(). If the program belongs to a
58+
// struct_ops map it records the program for later FD injection.
59+
func (sl *structOpsLoader) onProgramLoaded(p *Program, ps *ProgramSpec, cs *CollectionSpec) error {
60+
if ps.Type != StructOps {
61+
return nil
62+
}
63+
sl.progsByName[ps.Name] = p
64+
return nil
65+
}
66+
67+
// onProgramLoaded is called right after a Program has been successfully
68+
// loaded by collectionLoader.loadProgram(). If the program belongs to a
69+
// struct_ops map it records the program for later FD injection.
70+
func (sl *structOpsLoader) postLoad(loadedMaps map[string]*Map) error {
71+
return nil
72+
}

0 commit comments

Comments
 (0)