162306a36Sopenharmony_ci.. SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci.. Copyright (C) 2022 Red Hat, Inc.
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci================================================
562306a36Sopenharmony_ciBPF_MAP_TYPE_ARRAY and BPF_MAP_TYPE_PERCPU_ARRAY
662306a36Sopenharmony_ci================================================
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci.. note::
962306a36Sopenharmony_ci   - ``BPF_MAP_TYPE_ARRAY`` was introduced in kernel version 3.19
1062306a36Sopenharmony_ci   - ``BPF_MAP_TYPE_PERCPU_ARRAY`` was introduced in version 4.6
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci``BPF_MAP_TYPE_ARRAY`` and ``BPF_MAP_TYPE_PERCPU_ARRAY`` provide generic array
1362306a36Sopenharmony_cistorage. The key type is an unsigned 32-bit integer (4 bytes) and the map is
1462306a36Sopenharmony_ciof constant size. The size of the array is defined in ``max_entries`` at
1562306a36Sopenharmony_cicreation time. All array elements are pre-allocated and zero initialized when
1662306a36Sopenharmony_cicreated. ``BPF_MAP_TYPE_PERCPU_ARRAY`` uses a different memory region for each
1762306a36Sopenharmony_ciCPU whereas ``BPF_MAP_TYPE_ARRAY`` uses the same memory region. The value
1862306a36Sopenharmony_cistored can be of any size, however, all array elements are aligned to 8
1962306a36Sopenharmony_cibytes.
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ciSince kernel 5.5, memory mapping may be enabled for ``BPF_MAP_TYPE_ARRAY`` by
2262306a36Sopenharmony_cisetting the flag ``BPF_F_MMAPABLE``. The map definition is page-aligned and
2362306a36Sopenharmony_cistarts on the first page. Sufficient page-sized and page-aligned blocks of
2462306a36Sopenharmony_cimemory are allocated to store all array values, starting on the second page,
2562306a36Sopenharmony_ciwhich in some cases will result in over-allocation of memory. The benefit of
2662306a36Sopenharmony_ciusing this is increased performance and ease of use since userspace programs
2762306a36Sopenharmony_ciwould not be required to use helper functions to access and mutate data.
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ciUsage
3062306a36Sopenharmony_ci=====
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ciKernel BPF
3362306a36Sopenharmony_ci----------
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cibpf_map_lookup_elem()
3662306a36Sopenharmony_ci~~~~~~~~~~~~~~~~~~~~~
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci.. code-block:: c
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci   void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ciArray elements can be retrieved using the ``bpf_map_lookup_elem()`` helper.
4362306a36Sopenharmony_ciThis helper returns a pointer into the array element, so to avoid data races
4462306a36Sopenharmony_ciwith userspace reading the value, the user must use primitives like
4562306a36Sopenharmony_ci``__sync_fetch_and_add()`` when updating the value in-place.
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cibpf_map_update_elem()
4862306a36Sopenharmony_ci~~~~~~~~~~~~~~~~~~~~~
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci.. code-block:: c
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci   long bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ciArray elements can be updated using the ``bpf_map_update_elem()`` helper.
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci``bpf_map_update_elem()`` returns 0 on success, or negative error in case of
5762306a36Sopenharmony_cifailure.
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ciSince the array is of constant size, ``bpf_map_delete_elem()`` is not supported.
6062306a36Sopenharmony_ciTo clear an array element, you may use ``bpf_map_update_elem()`` to insert a
6162306a36Sopenharmony_cizero value to that index.
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ciPer CPU Array
6462306a36Sopenharmony_ci-------------
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ciValues stored in ``BPF_MAP_TYPE_ARRAY`` can be accessed by multiple programs
6762306a36Sopenharmony_ciacross different CPUs. To restrict storage to a single CPU, you may use a
6862306a36Sopenharmony_ci``BPF_MAP_TYPE_PERCPU_ARRAY``.
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ciWhen using a ``BPF_MAP_TYPE_PERCPU_ARRAY`` the ``bpf_map_update_elem()`` and
7162306a36Sopenharmony_ci``bpf_map_lookup_elem()`` helpers automatically access the slot for the current
7262306a36Sopenharmony_ciCPU.
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cibpf_map_lookup_percpu_elem()
7562306a36Sopenharmony_ci~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci.. code-block:: c
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci   void *bpf_map_lookup_percpu_elem(struct bpf_map *map, const void *key, u32 cpu)
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ciThe ``bpf_map_lookup_percpu_elem()`` helper can be used to lookup the array
8262306a36Sopenharmony_civalue for a specific CPU. Returns value on success , or ``NULL`` if no entry was
8362306a36Sopenharmony_cifound or ``cpu`` is invalid.
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ciConcurrency
8662306a36Sopenharmony_ci-----------
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ciSince kernel version 5.1, the BPF infrastructure provides ``struct bpf_spin_lock``
8962306a36Sopenharmony_cito synchronize access.
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ciUserspace
9262306a36Sopenharmony_ci---------
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ciAccess from userspace uses libbpf APIs with the same names as above, with
9562306a36Sopenharmony_cithe map identified by its ``fd``.
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ciExamples
9862306a36Sopenharmony_ci========
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ciPlease see the ``tools/testing/selftests/bpf`` directory for functional
10162306a36Sopenharmony_ciexamples. The code samples below demonstrate API usage.
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ciKernel BPF
10462306a36Sopenharmony_ci----------
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ciThis snippet shows how to declare an array in a BPF program.
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci.. code-block:: c
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci    struct {
11162306a36Sopenharmony_ci            __uint(type, BPF_MAP_TYPE_ARRAY);
11262306a36Sopenharmony_ci            __type(key, u32);
11362306a36Sopenharmony_ci            __type(value, long);
11462306a36Sopenharmony_ci            __uint(max_entries, 256);
11562306a36Sopenharmony_ci    } my_map SEC(".maps");
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ciThis example BPF program shows how to access an array element.
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci.. code-block:: c
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci    int bpf_prog(struct __sk_buff *skb)
12362306a36Sopenharmony_ci    {
12462306a36Sopenharmony_ci            struct iphdr ip;
12562306a36Sopenharmony_ci            int index;
12662306a36Sopenharmony_ci            long *value;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci            if (bpf_skb_load_bytes(skb, ETH_HLEN, &ip, sizeof(ip)) < 0)
12962306a36Sopenharmony_ci                    return 0;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci            index = ip.protocol;
13262306a36Sopenharmony_ci            value = bpf_map_lookup_elem(&my_map, &index);
13362306a36Sopenharmony_ci            if (value)
13462306a36Sopenharmony_ci                    __sync_fetch_and_add(value, skb->len);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci            return 0;
13762306a36Sopenharmony_ci    }
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ciUserspace
14062306a36Sopenharmony_ci---------
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ciBPF_MAP_TYPE_ARRAY
14362306a36Sopenharmony_ci~~~~~~~~~~~~~~~~~~
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ciThis snippet shows how to create an array, using ``bpf_map_create_opts`` to
14662306a36Sopenharmony_ciset flags.
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci.. code-block:: c
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci    #include <bpf/libbpf.h>
15162306a36Sopenharmony_ci    #include <bpf/bpf.h>
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci    int create_array()
15462306a36Sopenharmony_ci    {
15562306a36Sopenharmony_ci            int fd;
15662306a36Sopenharmony_ci            LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = BPF_F_MMAPABLE);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci            fd = bpf_map_create(BPF_MAP_TYPE_ARRAY,
15962306a36Sopenharmony_ci                                "example_array",       /* name */
16062306a36Sopenharmony_ci                                sizeof(__u32),         /* key size */
16162306a36Sopenharmony_ci                                sizeof(long),          /* value size */
16262306a36Sopenharmony_ci                                256,                   /* max entries */
16362306a36Sopenharmony_ci                                &opts);                /* create opts */
16462306a36Sopenharmony_ci            return fd;
16562306a36Sopenharmony_ci    }
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ciThis snippet shows how to initialize the elements of an array.
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci.. code-block:: c
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci    int initialize_array(int fd)
17262306a36Sopenharmony_ci    {
17362306a36Sopenharmony_ci            __u32 i;
17462306a36Sopenharmony_ci            long value;
17562306a36Sopenharmony_ci            int ret;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci            for (i = 0; i < 256; i++) {
17862306a36Sopenharmony_ci                    value = i;
17962306a36Sopenharmony_ci                    ret = bpf_map_update_elem(fd, &i, &value, BPF_ANY);
18062306a36Sopenharmony_ci                    if (ret < 0)
18162306a36Sopenharmony_ci                            return ret;
18262306a36Sopenharmony_ci            }
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci            return ret;
18562306a36Sopenharmony_ci    }
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ciThis snippet shows how to retrieve an element value from an array.
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci.. code-block:: c
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci    int lookup(int fd)
19262306a36Sopenharmony_ci    {
19362306a36Sopenharmony_ci            __u32 index = 42;
19462306a36Sopenharmony_ci            long value;
19562306a36Sopenharmony_ci            int ret;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci            ret = bpf_map_lookup_elem(fd, &index, &value);
19862306a36Sopenharmony_ci            if (ret < 0)
19962306a36Sopenharmony_ci                    return ret;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci            /* use value here */
20262306a36Sopenharmony_ci            assert(value == 42);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci            return ret;
20562306a36Sopenharmony_ci    }
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ciBPF_MAP_TYPE_PERCPU_ARRAY
20862306a36Sopenharmony_ci~~~~~~~~~~~~~~~~~~~~~~~~~
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ciThis snippet shows how to initialize the elements of a per CPU array.
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci.. code-block:: c
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci    int initialize_array(int fd)
21562306a36Sopenharmony_ci    {
21662306a36Sopenharmony_ci            int ncpus = libbpf_num_possible_cpus();
21762306a36Sopenharmony_ci            long values[ncpus];
21862306a36Sopenharmony_ci            __u32 i, j;
21962306a36Sopenharmony_ci            int ret;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci            for (i = 0; i < 256 ; i++) {
22262306a36Sopenharmony_ci                    for (j = 0; j < ncpus; j++)
22362306a36Sopenharmony_ci                            values[j] = i;
22462306a36Sopenharmony_ci                    ret = bpf_map_update_elem(fd, &i, &values, BPF_ANY);
22562306a36Sopenharmony_ci                    if (ret < 0)
22662306a36Sopenharmony_ci                            return ret;
22762306a36Sopenharmony_ci            }
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci            return ret;
23062306a36Sopenharmony_ci    }
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ciThis snippet shows how to access the per CPU elements of an array value.
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci.. code-block:: c
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci    int lookup(int fd)
23762306a36Sopenharmony_ci    {
23862306a36Sopenharmony_ci            int ncpus = libbpf_num_possible_cpus();
23962306a36Sopenharmony_ci            __u32 index = 42, j;
24062306a36Sopenharmony_ci            long values[ncpus];
24162306a36Sopenharmony_ci            int ret;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci            ret = bpf_map_lookup_elem(fd, &index, &values);
24462306a36Sopenharmony_ci            if (ret < 0)
24562306a36Sopenharmony_ci                    return ret;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci            for (j = 0; j < ncpus; j++) {
24862306a36Sopenharmony_ci                    /* Use per CPU value here */
24962306a36Sopenharmony_ci                    assert(values[j] == 42);
25062306a36Sopenharmony_ci            }
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci            return ret;
25362306a36Sopenharmony_ci    }
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ciSemantics
25662306a36Sopenharmony_ci=========
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ciAs shown in the example above, when accessing a ``BPF_MAP_TYPE_PERCPU_ARRAY``
25962306a36Sopenharmony_ciin userspace, each value is an array with ``ncpus`` elements.
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ciWhen calling ``bpf_map_update_elem()`` the flag ``BPF_NOEXIST`` can not be used
26262306a36Sopenharmony_cifor these maps.
263