From b2fd9493a8bcaf5819c2716a9175062f3d58b2da Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Wed, 15 Oct 2025 10:30:01 -0400 Subject: [PATCH] integration-test: handle !bpf_perf_link --- test/integration-test/src/tests/load.rs | 139 +++++++++++++++++------- 1 file changed, 102 insertions(+), 37 deletions(-) diff --git a/test/integration-test/src/tests/load.rs b/test/integration-test/src/tests/load.rs index ab784692..3ab9b7c8 100644 --- a/test/integration-test/src/tests/load.rs +++ b/test/integration-test/src/tests/load.rs @@ -1,5 +1,6 @@ use std::{convert::TryInto as _, fs::remove_file, path::Path, thread, time::Duration}; +use assert_matches::assert_matches; use aya::{ Ebpf, maps::Array, @@ -245,12 +246,23 @@ fn unload_xdp() { let program_name = "pass"; let attach = |prog: &mut P| prog.attach("lo", XdpFlags::default()).unwrap(); - run_unload_program_test(crate::TEST, program_name, attach); + run_unload_program_test( + crate::TEST, + program_name, + attach, + /* expect_fd_link: */ + true, // xdp fallback is automatic, minimum version unclear. + ); } -fn run_unload_program_test

(bpf_image: &[u8], program_name: &str, attach: fn(&mut P) -> P::LinkId) -where +fn run_unload_program_test

( + bpf_image: &[u8], + program_name: &str, + attach: fn(&mut P) -> P::LinkId, + expect_fd_link: bool, +) where P: UnloadProgramOps, + P::OwnedLink: TryInto, for<'a> &'a mut Program: TryInto<&'a mut P, Error = ProgramError>, { let mut bpf = Ebpf::load(bpf_image).unwrap(); @@ -258,10 +270,24 @@ where prog.load().unwrap(); assert_loaded(program_name); let link = attach(prog); - { - let _link_owned: P::OwnedLink = prog.take_link(link).unwrap(); - prog.unload().unwrap(); - assert_loaded_and_linked(program_name); + let owned_link: P::OwnedLink = prog.take_link(link).unwrap(); + match owned_link.try_into() { + Ok(_fd_link) => { + assert!( + expect_fd_link, + "{program_name}: unexpectedly obtained an fd-backed link", + ); + prog.unload().unwrap(); + assert_loaded_and_linked(program_name); + } + Err(err) => { + assert_matches!(err, LinkError::InvalidLink); + assert!( + !expect_fd_link, + "{program_name}: expected to obtain an fd-backed link on this kernel" + ); + prog.unload().unwrap(); + } } assert_unloaded(program_name); @@ -282,7 +308,12 @@ fn unload_kprobe() { let program_name = "test_kprobe"; let attach = |prog: &mut P| prog.attach("try_to_wake_up", 0).unwrap(); - run_unload_program_test(crate::TEST, program_name, attach); + run_unload_program_test( + crate::TEST, + program_name, + attach, + aya::features().bpf_perf_link(), // probe uses perf_attach. + ); } #[test_log::test] @@ -291,7 +322,12 @@ fn basic_tracepoint() { let program_name = "test_tracepoint"; let attach = |prog: &mut P| prog.attach("syscalls", "sys_enter_kill").unwrap(); - run_unload_program_test(crate::TEST, program_name, attach); + run_unload_program_test( + crate::TEST, + program_name, + attach, + aya::features().bpf_perf_link(), // tracepoint uses perf_attach. + ); } #[test_log::test] @@ -303,7 +339,12 @@ fn basic_uprobe() { prog.attach("uprobe_function", "/proc/self/exe", None, None) .unwrap() }; - run_unload_program_test(crate::TEST, program_name, attach); + run_unload_program_test( + crate::TEST, + program_name, + attach, + aya::features().bpf_perf_link(), // probe uses perf_attach. + ); } #[test_log::test] @@ -315,7 +356,12 @@ fn basic_flow_dissector() { let net_ns = std::fs::File::open("/proc/self/ns/net").unwrap(); prog.attach(net_ns).unwrap() }; - run_unload_program_test(crate::TEST, program_name, attach); + run_unload_program_test( + crate::TEST, + program_name, + attach, + KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0), // See FlowDissector::attach. + ); } #[test_log::test] @@ -325,12 +371,6 @@ fn pin_link() { let program_name = "pass"; let attach = |prog: &mut P| prog.attach("lo", XdpFlags::default()).unwrap(); - let kernel_version = KernelVersion::current().unwrap(); - if kernel_version < KernelVersion::new(5, 9, 0) { - eprintln!("skipping test on kernel {kernel_version:?}, XDP uses netlink"); - return; - } - let mut bpf = Ebpf::load(crate::TEST).unwrap(); let prog: &mut P = bpf.program_mut(program_name).unwrap().try_into().unwrap(); prog.load().unwrap(); @@ -405,9 +445,12 @@ fn pin_lifecycle() { Some(|prog: &mut P, pinned: FdLink| { prog.attach_to_link(pinned.try_into().unwrap()).unwrap() }), + /* expect_fd_link: */ + true, // xdp fallback is automatic, minimum version unclear. ); } +#[expect(clippy::too_many_arguments, reason = "let's see you do better")] fn run_pin_program_lifecycle_test

( bpf_image: &[u8], program_name: &str, @@ -416,6 +459,7 @@ fn run_pin_program_lifecycle_test

( from_pin: fn(&str) -> P, attach: fn(&mut P) -> P::LinkId, attach_to_link: Option P::LinkId>, + expect_fd_link: bool, ) where P: UnloadProgramOps + PinProgramOps, P::OwnedLink: TryInto, @@ -445,26 +489,44 @@ fn run_pin_program_lifecycle_test

( let mut prog = from_pin(program_pin); let link_id = attach(&mut prog); let link = prog.take_link(link_id).unwrap(); - let fd_link: FdLink = link.try_into().unwrap(); - fd_link.pin(link_pin).unwrap(); - - // Unpin the program. It will stay attached since its links were pinned. - prog.unpin().unwrap(); - } - - // should still be loaded since link was pinned - assert_loaded_and_linked(program_name); - - // 4. Load a new version of the program, unpin link, and atomically replace old program - { - let link = PinnedLink::from_pin(link_pin).unwrap().unpin().unwrap(); - if let Some(attach_to_link) = attach_to_link { - let mut bpf = Ebpf::load(bpf_image).unwrap(); - let prog: &mut P = bpf.program_mut(program_name).unwrap().try_into().unwrap(); - prog.load().unwrap(); - attach_to_link(prog, link); - assert_loaded(program_name); - } + match link.try_into() { + Ok(fd_link) => { + assert!( + expect_fd_link, + "{program_name}: unexpectedly obtained an fd-backed link when perf-link support is unavailable" + ); + fd_link.pin(link_pin).unwrap(); + + // Unpin the program. It will stay attached since its links were pinned. + prog.unpin().unwrap(); + + // should still be loaded since link was pinned + assert_loaded_and_linked(program_name); + + // 4. Load a new version of the program, unpin link, and atomically replace old program + { + let link = PinnedLink::from_pin(link_pin).unwrap().unpin().unwrap(); + if let Some(attach_to_link) = attach_to_link { + let mut bpf = Ebpf::load(bpf_image).unwrap(); + let prog: &mut P = + bpf.program_mut(program_name).unwrap().try_into().unwrap(); + prog.load().unwrap(); + attach_to_link(prog, link); + assert_loaded(program_name); + } + } + } + Err(err) => { + assert_matches!(err, LinkError::InvalidLink); + assert!( + !expect_fd_link, + "{program_name}: expected an fd-backed link on this kernel" + ); + + // Unpin the program. It will be unloaded since its link was not pinned. + prog.unpin().unwrap(); + } + }; } // program should be unloaded @@ -488,6 +550,7 @@ fn pin_lifecycle_tracepoint() { from_pin, attach, None, + aya::features().bpf_perf_link(), // tracepoint uses perf_attach. ); } @@ -508,6 +571,7 @@ fn pin_lifecycle_kprobe() { from_pin, attach, None, + aya::features().bpf_perf_link(), // probe uses perf_attach. ); } @@ -537,6 +601,7 @@ fn pin_lifecycle_uprobe() { from_pin, attach, None, + aya::features().bpf_perf_link(), // probe uses perf_attach. ); // Make sure the function isn't optimized out.