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