Skip to content

Commit

Permalink
C: Really force inline, and make it optional
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
arthurfabre committed Aug 9, 2023
1 parent f2f5c51 commit 31aa294
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 12 deletions.
14 changes: 11 additions & 3 deletions c.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import (

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

type cFunction struct {
Name string
Blocks []cBlock
Name string
NoInline bool
Blocks []cBlock
}

// cBPF reg to C symbol
Expand Down Expand Up @@ -78,6 +81,11 @@ type COpts struct {
// FunctionName is the symbol to use as the generated C function. Must match regex:
// [A-Za-z_][0-9A-Za-z_]*
FunctionName string

// NoInline doesn't force the generated function to be inlined, allowing clang to emit
// a BPF to BPF call.
// Requires at least kernel 5.10 (for x86, later for other architectures) if used with tail-calls.
NoInline bool
}

// ToC compiles a cBPF filter to a C function with a signature of:
Expand Down
12 changes: 4 additions & 8 deletions c_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func ExampleToC() {
bpf.RetConstant{Val: 1},
}

elf, err := buildC(filter, "example")
elf, err := buildC(filter, "example", COpts{FunctionName: "example_filter"})
if err != nil {
panic(err)
}
Expand All @@ -89,13 +89,9 @@ func ExampleToC() {
// and compiles the resulting C program to eBPF / XDP using clang.
// The XDP program XDP_DROP's incoming packets that match the filter.
// Returns the compiled ELF
func buildC(filter []bpf.Instruction, programName string) ([]byte, error) {
filterName := programName + "_filter"

func buildC(filter []bpf.Instruction, programName string, opts COpts) ([]byte, error) {
// convert filter to C
ebpfFilter, err := ToC(filter, COpts{
FunctionName: filterName,
})
ebpfFilter, err := ToC(filter, opts)
if err != nil {
return nil, errors.Wrap(err, "converting filter to C")
}
Expand All @@ -104,7 +100,7 @@ func buildC(filter []bpf.Instruction, programName string) ([]byte, error) {
c := bytes.Buffer{}
err = testTemplate.Execute(&c, testTemplateOpts{
Filter: ebpfFilter,
FilterName: filterName,
FilterName: opts.FunctionName,
ProgramName: programName,
})
if err != nil {
Expand Down
23 changes: 22 additions & 1 deletion c_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,32 @@ func TestFunctionName(t *testing.T) {
checkName(t, "a2", true)
}

func TestNoInline(t *testing.T) {
elf, err := buildC([]bpf.Instruction{
bpf.RetConstant{Val: 1},
}, entryPoint, COpts{
FunctionName: "filter",
NoInline: true,
})
if err != nil {
t.Fatal(err)
}

spec, err := ebpf.LoadCollectionSpecFromReader(bytes.NewReader(elf))
if err != nil {
t.Fatal(err)
}

if res := testProg(t, spec.Programs[entryPoint], []byte{1}); res != match {
t.Fatalf("expected match, got %v", res)
}
}

const entryPoint = "xdp_filter"

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

0 comments on commit 31aa294

Please sign in to comment.