Search code examples
ebpfxdp-bpf

Unable to initialize BPF_MAP_TYPE_PERCPU_ARRAY


Here's how I'm trying to initialize a BPF_MAP_TYPE_PERCPU_ARRAY of structs to a default value. The array contains counters the user space program will read.

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

struct xdp_stats_t {
    __u64 total_packets;
};

struct xdp_stats_t xdp_stats = {0};

struct {
    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
    __uint(key_size, sizeof(__u32));
    __uint(value_size, sizeof(struct xdp_stats_t));
    __uint(max_entries, 1);
    __array(values, struct xdp_stats_t);
} counter_table SEC(".maps") = {
    .values = {
        [0] = &xdp_stats
    }
};

SEC("xdp")
int main_prog(struct xdp_md *ctx) {
    return XDP_PASS;
}

char _license[] SEC("license") = "GPL";

But I get this error when I try to load the BPF program:

libbpf: map 'counter_table': should be map-in-map.

Solution

  • This specific part is triggering this error:

        .values = {
            [0] = &xdp_stats
        }
    

    In libbpf, when loading a BPF structure with a .values entry, it checks that the type is either a BPF_MAP_TYPE_ARRAY_OF_MAPS, a BPF_MAP_TYPE_HASH_OF_MAPS, or BPF_MAP_TYPE_PROG_ARRAY. If not, it won't let you use .values. The check is defined in libbpf here.

    I can't find any kernel documentation that says this directly, but I believe those arrays will be zero-initialized, so there's no need to initialize them. Here's the most on-point documentation I can find:

    BPF_MAP_TYPE_ARRAY
                  Array maps have the following characteristics:
    
                  [...]
    
                  *  All array elements pre-allocated and zero initialized
                     at init time
    

    (Source.)

    I can't find any documentation saying the same thing about BPF_MAP_TYPE_PERCPU_ARRAY. I assume it's similar.