devices: drop cilium/ebpf{,link} deps#64
Conversation
d5f40e9 to
733c596
Compare
…ranch Temporarily point the opencontainers/cgroups dependency at the drop-cilium-ebpf branch (opencontainers/cgroups#64) via a go.mod replace, and re-vendor, so CI can exercise the cilium/ebpf main+link package removal end-to-end in runc. This must not be merged: the replace directive points at a personal fork branch. Once opencontainers/cgroups#64 lands and is tagged, this should be replaced by a normal dependency bump. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
733c596 to
cf4738d
Compare
Let's drop the second biggest (by size) runc dependency and reduce the binary size by another ~1MB. Draft/DNM until opencontainers/cgroups#64 is merged/released. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
There was a problem hiding this comment.
Pull request overview
This PR refactors the devices eBPF cgroup-device filter implementation to stop importing github.com/cilium/ebpf’s main and link packages, and instead perform the required operations via direct bpf(2) syscalls while continuing to use github.com/cilium/ebpf/asm for instruction assembly. This aligns with the stated goal of reducing consumer binary size by avoiding heavy transitive dependencies.
Changes:
- Replaced
cilium/ebpfprogram/link usage with thin wrappers aroundBPF_PROG_*commands and raw program fds. - Updated cgroup device filter attach/query logic to operate on fds (with explicit closes) instead of
*ebpf.Program. - Added
nativeEndianselection via build-tagged endian-specific files to satisfyasm.Instructions.Marshalrequirements.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| go.sum | Removes now-unused transitive dependencies after dropping cilium/ebpf main/link usage. |
| devices/endian_le.go | Provides nativeEndian = binary.LittleEndian under little-endian arch build tags. |
| devices/endian_be.go | Provides nativeEndian = binary.BigEndian under big-endian arch build tags. |
| devices/ebpf_linux.go | Replaces ebpf/link usage with direct bpf(2) syscall wrappers and fd-based program management. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| _, err := bpf(unix.BPF_PROG_QUERY, unsafe.Pointer(&query), unsafe.Sizeof(query)) | ||
| size = int(query.ProgCnt) |
| err = bpfProgAttach(dirFd, progFd, attachFlags, replaceFd) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to call BPF_PROG_ATTACH (BPF_CGROUP_DEVICE, BPF_F_ALLOW_MULTI): %w", err) | ||
| } |
cf4738d to
0fd3600
Compare
Let's drop the second biggest (by size) runc dependency and reduce the binary size by another ~1MB. Draft/DNM until opencontainers/cgroups#64 is merged/released. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Let's drop the second biggest (by size) runc dependency and reduce the binary size by another ~1MB. Draft/DNM until opencontainers/cgroups#64 is merged/released. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
| // | ||
| // TODO: move this logic to cilium/ebpf |
There was a problem hiding this comment.
Was there anything in our code that would still be worth upstreaming (even if we don't use it?) not sure how generic our changes are.
There was a problem hiding this comment.
This code was/is there, just not as a public API.
I guess the only "unique" code we have here is how we use BPF_PROG_REPLACE. Also, it's not too much code.
See also opencontainers/runc#5218 (comment)
| // nativeEndian is used as a workaround for cilium/ebpf/asm | ||
| // which does not accept binary.NativeEndian. | ||
| var nativeEndian = binary.BigEndian |
There was a problem hiding this comment.
This is unfortunate; makes our code more brittle (if there would ever be more build-tags added). Would this be something that could be fixed in ebpf?
My AI-buddy suggested something like this could work (but haven't verified);
func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) {
var b [2]byte
bo.PutUint16(b[:], 0x0102)
switch b {
case [2]byte{0x02, 0x01}: // little
return bpfRegisters((src << 4) | (dst & 0xf)), nil
case [2]byte{0x01, 0x02}: // big
return bpfRegisters((dst << 4) | (src & 0xf)), nil
default:
return 0, fmt.Errorf("unrecognized ByteOrder %T", bo)
}
}There was a problem hiding this comment.
In that case, I guess we could do this locally as well if we don't want to depend on our build-tags being complete;
var nativeEndian = detectNativeEndian()
func detectNativeEndian() binary.ByteOrder {
var b [2]byte
binary.NativeEndian.PutUint16(b[:], 0x0102)
switch b {
case [2]byte{0x02, 0x01}:
return binary.LittleEndian
case [2]byte{0x01, 0x02}:
return binary.BigEndian
default:
panic("unreachable: invalid native byte order")
}
}or
var nativeEndian = detectNativeEndian()
func detectNativeEndian() binary.ByteOrder {
var x uint16 = 0x0102
if *(*byte)(unsafe.Pointer(&x)) == 0x02 {
return binary.LittleEndian
}
return binary.BigEndian
}There was a problem hiding this comment.
Initially I had a similar code but I prefer to determine that during compile time rather than run time.
There was a problem hiding this comment.
Also, if a whole new architecture is to be added to Golang, fixing this code is trivial.
There was a problem hiding this comment.
But, we can have a runtime test to ensure our endian-ness is correct.
Added.
There was a problem hiding this comment.
but I prefer to determine that during compile time rather than run time.
Yes, generally agreed. I wish there was a builtin endian-ness build-tag or something (and, yes, initially I thought; why not use binary.NativeEndian before realising ... why we had to do this.
The long list of platforms made me hesitate a bit (easy to miss one!)
But, we can have a runtime test to ensure our endian-ness is correct.
Works for me (for now); still would be great if upstream cilium didn't require us to do this 😅
There was a problem hiding this comment.
still would be great if upstream cilium didn't require us to do this 😅
I tried: cilium/ebpf#1523
Replace the use of the cilium/ebpf and cilium/ebpf/link with direct
bpf(2) syscalls. Keep cilium/ebpf/asm for instruction assembly.
Notes:
- The eBPF device-filter programs are now tracked by raw file
descriptors instead of *ebpf.Program handles;
- asm.Instructions.Marshal requires a concrete binary.LittleEndian or
binary.BigEndian, thus endian_{le,be}.go are introduced as a
workaround.
This reduces the runc binary size by about ~1M.
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
0fd3600 to
8decd05
Compare
Replace the use of the cilium/ebpf and cilium/ebpf/link with direct
bpf(2) syscalls. Keep cilium/ebpf/asm for instruction assembly.
Notes:
descriptors instead of *ebpf.Program handles;
binary.BigEndian, thus endian_{le,be}.go are introduced as a
workaround.
This reduces the runc binary size by about ~1M.
Being tested in opencontainers/runc#5340.
For initial discussion about this, see opencontainers/runc#5218.