1 use libc::E2BIG; 2 use libc::ENOSPC; 3 use std::io; 4 use std::ops::Deref; 5 use std::ops::DerefMut; 6 use std::os::fd::AsRawFd; 7 use std::os::raw::c_uint; 8 use std::os::raw::c_void; 9 use std::ptr::null_mut; 10 use std::ptr::NonNull; 11 use std::slice::from_raw_parts; 12 use std::slice::from_raw_parts_mut; 13 14 use crate::AsRawLibbpf; 15 use crate::Error; 16 use crate::MapCore; 17 use crate::MapType; 18 use crate::Result; 19 20 /// A mutable reference to sample from a [`UserRingBuffer`]. 21 /// 22 /// To write to the sample, dereference with `as_mut()` to get a mutable 23 /// reference to the raw byte slice. You may find libraries such as 24 /// [`plain`](https://crates.io/crates/plain) helpful to convert between raw 25 /// bytes and structs. 26 #[derive(Debug)] 27 pub struct UserRingBufferSample<'slf> { 28 // A pointer to an 8-byte aligned reserved region of the user ring buffer 29 ptr: NonNull<c_void>, 30 31 // The size of the sample in bytes. 32 size: usize, 33 34 // Reference to the owning ring buffer. This is used to discard the sample 35 // if it is not submitted before being dropped. 36 rb: &'slf UserRingBuffer, 37 38 // Track whether the sample has been submitted. 39 submitted: bool, 40 } 41 42 impl Deref for UserRingBufferSample<'_> { 43 type Target = [u8]; 44 deref(&self) -> &Self::Target45 fn deref(&self) -> &Self::Target { 46 unsafe { from_raw_parts(self.ptr.as_ptr() as *const u8, self.size) } 47 } 48 } 49 50 impl DerefMut for UserRingBufferSample<'_> { deref_mut(&mut self) -> &mut Self::Target51 fn deref_mut(&mut self) -> &mut Self::Target { 52 unsafe { from_raw_parts_mut(self.ptr.as_ptr() as *mut u8, self.size) } 53 } 54 } 55 56 impl Drop for UserRingBufferSample<'_> { drop(&mut self)57 fn drop(&mut self) { 58 // If the sample has not been submitted, explicitly discard it. 59 // This is necessary to avoid leaking ring buffer memory. 60 if !self.submitted { 61 unsafe { 62 libbpf_sys::user_ring_buffer__discard(self.rb.ptr.as_ptr(), self.ptr.as_ptr()); 63 } 64 } 65 } 66 } 67 68 /// Represents a user ring buffer. This is a special kind of map that is used to 69 /// transfer data between user space and kernel space. 70 #[derive(Debug)] 71 pub struct UserRingBuffer { 72 // A non-null pointer to the underlying user ring buffer. 73 ptr: NonNull<libbpf_sys::user_ring_buffer>, 74 } 75 76 impl UserRingBuffer { 77 /// Create a new user ring buffer from a map. 78 /// 79 /// # Errors 80 /// * If the map is not a user ring buffer. 81 /// * If the underlying libbpf function fails. new(map: &dyn MapCore) -> Result<Self>82 pub fn new(map: &dyn MapCore) -> Result<Self> { 83 if map.map_type() != MapType::UserRingBuf { 84 return Err(Error::with_invalid_data("must use a UserRingBuf map")); 85 } 86 87 let fd = map.as_fd(); 88 let raw_ptr = unsafe { libbpf_sys::user_ring_buffer__new(fd.as_raw_fd(), null_mut()) }; 89 90 let ptr = NonNull::new(raw_ptr).ok_or_else(|| { 91 // Safely get the last OS error after a failed call to user_ring_buffer__new 92 io::Error::last_os_error() 93 })?; 94 95 Ok(UserRingBuffer { ptr }) 96 } 97 98 /// Reserve a sample in the user ring buffer. 99 /// 100 /// Returns a [`UserRingBufferSample`](UserRingBufferSample<'slf>) 101 /// that contains a mutable reference to sample that can be written to. 102 /// The sample must be submitted via [`UserRingBuffer::submit`] before it is 103 /// dropped. 104 /// 105 /// # Parameters 106 /// * `size` - The size of the sample in bytes. 107 /// 108 /// This function is *not* thread-safe. It is necessary to synchronize 109 /// amongst multiple producers when invoking this function. reserve(&self, size: usize) -> Result<UserRingBufferSample<'_>>110 pub fn reserve(&self, size: usize) -> Result<UserRingBufferSample<'_>> { 111 let sample_ptr = 112 unsafe { libbpf_sys::user_ring_buffer__reserve(self.ptr.as_ptr(), size as c_uint) }; 113 114 let ptr = NonNull::new(sample_ptr).ok_or_else(|| { 115 // Fetch the current value of errno to determine the type of error. 116 let errno = io::Error::last_os_error(); 117 match errno.raw_os_error() { 118 Some(E2BIG) => Error::with_invalid_data("requested size is too large"), 119 Some(ENOSPC) => Error::with_invalid_data("not enough space in the ring buffer"), 120 _ => Error::from(errno), 121 } 122 })?; 123 124 Ok(UserRingBufferSample { 125 ptr, 126 size, 127 submitted: false, 128 rb: self, 129 }) 130 } 131 132 /// Submit a sample to the user ring buffer. 133 /// 134 /// This function takes ownership of the sample and submits it to the ring 135 /// buffer. After submission, the consumer will be able to read the sample 136 /// from the ring buffer. 137 /// 138 /// This function is thread-safe. It is *not* necessary to synchronize 139 /// amongst multiple producers when invoking this function. submit(&self, mut sample: UserRingBufferSample<'_>) -> Result<()>140 pub fn submit(&self, mut sample: UserRingBufferSample<'_>) -> Result<()> { 141 unsafe { 142 libbpf_sys::user_ring_buffer__submit(self.ptr.as_ptr(), sample.ptr.as_ptr()); 143 } 144 145 sample.submitted = true; 146 147 // The libbpf API does not return an error code, so we cannot determine 148 // if the submission was successful. Return a `Result` to enable future 149 // validation while maintaining backwards compatibility. 150 Ok(()) 151 } 152 } 153 154 impl AsRawLibbpf for UserRingBuffer { 155 type LibbpfType = libbpf_sys::user_ring_buffer; 156 157 /// Retrieve the underlying [`libbpf_sys::user_ring_buffer`]. as_libbpf_object(&self) -> NonNull<Self::LibbpfType>158 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> { 159 self.ptr 160 } 161 } 162 163 impl Drop for UserRingBuffer { drop(&mut self)164 fn drop(&mut self) { 165 unsafe { 166 libbpf_sys::user_ring_buffer__free(self.ptr.as_ptr()); 167 } 168 } 169 } 170