Skip to content

Commit 31aa294

Browse files
committed
C: Really force inline, and make it optional
With older kernel versions tail calls and BPF to BPF calls weren't supported together, so it was common to force clang to inline all functions. But we didn't do this forcibly enough, sometimes clang doesn't inline them. Add the right magic incantation to really force inlining. More recent kernel versions support mixing tail calls and BPF to BPF calls, make inlining optional for callers that want to use BPF to BPF calls.
1 parent f2f5c51 commit 31aa294

File tree

3 files changed

+37
-12
lines changed

3 files changed

+37
-12
lines changed

c.go

+11-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import (
1212

1313
const funcTemplate = `
1414
// True if packet matches, false otherwise
15-
static inline
15+
{{- if not .NoInline}}
16+
__attribute__((__always_inline__)) static inline
17+
{{- end}}
1618
uint32_t {{.Name}}(const uint8_t *const data, const uint8_t *const data_end) {
1719
__attribute__((unused))
1820
uint32_t a, x, m[16];
@@ -29,8 +31,9 @@ __attribute__((unused));
2931
}`
3032

3133
type cFunction struct {
32-
Name string
33-
Blocks []cBlock
34+
Name string
35+
NoInline bool
36+
Blocks []cBlock
3437
}
3538

3639
// cBPF reg to C symbol
@@ -78,6 +81,11 @@ type COpts struct {
7881
// FunctionName is the symbol to use as the generated C function. Must match regex:
7982
// [A-Za-z_][0-9A-Za-z_]*
8083
FunctionName string
84+
85+
// NoInline doesn't force the generated function to be inlined, allowing clang to emit
86+
// a BPF to BPF call.
87+
// Requires at least kernel 5.10 (for x86, later for other architectures) if used with tail-calls.
88+
NoInline bool
8189
}
8290

8391
// ToC compiles a cBPF filter to a C function with a signature of:

c_example_test.go

+4-8
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func ExampleToC() {
7575
bpf.RetConstant{Val: 1},
7676
}
7777

78-
elf, err := buildC(filter, "example")
78+
elf, err := buildC(filter, "example", COpts{FunctionName: "example_filter"})
7979
if err != nil {
8080
panic(err)
8181
}
@@ -89,13 +89,9 @@ func ExampleToC() {
8989
// and compiles the resulting C program to eBPF / XDP using clang.
9090
// The XDP program XDP_DROP's incoming packets that match the filter.
9191
// Returns the compiled ELF
92-
func buildC(filter []bpf.Instruction, programName string) ([]byte, error) {
93-
filterName := programName + "_filter"
94-
92+
func buildC(filter []bpf.Instruction, programName string, opts COpts) ([]byte, error) {
9593
// convert filter to C
96-
ebpfFilter, err := ToC(filter, COpts{
97-
FunctionName: filterName,
98-
})
94+
ebpfFilter, err := ToC(filter, opts)
9995
if err != nil {
10096
return nil, errors.Wrap(err, "converting filter to C")
10197
}
@@ -104,7 +100,7 @@ func buildC(filter []bpf.Instruction, programName string) ([]byte, error) {
104100
c := bytes.Buffer{}
105101
err = testTemplate.Execute(&c, testTemplateOpts{
106102
Filter: ebpfFilter,
107-
FilterName: filterName,
103+
FilterName: opts.FunctionName,
108104
ProgramName: programName,
109105
})
110106
if err != nil {

c_test.go

+22-1
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,32 @@ func TestFunctionName(t *testing.T) {
3030
checkName(t, "a2", true)
3131
}
3232

33+
func TestNoInline(t *testing.T) {
34+
elf, err := buildC([]bpf.Instruction{
35+
bpf.RetConstant{Val: 1},
36+
}, entryPoint, COpts{
37+
FunctionName: "filter",
38+
NoInline: true,
39+
})
40+
if err != nil {
41+
t.Fatal(err)
42+
}
43+
44+
spec, err := ebpf.LoadCollectionSpecFromReader(bytes.NewReader(elf))
45+
if err != nil {
46+
t.Fatal(err)
47+
}
48+
49+
if res := testProg(t, spec.Programs[entryPoint], []byte{1}); res != match {
50+
t.Fatalf("expected match, got %v", res)
51+
}
52+
}
53+
3354
const entryPoint = "xdp_filter"
3455

3556
// cBackend compiles classic BPF to C, which is compiled with clang
3657
func cBackend(tb testing.TB, insns []bpf.Instruction, in []byte) result {
37-
elf, err := buildC(insns, entryPoint)
58+
elf, err := buildC(insns, entryPoint, COpts{FunctionName: "filter"})
3859
if err != nil {
3960
tb.Fatal(err)
4061
}

0 commit comments

Comments
 (0)