libbpf_rs/
ringbuf.rs

1use core::ffi::c_void;
2use std::fmt::Debug;
3use std::fmt::Formatter;
4use std::fmt::Result as FmtResult;
5use std::ops::Deref as _;
6use std::ops::DerefMut as _;
7use std::os::raw::c_ulong;
8use std::os::unix::prelude::AsRawFd;
9use std::os::unix::prelude::BorrowedFd;
10use std::ptr::null_mut;
11use std::ptr::NonNull;
12use std::slice;
13use std::time::Duration;
14
15use crate::util;
16use crate::util::validate_bpf_ret;
17use crate::AsRawLibbpf;
18use crate::Error;
19use crate::ErrorExt as _;
20use crate::MapCore;
21use crate::MapType;
22use crate::Result;
23
24type Cb<'a> = Box<dyn FnMut(&[u8]) -> i32 + 'a>;
25
26struct RingBufferCallback<'a> {
27    cb: Cb<'a>,
28}
29
30impl<'a> RingBufferCallback<'a> {
31    fn new<F>(cb: F) -> Self
32    where
33        F: FnMut(&[u8]) -> i32 + 'a,
34    {
35        RingBufferCallback { cb: Box::new(cb) }
36    }
37}
38
39impl Debug for RingBufferCallback<'_> {
40    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
41        let Self { cb } = self;
42        f.debug_struct("RingBufferCallback")
43            .field("cb", &(cb.deref() as *const _))
44            .finish()
45    }
46}
47
48/// Builds [`RingBuffer`] instances.
49///
50/// `ringbuf`s are a special kind of [`Map`][crate::Map], used to transfer data
51/// between [`Program`][crate::Program]s and userspace. As of Linux 5.8, the
52/// `ringbuf` map is now preferred over the `perf buffer`.
53#[derive(Debug, Default)]
54pub struct RingBufferBuilder<'slf, 'cb> {
55    fd_callbacks: Vec<(BorrowedFd<'slf>, RingBufferCallback<'cb>)>,
56}
57
58impl<'slf, 'cb: 'slf> RingBufferBuilder<'slf, 'cb> {
59    /// Create a new `RingBufferBuilder` object.
60    pub fn new() -> Self {
61        RingBufferBuilder {
62            fd_callbacks: vec![],
63        }
64    }
65
66    /// Add a new ringbuf `map` and associated `callback` to this ring buffer
67    /// manager. The callback should take one argument, a slice of raw bytes,
68    /// and return an i32.
69    ///
70    /// Non-zero return values in the callback will stop ring buffer consumption early.
71    ///
72    /// The callback provides a raw byte slice. You may find libraries such as
73    /// [`plain`](https://crates.io/crates/plain) helpful.
74    pub fn add<NewF>(&mut self, map: &'slf dyn MapCore, callback: NewF) -> Result<&mut Self>
75    where
76        NewF: FnMut(&[u8]) -> i32 + 'cb,
77    {
78        if map.map_type() != MapType::RingBuf {
79            return Err(Error::with_invalid_data("Must use a RingBuf map"));
80        }
81        self.fd_callbacks
82            .push((map.as_fd(), RingBufferCallback::new(callback)));
83        Ok(self)
84    }
85
86    /// Build a new [`RingBuffer`]. Must have added at least one ringbuf.
87    pub fn build(self) -> Result<RingBuffer<'cb>> {
88        let mut cbs = vec![];
89        let mut rb_ptr: Option<NonNull<libbpf_sys::ring_buffer>> = None;
90        let c_sample_cb: libbpf_sys::ring_buffer_sample_fn = Some(Self::call_sample_cb);
91
92        for (fd, callback) in self.fd_callbacks {
93            let mut sample_cb = Box::new(callback);
94            match rb_ptr {
95                None => {
96                    // Allocate a new ringbuf manager and add a ringbuf to it
97                    // SAFETY: All pointers are valid or rightly NULL.
98                    //         The object referenced by `sample_cb` is
99                    //         not modified by `libbpf`
100                    let ptr = unsafe {
101                        libbpf_sys::ring_buffer__new(
102                            fd.as_raw_fd(),
103                            c_sample_cb,
104                            sample_cb.deref_mut() as *mut _ as *mut _,
105                            null_mut(),
106                        )
107                    };
108                    let ptr = validate_bpf_ret(ptr).context("failed to create new ring buffer")?;
109                    rb_ptr = Some(ptr)
110                }
111                Some(mut ptr) => {
112                    // Add a ringbuf to the existing ringbuf manager
113                    // SAFETY: All pointers are valid or rightly NULL.
114                    //         The object referenced by `sample_cb` is
115                    //         not modified by `libbpf`
116                    let err = unsafe {
117                        libbpf_sys::ring_buffer__add(
118                            ptr.as_ptr(),
119                            fd.as_raw_fd(),
120                            c_sample_cb,
121                            sample_cb.deref_mut() as *mut _ as *mut _,
122                        )
123                    };
124
125                    // Handle errors
126                    if err != 0 {
127                        // SAFETY: The pointer is valid.
128                        let () = unsafe { libbpf_sys::ring_buffer__free(ptr.as_mut()) };
129                        return Err(Error::from_raw_os_error(err));
130                    }
131                }
132            }
133
134            let () = cbs.push(sample_cb);
135        }
136
137        match rb_ptr {
138            Some(ptr) => Ok(RingBuffer { ptr, _cbs: cbs }),
139            None => Err(Error::with_invalid_data(
140                "You must add at least one ring buffer map and callback before building",
141            )),
142        }
143    }
144
145    unsafe extern "C" fn call_sample_cb(ctx: *mut c_void, data: *mut c_void, size: c_ulong) -> i32 {
146        let callback_struct = ctx as *mut RingBufferCallback<'_>;
147        let callback = unsafe { (*callback_struct).cb.as_mut() };
148        let slice = unsafe { slice::from_raw_parts(data as *const u8, size as usize) };
149
150        callback(slice)
151    }
152}
153
154/// The canonical interface for managing a collection of `ringbuf` maps.
155///
156/// `ringbuf`s are a special kind of [`Map`][crate::Map], used to transfer data
157/// between [`Program`][crate::Program]s and userspace. As of Linux 5.8, the
158/// `ringbuf` map is now preferred over the `perf buffer`.
159#[derive(Debug)]
160pub struct RingBuffer<'cb> {
161    ptr: NonNull<libbpf_sys::ring_buffer>,
162    #[allow(clippy::vec_box)]
163    _cbs: Vec<Box<RingBufferCallback<'cb>>>,
164}
165
166impl RingBuffer<'_> {
167    /// Poll from all open ring buffers, calling the registered callback for
168    /// each one. Polls continually until we either run out of events to consume
169    /// or `timeout` is reached. If `timeout` is Duration::MAX, this will block
170    /// indefinitely until an event occurs.
171    ///
172    /// Return the amount of events consumed, or a negative value in case of error.
173    pub fn poll_raw(&self, timeout: Duration) -> i32 {
174        let mut timeout_ms = -1;
175        if timeout != Duration::MAX {
176            timeout_ms = timeout.as_millis() as i32;
177        }
178
179        unsafe { libbpf_sys::ring_buffer__poll(self.ptr.as_ptr(), timeout_ms) }
180    }
181
182    /// Poll from all open ring buffers, calling the registered callback for
183    /// each one. Polls continually until we either run out of events to consume
184    /// or `timeout` is reached. If `timeout` is Duration::MAX, this will block
185    /// indefinitely until an event occurs.
186    pub fn poll(&self, timeout: Duration) -> Result<()> {
187        let ret = self.poll_raw(timeout);
188
189        util::parse_ret(ret)
190    }
191
192    /// Greedily consume from all open ring buffers, calling the registered
193    /// callback for each one. Consumes continually until we run out of events
194    /// to consume or one of the callbacks returns a non-zero integer.
195    ///
196    /// Return the amount of events consumed, or a negative value in case of error.
197    pub fn consume_raw(&self) -> i32 {
198        unsafe { libbpf_sys::ring_buffer__consume(self.ptr.as_ptr()) }
199    }
200
201    /// Greedily consume from all open ring buffers, calling the registered
202    /// callback for each one. Consumes continually until we run out of events
203    /// to consume or one of the callbacks returns a non-zero integer.
204    pub fn consume(&self) -> Result<()> {
205        let ret = self.consume_raw();
206
207        util::parse_ret(ret)
208    }
209
210    /// Get an fd that can be used to sleep until data is available
211    pub fn epoll_fd(&self) -> i32 {
212        unsafe { libbpf_sys::ring_buffer__epoll_fd(self.ptr.as_ptr()) }
213    }
214}
215
216impl AsRawLibbpf for RingBuffer<'_> {
217    type LibbpfType = libbpf_sys::ring_buffer;
218
219    /// Retrieve the underlying [`libbpf_sys::ring_buffer`].
220    fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
221        self.ptr
222    }
223}
224
225// SAFETY: `ring_buffer` objects can safely be polled from any thread.
226unsafe impl Send for RingBuffer<'_> {}
227
228impl Drop for RingBuffer<'_> {
229    fn drop(&mut self) {
230        unsafe {
231            libbpf_sys::ring_buffer__free(self.ptr.as_ptr());
232        }
233    }
234}
235
236#[cfg(test)]
237mod test {
238    use super::*;
239
240    /// Check that `RingBuffer` is `Send`.
241    #[test]
242    fn ringbuffer_is_send() {
243        fn test<T>()
244        where
245            T: Send,
246        {
247        }
248
249        test::<RingBuffer<'_>>();
250    }
251}