Skip to main content

libbpf_rs/
user_ringbuf.rs

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