2

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.
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
user2233706
  • 6,148
  • 5
  • 44
  • 86
  • Reading the source code of libbpf, it looks like this error is triggered when loading a BPF structure which contains a .values element, but is not of type `BPF_MAP_TYPE_ARRAY_OF_MAPS` or `BPF_MAP_TYPE_HASH_OF_MAPS`. Can you try declaring the counter table like this example? https://github.com/cilium/ebpf/blob/master/examples/kprobe_percpu/kprobe_percpu.c – Nick ODell Mar 16 '22 at 19:26
  • @NickODell I can do that, but then it won't be initialized to 0. Or is it by default 0? I was thinking I'd be able to do something like [this](https://github.com/cilium/ebpf/blob/42f2a98c68b56fbf2af6757ab86e2905435ecaaa/testdata/btf_map_init.c), which I've used for ```BPF_MAP_TYPE_PROG_ARRAY```. Other option is to initialize in user space, but I'd prefer to do this kernel side. – user2233706 Mar 16 '22 at 19:31
  • You can see the check here: https://github.com/libbpf/libbpf/blob/3591deb9bc6b0848721f831790b78ae0a593b4c5/src/libbpf.c#L2301 If it's not a map in map type or a prog array, and .values is used, then the error shows up. Currently trying to figure out if this is zero-filled by default. – Nick ODell Mar 16 '22 at 19:38
  • @NickODell Thanks. Feel free to post this as an answer. – user2233706 Mar 16 '22 at 19:40
  • According to [this](https://prototype-kernel.readthedocs.io/en/latest/bpf/ebpf_maps_types.html), "All array elements are pre-allocated and zero initialized at init time", so I should be good to not initialize to 0. – user2233706 Mar 16 '22 at 19:47

1 Answers1

3

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.

Nick ODell
  • 15,465
  • 3
  • 32
  • 66