| use core::ffi::c_void; |
| use std::fmt::Debug; |
| use std::fmt::Formatter; |
| use std::fmt::Result as FmtResult; |
| use std::ops::Deref as _; |
| use std::ops::DerefMut as _; |
| use std::os::raw::c_ulong; |
| use std::os::unix::prelude::AsRawFd; |
| use std::os::unix::prelude::BorrowedFd; |
| use std::ptr::null_mut; |
| use std::ptr::NonNull; |
| use std::slice; |
| use std::time::Duration; |
| |
| use crate::util; |
| use crate::util::validate_bpf_ret; |
| use crate::AsRawLibbpf; |
| use crate::Error; |
| use crate::ErrorExt as _; |
| use crate::MapCore; |
| use crate::MapType; |
| use crate::Result; |
| |
| type Cb<'a> = Box<dyn FnMut(&[u8]) -> i32 + 'a>; |
| |
| struct RingBufferCallback<'a> { |
| cb: Cb<'a>, |
| } |
| |
| impl<'a> RingBufferCallback<'a> { |
| fn new<F>(cb: F) -> Self |
| where |
| F: FnMut(&[u8]) -> i32 + 'a, |
| { |
| RingBufferCallback { cb: Box::new(cb) } |
| } |
| } |
| |
| impl Debug for RingBufferCallback<'_> { |
| fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { |
| let Self { cb } = self; |
| f.debug_struct("RingBufferCallback") |
| .field("cb", &(cb.deref() as *const _)) |
| .finish() |
| } |
| } |
| |
| /// Builds [`RingBuffer`] instances. |
| /// |
| /// `ringbuf`s are a special kind of [`Map`][crate::Map], used to transfer data |
| /// between [`Program`][crate::Program]s and userspace. As of Linux 5.8, the |
| /// `ringbuf` map is now preferred over the `perf buffer`. |
| #[derive(Debug, Default)] |
| pub struct RingBufferBuilder<'slf, 'cb> { |
| fd_callbacks: Vec<(BorrowedFd<'slf>, RingBufferCallback<'cb>)>, |
| } |
| |
| impl<'slf, 'cb: 'slf> RingBufferBuilder<'slf, 'cb> { |
| /// Create a new `RingBufferBuilder` object. |
| pub fn new() -> Self { |
| RingBufferBuilder { |
| fd_callbacks: vec![], |
| } |
| } |
| |
| /// Add a new ringbuf `map` and associated `callback` to this ring buffer |
| /// manager. The callback should take one argument, a slice of raw bytes, |
| /// and return an i32. |
| /// |
| /// Non-zero return values in the callback will stop ring buffer consumption early. |
| /// |
| /// The callback provides a raw byte slice. You may find libraries such as |
| /// [`plain`](https://crates.io/crates/plain) helpful. |
| pub fn add<NewF>(&mut self, map: &'slf dyn MapCore, callback: NewF) -> Result<&mut Self> |
| where |
| NewF: FnMut(&[u8]) -> i32 + 'cb, |
| { |
| if map.map_type() != MapType::RingBuf { |
| return Err(Error::with_invalid_data("Must use a RingBuf map")); |
| } |
| self.fd_callbacks |
| .push((map.as_fd(), RingBufferCallback::new(callback))); |
| Ok(self) |
| } |
| |
| /// Build a new [`RingBuffer`]. Must have added at least one ringbuf. |
| pub fn build(self) -> Result<RingBuffer<'cb>> { |
| let mut cbs = vec![]; |
| let mut rb_ptr: Option<NonNull<libbpf_sys::ring_buffer>> = None; |
| let c_sample_cb: libbpf_sys::ring_buffer_sample_fn = Some(Self::call_sample_cb); |
| |
| for (fd, callback) in self.fd_callbacks { |
| let mut sample_cb = Box::new(callback); |
| match rb_ptr { |
| None => { |
| // Allocate a new ringbuf manager and add a ringbuf to it |
| // SAFETY: All pointers are valid or rightly NULL. |
| // The object referenced by `sample_cb` is |
| // not modified by `libbpf` |
| let ptr = unsafe { |
| libbpf_sys::ring_buffer__new( |
| fd.as_raw_fd(), |
| c_sample_cb, |
| sample_cb.deref_mut() as *mut _ as *mut _, |
| null_mut(), |
| ) |
| }; |
| let ptr = validate_bpf_ret(ptr).context("failed to create new ring buffer")?; |
| rb_ptr = Some(ptr) |
| } |
| Some(mut ptr) => { |
| // Add a ringbuf to the existing ringbuf manager |
| // SAFETY: All pointers are valid or rightly NULL. |
| // The object referenced by `sample_cb` is |
| // not modified by `libbpf` |
| let err = unsafe { |
| libbpf_sys::ring_buffer__add( |
| ptr.as_ptr(), |
| fd.as_raw_fd(), |
| c_sample_cb, |
| sample_cb.deref_mut() as *mut _ as *mut _, |
| ) |
| }; |
| |
| // Handle errors |
| if err != 0 { |
| // SAFETY: The pointer is valid. |
| let () = unsafe { libbpf_sys::ring_buffer__free(ptr.as_mut()) }; |
| return Err(Error::from_raw_os_error(err)); |
| } |
| } |
| } |
| |
| let () = cbs.push(sample_cb); |
| } |
| |
| match rb_ptr { |
| Some(ptr) => Ok(RingBuffer { ptr, _cbs: cbs }), |
| None => Err(Error::with_invalid_data( |
| "You must add at least one ring buffer map and callback before building", |
| )), |
| } |
| } |
| |
| unsafe extern "C" fn call_sample_cb(ctx: *mut c_void, data: *mut c_void, size: c_ulong) -> i32 { |
| let callback_struct = ctx as *mut RingBufferCallback<'_>; |
| let callback = unsafe { (*callback_struct).cb.as_mut() }; |
| let slice = unsafe { slice::from_raw_parts(data as *const u8, size as usize) }; |
| |
| callback(slice) |
| } |
| } |
| |
| /// The canonical interface for managing a collection of `ringbuf` maps. |
| /// |
| /// `ringbuf`s are a special kind of [`Map`][crate::Map], used to transfer data |
| /// between [`Program`][crate::Program]s and userspace. As of Linux 5.8, the |
| /// `ringbuf` map is now preferred over the `perf buffer`. |
| #[derive(Debug)] |
| pub struct RingBuffer<'cb> { |
| ptr: NonNull<libbpf_sys::ring_buffer>, |
| #[allow(clippy::vec_box)] |
| _cbs: Vec<Box<RingBufferCallback<'cb>>>, |
| } |
| |
| impl RingBuffer<'_> { |
| /// Poll from all open ring buffers, calling the registered callback for |
| /// each one. Polls continually until we either run out of events to consume |
| /// or `timeout` is reached. If `timeout` is Duration::MAX, this will block |
| /// indefinitely until an event occurs. |
| /// |
| /// Return the amount of events consumed, or a negative value in case of error. |
| pub fn poll_raw(&self, timeout: Duration) -> i32 { |
| let mut timeout_ms = -1; |
| if timeout != Duration::MAX { |
| timeout_ms = timeout.as_millis() as i32; |
| } |
| |
| unsafe { libbpf_sys::ring_buffer__poll(self.ptr.as_ptr(), timeout_ms) } |
| } |
| |
| /// Poll from all open ring buffers, calling the registered callback for |
| /// each one. Polls continually until we either run out of events to consume |
| /// or `timeout` is reached. If `timeout` is Duration::MAX, this will block |
| /// indefinitely until an event occurs. |
| pub fn poll(&self, timeout: Duration) -> Result<()> { |
| let ret = self.poll_raw(timeout); |
| |
| util::parse_ret(ret) |
| } |
| |
| /// Greedily consume from all open ring buffers, calling the registered |
| /// callback for each one. Consumes continually until we run out of events |
| /// to consume or one of the callbacks returns a non-zero integer. |
| /// |
| /// Return the amount of events consumed, or a negative value in case of error. |
| pub fn consume_raw(&self) -> i32 { |
| unsafe { libbpf_sys::ring_buffer__consume(self.ptr.as_ptr()) } |
| } |
| |
| /// Greedily consume from all open ring buffers, calling the registered |
| /// callback for each one. Consumes continually until we run out of events |
| /// to consume or one of the callbacks returns a non-zero integer. |
| pub fn consume(&self) -> Result<()> { |
| let ret = self.consume_raw(); |
| |
| util::parse_ret(ret) |
| } |
| |
| /// Get an fd that can be used to sleep until data is available |
| pub fn epoll_fd(&self) -> i32 { |
| unsafe { libbpf_sys::ring_buffer__epoll_fd(self.ptr.as_ptr()) } |
| } |
| } |
| |
| impl AsRawLibbpf for RingBuffer<'_> { |
| type LibbpfType = libbpf_sys::ring_buffer; |
| |
| /// Retrieve the underlying [`libbpf_sys::ring_buffer`]. |
| fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> { |
| self.ptr |
| } |
| } |
| |
| // SAFETY: `ring_buffer` objects can safely be polled from any thread. |
| unsafe impl Send for RingBuffer<'_> {} |
| |
| impl Drop for RingBuffer<'_> { |
| fn drop(&mut self) { |
| unsafe { |
| libbpf_sys::ring_buffer__free(self.ptr.as_ptr()); |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| |
| /// Check that `RingBuffer` is `Send`. |
| #[test] |
| fn ringbuffer_is_send() { |
| fn test<T>() |
| where |
| T: Send, |
| { |
| } |
| |
| test::<RingBuffer<'_>>(); |
| } |
| } |