Skip to content

Commit 0597fa9

Browse files
committed
struct_ops: move some struct_ops logics to collection
Signed-off-by: shun159 <[email protected]>
1 parent 0d6b1a9 commit 0597fa9

File tree

2 files changed

+227
-285
lines changed

2 files changed

+227
-285
lines changed

collection.go

Lines changed: 221 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"path/filepath"
88
"reflect"
99
"strings"
10+
"unsafe"
1011

1112
"github.com/cilium/ebpf/asm"
1213
"github.com/cilium/ebpf/btf"
@@ -269,7 +270,7 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
269270
defer loader.close()
270271

271272
// initialize struct_ops maps
272-
if err := loader.stOps.preLoad(loader.coll); err != nil {
273+
if err := loader.initKernStructOps(loader.coll); err != nil {
273274
return err
274275
}
275276

@@ -309,7 +310,7 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
309310
}
310311

311312
// load programs to struct_ops maps
312-
if err := loader.stOps.postLoad(loader.maps, loader.programs); err != nil {
313+
if err := loader.populateAndPutStructOpsProgFds(); err != nil {
313314
return err
314315
}
315316

@@ -423,7 +424,10 @@ type collectionLoader struct {
423424
programs map[string]*Program
424425
vars map[string]*Variable
425426
types *btf.Cache
426-
stOps *structOpsLoader
427+
// map name -> structOpsSpec
428+
stOpsSpecs map[string]*structOpsSpec
429+
// structOps program name -> structOpsMap name
430+
stOpsProgsToMap map[string]string
427431
}
428432

429433
func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collectionLoader, error) {
@@ -449,7 +453,8 @@ func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collec
449453
make(map[string]*Program),
450454
make(map[string]*Variable),
451455
newBTFCache(&opts.Programs),
452-
newStructOpsLoader(),
456+
make(map[string]*structOpsSpec),
457+
make(map[string]string),
453458
}, nil
454459
}
455460

