From 04fde468556ace883009b4abe63840aa7b89f29f Mon Sep 17 00:00:00 2001 From: Alessandro Decina Date: Wed, 10 Mar 2021 07:12:22 +0000 Subject: [PATCH] aya: more docs --- aya/src/lib.rs | 1 - aya/src/maps/mod.rs | 25 ++++--- aya/src/maps/perf/async_perf_event_array.rs | 82 ++++++++++++++++++++- aya/src/maps/perf/mod.rs | 8 +- aya/src/maps/perf/perf_event_array.rs | 2 +- aya/src/maps/program_array.rs | 2 +- aya/src/util.rs | 4 +- 7 files changed, 101 insertions(+), 23 deletions(-) diff --git a/aya/src/lib.rs b/aya/src/lib.rs index 86276d65..6998ed6c 100644 --- a/aya/src/lib.rs +++ b/aya/src/lib.rs @@ -24,7 +24,6 @@ //! * Easy to deploy and fast to build: aya doesn't require kernel headers nor a //! C toolchain and a release build completes in a matter of seconds. //! -//! //! # Minimum kernel version //! //! Aya currently supports kernels version 5.4 (latest LTS) and newer. diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index cb0fd4ec..d9a3830a 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -1,19 +1,17 @@ -//! eBPF map types. +//! eBPF data structures used to exchange data with eBPF programs. //! //! The eBPF platform provides data structures - maps in eBPF speak - that can be used by eBPF -//! programs and user-space to exchange data. -//! -//! When you call [`Bpf::load_file`](crate::Bpf::load_file) or [`Bpf::load`](crate::Bpf::load), aya -//! transparently discovers all the maps defined in the loaded code and initializes them. The maps -//! can then be accessed using [`Bpf::map`](crate::Bpf::map) and [`Bpf::map_mut`](crate::Bpf::map_mut). +//! programs and user-space to exchange data. When you call +//! [`Bpf::load_file`](crate::Bpf::load_file) or [`Bpf::load`](crate::Bpf::load), all the maps +//! defined in the code get initialized and can then be accessed using +//! [`Bpf::map`](crate::Bpf::map) and [`Bpf::map_mut`](crate::Bpf::map_mut). //! //! # Concrete map types //! -//! Different map types support different operations. [`Bpf::map`](crate::Bpf::map) and -//! [`Bpf::map_mut`](crate::Bpf::map_mut) always return the opaque [`MapRef`] and [`MapRefMut`] -//! types respectively, which you can convert to concrete map types using the -//! [`TryFrom`](std::convert::TryFrom) trait. For example the code below shows how to insert a -//! value inside a [`HashMap`](crate::maps::hash_map::HashMap): +//! The eBPF platform provides many map types each supporting different operations. +//! [`Bpf::map`](crate::Bpf::map) and [`Bpf::map_mut`](crate::Bpf::map_mut) always return the +//! opaque [`MapRef`] and [`MapRefMut`] types respectively. Those two types can be converted to +//! *concrete map types* using the [`TryFrom`](std::convert::TryFrom) trait. For example: //! //! ```no_run //! # let bpf = aya::Bpf::load(&[], None)?; @@ -22,12 +20,15 @@ //! //! const CONFIG_KEY_NUM_RETRIES: u8 = 1; //! +//! // HashMap::try_from() converts MapRefMut to HashMap. It will fail if CONFIG is not an eBPF +//! // hash map. //! let mut hm = HashMap::try_from(bpf.map_mut("CONFIG")?)?; //! hm.insert(CONFIG_KEY_NUM_RETRIES, 3, 0 /* flags */); //! # Ok::<(), aya::BpfError>(()) //! ``` //! -//! All the concrete map types implement the [`TryFrom`](std::convert::TryFrom) trait. +//! The code above uses `HashMap`, but all the concrete map types implement the +//! `TryFrom` trait. use std::{convert::TryFrom, ffi::CString, io, os::unix::io::RawFd}; use thiserror::Error; diff --git a/aya/src/maps/perf/async_perf_event_array.rs b/aya/src/maps/perf/async_perf_event_array.rs index afe1924f..cf5bff32 100644 --- a/aya/src/maps/perf/async_perf_event_array.rs +++ b/aya/src/maps/perf/async_perf_event_array.rs @@ -16,6 +16,75 @@ use crate::maps::{ Map, MapError, MapRefMut, }; +/// A `Future` based map that can be used to receive events from eBPF programs using the linux +/// [`perf`](https://perf.wiki.kernel.org/index.php/Main_Page) API. +/// +/// This is the async version of [`PerfEventArray`], which provides integration +/// with [tokio](https://docs.rs/tokio) and [async-std](https:/docs.rs/async-std) and a nice `Future` based API. +/// +/// To receive events you need to: +/// * call [`AsyncPerfEventArray::open`] +/// * call [`AsyncPerfEventArrayBuffer::read_events`] to read the events +/// +/// # 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), +/// # #[error(transparent)] +/// # PerfBuf(#[from] aya::maps::perf::PerfBufferError), +/// # } +/// # async fn try_main() -> Result<(), Error> { +/// # use async_std::task; +/// # let bpf = aya::Bpf::load(&[], None)?; +/// use aya::maps::perf::{AsyncPerfEventArray, PerfBufferError}; +/// use aya::util::online_cpus; +/// use std::convert::TryFrom; +/// use futures::future; +/// use bytes::BytesMut; +/// +/// // try to convert the PERF_ARRAY map to an AsyncPerfEventArray +/// let mut perf_array = AsyncPerfEventArray::try_from(bpf.map_mut("PERF_ARRAY")?)?; +/// +/// let mut futs = Vec::new(); +/// for cpu_id in online_cpus()? { +/// // open a separate perf buffer for each cpu +/// let mut buf = perf_array.open(cpu_id, None)?; +/// +/// // process each perf buffer in a separate task +/// // NOTE: use async_std::task::spawn with async-std and tokio::spawn with tokio +/// futs.push(task::spawn(async move { +/// let mut buffers = (0..10) +/// .map(|_| BytesMut::with_capacity(1024)) +/// .collect::>(); +/// +/// loop { +/// // wait for events +/// let events = buf.read_events(&mut buffers).await?; +/// +/// // events.read contains the number of events that have been read, +/// // and is always <= buffers.len() +/// for i in 0..events.read { +/// let buf = &mut buffers[i]; +/// // process buf +/// } +/// } +/// +/// Ok::<_, PerfBufferError>(()) +/// })); +/// } +/// +/// +/// future::join_all(futs).await; +/// # Ok(()) +/// # } +/// ``` pub struct AsyncPerfEventArray> { perf_map: PerfEventArray, } @@ -34,7 +103,7 @@ impl> AsyncPerfEventArray { #[cfg(feature = "async_tokio")] async_fd: AsyncFd::new(fd)?, - #[cfg(feature = "async_std")] + #[cfg(all(not(feature = "async_tokio"), feature = "async_std"))] async_fd: Async::new(fd)?, }) } @@ -48,13 +117,20 @@ impl> AsyncPerfEventArray { } } +/// A `Future` based ring buffer that can receive events from eBPF programs. +/// +/// [`AsyncPerfEventArrayBuffer`] is a ring buffer that can receive events from eBPF programs that +/// use `bpf_perf_event_output()`. It's returned by [`AsyncPerfEventArray::open`]. +/// +/// See the [`AsyncPerfEventArray` documentation](AsyncPerfEventArray) for an overview of how to +/// use perf buffers. pub struct AsyncPerfEventArrayBuffer> { buf: PerfEventArrayBuffer, #[cfg(feature = "async_tokio")] async_fd: AsyncFd, - #[cfg(feature = "async_std")] + #[cfg(all(not(feature = "async_tokio"), feature = "async_std"))] async_fd: Async, } @@ -79,7 +155,7 @@ impl> AsyncPerfEventArrayBuffer { } } -#[cfg(feature = "async_std")] +#[cfg(all(not(feature = "async_tokio"), feature = "async_std"))] impl> AsyncPerfEventArrayBuffer { pub async fn read_events( &mut self, diff --git a/aya/src/maps/perf/mod.rs b/aya/src/maps/perf/mod.rs index 8fae9fc6..f4770275 100644 --- a/aya/src/maps/perf/mod.rs +++ b/aya/src/maps/perf/mod.rs @@ -1,12 +1,12 @@ -//! Receive events from eBPF programs using the linux `perf` API. +//! Ring buffer types used to receive events from eBPF programs using the linux `perf` API. //! -//! See the [`PerfEventArray` documentation](self::PerfEventArray). -#[cfg(feature = "async")] +//! See the [`PerfEventArray`] and [`AsyncPerfEventArray`]. +#[cfg(any(feature = "async", doc))] mod async_perf_event_array; mod perf_buffer; mod perf_event_array; -#[cfg(feature = "async")] +#[cfg(any(feature = "async", doc))] pub use async_perf_event_array::*; pub use perf_buffer::*; pub use perf_event_array::*; diff --git a/aya/src/maps/perf/perf_event_array.rs b/aya/src/maps/perf/perf_event_array.rs index e760ffb9..bd16102d 100644 --- a/aya/src/maps/perf/perf_event_array.rs +++ b/aya/src/maps/perf/perf_event_array.rs @@ -20,7 +20,7 @@ use crate::{ sys::bpf_map_update_elem, }; -/// A buffer that can receive events from eBPF programs. +/// A ring buffer that can receive events from eBPF programs. /// /// [`PerfEventArrayBuffer`] is a ring buffer that can receive events from eBPF /// programs that use `bpf_perf_event_output()`. It's returned by [`PerfEventArray::open`]. diff --git a/aya/src/maps/program_array.rs b/aya/src/maps/program_array.rs index 5a3a3c6b..d2c640c1 100644 --- a/aya/src/maps/program_array.rs +++ b/aya/src/maps/program_array.rs @@ -1,4 +1,4 @@ -//! Program array (jump table) for eBPF programs. +//! An array of eBPF program file descriptors used as a jump table. use std::{ convert::TryFrom, diff --git a/aya/src/util.rs b/aya/src/util.rs index f4c08491..eb97410d 100644 --- a/aya/src/util.rs +++ b/aya/src/util.rs @@ -1,8 +1,10 @@ +//! Utility functions. use std::{fs, io, str::FromStr}; const ONLINE_CPUS: &str = "/sys/devices/system/cpu/online"; pub(crate) const POSSIBLE_CPUS: &str = "/sys/devices/system/cpu/possible"; +/// Returns the numeric IDs of the available CPUs. pub fn online_cpus() -> Result, io::Error> { let data = fs::read_to_string(ONLINE_CPUS)?; parse_cpu_ranges(data.trim()).map_err(|_| { @@ -13,7 +15,7 @@ pub fn online_cpus() -> Result, io::Error> { }) } -pub fn possible_cpus() -> Result, io::Error> { +pub(crate) fn possible_cpus() -> Result, io::Error> { let data = fs::read_to_string(POSSIBLE_CPUS)?; parse_cpu_ranges(data.trim()).map_err(|_| { io::Error::new(