Skip to content

Commit 607e654

Browse files
committed
struct_ops: refactor kern_vdata population
- add translateStructData() and populateFuncPtr() and use them in populateDeferredMaps - delete structOpsMeta and redundant helpers Signed-off-by: shun159 <[email protected]>
1 parent 7eab4c3 commit 607e654

File tree

4 files changed

+126
-217
lines changed

4 files changed

+126
-217
lines changed

collection.go

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

1211
"github.com/cilium/ebpf/asm"
1312
"github.com/cilium/ebpf/btf"
@@ -676,6 +675,7 @@ func (cl *collectionLoader) loadVariable(varName string) (*Variable, error) {
676675
// populateDeferredMaps iterates maps holding programs or other maps and loads
677676
// any dependencies. Populates all maps in cl and freezes them if specified.
678677
func (cl *collectionLoader) populateDeferredMaps() error {
678+
fmt.Println("hogehogehogehoge")
679679
for mapName, m := range cl.maps {
680680
mapSpec, ok := cl.coll.Maps[mapName]
681681
if !ok {
@@ -721,31 +721,37 @@ func (cl *collectionLoader) populateDeferredMaps() error {
721721
}
722722

723723
if mapSpec.Type == StructOpsMap {
724-
structOps, ok := cl.stOpsSpecs[mapName]
724+
userType, ok := btf.As[*btf.Struct](mapSpec.Value)
725725
if !ok {
726-
return fmt.Errorf("struct_ops Map: %s is not initialized", mapName)
726+
return fmt.Errorf("value should be a *Struct")
727727
}
728728

729-
for idx, progName := range structOps.programName {
730-
if progName == "" {
731-
continue // unlikely to happen
732-
}
733-
// loadProgram is idempotent and could return an existing Program.
734-
prog, err := cl.loadProgram(progName)
735-
if err != nil {
736-
return fmt.Errorf("loading program %s is not loaded, for map %s: %w",
737-
progName, mapName, err)
738-
}
739-
defer prog.Close()
729+
userData, ok := mapSpec.Contents[0].Value.([]byte)
730+
if !ok {
731+
return fmt.Errorf("value should be an array of byte")
732+
}
733+
734+
s, err := btf.LoadKernelSpec()
735+
if err != nil {
736+
return fmt.Errorf("load vmlinux BTF: %w", err)
737+
}
738+
739+
kernType, _, _, err := findStructByNameWithPrefix(s, userType)
740+
if err != nil {
741+
return fmt.Errorf("find wrapper type: %w", err)
742+
}
743+
744+
kernVData, err := translateStructData(userType, userData, kernType)
745+
if err != nil {
746+
return err
747+
}
740748

741-
// populate ProgFDs as Values
742-
off := structOps.kernFuncOff[idx]
743-
ptr := unsafe.Pointer(&structOps.kernVData[0])
744-
*(*uint64)(unsafe.Pointer(uintptr(ptr) + uintptr(off))) = uint64(prog.FD())
749+
if err := populateFuncPtr(kernType, kernVData, cl.programs); err != nil {
750+
return err
745751
}
746752

747753
mapSpec.Contents[0] =
748-
MapKV{Key: uint32(0), Value: structOps.kernVData}
754+
MapKV{Key: uint32(0), Value: kernVData}
749755
}
750756

751757
// Populate and freeze the map if specified.
@@ -757,99 +763,100 @@ func (cl *collectionLoader) populateDeferredMaps() error {
757763
return nil
758764
}
759765

760-
// copyDataMember copies one field from the user struct into the kernel buffer.
761-
// If the field is a function pointer, record attach metadata instead.
762-
func (cl *collectionLoader) copyDataMember(
763-
idx int,
764-
member btf.Member,
765-
kern *structOpsKernTypes,
766-
structOps *structOpsSpec,
767-
structOpsMeta *structOpsMeta,
768-
ms *MapSpec,
769-
) error {
770-
memberName := member.Name
771-
memberOff := member.Offset / 8
772-
memberData := structOpsMeta.data[memberOff:]
773-
774-
memberSize, err := btf.Sizeof(member.Type)
775-
if err != nil {
776-
return fmt.Errorf("failed to resolve the size of member %s: %w", memberName, err)
777-
}
766+
// translateStructData fills in all fields in `from` that exist in `to` with contents of fromData.
767+
func translateStructData(from *btf.Struct, fromData []byte, to *btf.Struct) ([]byte, error) {
768+
innerName := strings.TrimPrefix(to.Name, structOpsValuePrefix)
769+
toData := make([]byte, int(to.Size))
778770

779-
kernMember, err := getStructMemberByName(kern.typ, memberName)
780-
if err != nil {
781-
// allow missing kernel member if user data is zero
782-
if isMemoryZero(memberData[:memberSize]) {
783-
return nil
784-
}
785-
return fmt.Errorf("member %s not found in kernel BTF and data is not zero", memberName)
786-
}
771+
var inner *btf.Struct
772+
var innerOff int
787773

788-
kernMemberIdx := getStructMemberIndexOf(kern.typ, kernMember)
789-
if member.BitfieldSize > 0 || kernMember.BitfieldSize > 0 {
790-
return fmt.Errorf("bitfield %s is not supported", memberName)
774+
for _, m := range to.Members {
775+
st, ok := btf.As[*btf.Struct](btf.UnderlyingType(m.Type))
776+
fmt.Println(m.Name, st.Name)
777+
if ok && st.Name == innerName {
778+
inner = st
779+
innerOff = int(m.Offset / 8)
780+
}
791781
}
792782

793-
kernMemberOff := kernMember.Offset / 8
794-
kernMemberData := structOps.kernVData[kernMemberOff:]
795-
796-
// normalize user/kernel types (strip typedef/qualifiers, one level)
797-
memberType, err := skipModsAndTypedefs(kern.spec, member.Type)
798-
if err != nil {
799-
return fmt.Errorf("user: failed to skip typedefs for %s: %w", memberName, err)
783+
if inner == nil {
784+
return nil, fmt.Errorf("member %s not found in %s", innerName, to.Name)
800785
}
801786

802-
kernMemberType, err := skipModsAndTypedefs(kern.spec, kernMember.Type)
803-
if err != nil {
804-
return fmt.Errorf("kern: failed to skip typedefs for %s: %w", kernMember.Name, err)
787+
kernIndexByName := make(map[string]btf.Member, len(inner.Members))
788+
for _, m := range inner.Members {
789+
kernIndexByName[m.Name] = m
805790
}
806791

807-
// function-pointer member: map to program & record attach info
808-
if _, ok := memberType.(*btf.Pointer); ok {
809-
var fnName string
810-
811-
for _, fn := range structOpsMeta.funcs {
812-
if fn.member == memberName {
813-
fnName = fn.progName
814-
break
815-
}
792+
for _, m := range from.Members {
793+
if m.BitfieldSize > 0 {
794+
return nil, fmt.Errorf("bitfield %s not supported", m.Name)
795+
}
816796

797+
kernMember, ok := kernIndexByName[m.Name]
798+
if !ok {
799+
continue
817800
}
818-
if fnName == "" {
819-
// skip if the member is not specified in the MapSpec
820-
return nil
801+
802+
if kernMember.BitfieldSize > 0 {
803+
return nil, fmt.Errorf("bitfield %s not supported in kern struct", kernMember.Name)
821804
}
822805

823-
ps, ok := cl.coll.Programs[fnName]
824-
if !ok {
825-
return fmt.Errorf("Program %s is not found in CollectionSpec", fnName)
806+
sz, err := btf.Sizeof(m.Type)
807+
if err != nil {
808+
return nil, fmt.Errorf("failed to resolve size of %s: %w", m.Name, err)
826809
}
827-
if ps.Type != StructOps {
828-
return fmt.Errorf("program %s is not a valid StructOps program", fnName)
810+
811+
kernSz, err := btf.Sizeof(kernMember.Type)
812+
if err != nil {
813+
return nil, fmt.Errorf("failed to resolve size of %s: %w", kernMember.Name, err)
829814
}
830815

831-
attachType := sys.AttachType(kernMemberIdx)
832-
if int(attachType) > len(kern.typ.Members) {
833-
return fmt.Errorf("program %s: unexpected attach type %d", ps.Name, attachType)
816+
if sz != kernSz {
817+
return nil, fmt.Errorf("size mismatch for %s: %d != %d", m.Name, sz, kernSz)
834818
}
835819

836-
// where the kernel expects the prog FD to be written
837-
kernFuncOff := kern.dataMember.Offset/8 + kern.typ.Members[kernMemberIdx].Offset/8
838-
structOps.kernFuncOff[idx] = int(kernFuncOff)
839-
structOps.progAttachType[ps.Name] = attachType
840-
cl.stOpsProgsToMap[ps.Name] = ms.Name
820+
src := int(m.Offset / 8)
821+
dst := innerOff + int(kernMember.Offset/8)
822+
copy(toData[dst:dst+int(sz)], fromData[src:src+int(sz)])
823+
}
824+
825+
return toData, nil
826+
}
827+
828+
// populateFuncPtr writes progFD into `data` at the offset
829+
func populateFuncPtr(wrapper *btf.Struct, data []byte, programs map[string]*Program) error {
830+
innerName := strings.TrimPrefix(wrapper.Name, structOpsValuePrefix)
841831

842-
// set `AttachTo` for struct_ops prog; e.g. "bpf_testmod_ops:test_1"
843-
ps.AttachTo = fmt.Sprintf("%s:%s", kern.typ.Name, memberName)
832+
var inner *btf.Struct
833+
var innerOff int
834+
835+
for _, m := range wrapper.Members {
836+
st, ok := btf.As[*btf.Struct](btf.UnderlyingType(m.Type))
837+
if ok && st.Name == innerName {
838+
inner = st
839+
innerOff = int(m.Offset / 8)
840+
}
841+
}
842+
843+
if inner == nil {
844+
return fmt.Errorf("member %s not found in %s", innerName, wrapper.Name)
844845
}
845846

846-
// Handle data member. copy data members from the user-defined struct to the kernel data buffer.
847-
// It ensures that the sizes match between user and kernel types before copying the data.
848-
kernMemberSize, err := btf.Sizeof(kernMemberType)
849-
if err != nil || memberSize != kernMemberSize {
850-
return fmt.Errorf("size mismatch for member %s: %d != %d (kernel)", memberName, memberSize, kernMemberSize)
847+
for _, m := range inner.Members {
848+
if _, isPtr := btf.As[*btf.Pointer](m.Type); !isPtr {
849+
continue
850+
}
851+
852+
p, ok := programs[m.Name]
853+
if !ok || p == nil {
854+
continue
855+
}
856+
857+
off := innerOff + int(m.Offset/8)
858+
binary.LittleEndian.PutUint64(data[off:off+8], uint64(p.FD()))
851859
}
852-
copy(kernMemberData[:memberSize], memberData[:memberSize])
853860

854861
return nil
855862
}
@@ -867,7 +874,7 @@ func (cl *collectionLoader) initKernStructOps() error {
867874
return fmt.Errorf("user struct type should be specified as Value")
868875
}
869876

870-
userStructType, ok := ms.Value.(*btf.Struct)
877+
userStructType, ok := btf.As[*btf.Struct](ms.Value)
871878
if !ok {
872879
return fmt.Errorf("user struct type should be a Struct")
873880
}
@@ -888,28 +895,31 @@ func (cl *collectionLoader) initKernStructOps() error {
888895
make(map[string]sys.AttachType),
889896
kernTypes.typeID,
890897
}
898+
891899
cl.stOpsSpecs[ms.Name] = structOps
900+
cl.setStructOpsProgAttachTo(kernTypes.typ, ms.Name)
901+
}
892902

893-
structOpsMeta, err := extractStructOpsMeta(ms.Contents)
894-
if err != nil {
895-
return err
903+
return nil
904+
}
905+
906+
func (cl *collectionLoader) setStructOpsProgAttachTo(kernType *btf.Struct, mapName string) {
907+
for _, m := range kernType.Members {
908+
if _, ok := btf.As[*btf.Pointer](btf.UnderlyingType(m.Type)); !ok {
909+
continue
896910
}
897911

898-
// populate kernel buffer & record attach info per member
899-
for idx, member := range kernTypes.typ.Members {
900-
if err := cl.copyDataMember(
901-
idx, member,
902-
kernTypes,
903-
structOps,
904-
structOpsMeta,
905-
ms,
906-
); err != nil {
907-
return err
908-
}
912+
member := m.Name
913+
ps, ok := cl.coll.Programs[member]
914+
if !ok || ps == nil || ps.Type != StructOps {
915+
continue
909916
}
910-
}
911917

912-
return nil
918+
cl.stOpsProgsToMap[ps.Name] = mapName
919+
if ps.AttachTo == "" {
920+
ps.AttachTo = fmt.Sprintf("%s:%s", kernType.Name, member)
921+
}
922+
}
913923
}
914924

915925
// resolveKconfig resolves all variables declared in .kconfig and populates

collection_test.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -798,12 +798,10 @@ func TestStructOpsMapSpecSimpleLoadAndAssign(t *testing.T) {
798798
MaxEntries: 1,
799799
Value: &btf.Struct{Name: "bpf_testmod_ops"},
800800
Contents: []MapKV{
801-
{Key: uint32(0), Value: structOpsMeta{
802-
data: make([]byte, 448),
803-
funcs: []structOpsFunc{
804-
{"test_1", "test_1"},
805-
},
806-
}},
801+
{
802+
Key: uint32(0),
803+
Value: make([]byte, 448),
804+
},
807805
},
808806
},
809807
},

0 commit comments

Comments
 (0)