Skip to content

Commit ba0845c

Browse files
committed
struct_ops: add code comments
Signed-off-by: shun159 <[email protected]>
1 parent 7bc44e8 commit ba0845c

File tree

4 files changed

+117
-56
lines changed

4 files changed

+117
-56
lines changed

collection.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -757,9 +757,8 @@ func (cl *collectionLoader) populateDeferredMaps() error {
757757
return nil
758758
}
759759

760-
// copyDataMember processes an individual member of the user-defined struct.
761-
// It determines whether the member is a function pointer or data member,
762-
// and handles it accordingly by setting up program attachments or copying data.
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.
763762
func (cl *collectionLoader) copyDataMember(
764763
idx int,
765764
member btf.Member,
@@ -779,8 +778,8 @@ func (cl *collectionLoader) copyDataMember(
779778

780779
kernMember, err := getStructMemberByName(kern.typ, memberName)
781780
if err != nil {
781+
// allow missing kernel member if user data is zero
782782
if isMemoryZero(memberData[:memberSize]) {
783-
// Skip if member doesn't exist in kernel BTF and data is zero
784783
return nil
785784
}
786785
return fmt.Errorf("member %s not found in kernel BTF and data is not zero", memberName)
@@ -793,6 +792,8 @@ func (cl *collectionLoader) copyDataMember(
793792

794793
kernMemberOff := kernMember.Offset / 8
795794
kernMemberData := structOps.kernVData[kernMemberOff:]
795+
796+
// normalize user/kernel types (strip typedef/qualifiers, one level)
796797
memberType, err := skipModsAndTypedefs(kern.spec, member.Type)
797798
if err != nil {
798799
return fmt.Errorf("user: failed to skip typedefs for %s: %w", memberName, err)
@@ -803,6 +804,7 @@ func (cl *collectionLoader) copyDataMember(
803804
return fmt.Errorf("kern: failed to skip typedefs for %s: %w", kernMember.Name, err)
804805
}
805806

807+
// function-pointer member: map to program & record attach info
806808
if _, ok := memberType.(*btf.Pointer); ok {
807809
var fnName string
808810

@@ -831,11 +833,13 @@ func (cl *collectionLoader) copyDataMember(
831833
return fmt.Errorf("program %s: unexpected attach type %d", ps.Name, attachType)
832834
}
833835

836+
// where the kernel expects the prog FD to be written
834837
kernFuncOff := kern.dataMember.Offset/8 + kern.typ.Members[kernMemberIdx].Offset/8
835838
structOps.kernFuncOff[idx] = int(kernFuncOff)
836839
structOps.progAttachType[ps.Name] = attachType
837840
cl.stOpsProgsToMap[ps.Name] = ms.Name
838841

842+
// seed program with attach metadata (checked at load time)
839843
ps.Instructions[0].Metadata.Set(structOpsProgMetaKey{}, &structOpsProgMeta{
840844
attachBtfId: kern.typeID,
841845
attachType: attachType,
@@ -854,8 +858,8 @@ func (cl *collectionLoader) copyDataMember(
854858
return nil
855859
}
856860

857-
// initKernStructOps collects typed metadata for struct_ops maps from the CollectionSpec.
858-
// It does not modify specs nor create kernel objects. Value population happens in a follow-up PR.
861+
// initKernStructOps indexes struct_ops maps: resolve kernel types, allocate per-map state,
862+
// and stage copy/attach metadata. No kernel objects are created here.
859863
func (cl *collectionLoader) initKernStructOps() error {
860864
for _, ms := range cl.coll.Maps {
861865
if ms.Type != StructOpsMap {
@@ -872,12 +876,14 @@ func (cl *collectionLoader) initKernStructOps() error {
872876
return fmt.Errorf("user struct type should be a Struct")
873877
}
874878

879+
// resolve kernel-side types (target + wrapper)
875880
kernTypes, err := findStructOpsKernTypes(userStructType)
876881
if err != nil {
877882
return fmt.Errorf("find kern_type: %w", err)
878883
}
879884
ms.Value = kernTypes.typ
880885

886+
// allocate per-map state (names, offsets, kernel buffer, attach types, typeID)
881887
structOps := &structOpsSpec{
882888
make([]string, len(kernTypes.typ.Members)),
883889
make([]int, len(kernTypes.typ.Members)),
@@ -893,7 +899,7 @@ func (cl *collectionLoader) initKernStructOps() error {
893899
return err
894900
}
895901

896-
// copy data from user-defined struct to kern_vdata
902+
// populate kernel buffer & record attach info per member
897903
for idx, member := range kernTypes.typ.Members {
898904
if err := cl.copyDataMember(
899905
idx, member,

map.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,8 @@ func (spec *MapSpec) createMap(inner *sys.FD) (_ *Map, err error) {
574574
return nil, fmt.Errorf("value must be Struct type")
575575
}
576576

577+
// struct_ops: resolve wrapper type ("bpf_struct_ops_<name>") and
578+
// record kernel-specific BTF IDs / FDs needed for map creation.
577579
userStructType, s, modBtfObjId, err := findStructByNameWithPrefix(s, userStType)
578580
if err != nil {
579581
return nil, fmt.Errorf("lookup struct type: %w", err)
@@ -589,11 +591,14 @@ func (spec *MapSpec) createMap(inner *sys.FD) (_ *Map, err error) {
589591
attr.BtfFd = uint32(h.FD())
590592

591593
if modBtfObjId != 0 {
594+
// BPF_F_VTYPE_BTF_OBJ_FD is required if the type comes from a module
592595
attr.MapFlags |= sys.BPF_F_VTYPE_BTF_OBJ_FD
596+
// resolve a module handler for the kernel model
593597
modH, err := btf.NewHandleFromID(btf.ID(modBtfObjId))
594598
if err != nil {
595599
return nil, fmt.Errorf("open module BTF (id=%d): %w", modBtfObjId, err)
596600
}
601+
// set FD for the kernel module
597602
attr.ValueTypeBtfObjFd = int32(modH.FD())
598603
defer modH.Close()
599604
}

prog.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache)
397397

398398
if spec.Type == StructOps {
399399
if meta, ok := spec.Instructions[0].Metadata.Get(structOpsProgMetaKey{}).(*structOpsProgMeta); ok {
400+
// set AttachBtfId / ExpectedAttachType from metadata
400401
attr.AttachBtfId = sys.TypeID(meta.attachBtfId)
401402
attr.ExpectedAttachType = meta.attachType
402403

@@ -407,6 +408,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache)
407408
return nil, fmt.Errorf("open module BTF handle (id=%d): %w", meta.modBtfObjID, err)
408409
}
409410
modH = h
411+
// set AttachBtfObjFd if the type comes from a module
410412
attr.AttachBtfObjFd = uint32(h.FD())
411413
defer modH.Close()
412414
}

struct_ops.go

Lines changed: 97 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -18,39 +18,57 @@ type structOpsProgMeta struct {
1818
modBtfObjID uint32
1919
}
2020

21-
// structOpsKernTypes holds information about kernel types related to struct_ops
21+
// structOpsKernTypes groups all kernel-side BTF artefacts that belong to a
22+
// resolved struct_ops.
2223
type structOpsKernTypes struct {
2324
spec *btf.Spec
24-
// The target kernel struct type
25-
typ *btf.Struct
26-
// The BTF type ID of the target kernel struct
25+
// target kernel struct type (e.g. tcp_congestion_ops).
26+
typ *btf.Struct
2727
typeID btf.TypeID
28-
// The wrapper struct type that contains the target struct
28+
// wrapper struct "bpf_struct_ops_<name>" that contains typ.
2929
valueType *btf.Struct
30-
// The member within ValueType that holds the target struct
30+
// The *btf.Member within valueType that embeds typ.
3131
dataMember *btf.Member
32-
// mod_btf
32+
// The BTF object ID of the module where the type was found
33+
// 0 if resolved in vmlinux.
3334
modBtfObjId uint32
3435
}
3536

3637
// used to holds "environment specific" data
3738
type structOpsSpec struct {
38-
// attachType -> programSpec
39+
// programName keeps track of program symbols by attach order.
3940
programName []string
41+
42+
// kernFuncOff contains the byte offsets into kernVData where
43+
// program FDs must be written for function pointer members.
4044
kernFuncOff []int
41-
/* e.g. struct bpf_struct_ops_tcp_congestion_ops in
42-
* btf_vmlinux's format.
43-
* struct bpf_struct_ops_tcp_congestion_ops {
44-
* [... some other kernel fields ...]
45-
* struct tcp_congestion_ops data;
46-
* }
47-
* kern_vdata-size == sizeof(struct bpf_struct_ops_tcp_congestion_ops)
48-
* bpf_map__init_kern_struct_ops() will populate the "kern_vdata"
49-
* from "data".
45+
46+
/*
47+
* kernVData mirrors the kernel-side representation of the
48+
* struct_ops type, including its nested data. For example:
49+
*
50+
* struct bpf_struct_ops_tcp_congestion_ops {
51+
* [... kernel internal fields ...]
52+
* struct tcp_congestion_ops data;
53+
* }
54+
*
55+
* In this case, len(kernVData) == sizeof(struct bpf_struct_ops_tcp_congestion_ops).
56+
* copyDataMember() will copy user-supplied data
57+
* into kernVData, which is then pushed into the map.
5058
*/
51-
kernVData []byte
52-
kernTypes *structOpsKernTypes
53-
progAttachType map[string]sys.AttachType
59+
kernVData []byte
60+
61+
// kernTypes describes the BTF types of the target struct_ops
62+
// object and its nested members, used for resolving offsets
63+
// and function pointer
64+
kernTypes *structOpsKernTypes
65+
66+
// progAttachType maps program names to the sys.AttachType
67+
// expected by the kernel when attaching each function pointer.
68+
progAttachType map[string]sys.AttachType
69+
70+
// progAttachBtfID holds the BTF type ID of the struct_ops
71+
// target in vmlinux
5472
progAttachBtfID btf.TypeID
5573
}
5674

@@ -69,11 +87,8 @@ type structOpsMeta struct {
6987
data []byte
7088
}
7189

72-
// extractStructOpsMeta returns the *structops.Meta embedded in a MapSpec’s Contents
73-
// according to the struct-ops convention:
74-
//
75-
// contents[0].Key == uint32(0)
76-
// contents[0].Value == *structopsMeta
90+
// extractStructOpsMeta retrieves the structOpsMeta value embedded in a
91+
// MapSpec's Contents, following the struct_ops map convention.
7792
func extractStructOpsMeta(contents []MapKV) (*structOpsMeta, error) {
7893
if len(contents) == 0 {
7994
return nil, fmt.Errorf("struct_ops: missing meta at Contents[0]")
@@ -92,11 +107,15 @@ func extractStructOpsMeta(contents []MapKV) (*structOpsMeta, error) {
92107
return &meta, nil
93108
}
94109

95-
// findByTypeFromStruct searches the given BTF struct `st` for the *first* member
96-
// whose BTF type **sidentity** equals `typ` (after resolving modifiers).
110+
// findByTypeFromStruct searches for the first member of a struct whose
111+
// resolved BTF type ID matches the given typ.
97112
//
98-
// The comparison is done via TypeID equality inside the same Spec, so a
99-
// typedef chain that ultimately refers to the same concrete type will match.
113+
// It resolves the BTF type ID of typ and compares it against each
114+
// member’s TypeID in st.Members. If a match is found, the corresponding
115+
// *btf.Member is returned.
116+
//
117+
// Returns an error if typ cannot be resolved, if any member type
118+
// resolution fails, or if no member with the requested type exists.
100119
func findByTypeFromStruct(spec *btf.Spec, st *btf.Struct, typ btf.Type) (*btf.Member, error) {
101120
typeId, err := spec.TypeID(typ)
102121
if err != nil {
@@ -116,20 +135,45 @@ func findByTypeFromStruct(spec *btf.Spec, st *btf.Struct, typ btf.Type) (*btf.Me
116135
return nil, fmt.Errorf("member of type %s not found in %s", typ.TypeName(), st.Name)
117136
}
118137

119-
// findStructByNameWithPrefix looks up a BTF struct whose name is the given `name`
120-
// prefixed by `structOpsValuePrefix` (“bpf_dummy_ops” → “bpf_struct_ops_bpf_dummy_ops”).
138+
// findStructByNameWithPrefix resolves a struct_ops "value" type by name,
139+
// after applying the standard prefix convention.
140+
//
141+
// It expects val to be the user-visible struct type (e.g. "bpf_dummy_ops")
142+
// and looks up the kernel-side wrapper name:
143+
//
144+
// "bpf_dummy_ops" -> "bpf_struct_ops_bpf_dummy_ops"
145+
//
146+
// Returns the matching *btf.Struct, the *btf.Spec it was found in (either the
147+
// base vmlinux spec or a module spec), and the module BTF ID (0 for vmlinux).
148+
// See doFindStructTypeByName for resolution details and error behavior.
121149
func findStructByNameWithPrefix(s *btf.Spec, val *btf.Struct) (*btf.Struct, *btf.Spec, uint32, error) {
122150
return doFindStructTypeByName(s, structOpsValuePrefix+val.TypeName())
123151
}
124152

125-
// findStructTypeByName iterates over *all* BTF types contained in the given Spec and
126-
// returns the first *btf.Struct whose TypeName() exactly matches `name`.
153+
// findStructTypeByName resolves the exact BTF struct type that corresponds
154+
// to typ.TypeName() by searching first in vmlinux and then across all loaded
155+
// kernel modules.
156+
//
157+
// Returns the first *btf.Struct that matches the name verbatim, the *btf.Spec
158+
// where it was found, and the module BTF ID (0 if found in vmlinux).
159+
// If no matching struct exists anywhere, btf.ErrNotFound is returned.
127160
func findStructTypeByName(s *btf.Spec, typ *btf.Struct) (*btf.Struct, *btf.Spec, uint32, error) {
128161
return doFindStructTypeByName(s, typ.TypeName())
129162
}
130163

131-
// doFindStructTypeByName iterates over *all* BTF types contained in the given Spec and
132-
// returns the first *btf.Struct whose TypeName() exactly matches `name`.
164+
// doFindStructTypeByName looks up a struct type with the exact name in the
165+
// provided base BTF spec, and falls back to scanning all loaded module BTFs
166+
// if it is not present in vmlinux.
167+
//
168+
// Search order and behavior:
169+
// 1. vmlinux (base spec): try AnyTypeByName(name). If it exists and is a
170+
// *btf.Struct, return it immediately with moduleID=0.
171+
// - If AnyTypeByName returns a non-notfound error, the error is propagated.
172+
// - If a type is found but is not a *btf.Struct, we fall back to modules.
173+
// 2. modules: see findStructTypeByNameFromModule.
174+
//
175+
// Returns (*btf.Struct, *btf.Spec, moduleID, nil) on success, or btf.ErrNotFound
176+
// if no matching struct is present in vmlinux or any module.
133177
func doFindStructTypeByName(s *btf.Spec, name string) (*btf.Struct, *btf.Spec, uint32, error) {
134178
if s == nil {
135179
return nil, nil, 0, fmt.Errorf("nil BTF: %w", btf.ErrNotFound)
@@ -146,8 +190,13 @@ func doFindStructTypeByName(s *btf.Spec, name string) (*btf.Struct, *btf.Spec, u
146190
return findStructTypeByNameFromModule(s, name)
147191
}
148192

149-
// findStructTypeByNameFromModule walks over the BTF info of loaded modules and
150-
// searches for struct `name`.
193+
// findStructTypeByNameFromModule scans all loaded kernel modules and tries
194+
// to resolve a struct type with the exact name. The iteration uses the base
195+
// vmlinux spec for string/ID interning as required by btf.Handle.Spec(base).
196+
//
197+
// For the first module where AnyTypeByName(name) returns a *btf.Struct,
198+
// the function returns that struct, the module's *btf.Spec, and its BTF ID.
199+
// If the type is not found in any module, btf.ErrNotFound is returned.
151200
func findStructTypeByNameFromModule(base *btf.Spec, name string) (*btf.Struct, *btf.Spec, uint32, error) {
152201
it := new(btf.HandleIterator)
153202

@@ -184,9 +233,9 @@ func findStructTypeByNameFromModule(base *btf.Spec, name string) (*btf.Struct, *
184233
return nil, nil, 0, btf.ErrNotFound
185234
}
186235

187-
// findStructOpsKernTypes discovers all kernel-side BTF artefacts related to a given
188-
//
189-
// *struct_ops* family identified by its **base name** (e.g. "tcp_congestion_ops").
236+
// findStructOpsKernTypes discovers all kernel-side BTF artifacts that belong to
237+
// a given struct_ops, identified by the user-visible base struct name
238+
// (e.g., "tcp_congestion_ops").
190239
func findStructOpsKernTypes(userStructType *btf.Struct) (*structOpsKernTypes, error) {
191240
spec, err := btf.LoadKernelSpec()
192241
if err != nil {
@@ -227,16 +276,14 @@ func findStructOpsKernTypes(userStructType *btf.Struct) (*structOpsKernTypes, er
227276
}, nil
228277
}
229278

230-
// skipModsAndTypedefs returns the **next underlying type** by peeling off a
231-
// single layer of “type wrappers” in BTF:
232-
//
233-
// - btf.Typedef
234-
// - btf.Const
235-
// - btf.Volatile
236-
// - btf.Restrict
279+
// skipModsAndTypedefs resolves a single layer of BTF indirection/qualification
280+
// for the given type within the provided *btf.Spec.
237281
//
238-
// If `typ` is already a concrete type (struct, int, ptr, etc.) it is returned
239-
// unchanged.
282+
// Behavior:
283+
// - Uses s.TypeID(typ) and s.TypeByID(id) to canonicalize the type within s.
284+
// - If the resolved type is a Typedef or C qualifier (Const/Volatile/Restrict),
285+
// returns its immediate underlying type (one level).
286+
// - Otherwise returns the resolved type as-is.
240287
func skipModsAndTypedefs(s *btf.Spec, typ btf.Type) (btf.Type, error) {
241288
typeID, err := s.TypeID(typ)
242289
if err != nil {
@@ -269,6 +316,7 @@ func getStructMemberByName(s *btf.Struct, name string) (btf.Member, error) {
269316
if member.Name == name {
270317
return member, nil
271318
}
319+
// target kernel struct type (e.g. tcp_congestion_ops).
272320
}
273321
return btf.Member{}, fmt.Errorf("member %s not found in struct %s", name, s.Name)
274322
}

0 commit comments

Comments
 (0)