I have an XDP program, which contains the following maps:
// The flow state of a single core
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 512000);
__type(key, struct flow_key);
__type(value, struct flow_entry);
} flows SEC(".maps");
// Stores a flow table for each CPU core + an additional global flow state
struct {
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
__uint(max_entries, MAX_CPUs + 1);
__type(key, __u32);
__type(value, __u32);
__array(values, flows);
} per_cpu_flows SEC(".maps");
In my loader I would need to create those inner maps using the skeleton code generated by libbpf-rs and insert them into the array. I'm lost with what I should do here. The template loop I would like to implement is something along the lines of:
let cpus = num_cpus::get() as u32;
let flows = skel.maps.flows;
let per_cpu_flows = &skel.maps.per_cpu_flows;
for i in 0..cpus+1 {
let key = &i.to_ne_bytes();
let value = ???? // What should I do here?
per_cpu_flows.update(key, value, MapFlags::ANY).expect("Unable to initialize flow cache for CPU core");
}
The problem is that I don't see anything relevant in the generated skeleton code for creating the actual map that should be inserted in the array. Any idea what should be done?
I also tried a few LLMs for pointers, but they all seem to hallunicate garbage.
There are two approaches here. Either you define and initialize the maps in the BPF program or you initialize the outer map in userspace. Right now, you seem to be mixing both approaches and it won't work.
If you want to initialize the outer map in its declaration, you'll need to know how many entries you want to initialize, which doesn't seem to be your case. Note you'll also need to fix the values
for the outer map; it expects a type, not an object.
struct flows_t {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 512000);
__type(key, struct flow_key);
__type(value, struct flow_entry);
} flows1 SEC(".maps"), flows2 SEC(".maps");
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") = {
.values = { &flows1,
&flows2 }
};
In this case, you can create the inner maps in userspace:
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 value = MapHandle::create(MapType::LruHash, ...);
per_cpu_flows.update(key, value, MapFlags::ANY).expect("Unable to initialize flow cache for CPU core");
}
The outer map definition stays as above, without the initialization:
struct flows_t {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 512000);
__type(key, struct flow_key);
__type(value, struct flow_entry);
};
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");