From 5dc007caf5fe400534c74331590f09f8fdd46f89 Mon Sep 17 00:00:00 2001 From: astoycos Date: Thu, 19 Oct 2023 16:29:43 -0400 Subject: [PATCH] aya: fix map_pin_path logic If a user specifies map_pin_path then the maps should ALWAYS be pinned by name at that path, or consumed from existing pins. Add an integration test to verify this logic is functioning as expected. Signed-off-by: astoycos --- aya/src/bpf.rs | 14 +++- test/integration-test/bpf/multimap-btf.bpf.c | 19 ++++- test/integration-test/src/tests/load.rs | 83 +++++++++++++++++++- 3 files changed, 109 insertions(+), 7 deletions(-) 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() {