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}