mirror of https://github.com/aya-rs/aya
				
				
				
			aya: add PerCpuHashMap
							parent
							
								
									d5098c9e57
								
							
						
					
					
						commit
						3a5b289163
					
				| @ -0,0 +1,181 @@ | |||||||
|  | //! Per-CPU hash map.
 | ||||||
|  | use std::{ | ||||||
|  |     convert::TryFrom, | ||||||
|  |     marker::PhantomData, | ||||||
|  |     ops::{Deref, DerefMut}, | ||||||
|  |     os::unix::io::RawFd, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use crate::{ | ||||||
|  |     generated::bpf_map_type::BPF_MAP_TYPE_PERCPU_HASH, | ||||||
|  |     maps::{ | ||||||
|  |         hash_map, IterableMap, Map, MapError, MapIter, MapKeys, MapRef, MapRefMut, PerCpuValues, | ||||||
|  |     }, | ||||||
|  |     sys::{bpf_map_lookup_elem_per_cpu, bpf_map_update_elem_per_cpu}, | ||||||
|  |     Pod, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /// Similar to [`HashMap`](crate::maps::HashMap) but each CPU holds a separate value for a given key. Tipically used to
 | ||||||
|  | /// minimize lock contention in eBPF programs.
 | ||||||
|  | ///
 | ||||||
|  | /// # Example
 | ||||||
|  | ///
 | ||||||
|  | /// ```no_run
 | ||||||
|  | /// # let bpf = aya::Bpf::load(&[], None)?;
 | ||||||
|  | /// use aya::maps::PerCpuHashMap;
 | ||||||
|  | /// use std::convert::TryFrom;
 | ||||||
|  | ///
 | ||||||
|  | /// const CPU_IDS: u8 = 1;
 | ||||||
|  | /// const WAKEUPS: u8 = 2;
 | ||||||
|  | ///
 | ||||||
|  | /// let mut hm = PerCpuHashMap::<_, u8, u32>::try_from(bpf.map("COUNTERS")?)?;
 | ||||||
|  | /// let cpu_ids = unsafe { hm.get(&CPU_IDS, 0)?.unwrap() };
 | ||||||
|  | /// let wakeups = unsafe { hm.get(&WAKEUPS, 0)?.unwrap() };
 | ||||||
|  | /// for (cpu_id, wakeups) in cpu_ids.iter().zip(wakeups.iter()) {
 | ||||||
|  | ///     println!("cpu {} woke up {} times", cpu_id, wakeups);
 | ||||||
|  | /// }
 | ||||||
|  | /// # Ok::<(), aya::BpfError>(())
 | ||||||
|  | /// ```
 | ||||||
|  | pub struct PerCpuHashMap<T: Deref<Target = Map>, K: Pod, V: Pod> { | ||||||
|  |     inner: T, | ||||||
|  |     _k: PhantomData<K>, | ||||||
|  |     _v: PhantomData<V>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: Deref<Target = Map>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> { | ||||||
|  |     pub(crate) fn new(map: T) -> Result<PerCpuHashMap<T, K, V>, MapError> { | ||||||
|  |         let map_type = map.obj.def.map_type; | ||||||
|  | 
 | ||||||
|  |         // validate the map definition
 | ||||||
|  |         if map_type != BPF_MAP_TYPE_PERCPU_HASH as u32 { | ||||||
|  |             return Err(MapError::InvalidMapType { | ||||||
|  |                 map_type: map_type as u32, | ||||||
|  |             })?; | ||||||
|  |         } | ||||||
|  |         hash_map::check_kv_size::<K, V>(&map)?; | ||||||
|  |         let _ = map.fd_or_err()?; | ||||||
|  | 
 | ||||||
|  |         Ok(PerCpuHashMap { | ||||||
|  |             inner: map, | ||||||
|  |             _k: PhantomData, | ||||||
|  |             _v: PhantomData, | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Returns a slice of values - one for each CPU - associated with the key.
 | ||||||
|  |     pub unsafe fn get(&self, key: &K, flags: u64) -> Result<Option<PerCpuValues<V>>, MapError> { | ||||||
|  |         let fd = self.inner.deref().fd_or_err()?; | ||||||
|  |         bpf_map_lookup_elem_per_cpu(fd, key, flags).map_err(|(code, io_error)| { | ||||||
|  |             MapError::SyscallError { | ||||||
|  |                 call: "bpf_map_lookup_elem".to_owned(), | ||||||
|  |                 code, | ||||||
|  |                 io_error, | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// An iterator visiting all key-value pairs in arbitrary order. The
 | ||||||
|  |     /// iterator item type is `Result<(K, PerCpuValues<V>), MapError>`.
 | ||||||
|  |     pub unsafe fn iter(&self) -> MapIter<'_, K, PerCpuValues<V>> { | ||||||
|  |         MapIter::new(self) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// An iterator visiting all keys in arbitrary order. The iterator element
 | ||||||
|  |     /// type is `Result<K, MapError>`.
 | ||||||
|  |     pub unsafe fn keys(&self) -> MapKeys<'_, K, PerCpuValues<V>> { | ||||||
|  |         MapKeys::new(self) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: DerefMut<Target = Map>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> { | ||||||
|  |     /// Inserts a slice of values - one for each CPU - for the given key.
 | ||||||
|  |     ///
 | ||||||
|  |     /// # Example
 | ||||||
|  |     ///
 | ||||||
|  |     /// ```no_run
 | ||||||
|  |     /// # #[derive(thiserror::Error, Debug)]
 | ||||||
|  |     /// # enum Error {
 | ||||||
|  |     /// #     #[error(transparent)]
 | ||||||
|  |     /// #     IO(#[from] std::io::Error),
 | ||||||
|  |     /// #     #[error(transparent)]
 | ||||||
|  |     /// #     Map(#[from] aya::maps::MapError),
 | ||||||
|  |     /// #     #[error(transparent)]
 | ||||||
|  |     /// #     Bpf(#[from] aya::BpfError)
 | ||||||
|  |     /// # }
 | ||||||
|  |     /// # let bpf = aya::Bpf::load(&[], None)?;
 | ||||||
|  |     /// use aya::maps::{PerCpuHashMap, PerCpuValues};
 | ||||||
|  |     /// use aya::util::nr_cpus;
 | ||||||
|  |     /// use std::convert::TryFrom;
 | ||||||
|  |     ///
 | ||||||
|  |     /// const RETRIES: u8 = 1;
 | ||||||
|  |     ///
 | ||||||
|  |     /// let mut hm = PerCpuHashMap::<_, u8, u32>::try_from(bpf.map_mut("PER_CPU_STORAGE")?)?;
 | ||||||
|  |     /// hm.insert(
 | ||||||
|  |     ///     RETRIES,
 | ||||||
|  |     ///     PerCpuValues::try_from(vec![3u32; nr_cpus()?])?,
 | ||||||
|  |     ///     0,
 | ||||||
|  |     /// )?;
 | ||||||
|  |     /// # Ok::<(), Error>(())
 | ||||||
|  |     /// ```
 | ||||||
|  |     pub fn insert(&mut self, key: K, values: PerCpuValues<V>, flags: u64) -> Result<(), MapError> { | ||||||
|  |         let fd = self.inner.fd_or_err()?; | ||||||
|  |         bpf_map_update_elem_per_cpu(fd, &key, &values, flags).map_err(|(code, io_error)| { | ||||||
|  |             MapError::SyscallError { | ||||||
|  |                 call: "bpf_map_update_elem".to_owned(), | ||||||
|  |                 code, | ||||||
|  |                 io_error, | ||||||
|  |             } | ||||||
|  |         })?; | ||||||
|  | 
 | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Removes a key from the map.
 | ||||||
|  |     pub fn remove(&mut self, key: &K) -> Result<(), MapError> { | ||||||
|  |         hash_map::remove(&mut self.inner, key) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: Deref<Target = Map>, K: Pod, V: Pod> IterableMap<K, PerCpuValues<V>> | ||||||
|  |     for PerCpuHashMap<T, K, V> | ||||||
|  | { | ||||||
|  |     fn fd(&self) -> Result<RawFd, MapError> { | ||||||
|  |         self.inner.deref().fd_or_err() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     unsafe fn get(&self, key: &K) -> Result<Option<PerCpuValues<V>>, MapError> { | ||||||
|  |         PerCpuHashMap::get(self, key, 0) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<K: Pod, V: Pod> TryFrom<MapRef> for PerCpuHashMap<MapRef, K, V> { | ||||||
|  |     type Error = MapError; | ||||||
|  | 
 | ||||||
|  |     fn try_from(a: MapRef) -> Result<PerCpuHashMap<MapRef, K, V>, MapError> { | ||||||
|  |         PerCpuHashMap::new(a) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<K: Pod, V: Pod> TryFrom<MapRefMut> for PerCpuHashMap<MapRefMut, K, V> { | ||||||
|  |     type Error = MapError; | ||||||
|  | 
 | ||||||
|  |     fn try_from(a: MapRefMut) -> Result<PerCpuHashMap<MapRefMut, K, V>, MapError> { | ||||||
|  |         PerCpuHashMap::new(a) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a, K: Pod, V: Pod> TryFrom<&'a Map> for PerCpuHashMap<&'a Map, K, V> { | ||||||
|  |     type Error = MapError; | ||||||
|  | 
 | ||||||
|  |     fn try_from(a: &'a Map) -> Result<PerCpuHashMap<&'a Map, K, V>, MapError> { | ||||||
|  |         PerCpuHashMap::new(a) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a, K: Pod, V: Pod> TryFrom<&'a mut Map> for PerCpuHashMap<&'a mut Map, K, V> { | ||||||
|  |     type Error = MapError; | ||||||
|  | 
 | ||||||
|  |     fn try_from(a: &'a mut Map) -> Result<PerCpuHashMap<&'a mut Map, K, V>, MapError> { | ||||||
|  |         PerCpuHashMap::new(a) | ||||||
|  |     } | ||||||
|  | } | ||||||
					Loading…
					
					
				
		Reference in New Issue