1 use core::ffi::c_void;
2 use std::fmt::Debug;
3 use std::fmt::Formatter;
4 use std::fmt::Result as FmtResult;
5 use std::ops::Deref as _;
6 use std::ops::DerefMut as _;
7 use std::os::raw::c_ulong;
8 use std::os::unix::prelude::AsRawFd;
9 use std::os::unix::prelude::BorrowedFd;
10 use std::ptr::null_mut;
11 use std::ptr::NonNull;
12 use std::slice;
13 use std::time::Duration;
14 
15 use crate::util;
16 use crate::util::validate_bpf_ret;
17 use crate::AsRawLibbpf;
18 use crate::Error;
19 use crate::ErrorExt as _;
20 use crate::MapCore;
21 use crate::MapType;
22 use crate::Result;
23 
24 type Cb<'a> = Box<dyn FnMut(&[u8]) -> i32 + 'a>;
25 
26 struct RingBufferCallback<'a> {
27     cb: Cb<'a>,
28 }
29 
30 impl<'a> RingBufferCallback<'a> {
new<F>(cb: F) -> Self where F: FnMut(&[u8]) -> i32 + '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 
39 impl Debug for RingBufferCallback<'_> {
fmt(&self, f: &mut Formatter<'_>) -> FmtResult40     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)]
54 pub struct RingBufferBuilder<'slf, 'cb> {
55     fd_callbacks: Vec<(BorrowedFd<'slf>, RingBufferCallback<'cb>)>,
56 }
57 
58 impl<'slf, 'cb: 'slf> RingBufferBuilder<'slf, 'cb> {
59     /// Create a new `RingBufferBuilder` object.
new() -> Self60     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.
add<NewF>(&mut self, map: &'slf dyn MapCore, callback: NewF) -> Result<&mut Self> where NewF: FnMut(&[u8]) -> i32 + 'cb,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.
build(self) -> Result<RingBuffer<'cb>>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 
call_sample_cb(ctx: *mut c_void, data: *mut c_void, size: c_ulong) -> i32145     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)]
160 pub struct RingBuffer<'cb> {
161     ptr: NonNull<libbpf_sys::ring_buffer>,
162     #[allow(clippy::vec_box)]
163     _cbs: Vec<Box<RingBufferCallback<'cb>>>,
164 }
165 
166 impl 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.
poll_raw(&self, timeout: Duration) -> i32173     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.
poll(&self, timeout: Duration) -> Result<()>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.
consume_raw(&self) -> i32197     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.
consume(&self) -> Result<()>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
epoll_fd(&self) -> i32211     pub fn epoll_fd(&self) -> i32 {
212         unsafe { libbpf_sys::ring_buffer__epoll_fd(self.ptr.as_ptr()) }
213     }
214 }
215 
216 impl AsRawLibbpf for RingBuffer<'_> {
217     type LibbpfType = libbpf_sys::ring_buffer;
218 
219     /// Retrieve the underlying [`libbpf_sys::ring_buffer`].
as_libbpf_object(&self) -> NonNull<Self::LibbpfType>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.
226 unsafe impl Send for RingBuffer<'_> {}
227 
228 impl Drop for RingBuffer<'_> {
drop(&mut self)229     fn drop(&mut self) {
230         unsafe {
231             libbpf_sys::ring_buffer__free(self.ptr.as_ptr());
232         }
233     }
234 }
235 
236 #[cfg(test)]
237 mod test {
238     use super::*;
239 
240     /// Check that `RingBuffer` is `Send`.
241     #[test]
ringbuffer_is_send()242     fn ringbuffer_is_send() {
243         fn test<T>()
244         where
245             T: Send,
246         {
247         }
248 
249         test::<RingBuffer<'_>>();
250     }
251 }
252