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()

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()

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()

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.

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.

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.

#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.

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.

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.

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.

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.