diff --git a/ebpf/aya-ebpf/src/maps/inode_storage.rs b/ebpf/aya-ebpf/src/maps/inode_storage.rs new file mode 100644 index 00000000..f60ac45b --- /dev/null +++ b/ebpf/aya-ebpf/src/maps/inode_storage.rs @@ -0,0 +1,148 @@ +use core::{cell::UnsafeCell, marker::PhantomData, mem, ptr::NonNull}; + +use aya_ebpf_bindings::{ + bindings::{ + bpf_map_def, + bpf_map_type::{BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_INODE_STORAGE}, + BPF_LOCAL_STORAGE_GET_F_CREATE, + }, + helpers::{bpf_inode_storage_delete, bpf_inode_storage_get}, +}; +use aya_ebpf_cty::{c_int, c_void}; + +use crate::maps::PinningType; + +/// A BPF map of type BPF_MAP_TYPE_INODE_STORAGE, used for attaching local storage data to an inode. +/// See bpf_inode_storage_get in bpf-helpers(7) for details. +#[repr(transparent)] +pub struct InodeStorage { + def: UnsafeCell, + _v: PhantomData, +} + +impl InodeStorage { + /// Instantiate a [`InodeStorage`] map with the provided flags. + pub const fn new(flags: u32) -> InodeStorage { + InodeStorage { + def: UnsafeCell::new(build_def::( + BPF_MAP_TYPE_INODE_STORAGE, + flags, + PinningType::None, + )), + _v: PhantomData, + } + } + + /// Instantiate a pinned [`InodeStorage`] map with the provided flags. + pub const fn pinned(flags: u32) -> InodeStorage { + InodeStorage { + def: UnsafeCell::new(build_def::( + BPF_MAP_TYPE_INODE_STORAGE, + flags, + PinningType::ByName, + )), + _v: PhantomData, + } + } + + /// Get a local storage entry associated with this inode, or insert the provided value and get + /// the mutable reference to the information stored within the inode. Returns [`None`] if there + /// was an issue with inserting the new value. + /// + /// ## Safety + /// + /// This function is marked unsafe as accessing the same inode's local storage multiple times + /// would create multiple mutable references to the same data, which is not supported by Rust's + /// memory model. + #[inline] + pub unsafe fn get_or_insert(&self, inode: *mut c_void, initial: &V) -> Option<&mut V> { + self.get_or_insert_ptr(inode, initial).map(|p| &mut *p) + } + + /// Get a pointer to the local storage entry associated with this inode, or insert the provided + /// value and get the mutable reference to the information stored within the inode. Returns + /// [`None`] if there was an issue with inserting the new value. + #[inline] + pub fn get_or_insert_ptr(&self, inode: *mut c_void, initial: &V) -> Option<*mut V> { + unsafe { + let ptr = bpf_inode_storage_get( + self.def.get() as *mut c_void, + inode, + initial as *const V as *const c_void as *mut c_void, + BPF_LOCAL_STORAGE_GET_F_CREATE as u64, + ); + NonNull::new(ptr as *mut V).map(|p| p.as_ptr()) + } + } + + /// Get a local storage entry associated with this inode, or [`None`] if no such value exists. + /// + /// ## Safety + /// + /// This function is marked unsafe as accessing the same inode's local storage immutably at the + /// same time as mutably (e.g., but way of [`InodeStorage::get_mut`]) is not supported by Rust's + /// memory model. + #[inline] + pub unsafe fn get(&self, inode: *mut c_void) -> Option<&V> { + self.get_ptr(inode).map(|p| &*p) + } + + /// Mutably access a local storage entry associated with this inode, or [`None`] if no such + /// value exists. + /// + /// ## Safety + /// + /// This function is marked unsafe as accessing the same inode's local storage mutably multiple + /// times is not supported by Rust's memory model. + #[inline] + pub unsafe fn get_mut(&self, inode: *mut c_void) -> Option<&mut V> { + self.get_ptr_mut(inode).map(|p| &mut *p) + } + + /// Get a pointer to the local storage entry associated with this inode, or [`None`] if no such + /// value exists. + #[inline] + pub fn get_ptr(&self, inode: *mut c_void) -> Option<*const V> { + self.get_ptr_mut(inode).map(|p| p as *const V) + } + + /// Get a mutable pointer to the local storage entry associated with this inode, or [`None`] if + /// no such value exists. You are responsible for ensuring that at most one mutable reference to + /// the same inode local storage exists at a given time. + #[inline] + pub fn get_ptr_mut(&self, inode: *mut c_void) -> Option<*mut V> { + unsafe { + let ptr = bpf_inode_storage_get( + self.def.get() as *mut c_void, + inode, + core::ptr::null_mut(), + 0, + ); + NonNull::new(ptr as *mut V).map(|p| p.as_ptr()) + } + } + + /// Remove a local storage entry associated with this inode. Returns `Err(-ENOENT)` if no such + /// value was present. + #[inline] + pub fn remove(&self, inode: *mut c_void) -> Result<(), c_int> { + let ret = unsafe { bpf_inode_storage_delete(self.def.get() as *mut c_void, inode) }; + if ret == 0 { + Ok(()) + } else { + Err(ret) + } + } +} + +const fn build_def(ty: u32, flags: u32, pin: PinningType) -> bpf_map_def { + bpf_map_def { + type_: ty, + key_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, + max_entries: 0, + map_flags: flags, + id: 0, + pinning: pin as u32, + } +} diff --git a/ebpf/aya-ebpf/src/maps/mod.rs b/ebpf/aya-ebpf/src/maps/mod.rs index ead24dc3..dd0b48a0 100644 --- a/ebpf/aya-ebpf/src/maps/mod.rs +++ b/ebpf/aya-ebpf/src/maps/mod.rs @@ -8,6 +8,7 @@ pub(crate) enum PinningType { pub mod array; pub mod bloom_filter; pub mod hash_map; +pub mod inode_storage; pub mod lpm_trie; pub mod per_cpu_array; pub mod perf; diff --git a/test/integration-ebpf/Cargo.toml b/test/integration-ebpf/Cargo.toml index b4146daa..7148e7aa 100644 --- a/test/integration-ebpf/Cargo.toml +++ b/test/integration-ebpf/Cargo.toml @@ -24,6 +24,10 @@ path = "src/log.rs" name = "map_test" path = "src/map_test.rs" +[[bin]] +name = "inode_storage" +path = "src/inode_storage.rs" + [[bin]] name = "name_test" path = "src/name_test.rs" diff --git a/test/integration-ebpf/src/inode_storage.rs b/test/integration-ebpf/src/inode_storage.rs new file mode 100644 index 00000000..7aba108f --- /dev/null +++ b/test/integration-ebpf/src/inode_storage.rs @@ -0,0 +1,7 @@ +#![no_std] +#![no_main] + +use aya_ebpf::{macros::map, maps::inode_storage::InodeStorage}; + +#[map] +static INODE_STORE: InodeStorage = InodeStorage::new(0);