Skip to content

Commit f4df1a9

Browse files
tyrone-wutamird
authored andcommitted
aya,aya-obj: cache feat probed info fields
Cached probed for ProgramInfo fields instead of exposing it through global FEATURE. Probing occurs on cache miss, which happens when first accessing the field, *and* if the field is 0.
1 parent 59e99f4 commit f4df1a9

File tree

9 files changed

+124
-82
lines changed

9 files changed

+124
-82
lines changed

aya-obj/src/obj.rs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ pub struct Features {
4646
bpf_cookie: bool,
4747
cpumap_prog_id: bool,
4848
devmap_prog_id: bool,
49-
prog_info_map_ids: bool,
50-
prog_info_gpl_compatible: bool,
5149
btf: Option<BtfFeatures>,
5250
}
5351

@@ -62,8 +60,6 @@ impl Features {
6260
bpf_cookie: bool,
6361
cpumap_prog_id: bool,
6462
devmap_prog_id: bool,
65-
prog_info_map_ids: bool,
66-
prog_info_gpl_compatible: bool,
6763
btf: Option<BtfFeatures>,
6864
) -> Self {
6965
Self {
@@ -74,8 +70,6 @@ impl Features {
7470
bpf_cookie,
7571
cpumap_prog_id,
7672
devmap_prog_id,
77-
prog_info_map_ids,
78-
prog_info_gpl_compatible,
7973
btf,
8074
}
8175
}
@@ -118,16 +112,6 @@ impl Features {
118112
self.devmap_prog_id
119113
}
120114

121-
/// Returns whether `bpf_prog_info` supports `nr_map_ids` & `map_ids` fields.
122-
pub fn prog_info_map_ids(&self) -> bool {
123-
self.prog_info_map_ids
124-
}
125-
126-
/// Returns whether `bpf_prog_info` supports `gpl_compatible` field.
127-
pub fn prog_info_gpl_compatible(&self) -> bool {
128-
self.prog_info_gpl_compatible
129-
}
130-
131115
/// If BTF is supported, returns which BTF features are supported.
132116
pub fn btf(&self) -> Option<&BtfFeatures> {
133117
self.btf.as_ref()

aya/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ hashbrown = { workspace = true }
2222
libc = { workspace = true }
2323
log = { workspace = true }
2424
object = { workspace = true, features = ["elf", "read_core", "std", "write"] }
25-
once_cell = { workspace = true }
25+
once_cell = { workspace = true, features = ["race"] }
2626
thiserror = { workspace = true }
2727
tokio = { workspace = true, features = ["rt"], optional = true }
2828

aya/src/bpf.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ use crate::{
3131
bpf_load_btf, is_bpf_cookie_supported, is_bpf_global_data_supported,
3232
is_btf_datasec_supported, is_btf_decl_tag_supported, is_btf_enum64_supported,
3333
is_btf_float_supported, is_btf_func_global_supported, is_btf_func_supported,
34-
is_btf_supported, is_btf_type_tag_supported, is_info_gpl_compatible_supported,
35-
is_info_map_ids_supported, is_perf_link_supported, is_probe_read_kernel_supported,
36-
is_prog_id_supported, is_prog_name_supported, retry_with_verifier_logs,
34+
is_btf_supported, is_btf_type_tag_supported, is_perf_link_supported,
35+
is_probe_read_kernel_supported, is_prog_id_supported, is_prog_name_supported,
36+
retry_with_verifier_logs,
3737
},
3838
util::{bytes_of, bytes_of_slice, nr_cpus, page_size},
3939
};
@@ -80,8 +80,6 @@ fn detect_features() -> Features {
8080
is_bpf_cookie_supported(),
8181
is_prog_id_supported(BPF_MAP_TYPE_CPUMAP),
8282
is_prog_id_supported(BPF_MAP_TYPE_DEVMAP),
83-
is_info_map_ids_supported(),
84-
is_info_gpl_compatible_supported(),
8583
btf,
8684
);
8785
debug!("BPF Feature Detection: {:#?}", f);

aya/src/programs/info.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@ use std::{
88
};
99

1010
use aya_obj::generated::{bpf_prog_info, bpf_prog_type};
11+
use once_cell::race::OnceBool;
1112

1213
use super::{
1314
utils::{boot_time, get_fdinfo},
1415
ProgramError, ProgramFd,
1516
};
1617
use crate::{
1718
sys::{
18-
bpf_get_object, bpf_prog_get_fd_by_id, bpf_prog_get_info_by_fd, iter_prog_ids, SyscallError,
19+
bpf_get_object, bpf_prog_get_fd_by_id, bpf_prog_get_info_by_fd,
20+
feature_probe::{is_prog_info_license_supported, is_prog_info_map_ids_supported},
21+
iter_prog_ids, SyscallError,
1922
},
2023
util::bytes_of_bpf_name,
2124
FEATURES,
@@ -108,10 +111,15 @@ impl ProgramInfo {
108111
///
109112
/// Introduced in kernel v4.15.
110113
pub fn map_ids(&self) -> Result<Option<Vec<u32>>, ProgramError> {
111-
if FEATURES.prog_info_map_ids() {
114+
if self.0.nr_map_ids > 0 {
112115
let mut map_ids = vec![0u32; self.0.nr_map_ids as usize];
113116
bpf_prog_get_info_by_fd(self.fd()?.as_fd(), &mut map_ids)?;
114-
Ok(Some(map_ids))
117+
return Ok(Some(map_ids));
118+
}
119+
120+
static CACHE: OnceBool = OnceBool::new();
121+
if CACHE.get_or_init(|| matches!(is_prog_info_map_ids_supported(), Ok(true))) {
122+
Ok(Some(vec![]))
115123
} else {
116124
Ok(None)
117125
}
@@ -140,9 +148,16 @@ impl ProgramInfo {
140148
///
141149
/// Introduced in kernel v4.18.
142150
pub fn gpl_compatible(&self) -> Option<bool> {
143-
FEATURES
144-
.prog_info_gpl_compatible()
145-
.then_some(self.0.gpl_compatible() != 0)
151+
if self.0.gpl_compatible() != 0 {
152+
return Some(true);
153+
}
154+
155+
static CACHE: OnceBool = OnceBool::new();
156+
if CACHE.get_or_init(|| matches!(is_prog_info_license_supported(), Ok(true))) {
157+
Some(false)
158+
} else {
159+
None
160+
}
146161
}
147162

148163
/// The BTF ID for the program.

aya/src/sys/bpf.rs

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crate::{
2929
programs::links::LinkRef,
3030
sys::{syscall, Syscall, SyscallError},
3131
util::KernelVersion,
32-
Btf, Pod, VerifierLogLevel, FEATURES,
32+
Btf, Pod, VerifierLogLevel,
3333
};
3434

3535
pub(crate) fn bpf_create_iter(link_fd: BorrowedFd<'_>) -> io::Result<crate::MockableFd> {
@@ -595,7 +595,7 @@ pub(crate) fn bpf_prog_get_info_by_fd(
595595
// An `E2BIG` error can occur on kernels below v4.15 when handing over a large struct where the
596596
// extra space is not all-zero bytes.
597597
bpf_obj_get_info_by_fd(fd, |info: &mut bpf_prog_info| {
598-
if FEATURES.prog_info_map_ids() {
598+
if !map_ids.is_empty() {
599599
info.nr_map_ids = map_ids.len() as _;
600600
info.map_ids = map_ids.as_mut_ptr() as _;
601601
}
@@ -753,34 +753,6 @@ where
753753
op(&mut attr)
754754
}
755755

756-
/// Tests whether `nr_map_ids` & `map_ids` fields in `bpf_prog_info` is available.
757-
pub(crate) fn is_info_map_ids_supported() -> bool {
758-
with_trivial_prog(|attr| {
759-
let prog_fd = match bpf_prog_load(attr) {
760-
Ok(fd) => fd,
761-
Err(_) => return false,
762-
};
763-
bpf_obj_get_info_by_fd(prog_fd.as_fd(), |info: &mut bpf_prog_info| {
764-
info.nr_map_ids = 1
765-
})
766-
.is_ok()
767-
})
768-
}
769-
770-
/// Tests whether `gpl_compatible` field in `bpf_prog_info` is available.
771-
pub(crate) fn is_info_gpl_compatible_supported() -> bool {
772-
with_trivial_prog(|attr| {
773-
let prog_fd = match bpf_prog_load(attr) {
774-
Ok(fd) => fd,
775-
Err(_) => return false,
776-
};
777-
if let Ok::<bpf_prog_info, _>(info) = bpf_obj_get_info_by_fd(prog_fd.as_fd(), |_| {}) {
778-
return info.gpl_compatible() != 0;
779-
}
780-
false
781-
})
782-
}
783-
784756
pub(crate) fn is_probe_read_kernel_supported() -> bool {
785757
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
786758
let u = unsafe { &mut attr.__bindgen_anon_3 };
@@ -1090,7 +1062,7 @@ fn sys_bpf(cmd: bpf_cmd, attr: &mut bpf_attr) -> io::Result<c_long> {
10901062
})
10911063
}
10921064

1093-
fn unit_sys_bpf(cmd: bpf_cmd, attr: &mut bpf_attr) -> io::Result<()> {
1065+
pub(super) fn unit_sys_bpf(cmd: bpf_cmd, attr: &mut bpf_attr) -> io::Result<()> {
10941066
sys_bpf(cmd, attr).map(|code| assert_eq!(code, 0))
10951067
}
10961068

aya/src/sys/feature_probe.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,18 @@ use std::{io::ErrorKind, mem, os::fd::AsRawFd};
55
use aya_obj::{
66
btf::{Btf, BtfError, BtfKind},
77
generated::{
8-
bpf_attach_type, bpf_attr, bpf_cmd, bpf_map_type, BPF_F_MMAPABLE, BPF_F_NO_PREALLOC,
9-
BPF_F_SLEEPABLE,
8+
bpf_attach_type, bpf_attr, bpf_cmd, bpf_map_type, bpf_prog_info, BPF_F_MMAPABLE,
9+
BPF_F_NO_PREALLOC, BPF_F_SLEEPABLE,
1010
},
1111
};
1212
use libc::{E2BIG, EBADF, EINVAL};
1313

14-
use super::{bpf_prog_load, fd_sys_bpf, with_trivial_prog, SyscallError};
14+
use super::{bpf_prog_load, fd_sys_bpf, unit_sys_bpf, with_trivial_prog, SyscallError};
1515
use crate::{
1616
maps::MapType,
1717
programs::{ProgramError, ProgramType},
1818
util::{page_size, KernelVersion},
19+
MockableFd,
1920
};
2021

2122
/// Whether the host kernel supports the [`ProgramType`].
@@ -226,6 +227,26 @@ pub fn is_map_supported(map_type: MapType) -> Result<bool, SyscallError> {
226227
}
227228
}
228229

230+
/// Whether `nr_map_ids` & `map_ids` fields in `bpf_prog_info` are supported.
231+
pub(crate) fn is_prog_info_map_ids_supported() -> Result<bool, ProgramError> {
232+
let fd = create_minimal_program(ProgramType::SocketFilter, &mut [])?;
233+
// SAFETY: all-zero byte-pattern valid for `bpf_prog_info`
234+
let mut info = unsafe { mem::zeroed::<bpf_prog_info>() };
235+
info.nr_map_ids = 1;
236+
237+
probe_bpf_info(fd, info).map_err(ProgramError::from)
238+
}
239+
240+
/// Tests whether `bpf_prog_info.gpl_compatible` field is supported.
241+
pub(crate) fn is_prog_info_license_supported() -> Result<bool, ProgramError> {
242+
let fd = create_minimal_program(ProgramType::SocketFilter, &mut [])?;
243+
// SAFETY: all-zero byte-pattern valid for `bpf_prog_info`
244+
let mut info = unsafe { mem::zeroed::<bpf_prog_info>() };
245+
info.set_gpl_compatible(1);
246+
247+
probe_bpf_info(fd, info).map_err(ProgramError::from)
248+
}
249+
229250
/// Create a minimal program with the specified type.
230251
/// Types not created for `Extension` and `StructOps`.
231252
fn create_minimal_program(
@@ -281,3 +302,25 @@ fn create_minimal_program(
281302
})
282303
})
283304
}
305+
306+
/// Probes program and map info.
307+
fn probe_bpf_info<T>(fd: MockableFd, info: T) -> Result<bool, SyscallError> {
308+
// SAFETY: all-zero byte-pattern valid for `bpf_attr`
309+
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
310+
attr.info.bpf_fd = fd.as_raw_fd() as u32;
311+
attr.info.info_len = mem::size_of_val(&info) as u32;
312+
attr.info.info = &info as *const _ as u64;
313+
314+
let io_error = match unit_sys_bpf(bpf_cmd::BPF_OBJ_GET_INFO_BY_FD, &mut attr) {
315+
Ok(()) => return Ok(true),
316+
Err(io_error) => io_error,
317+
};
318+
match io_error.raw_os_error() {
319+
// `E2BIG` from `bpf_check_uarg_tail_zero()`
320+
Some(E2BIG) => Ok(false),
321+
_ => Err(SyscallError {
322+
call: "bpf_obj_get_info_by_fd",
323+
io_error,
324+
}),
325+
}
326+
}

test/integration-test/src/tests/feature_probe.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ use aya::{maps::MapType, programs::ProgramType, sys::feature_probe::*, util::Ker
77

88
use super::load::RETRY_DURATION;
99

10-
use super::load::RETRY_DURATION;
11-
1210
// TODO: Enable certain CONFIG_* options when compiling the image for VM tests.
1311
#[test]
1412
fn probe_supported_programs() {

0 commit comments

Comments
 (0)