Understanding eBPF Timers in Linux

eBPF Timers

v5.15

Timers allow eBPF programs to schedule the execution of an eBPF function at a later time. Use cases for this functionality include garbage collection of mapping values or performing periodic checks. For example, if the TTL (Time to Live) of a DNS record has expired, we may want to remove those records from the LRU (Least Recently Used) mapping to proactively free up space and avoid the removal of records that are still valid due to their TTL being active.

Timers are stored as fields in the <span>struct bpf_timer</span> within mapping values. For example:

struct map_elem {
    int counter;
    struct bpf_timer timer;
};

struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 1000);
    __type(key, int);
    __type(value, struct map_elem);
} hmap SEC(".maps");

The definition of this timer is:<span>struct bpf_timer { __u64 :64; __u64 :64; };</span>.

Note

Only programs with <span>CAP_BPF</span> permission are allowed to use <span>bpf_timer</span>.

Timers are associated with the lifecycle of the mapping; if the mapping is released or deleted, all pending timers in that mapping will be canceled.

Additionally, pending timers hold a reference to the eBPF program where their callback function resides. Therefore, even if there are no other references, the program will remain in the kernel until all related timers have triggered or been explicitly canceled.

Timers must be initialized using the <span>bpf_timer_init</span> helper function. After initialization, the <span>bpf_timer_set_callback</span> helper function can be used to assign a callback function to the timer. Finally, the <span>bpf_timer_start</span> helper function is used to start the timer. A pending timer can also be canceled using the <span>bpf_timer_cancel</span> helper function.

These three helper function calls do not necessarily have to occur in the same program. The following usage scenarios are valid:

  • <span>map1</span> is shared by <span>prog1</span>, <span>prog2</span>, and <span>prog3</span>.
  • <span>prog1</span> calls <span>bpf_timer_init</span> for some elements of <span>map1</span>.
  • <span>prog2</span> calls <span>bpf_timer_set_callback</span> for some elements of <span>map1</span>.
  • Elements that have not been initialized with <span>bpf_timer_init</span> will return <span>-EINVAL</span>.
  • <span>prog3</span> calls <span>bpf_timer_start</span> for some elements of <span>map1</span>.
  • Elements that have neither been initialized with <span>bpf_timer_init</span> nor had a callback function set with <span>bpf_timer_set_callback</span> will return <span>-EINVAL</span>.

If the mapping has no user references (i.e., it is not held by an open file descriptor from user space and is not pinned in bpffs), <span>bpf_timer_init</span> and <span>bpf_timer_set_callback</span> will return <span>-EPERM</span>.

The callback function passed to the timer has the following signature:<span>static int callback_fn(void *map, {map key type} *key, {map value type} *value)</span>. When the callback function is invoked, it receives a pointer to the mapping, the mapping key, and the mapping value associated with the timer. Unlike regular eBPF program execution, it does not have a context, so it cannot perform work that requires context operations or relies on side effects from helper functions. Its only input and output are the mapping.

Note

The callback function must always return 0; otherwise, the verifier will reject the program.

The callback function can reschedule its own timer by calling <span>value->timer</span> with <span>bpf_timer_start</span>. This allows for a one-time delay after the execution of a given eBPF program, as well as enabling a periodic function to run indefinitely after a single trigger event (i.e., a periodic timer).

Source

https://docs.ebpf.io/linux/concepts/timers/

Last updated: August 30, 2024

Leave a Comment