aya-rs / aya

Aya is an eBPF library for the Rust programming language, built with a focus on developer experience and operability.
https://aya-rs.dev/book/
Apache License 2.0
3.24k stars 290 forks source link

error: failed to create `BPF_MAP_TYPE_ARRAY_OF_MAPS` of `BPF_MAP_TYPE_RINGBUF` #934

Closed Andreagit97 closed 7 months ago

Andreagit97 commented 7 months ago

Hi all, I was playing with aya library and I faced an error.

Context

In Falco we create an BPF_MAP_TYPE_ARRAY_OF_MAPS of BPF_MAP_TYPE_RINGBUF to send events to userspace since we need to support huge throughputs of events. What we usually do with the libbpf C library is:

  1. create the BPF_MAP_TYPE_ARRAY_OF_MAPS
  2. set an inner map inside it before the loading phase, using the libbpf API bpf_map__set_inner_map_fd
  3. create all the BPF_MAP_TYPE_RINGBUF we need and add them into BPF_MAP_TYPE_ARRAY_OF_MAPS

Issue

I'm not familiar with aya APIs so I tried something very simple to check what is supported. First I created a simple C file with the ebpf code:

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>

char LICENSE[] SEC("license") = "Dual BSD/GPL";

struct event
{
    char comm[16];
};

struct ringbuf_map
{
    __uint(type, BPF_MAP_TYPE_RINGBUF);
    __uint(max_entries, 8 * 1024 * 1024);
} ringbuf1 SEC(".maps"), ringbuf2 SEC(".maps");

struct
{
    __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
    __uint(max_entries, 4);
    __type(key, int);
    __array(values, struct ringbuf_map);
} ringbuf_arr SEC(".maps") = {
    .values =
        {
            [0] = &ringbuf1,
            [2] = &ringbuf2,
        },
};

SEC("fexit/security_file_open")
int example(void *ctx)
{
    int cpu_id = 0;
    struct ringbuf_map *rb = (struct ringbuf_map *)bpf_map_lookup_elem(&ringbuf_arr, &cpu_id);
    if(!rb)
    {
        return 0;
    }

    struct event e = {0};
    if(bpf_get_current_comm(&e.comm, 16) < 0)
    {
        bpf_printk("failed to get current comm");
        return 0;
    }

    bpf_ringbuf_output(rb, &e, sizeof(e), BPF_RB_NO_WAKEUP);
    return 0;
}

I compiled it:

clang -O2 -target bpf -c ./ebpf/example.bpf.c -o ./ebpf/build/example.o -g

then i built a very simple loader in Rust to check that everything works:

use aya::Bpf;
fn main() -> Result<(), anyhow::Error> {
    let mut bpf = Bpf::load_file("/home/andrea/Desktop/aya_example/ebpf/build/bpf_map.o")?;
    Ok(())
}

The initial code was bigger but the issue seems in Bpf::load_file. This is the error I faced:

Error: map error: failed to create map `ringbuf_arr` with code -1

Caused by:
    0: failed to create map `ringbuf_arr` with code -1
    1: Invalid argument (os error 22)

Looking around the kernel with bpftrace the error seems related to the value_size of the map to be 0.

sudo bpftrace -e 'fexit:fd_array_map_alloc_check /comm == "aya_example" / { printf("ret value: %ld, attr_value: %d\n", retval, args->attr->value_size); }'

ret value: -22, attr_value: 0

It's the first time that I have used this library so maybe I'm missing something obvious... The same ebpf code I provided here works great with libbpf C library, so the issue seems in the loader.

alessandrod commented 7 months ago

We support ringbuf but we don't support array/hash of maps yet. We have a PR for it, but it needs to be rebased https://github.com/aya-rs/aya/pull/70

I need this too exactly for the same purpose (a profiler that sends a lot of ringbuf records), so this is close to the top of my todo list, but feel free to take it from me if you want :)

Andreagit97 commented 7 months ago

Oh, I see, thank you for the quick feedback! I think i can close this :)