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() as *mut u8, self.size) }
53    }
54}
55
56impl Drop for UserRingBufferSample<'_> {
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)]
71pub struct UserRingBuffer {
72    // A non-null pointer to the underlying user ring buffer.
73    ptr: NonNull<libbpf_sys::user_ring_buffer>,
74}
75
76impl 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.
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.
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.
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
154impl AsRawLibbpf for UserRingBuffer {
155    type LibbpfType = libbpf_sys::user_ring_buffer;
156
157    /// Retrieve the underlying [`libbpf_sys::user_ring_buffer`].
158    fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
159        self.ptr
160    }
161}
162
163impl Drop for UserRingBuffer {
164    fn drop(&mut self) {
165        unsafe {
166            libbpf_sys::user_ring_buffer__free(self.ptr.as_ptr());
167        }
168    }
169}