Skip to main content

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    /// Negative return values in the callback will stop ring buffer consumption early and
71    /// propagate the error code to the polling caller.
72    ///
73    /// The callback provides a raw byte slice. You may find libraries such as
74    /// [`plain`](https://crates.io/crates/plain) helpful.
75    #[doc(alias = "ring_buffer__add")]
76    pub fn add<NewF>(&mut self, map: &'slf dyn MapCore, callback: NewF) -> Result<&mut Self>
77    where
78        NewF: FnMut(&[u8]) -> i32 + 'cb,
79    {
80        if map.map_type() != MapType::RingBuf {
81            return Err(Error::with_invalid_data("Must use a RingBuf map"));
82        }
83        self.fd_callbacks
84            .push((map.as_fd(), RingBufferCallback::new(callback)));
85        Ok(self)
86    }
87
88    /// Build a new [`RingBuffer`]. Must have added at least one ringbuf.
89    #[doc(alias = "ring_buffer__new")]
90    pub fn build(self) -> Result<RingBuffer<'cb>> {
91        let mut cbs = vec![];
92        let mut rb_ptr: Option<NonNull<libbpf_sys::ring_buffer>> = None;
93        let c_sample_cb: libbpf_sys::ring_buffer_sample_fn = Some(Self::call_sample_cb);
94
95        for (fd, callback) in self.fd_callbacks {
96            let mut sample_cb = Box::new(callback);
97            match rb_ptr {
98                None => {
99                    // Allocate a new ringbuf manager and add a ringbuf to it
100                    // SAFETY: All pointers are valid or rightly NULL.
101                    //         The object referenced by `sample_cb` is
102                    //         not modified by `libbpf`
103                    let ptr = unsafe {
104                        libbpf_sys::ring_buffer__new(
105                            fd.as_raw_fd(),
106                            c_sample_cb,
107                            (&raw mut *sample_cb.deref_mut()).cast(),
108                            null_mut(),
109                        )
110                    };
111                    let ptr = validate_bpf_ret(ptr).context("failed to create new ring buffer")?;
112                    rb_ptr = Some(ptr)
113                }
114                Some(mut ptr) => {
115                    // Add a ringbuf to the existing ringbuf manager
116                    // SAFETY: All pointers are valid or rightly NULL.
117                    //         The object referenced by `sample_cb` is
118                    //         not modified by `libbpf`
119                    let err = unsafe {
120                        libbpf_sys::ring_buffer__add(
121                            ptr.as_ptr(),
122                            fd.as_raw_fd(),
123                            c_sample_cb,
124                            (&raw mut *sample_cb.deref_mut()).cast(),
125                        )
126                    };
127
128                    // Handle errors
129                    if err != 0 {
130                        // SAFETY: The pointer is valid.
131                        let () = unsafe { libbpf_sys::ring_buffer__free(ptr.as_mut()) };
132                        return Err(Error::from_raw_os_error(err));
133                    }
134                }
135            }
136
137            let () = cbs.push(sample_cb);
138        }
139
140        match rb_ptr {
141            Some(ptr) => Ok(RingBuffer { ptr, _cbs: cbs }),
142            None => Err(Error::with_invalid_data(
143                "You must add at least one ring buffer map and callback before building",
144            )),
145        }
146    }
147
148    unsafe extern "C" fn call_sample_cb(ctx: *mut c_void, data: *mut c_void, size: c_ulong) -> i32 {
149        let callback_struct = ctx.cast::<RingBufferCallback<'_>>();
150        let callback = unsafe { (*callback_struct).cb.as_mut() };
151        let slice = unsafe { slice::from_raw_parts(data as *const u8, size as usize) };
152
153        callback(slice)
154    }
155}
156
157/// The canonical interface for managing a collection of `ringbuf` maps.
158///
159/// `ringbuf`s are a special kind of [`Map`][crate::Map], used to transfer data
160/// between [`Program`][crate::Program]s and userspace. As of Linux 5.8, the
161/// `ringbuf` map is now preferred over the `perf buffer`.
162#[derive(Debug)]
163#[doc(alias = "ring_buffer")]
164pub struct RingBuffer<'cb> {
165    ptr: NonNull<libbpf_sys::ring_buffer>,
166    #[expect(clippy::vec_box)]
167    _cbs: Vec<Box<RingBufferCallback<'cb>>>,
168}
169
170impl RingBuffer<'_> {
171    /// Poll from all open ring buffers, calling the registered callback for
172    /// each one. Polls continually until we either run out of events to consume
173    /// or `timeout` is reached. If `timeout` is `Duration::MAX`, this will block
174    /// indefinitely until an event occurs.
175    ///
176    /// Return the amount of events consumed, or a negative value in case of error.
177    #[doc(alias = "ring_buffer__poll")]
178    pub fn poll_raw(&self, timeout: Duration) -> i32 {
179        let mut timeout_ms = -1;
180        if timeout != Duration::MAX {
181            timeout_ms = timeout.as_millis() as i32;
182        }
183
184        unsafe { libbpf_sys::ring_buffer__poll(self.ptr.as_ptr(), timeout_ms) }
185    }
186
187    /// Poll from all open ring buffers, calling the registered callback for
188    /// each one. Polls continually until we either run out of events to consume
189    /// or `timeout` is reached. If `timeout` is `Duration::MAX`, this will block
190    /// indefinitely until an event occurs.
191    #[doc(alias = "ring_buffer__poll")]
192    pub fn poll(&self, timeout: Duration) -> Result<()> {
193        let ret = self.poll_raw(timeout);
194
195        util::parse_ret(ret)
196    }
197
198    /// Greedily consume from all open ring buffers, calling the registered
199    /// callback for each one. Consumes continually until we run out of events
200    /// to consume or one of the callbacks returns a non-zero integer.
201    ///
202    /// Return the amount of events consumed, or a negative value in case of error.
203    #[doc(alias = "ring_buffer__consume")]
204    pub fn consume_raw(&self) -> i32 {
205        unsafe { libbpf_sys::ring_buffer__consume(self.ptr.as_ptr()) }
206    }
207
208    /// Greedily consume from all open ring buffers, calling the registered
209    /// callback for each one. Continues until `len` items have been consumed,
210    /// no more events are available, or a callback returns a non-zero value.
211    ///
212    /// Return the amount of events consumed, or a negative value in case of error.
213    #[doc(alias = "ring_buffer__consume_n")]
214    pub fn consume_raw_n(&self, len: usize) -> i32 {
215        unsafe { libbpf_sys::ring_buffer__consume_n(self.ptr.as_ptr(), len as libbpf_sys::size_t) }
216    }
217
218    /// Greedily consume from all open ring buffers, calling the registered
219    /// callback for each one. Consumes continually until we run out of events
220    /// to consume or one of the callbacks returns a non-zero integer.
221    #[doc(alias = "ring_buffer__consume")]
222    pub fn consume(&self) -> Result<()> {
223        let ret = self.consume_raw();
224
225        util::parse_ret(ret)
226    }
227
228    /// Get an fd that can be used to sleep until data is available
229    #[doc(alias = "ring_buffer__epoll_fd")]
230    pub fn epoll_fd(&self) -> i32 {
231        unsafe { libbpf_sys::ring_buffer__epoll_fd(self.ptr.as_ptr()) }
232    }
233}
234
235impl AsRawLibbpf for RingBuffer<'_> {
236    type LibbpfType = libbpf_sys::ring_buffer;
237
238    /// Retrieve the underlying [`libbpf_sys::ring_buffer`].
239    fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
240        self.ptr
241    }
242}
243
244// SAFETY: `ring_buffer` objects can safely be polled from any thread.
245unsafe impl Send for RingBuffer<'_> {}
246
247impl Drop for RingBuffer<'_> {
248    #[doc(alias = "ring_buffer__free")]
249    fn drop(&mut self) {
250        unsafe {
251            libbpf_sys::ring_buffer__free(self.ptr.as_ptr());
252        }
253    }
254}
255
256#[cfg(test)]
257mod test {
258    use super::*;
259
260    /// Check that `RingBuffer` is `Send`.
261    #[test]
262    fn ringbuffer_is_send() {
263        fn test<T>()
264        where
265            T: Send,
266        {
267        }
268
269        test::<RingBuffer<'_>>();
270    }
271}