7
7
"path/filepath"
8
8
"reflect"
9
9
"strings"
10
+ "unsafe"
10
11
11
12
"github.com/cilium/ebpf/asm"
12
13
"github.com/cilium/ebpf/btf"
@@ -269,7 +270,7 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
269
270
defer loader .close ()
270
271
271
272
// initialize struct_ops maps
272
- if err := loader .stOps . preLoad (loader .coll ); err != nil {
273
+ if err := loader .initKernStructOps (loader .coll ); err != nil {
273
274
return err
274
275
}
275
276
@@ -309,7 +310,7 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
309
310
}
310
311
311
312
// 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 {
313
314
return err
314
315
}
315
316
@@ -423,7 +424,10 @@ type collectionLoader struct {
423
424
programs map [string ]* Program
424
425
vars map [string ]* Variable
425
426
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
427
431
}
428
432
429
433
func newCollectionLoader (coll * CollectionSpec , opts * CollectionOptions ) (* collectionLoader , error ) {
@@ -449,7 +453,8 @@ func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collec
449
453
make (map [string ]* Program ),
450
454
make (map [string ]* Variable ),
451
455
newBTFCache (& opts .Programs ),
452
- newStructOpsLoader (),
456
+ make (map [string ]* structOpsSpec ),
457
+ make (map [string ]string ),
453
458
}, nil
454
459
}
455
460
@@ -595,9 +600,22 @@ func (cl *collectionLoader) loadProgram(progName string) (*Program, error) {
595
600
cl .programs [progName ] = prog
596
601
597
602
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 )
600
611
}
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
601
619
}
602
620
603
621
return prog , nil
@@ -716,6 +734,203 @@ func (cl *collectionLoader) populateDeferredMaps() error {
716
734
return nil
717
735
}
718
736
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
+
719
934
// resolveKconfig resolves all variables declared in .kconfig and populates
720
935
// m.Contents. Does nothing if the given m.Contents is non-empty.
721
936
func resolveKconfig (m * MapSpec ) error {
0 commit comments