You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用libbpf-rs手动创建含LRU_HASH子映射的数组映射时遭遇参数无效错误的问题求助

使用libbpf-rs手动创建含LRU_HASH子映射的数组映射时遭遇参数无效错误的问题求助

各位好,我最近在做一个基于eBPF的项目,用libbpf-rs加载包含数组映射(ARRAY_OF_MAPS)的BPF程序时卡壳了——每次调用MapHandle::create创建LRU_HASH子映射都会报错Invalid argument (os error 22),试了好几种调整参数的方式都没解决,想请大家帮忙看看问题出在哪。

先把相关代码和配置贴出来:


我的BPF程序(src/bpf/test.bpf.c)

这个程序定义了一个LRU_HASH类型的子映射flows_t,然后外层是一个数组映射per_cpu_flows用来按CPU存储这些子映射:

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#define MAX_CPUS 32

// Size: 8 bytes
struct flow_key_t {
    __u32 src_ip_be;
    __u16 src_port_be;
    __u16 dst_port_be;
};

// Size: 16 bytes
struct flow_entry_t {
    __u64 timestamp_ns;
    __u32 dst_ip_be;
    __u16 dst_port_be;
    __u16 src_port_be;
};

// Clang generates FWD types for the above without these...
struct flow_key_t key __attribute__((unused));
struct flow_entry_t entry __attribute__((unused));

struct flows_t {
    __uint(type, BPF_MAP_TYPE_LRU_HASH);
    __uint(max_entries, 1024);
    __type(key, struct flow_key_t);
    __type(value, struct flow_entry_t);
};

struct {
    __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
    __uint(max_entries, MAX_CPUS + 1);
    __type(key, __u32);
    __array(values, struct flows_t);
} per_cpu_flows SEC(".maps");

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

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

Rust主程序(src/main.rs)

我尝试手动创建每个CPU对应的LRU_HASH子映射,然后更新到外层的数组映射里:

use anyhow::Result;
use libbpf_rs::libbpf_sys::bpf_map_create_opts;
use libbpf_rs::skel::OpenSkel;
use libbpf_rs::skel::SkelBuilder;
use libbpf_rs::MapCore;
use libbpf_rs::MapFlags;
use libbpf_rs::MapHandle;
use libbpf_rs::MapType;
use std::ffi::OsStr;
use std::mem::MaybeUninit;
use std::os::fd::AsFd;
use std::os::fd::AsRawFd;

mod test {
    include!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/bpf/test.skel.rs"));
}

#[allow(clippy::wildcard_imports)]
use test::*;

fn main() -> Result<()> {
    let skel_builder = TestSkelBuilder::default();
    let mut open_object = MaybeUninit::uninit();
    let open_skel = skel_builder.open(&mut open_object)?;
    let skel = open_skel.load()?;

    let cpus = num_cpus::get() as u32;
    let per_cpu_flows = &skel.maps.per_cpu_flows;

    for i in 0..cpus + 1 {
        let key = &i.to_ne_bytes();
        let name = format!("cpu-{}-flows", i);
        let name: &OsStr = OsStr::new(&name);

        let mut opts: bpf_map_create_opts = unsafe { std::mem::zeroed() };
        opts.sz = size_of::<bpf_map_create_opts>() as u64;

        let value = MapHandle::create(
            MapType::LruHash,
            name.into(),
            8,
            16,
            1024,
            &opts
        ).expect("Error creating map");

        per_cpu_flows
            .update(
                key,
                &value.as_fd().as_raw_fd().to_ne_bytes(),
                MapFlags::ANY
            )
            .expect("Unable to initialize map for CPU");
    }

    let link = skel
        .progs
        .test
        .attach_xdp(1)
        .expect("Failed to attach XDP program");

    Ok(())
}

build.rs 构建脚本

用来生成BPF程序的skeleton:

use std::env;
use std::ffi::OsStr;
use std::path::PathBuf;
use libbpf_cargo::SkeletonBuilder;

const SRC: &str = "src/bpf/test.bpf.c";

fn main() {
    let out = PathBuf::from(
        env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR must be set in build script"),
    )
    .join("src")
    .join("bpf")
    .join("test.skel.rs");

    let arch = env::var("CARGO_CFG_TARGET_ARCH")
        .expect("CARGO_CFG_TARGET_ARCH must be set in build script");

    SkeletonBuilder::new()
        .source(SRC)
        .clang_args([
            OsStr::new("-I"),
            vmlinux::include_path_root().join(arch).as_os_str(),
        ])
        .build_and_generate(&out)
        .unwrap();

    println!("cargo:rerun-if-changed={SRC}");
}

Cargo.toml 依赖配置

[package]
name = "libbpf-test"
version = "0.1.0"
edition = "2021"

[dependencies]
anyhow = "1.0.98"
libbpf-rs = { version = "0.25.0", features = [ "libbpf-sys" ] }
num_cpus = "1.17.0"

[build-dependencies]
libbpf-cargo = "0.25.0"
vmlinux = { git = "https://github.com/libbpf/vmlinux.h.git", rev = "83a228cf37fc65f2d14e4896a04922b5ee531a94" }

遇到的错误

运行程序后直接panic,错误信息是:

thread 'main' panicked at src/main.rs:40:14:
Error creating map: Error: Invalid argument (os error 22)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

补充:用ebpf-go实现的成功版本

之前我用ebpf-go实现过完全相同的逻辑是可以正常工作的,代码大概是这样:

for i := uint32(0); i <= numCPUs+1; i++ {
    flowMapSpec := spec.Maps["flows"]
    innerMap, err := ebpf.NewMap(flowMapSpec)
    if err != nil {
        log.Fatalf("creating inner map %d: %v", i, err)
    }
    if err := objs.UdpRouterMaps.PerCpuFlows.Put(i, innerMap); err != nil {
        log.Fatalf("inserting into per_cpu_flows[%d]: %v", i, err)
    }
    defer innerMap.Close()
}

这个Go版本里,我在C程序里给struct flows_t加上了SEC(".maps")定义,直接通过读取spec来创建子映射。用strace跟踪Go程序时看到的bpf调用片段是:

bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_LRU_HASH, key_size=8, value_size=16, max_entries=512000, map_flags=0, inner_map_fd=0, map_name="per_cpu_flows_i", map_...

我感觉问题应该出在bpf_map_create_opts的参数设置上,或者是libbpf-rs创建映射时的某些细节没处理对,但就是找不到具体原因。有没有熟悉libbpf-rs的朋友能帮我排查一下?

内容来源于stack exchange

火山引擎 最新活动