@@ -595,9 +600,22 @@ func (cl *collectionLoader) loadProgram(progName string) (*Program, error) {
595600
cl.programs[progName] = prog
596601

597602
if prog.Type() == StructOps {
598-
if err := cl.stOps.onProgramLoaded(prog, progSpec); err != nil {
599-
return nil, err
603+
mapName, ok := cl.stOpsProgsToMap[prog.name]
604+
if !ok {
605+
return nil, fmt.Errorf("program %s should be linked to a StructOpsMap", prog.name)
606+
}
607+
608+
structOps, ok := cl.stOpsSpecs[mapName]
609+
if !ok {
610+
return nil, fmt.Errorf("program %s has been loaded but not associated", prog.name)
600611
}
612+
613+
attachType := structOps.progAttachType[prog.name]
614+
if int(attachType) > len(structOps.kernTypes.typ.Members) {
615+
return nil, fmt.Errorf("program %s: unexpected attach type %d",
616+
prog.name, attachType)
617+
}
618+
structOps.programName[attachType] = progSpec.Name
601619
}
602620

603621
return prog, nil
@@ -716,6 +734,203 @@ func (cl *collectionLoader) populateDeferredMaps() error {
716734
return nil
717735
}
718736

737+
// copyDataMember processes an individual member of the user-defined struct.
738+
// It determines whether the member is a function pointer or data member,
739+
// and handles it accordingly by setting up program attachments or copying data.
740+
func (cl *collectionLoader) copyDataMember(
741+
idx int,
742+
member btf.Member,
743+
kern *structOpsKernTypes,
744+
structOps *structOpsSpec,
745+
structOpsMeta *structOpsMeta,
746+
ms *MapSpec,
747+
cs *CollectionSpec,
748+
) error {
749+
memberName := member.Name
750+
memberOff := member.Offset / 8
751+
memberData := structOpsMeta.data[memberOff:]
752+
753+
memberSize, err := btf.Sizeof(member.Type)
754+
if err != nil {
755+
return fmt.Errorf("failed to resolve the size of member %s: %w", memberName, err)
756+
}
757+
758+
kernMember, err := getStructMemberByName(kern.typ, memberName)
759+
if err != nil {
760+
if isMemoryZero(memberData[:memberSize]) {
761+
// Skip if member doesn't exist in kernel BTF and data is zero
762+
return nil
763+
}
764+
return fmt.Errorf("member %s not found in kernel BTF and data is not zero", memberName)
765+
}
766+
767+
kernMemberIdx := getStructMemberIndexOf(kern.typ, kernMember)
768+
if member.BitfieldSize > 0 || kernMember.BitfieldSize > 0 {
769+
return fmt.Errorf("bitfield %s is not supported", memberName)
770+
}
771+
772+
kernMemberOff := kernMember.Offset / 8
773+
kernMemberData := structOps.kernVData[kernMemberOff:]
774+
memberType, err := skipModsAndTypedefs(kern.spec, member.Type)
775+
if err != nil {
776+
return fmt.Errorf("user: failed to skip typedefs for %s: %w", memberName, err)
777+
}
778+
779+
kernMemberType, err := skipModsAndTypedefs(kern.spec, kernMember.Type)
780+
if err != nil {
781+
return fmt.Errorf("kern: failed to skip typedefs for %s: %w", kernMember.Name, err)
782+
}
783+
784+
if _, ok := memberType.(*btf.Pointer); ok {
785+
var fnName string
786+
787+
for _, fn := range structOpsMeta.funcs {
788+
if fn.member == memberName {
789+
fnName = fn.progName
790+
break
791+
}
792+
793+
}
794+
if fnName == "" {
795+
// skip if the member is not specified in the MapSpec
796+
return nil
797+
}
798+
799+
ps, ok := cs.Programs[fnName]
800+
if !ok {
801+
return fmt.Errorf("Program %s is not found in CollectionSpec", fnName)
802+
}
803+
if ps.Type != StructOps {
804+
return fmt.Errorf("program %s is not a valid StructOps program", fnName)
805+
}
806+
807+
attachType := sys.AttachType(kernMemberIdx)
808+
if int(attachType) > len(kern.typ.Members) {
809+
return fmt.Errorf("program %s: unexpected attach type %d", ps.Name, attachType)
810+
}
811+
812+
kernFuncOff := kern.dataMember.Offset/8 + kern.typ.Members[kernMemberIdx].Offset/8
813+
structOps.kernFuncOff[idx] = int(kernFuncOff)
814+
structOps.progAttachType[ps.Name] = attachType
815+
cl.stOpsProgsToMap[ps.Name] = ms.Name
816+
817+
ps.Instructions[0].Metadata.Set(structOpsProgMetaKey{}, &structOpsProgMeta{
818+
attachBtfId: kern.typeID,
819+
attachType: attachType,
820+
modBtfObjID: kern.modBtfObjId,
821+
})
822+
}
823+
824+
// Handle data member. copy data members from the user-defined struct to the kernel data buffer.
825+
// It ensures that the sizes match between user and kernel types before copying the data.
826+
kernMemberSize, err := btf.Sizeof(kernMemberType)
827+
if err != nil || memberSize != kernMemberSize {
828+
return fmt.Errorf("size mismatch for member %s: %d != %d (kernel)", memberName, memberSize, kernMemberSize)
829+
}
830+
copy(kernMemberData[:memberSize], memberData[:memberSize])
831+
832+
return nil
833+
}
834+
835+
// initKernStructOps collects typed metadata for struct_ops maps from the CollectionSpec.
836+
// It does not modify specs nor create kernel objects. Value population happens in a follow-up PR.
837+
func (cl *collectionLoader) initKernStructOps(cs *CollectionSpec) error {
838+
for _, ms := range cs.Maps {
839+
if ms.Type != StructOpsMap {
840+
continue
841+
}
842+
843+
userSt := ms.Value
844+
if userSt == nil {
845+
return fmt.Errorf("user struct type should be specified as Value")
846+
}
847+
848+
userStructType, ok := ms.Value.(*btf.Struct)
849+
if !ok {
850+
return fmt.Errorf("user struct type should be a Struct")
851+
}
852+
853+
kernTypes, err := findStructOpsKernTypes(userStructType)
854+
if err != nil {
855+
return fmt.Errorf("find kern_type: %w", err)
856+
}
857+
ms.Value = kernTypes.typ
858+
859+
structOps := &structOpsSpec{
860+
make([]string, len(kernTypes.typ.Members)),
861+
make([]int, len(kernTypes.typ.Members)),
862+
make([]byte, kernTypes.valueType.Size),
863+
kernTypes,
864+
make(map[string]sys.AttachType),
865+
kernTypes.typeID,
866+
}
867+
cl.stOpsSpecs[ms.Name] = structOps
868+
869+
structOpsMeta, err := extractStructOpsMeta(ms.Contents)
870+
if err != nil {
871+
return err
872+
}
873+
874+
// copy data from user-defined struct to kern_vdata
875+
for idx, member := range kernTypes.typ.Members {
876+
if err := cl.copyDataMember(
877+
idx, member,
878+
kernTypes,
879+
structOps,
880+
structOpsMeta,
881+
ms, cs,
882+
); err != nil {
883+
return err
884+
}
885+
}
886+
}
887+
888+
return nil
889+
}
890+
891+
// TODO: should be RENAMED AND MOVED!!!!
892+
893+
// populateStructOpsProgFds runs after all maps and programs have been loaded.
894+
// It writes program FDs into struct_ops.KernVData and updates the map entry.
895+
func (cl *collectionLoader) populateAndPutStructOpsProgFds() error {
896+
for mapName, m := range cl.maps {
897+
if m.Type() != StructOpsMap {
898+
continue
899+
}
900+
901+
structOps, ok := cl.stOpsSpecs[mapName]
902+
if !ok {
903+
return fmt.Errorf("struct_ops Map: %s is not initialized", mapName)
904+
}
905+
906+
for idx, progName := range structOps.programName {
907+
if progName == "" {
908+
continue
909+
}
910+
911+
prog, ok := cl.programs[progName]
912+
if !ok {
913+
return fmt.Errorf("program %s should be loaded", progName)
914+
}
915+
defer prog.Close()
916+
917+
off := structOps.kernFuncOff[idx]
918+
ptr := unsafe.Pointer(&structOps.kernVData[0])
919+
*(*uint64)(unsafe.Pointer(uintptr(ptr) + uintptr(off))) = uint64(prog.FD())
920+
}
921+
922+
m, ok := cl.maps[mapName]
923+
if !ok {
924+
return fmt.Errorf("map %s should be loaded", mapName)
925+
}
926+
927+
if err := m.Put(uint32(0), structOps.kernVData); err != nil {
928+
return err
929+
}
930+
}
931+
return nil
932+
}
933+
719934
// resolveKconfig resolves all variables declared in .kconfig and populates
720935
// m.Contents. Does nothing if the given m.Contents is non-empty.
721936
func resolveKconfig(m *MapSpec) error {

0 commit comments

Comments
 (0)