|
@@ -59,6 +59,7 @@ use std::{
|
|
|
ptr,
|
|
|
};
|
|
|
|
|
|
+use aya_obj::generated::bpf_map_type;
|
|
|
use libc::{getrlimit, rlim_t, rlimit, RLIMIT_MEMLOCK, RLIM_INFINITY};
|
|
|
use log::warn;
|
|
|
use obj::maps::InvalidMapTypeError;
|
|
@@ -172,6 +173,10 @@ pub enum MapError {
|
|
|
#[error("the program is not loaded")]
|
|
|
ProgramNotLoaded,
|
|
|
|
|
|
+ /// An IO error occurred
|
|
|
+ #[error(transparent)]
|
|
|
+ IoError(#[from] io::Error),
|
|
|
+
|
|
|
/// Syscall failed
|
|
|
#[error(transparent)]
|
|
|
SyscallError(#[from] SyscallError),
|
|
@@ -550,12 +555,30 @@ pub struct MapData {
|
|
|
impl MapData {
|
|
|
/// Creates a new map with the provided `name`
|
|
|
pub fn create(
|
|
|
- obj: obj::Map,
|
|
|
+ mut obj: obj::Map,
|
|
|
name: &str,
|
|
|
btf_fd: Option<BorrowedFd<'_>>,
|
|
|
) -> Result<Self, MapError> {
|
|
|
let c_name = CString::new(name).map_err(|_| MapError::InvalidName { name: name.into() })?;
|
|
|
|
|
|
+ // BPF_MAP_TYPE_PERF_EVENT_ARRAY's max_entries should not exceed the number of
|
|
|
+ // CPUs.
|
|
|
+ //
|
|
|
+ // By default, the newest versions of Aya, libbpf and cilium/ebpf define `max_entries` of
|
|
|
+ // `PerfEventArray` as `0`, with an intention to get it replaced with a correct value
|
|
|
+ // by the loader.
|
|
|
+ //
|
|
|
+ // We allow custom values (potentially coming either from older versions of aya-ebpf or
|
|
|
+ // programs written in C) as long as they don't exceed the number of CPUs.
|
|
|
+ //
|
|
|
+ // Otherwise, when the value is `0` or too large, we set it to the number of CPUs.
|
|
|
+ if obj.map_type() == bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32 {
|
|
|
+ let ncpus = nr_cpus().map_err(MapError::IoError)? as u32;
|
|
|
+ if obj.max_entries() == 0 || obj.max_entries() > ncpus {
|
|
|
+ obj.set_max_entries(ncpus);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
#[cfg(not(test))]
|
|
|
let kernel_version = KernelVersion::current().unwrap();
|
|
|
#[cfg(test)]
|
|
@@ -1077,6 +1100,25 @@ mod test_utils {
|
|
|
symbol_index: None,
|
|
|
})
|
|
|
}
|
|
|
+
|
|
|
+ pub(super) fn new_obj_map_with_max_entries<K>(
|
|
|
+ map_type: bpf_map_type,
|
|
|
+ max_entries: u32,
|
|
|
+ ) -> obj::Map {
|
|
|
+ obj::Map::Legacy(LegacyMap {
|
|
|
+ def: bpf_map_def {
|
|
|
+ map_type: map_type as u32,
|
|
|
+ key_size: std::mem::size_of::<K>() as u32,
|
|
|
+ value_size: 4,
|
|
|
+ max_entries,
|
|
|
+ ..Default::default()
|
|
|
+ },
|
|
|
+ section_index: 0,
|
|
|
+ section_kind: EbpfSectionKind::Maps,
|
|
|
+ data: Vec::new(),
|
|
|
+ symbol_index: None,
|
|
|
+ })
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|
|
@@ -1150,6 +1192,65 @@ mod tests {
|
|
|
);
|
|
|
}
|
|
|
|
|
|
+ #[test]
|
|
|
+ #[cfg_attr(miri, ignore = "nr_cpus() opens a file on procfs that upsets miri")]
|
|
|
+ fn test_create_perf_event_array() {
|
|
|
+ override_syscall(|call| match call {
|
|
|
+ Syscall::Ebpf {
|
|
|
+ cmd: bpf_cmd::BPF_MAP_CREATE,
|
|
|
+ ..
|
|
|
+ } => Ok(crate::MockableFd::mock_signed_fd().into()),
|
|
|
+ _ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
|
|
|
+ });
|
|
|
+
|
|
|
+ let ncpus = nr_cpus().unwrap();
|
|
|
+
|
|
|
+ // Create with max_entries > ncpus is clamped to ncpus
|
|
|
+ assert_matches!(
|
|
|
+ MapData::create(test_utils::new_obj_map_with_max_entries::<u32>(
|
|
|
+ crate::generated::bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY,
|
|
|
+ 65535,
|
|
|
+ ), "foo", None),
|
|
|
+ Ok(MapData {
|
|
|
+ obj,
|
|
|
+ fd,
|
|
|
+ }) => {
|
|
|
+ assert_eq!(fd.as_fd().as_raw_fd(), crate::MockableFd::mock_signed_fd());
|
|
|
+ assert_eq!(obj.max_entries(), ncpus as u32)
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ // Create with max_entries = 0 is set to ncpus
|
|
|
+ assert_matches!(
|
|
|
+ MapData::create(test_utils::new_obj_map_with_max_entries::<u32>(
|
|
|
+ crate::generated::bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY,
|
|
|
+ 0,
|
|
|
+ ), "foo", None),
|
|
|
+ Ok(MapData {
|
|
|
+ obj,
|
|
|
+ fd,
|
|
|
+ }) => {
|
|
|
+ assert_eq!(fd.as_fd().as_raw_fd(), crate::MockableFd::mock_signed_fd());
|
|
|
+ assert_eq!(obj.max_entries(), ncpus as u32)
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ // Create with max_entries < ncpus is unchanged
|
|
|
+ assert_matches!(
|
|
|
+ MapData::create(test_utils::new_obj_map_with_max_entries::<u32>(
|
|
|
+ crate::generated::bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY,
|
|
|
+ 1,
|
|
|
+ ), "foo", None),
|
|
|
+ Ok(MapData {
|
|
|
+ obj,
|
|
|
+ fd,
|
|
|
+ }) => {
|
|
|
+ assert_eq!(fd.as_fd().as_raw_fd(), crate::MockableFd::mock_signed_fd());
|
|
|
+ assert_eq!(obj.max_entries(), 1)
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
#[test]
|
|
|
#[cfg_attr(
|
|
|
miri,
|