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