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