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 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 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 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 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 if err != 0 {
127 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#[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 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 pub fn poll(&self, timeout: Duration) -> Result<()> {
187 let ret = self.poll_raw(timeout);
188
189 util::parse_ret(ret)
190 }
191
192 pub fn consume_raw(&self) -> i32 {
198 unsafe { libbpf_sys::ring_buffer__consume(self.ptr.as_ptr()) }
199 }
200
201 pub fn consume(&self) -> Result<()> {
205 let ret = self.consume_raw();
206
207 util::parse_ret(ret)
208 }
209
210 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 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
221 self.ptr
222 }
223}
224
225unsafe 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 #[test]
242 fn ringbuffer_is_send() {
243 fn test<T>()
244 where
245 T: Send,
246 {
247 }
248
249 test::<RingBuffer<'_>>();
250 }
251}