Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | .. SPDX-License-Identifier: GPL-2.0-only .. Copyright (C) 2022 Red Hat, Inc. ================================================ BPF_MAP_TYPE_ARRAY and BPF_MAP_TYPE_PERCPU_ARRAY ================================================ .. note:: - ``BPF_MAP_TYPE_ARRAY`` was introduced in kernel version 3.19 - ``BPF_MAP_TYPE_PERCPU_ARRAY`` was introduced in version 4.6 ``BPF_MAP_TYPE_ARRAY`` and ``BPF_MAP_TYPE_PERCPU_ARRAY`` provide generic array storage. The key type is an unsigned 32-bit integer (4 bytes) and the map is of constant size. The size of the array is defined in ``max_entries`` at creation time. All array elements are pre-allocated and zero initialized when created. ``BPF_MAP_TYPE_PERCPU_ARRAY`` uses a different memory region for each CPU whereas ``BPF_MAP_TYPE_ARRAY`` uses the same memory region. The value stored can be of any size, however, all array elements are aligned to 8 bytes. Since kernel 5.5, memory mapping may be enabled for ``BPF_MAP_TYPE_ARRAY`` by setting the flag ``BPF_F_MMAPABLE``. The map definition is page-aligned and starts on the first page. Sufficient page-sized and page-aligned blocks of memory are allocated to store all array values, starting on the second page, which in some cases will result in over-allocation of memory. The benefit of using this is increased performance and ease of use since userspace programs would not be required to use helper functions to access and mutate data. Usage ===== Kernel BPF ---------- bpf_map_lookup_elem() ~~~~~~~~~~~~~~~~~~~~~ .. code-block:: c void *bpf_map_lookup_elem(struct bpf_map *map, const void *key) Array elements can be retrieved using the ``bpf_map_lookup_elem()`` helper. This helper returns a pointer into the array element, so to avoid data races with userspace reading the value, the user must use primitives like ``__sync_fetch_and_add()`` when updating the value in-place. bpf_map_update_elem() ~~~~~~~~~~~~~~~~~~~~~ .. code-block:: c long bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags) Array elements can be updated using the ``bpf_map_update_elem()`` helper. ``bpf_map_update_elem()`` returns 0 on success, or negative error in case of failure. Since the array is of constant size, ``bpf_map_delete_elem()`` is not supported. To clear an array element, you may use ``bpf_map_update_elem()`` to insert a zero value to that index. Per CPU Array ------------- Values stored in ``BPF_MAP_TYPE_ARRAY`` can be accessed by multiple programs across different CPUs. To restrict storage to a single CPU, you may use a ``BPF_MAP_TYPE_PERCPU_ARRAY``. When using a ``BPF_MAP_TYPE_PERCPU_ARRAY`` the ``bpf_map_update_elem()`` and ``bpf_map_lookup_elem()`` helpers automatically access the slot for the current CPU. bpf_map_lookup_percpu_elem() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: c void *bpf_map_lookup_percpu_elem(struct bpf_map *map, const void *key, u32 cpu) The ``bpf_map_lookup_percpu_elem()`` helper can be used to lookup the array value for a specific CPU. Returns value on success , or ``NULL`` if no entry was found or ``cpu`` is invalid. Concurrency ----------- Since kernel version 5.1, the BPF infrastructure provides ``struct bpf_spin_lock`` to synchronize access. Userspace --------- Access from userspace uses libbpf APIs with the same names as above, with the map identified by its ``fd``. Examples ======== Please see the ``tools/testing/selftests/bpf`` directory for functional examples. The code samples below demonstrate API usage. Kernel BPF ---------- This snippet shows how to declare an array in a BPF program. .. code-block:: c struct { __uint(type, BPF_MAP_TYPE_ARRAY); __type(key, u32); __type(value, long); __uint(max_entries, 256); } my_map SEC(".maps"); This example BPF program shows how to access an array element. .. code-block:: c int bpf_prog(struct __sk_buff *skb) { struct iphdr ip; int index; long *value; if (bpf_skb_load_bytes(skb, ETH_HLEN, &ip, sizeof(ip)) < 0) return 0; index = ip.protocol; value = bpf_map_lookup_elem(&my_map, &index); if (value) __sync_fetch_and_add(value, skb->len); return 0; } Userspace --------- BPF_MAP_TYPE_ARRAY ~~~~~~~~~~~~~~~~~~ This snippet shows how to create an array, using ``bpf_map_create_opts`` to set flags. .. code-block:: c #include <bpf/libbpf.h> #include <bpf/bpf.h> int create_array() { int fd; LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = BPF_F_MMAPABLE); fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "example_array", /* name */ sizeof(__u32), /* key size */ sizeof(long), /* value size */ 256, /* max entries */ &opts); /* create opts */ return fd; } This snippet shows how to initialize the elements of an array. .. code-block:: c int initialize_array(int fd) { __u32 i; long value; int ret; for (i = 0; i < 256; i++) { value = i; ret = bpf_map_update_elem(fd, &i, &value, BPF_ANY); if (ret < 0) return ret; } return ret; } This snippet shows how to retrieve an element value from an array. .. code-block:: c int lookup(int fd) { __u32 index = 42; long value; int ret; ret = bpf_map_lookup_elem(fd, &index, &value); if (ret < 0) return ret; /* use value here */ assert(value == 42); return ret; } BPF_MAP_TYPE_PERCPU_ARRAY ~~~~~~~~~~~~~~~~~~~~~~~~~ This snippet shows how to initialize the elements of a per CPU array. .. code-block:: c int initialize_array(int fd) { int ncpus = libbpf_num_possible_cpus(); long values[ncpus]; __u32 i, j; int ret; for (i = 0; i < 256 ; i++) { for (j = 0; j < ncpus; j++) values[j] = i; ret = bpf_map_update_elem(fd, &i, &values, BPF_ANY); if (ret < 0) return ret; } return ret; } This snippet shows how to access the per CPU elements of an array value. .. code-block:: c int lookup(int fd) { int ncpus = libbpf_num_possible_cpus(); __u32 index = 42, j; long values[ncpus]; int ret; ret = bpf_map_lookup_elem(fd, &index, &values); if (ret < 0) return ret; for (j = 0; j < ncpus; j++) { /* Use per CPU value here */ assert(values[j] == 42); } return ret; } Semantics ========= As shown in the example above, when accessing a ``BPF_MAP_TYPE_PERCPU_ARRAY`` in userspace, each value is an array with ``ncpus`` elements. When calling ``bpf_map_update_elem()`` the flag ``BPF_NOEXIST`` can not be used for these maps. |