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#[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 pub fn new() -> Self {
61 RingBufferBuilder {
62 fd_callbacks: vec![],
63 }
64 }
65
66 #[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 #[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 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 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 if err != 0 {
130 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#[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 #[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 #[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 #[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 #[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 #[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 #[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 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
240 self.ptr
241 }
242}
243
244unsafe 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 #[test]
262 fn ringbuffer_is_send() {
263 fn test<T>()
264 where
265 T: Send,
266 {
267 }
268
269 test::<RingBuffer<'_>>();
270 }
271}