Skip to content

Functions

When we talk about a function, we are referring to a function you would write in C or comparable programming language. In eBPF, the term function, program, and sub-program are often used interchangeably. A program refers to a function with a single argument, that being the context. A program can be attached to a hook point. Sub-programs, also referred to as BPF-to-BPF functions, are functions with zero to five arguments, they cannot be attached to a hook point, and are called from a program or special mechanism.

Calling convention

The eBPF instruction set defines the calling convention for functions, these include programs, sub-programs, helper functions and kfuncs. Every function no matter who defines it uses the same calling convention. The R0 register is uses as the return value, a function should set it before returning unless it is a void function. Registers R1-R5 are used for arguments, R1 for the first argument, R2 for the second, and so on. Unlike calling conventions on native architectures, arguments are never passed via the stack. So 5 arguments is a hard limit, structures must be used to work around this. Registers R1-5 are clobbered after a function call, the verifier will not allow you to read from them until they are set with a known value. R6-9 are callee saved registers, they are preserved across function calls.

BPF to BPF functions (sub-programs)

In v4.16 BPF to BPF function calls were added. This allows BPF programs to reuse logic within the same program. A function can take up to 5 arguments, limited by the calling convention. It gets a fresh stack frame, which like any stack is free-ed and reused after the program exits. Functions can also access memory on the stack of a caller if a pointer to it is passed as an argument. Functions are limited to a maximum call depth of 8, so recursion of any meaningful depth is not possible.

Function inlining

By default, the compiler will chose inline a function or to keep it a separate function. Compilers can be encouraged to inline or not inline a function with arguments like __attribute__((always_inline)) or __attribute__((noinline)). Inlined functions do not incur the overhead of a function call as they will become part of the calling function. Inlined functions can also be optimized per call site since arguments are known.

Tail calls

When function calls are combined with tail calls the available stack size per program will shrink from 512 bytes to 256 bytes. The reasoning being that when a tail call is made, the current stack frame is reused, but if that tail call is made from a function, the stack of the caller can not be reused. By default, kernel threads are limited to 8k stack sizes. By decreasing the max stack size, it is harder to run out of stack space, though it is still possible.

Function by function verification

Until v5.6 the verifier would re-verify that a function was safe for every call site. Meaning that if you have a function which is called 10 times, then the verifier would check for every call that with the given inputs the function was save. This defeats the purpose of functions somewhat since you still incur verifier complexity for every call.

Since v5.6 a distinction is made between "static" and "global" functions. Static functions are still verified as usual. But global functions undergo "function by function verification". This means that the verifier will verify every function once, and even out of order. It will assume all possible input values are possible, since it will not check every call site anymore. Therefor, functions might require more input checking to pass the verifier. This change reduces verification times and complexity. Static functions are functions marked with the static keyword in the C code, global functions regular non-static functions.

Global function replacement

Also added in v5.6 is the ability to replace global functions. The primary use case for this is libxdp which used this to implement XDP program chaining from a dispatcher program.

See Program Type BPF_PROG_TYPE_EXT for more information.

Callbacks

In v5.13 the verifier was extended to allow for callbacks. Since then a number of helper function and kfuncs have been added that call back into given functions.

Argument annotations

In v6.8 global function argument annotation were added. These are a set of annotations (in practice these are BTF decl tags), which if added to an attribute, tell the verifier to restrict the input values to the function. Possible tags are:

  • __arg_ctx - The argument is a pointer to a program context.
  • __arg_nonnull - The argument can not be NULL.
  • __arg_nullable - The argument can be NULL.
  • __arg_trusted - The argument must be a trusted value.
  • __arg_arena - The argument must be a pointer to a memory arena.