|
| 1 | +{{ linux_version("5.2", "For all global variable-related BPF operations, |
| 2 | +the kernel needs to understand the BPF_PSEUDO_MAP_VALUE value in ldimm64 |
| 3 | +instructions. This is needed for direct, lookup-free map access." )}} |
| 4 | + |
| 5 | +Like typical C programs, BPF programs allow the use of global variables. These |
| 6 | +variables can be initialized from the BPF C code itself, or they can be modified |
| 7 | +by the loading user space application before handing it off to the kernel. |
| 8 | + |
| 9 | +The abstraction {{ proj }} provides to interact with global variables is the |
| 10 | +{{ godoc('VariableSpec') }}, found in the {{ godoc('CollectionSpec.Variables') }} |
| 11 | +field. This page describes how to declare variables in BPF C and how to interact |
| 12 | +with them in Go. |
| 13 | + |
| 14 | +## Runtime Constants |
| 15 | + |
| 16 | +{{ linux_version("5.2", "Read-only maps and the BPF_MAP_FREEZE command are needed |
| 17 | +for implementing constant variables.") }} |
| 18 | + |
| 19 | +Global runtime constants are typically used for configuration values that |
| 20 | +influence the functionality of a BPF program. Think all sorts of network or |
| 21 | +hardware addresses for network filtering, or timeouts for rate limiting. The C |
| 22 | +compiler will reject any runtime modifications to these variables in the BPF |
| 23 | +program, like a typical const. |
| 24 | + |
| 25 | +Crucially, the BPF verifier will also perform dead code analysis if constants |
| 26 | +are used in if statements. If a condition is always true or false, it will |
| 27 | +remove unused code paths from the BPF program, reducing verification time and |
| 28 | +increasing runtime performance. |
| 29 | + |
| 30 | +This enables many features like portable kfuncs, allowing C code to refer to |
| 31 | +kfuncs that may not exist in some kernels, as long as those code paths are |
| 32 | +guaranteed not to execute at runtime. Similarly, this can be used to your |
| 33 | +advantage to disable code paths that are not needed in certain configurations, |
| 34 | +or would result in a verifier error on some kernels or in some contexts. |
| 35 | + |
| 36 | +:ebee-color: Consider the following C BPF program that reads a global constant |
| 37 | +and returns it: |
| 38 | + |
| 39 | +{{ c_example('variables_const', title='BPF C program declaring global constant const_u32') }} |
| 40 | + |
| 41 | +??? warning "Why is `const_u32` declared `volatile`?" |
| 42 | + |
| 43 | + In short: without the `volatile` qualifier, the variable would be optimized |
| 44 | + away and not appear in the BPF object file, leaving us unable to modify it |
| 45 | + from our user space application. |
| 46 | + |
| 47 | + In this program, the compiler (in)correctly deduces two things about `const_u32`: |
| 48 | + it is never assigned a value, and it doesn't change over the course of the program. |
| 49 | + Implementation details aside, it will now assume that the return value of |
| 50 | + `const_example()` is always 0 and omit the variable from the ELF altogether. |
| 51 | + |
| 52 | + For BPF programs, it's common practice to declare all global variables that |
| 53 | + need to be accessed from user space as `volatile`, especially non-`const` |
| 54 | + globals. Doing so ensures the compiler reliably allocates them in a data |
| 55 | + section in the ELF. |
| 56 | + |
| 57 | +:simple-go: First, let's take a look at a full Go example that will comprise the |
| 58 | +majority of interactions with constants. In the example below, we'll load a BPF |
| 59 | +object from disk, pull out a variable, set its value and call the BPF program |
| 60 | +once with an empty context. Variations on this pattern will follow later. |
| 61 | + |
| 62 | +{{ go_example('DocVariablesSetConst', title='Go program modifying a const, loading and running the BPF program') }} |
| 63 | + |
| 64 | +1. Any values passed into {{ godoc('VariableSpec.Set') }} must marshal to a |
| 65 | + fixed width. This behaviour is identical to {{ godoc('Map.Put') }} and |
| 66 | + friends. Using untyped integers is not supported since their size is platform |
| 67 | + dependent. We recommend the same approach in BPF C to keep data size |
| 68 | + predictable. |
| 69 | +2. A 15-byte context is the minimum the kernel will accept for dry-running a BPF |
| 70 | + program. If your BPF program reads from its context, populating this slice is |
| 71 | + a great way of doing unit testing without setting up a live testing environment. |
| 72 | + |
| 73 | +## Global Variables |
| 74 | + |
| 75 | +Non-const global variables are mutable and can be modified by both the BPF |
| 76 | +program and the user space application. They are typically used for keeping |
| 77 | +state like metrics, counters, rate limiting, etc. |
| 78 | + |
| 79 | +These variables can also be initialized from user space, much like their `const` |
| 80 | +counterparts, and can be both read and written to from the BPF program as well |
| 81 | +as the user space application. More on that in a future section. |
| 82 | + |
| 83 | +:ebee-color: The following C BPF program reads a global variable and returns it: |
| 84 | + |
| 85 | +{{ c_example('variables_global', title='BPF C program declaring global variable global_u16') }} |
| 86 | + |
| 87 | +??? warning "Why is `global_u16` declared `volatile`?" |
| 88 | + |
| 89 | + Similar to `volatile const` in a prior example, `volatile` is used here to |
| 90 | + make compiler output more deterministic. Without it, the compiler may |
| 91 | + choose to optimize away a variable if it's never assigned to, not knowing |
| 92 | + its value is actually provided by user space. The `volatile` qualifier |
| 93 | + doesn't change the variable's semantics. |
| 94 | + |
| 95 | +:simple-go: Next, in user space, initialize `global_u16` to 9000: |
| 96 | + |
| 97 | +{{ go_example('DocVariablesSetGlobalU16') }} |
| 98 | + |
| 99 | +Dry-running `global_example()` a few times results in our value increasing on |
| 100 | +every invocation: |
| 101 | + |
| 102 | +{{ go_example('DocVariablesSetGlobalRun') }} |
| 103 | + |
| 104 | +## Internal/Hidden Global Variables |
| 105 | + |
| 106 | +By default, all global variables described in an ELF's data sections are exposed |
| 107 | +through {{ godoc('CollectionSpec.Variables') }}. However, there may be cases |
| 108 | +where you don't want user space to interfere with a variable (either on purpose |
| 109 | +or by accident) and you want to keep the variable internal to the BPF program. |
| 110 | + |
| 111 | +{{ c_example('variables_hidden', title='BPF C program declaring internal global variable internal_var') }} |
| 112 | + |
| 113 | +The `__hidden` macro is found in Linux' `<bpf/bpf_helpers.h>` as of version 5.13 |
| 114 | +and is defined as follows: |
| 115 | + |
| 116 | +```c |
| 117 | +#define __hidden __attribute__((visibility("hidden"))) |
| 118 | +``` |
| 119 | +
|
| 120 | +This will cause the VariableSpec for `hidden_var` to not be included in |
| 121 | +the CollectionSpec. |
0 commit comments