diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 6086dfa3..24ba517f 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -490,7 +490,19 @@ impl<'a> BpfLoader<'a> { } let btf_fd = btf_fd.as_deref().map(|fd| fd.as_fd()); let mut map = match obj.pinning() { - PinningType::None => MapData::create(obj, &name, btf_fd)?, + PinningType::None => { + // If map_pin_path is provided be sure to create the maps if they don't exist + // and pin by name at the provided path. + if let BpfSectionKind::Bss | BpfSectionKind::Rodata | BpfSectionKind::Data = + obj.section_kind() + { + MapData::create(obj, &name, btf_fd)? + } else if let Some(path) = map_pin_path { + MapData::create_pinned_by_name(path, obj, &name, btf_fd)? + } else { + MapData::create(obj, &name, btf_fd)? + } + } PinningType::ByName => { // pin maps in /sys/fs/bpf by default to align with libbpf // behavior https://github.com/libbpf/libbpf/blob/v1.2.2/src/libbpf.c#L2161. diff --git a/test/integration-test/bpf/multimap-btf.bpf.c b/test/integration-test/bpf/multimap-btf.bpf.c index 9e595165..08486291 100644 --- a/test/integration-test/bpf/multimap-btf.bpf.c +++ b/test/integration-test/bpf/multimap-btf.bpf.c @@ -7,21 +7,21 @@ struct { __uint(type, BPF_MAP_TYPE_ARRAY); __type(key, __u32); __type(value, __u64); - __uint(max_entries, 1); + __uint(max_entries, 2); } map_1 SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_ARRAY); __type(key, __u32); __type(value, __u64); - __uint(max_entries, 1); + __uint(max_entries, 2); } map_2 SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_ARRAY); __type(key, __u32); __type(value, __u64); - __uint(max_entries, 1); + __uint(max_entries, 2); __uint(pinning, LIBBPF_PIN_BY_NAME); } map_pin_by_name SEC(".maps"); @@ -38,4 +38,17 @@ int bpf_prog(void *ctx) { return 0; } +SEC("uprobe") +int bpf_prog1(void *ctx) { + __u32 key = 1; + __u64 twenty_four = 35; + __u64 forty_two = 53; + __u64 forty_four = 55; + + bpf_map_update_elem(&map_1, &key, &twenty_four, BPF_ANY); + bpf_map_update_elem(&map_2, &key, &forty_two, BPF_ANY); + bpf_map_update_elem(&map_pin_by_name, &key, &forty_four, BPF_ANY); + return 0; +} + char _license[] SEC("license") = "GPL"; diff --git a/test/integration-test/src/tests/load.rs b/test/integration-test/src/tests/load.rs index 6707a99e..e968a5d9 100644 --- a/test/integration-test/src/tests/load.rs +++ b/test/integration-test/src/tests/load.rs @@ -1,6 +1,6 @@ use std::{ convert::TryInto as _, - fs::remove_file, + fs::{create_dir, remove_dir_all, remove_file}, path::Path, thread, time::{Duration, SystemTime}, @@ -13,7 +13,7 @@ use aya::{ loaded_links, loaded_programs, KProbe, TracePoint, UProbe, Xdp, XdpFlags, }, util::KernelVersion, - Bpf, + Bpf, BpfLoader, }; use aya_obj::programs::XdpAttachType; use test_log::test; @@ -79,7 +79,7 @@ fn pin_lifecycle_multiple_btf_maps() { // pin and unpin all maps before casting to explicit types for (i, (name, map)) in bpf.maps_mut().enumerate() { - // Don't pin system maps or the map that's already pinned by name. + // Don't pin system maps. if name.contains(".rodata") || name.contains(".bss") { continue; } @@ -127,6 +127,83 @@ fn pin_lifecycle_multiple_btf_maps() { remove_file(map_pin_by_name_path).unwrap(); } +#[test] +fn pin_lifecycle_sharing_btf_maps() { + let map_pin_path = Path::new("/sys/fs/bpf/map-sharing-test"); + if map_pin_path.exists() { + remove_dir_all(map_pin_path).unwrap(); + } + create_dir(map_pin_path).unwrap(); + + let mut loader = BpfLoader::new(); + + let mut bpf = loader.load(crate::MULTIMAP_BTF).unwrap(); + + // "map_pin_by_name" should already be pinned at the default bpffs. + let map_pin_by_name_path = Path::new("/sys/fs/bpf/map_pin_by_name"); + + assert!(map_pin_by_name_path.exists()); + remove_file(map_pin_by_name_path).unwrap(); + + for (_, (name, map)) in bpf.maps_mut().enumerate() { + // Don't pin system maps. + if name.contains(".rodata") || name.contains(".bss") { + continue; + } + + // Pin the map by it's name. + map.pin(map_pin_path.join(name)).unwrap(); + assert!(map_pin_path.exists()); + } + + // Load the same program using the existing maps at map_pin_path. + let mut bpf1 = loader + .map_pin_path(map_pin_path) + .load(crate::MULTIMAP_BTF) + .unwrap(); + + // Get the maps from the first program. + let map_1: Array<_, u64> = bpf.take_map("map_1").unwrap().try_into().unwrap(); + let map_2: Array<_, u64> = bpf.take_map("map_2").unwrap().try_into().unwrap(); + let map_pin_by_name: Array<_, u64> = + bpf.take_map("map_pin_by_name").unwrap().try_into().unwrap(); + + // Attach both programs so they can be triggered. + let prog: &mut UProbe = bpf.program_mut("bpf_prog").unwrap().try_into().unwrap(); + prog.load().unwrap(); + prog.attach(Some("trigger_bpf_program"), 0, "/proc/self/exe", None) + .unwrap(); + + let prog1: &mut UProbe = bpf1.program_mut("bpf_prog1").unwrap().try_into().unwrap(); + prog1.load().unwrap(); + prog1 + .attach(Some("trigger_bpf_program"), 0, "/proc/self/exe", None) + .unwrap(); + + trigger_bpf_program(); + + // Ensure both program's updated the same maps. + let key = 0; + let val_1 = map_1.get(&key, 0).unwrap(); + let val_2 = map_2.get(&key, 0).unwrap(); + let val_3 = map_pin_by_name.get(&key, 0).unwrap(); + + assert_eq!(val_1, 24); + assert_eq!(val_2, 42); + assert_eq!(val_3, 44); + + let key = 1; + let val_1 = map_1.get(&key, 0).unwrap(); + let val_2 = map_2.get(&key, 0).unwrap(); + let val_3 = map_pin_by_name.get(&key, 0).unwrap(); + + assert_eq!(val_1, 35); + assert_eq!(val_2, 53); + assert_eq!(val_3, 55); + + remove_dir_all(map_pin_path).unwrap() +} + #[no_mangle] #[inline(never)] pub extern "C" fn trigger_bpf_program() {