1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use core::{borrow::Borrow, mem::MaybeUninit, ptr};
#[cfg(generic_const_exprs)]
use crate::const_assert::{Assert, IsTrue};
use crate::{
btf_maps::btf_map_def,
helpers::{bpf_ringbuf_output, bpf_ringbuf_reserve},
maps::ring_buf::{RingBufBytes, RingBufEntry},
};
btf_map_def!(
/// A BTF-compatible BPF ring buffer map.
///
/// Ring buffers have a special `value_size` field set to 0.
pub struct RingBuf<T; const MAX_ENTRIES: usize, const FLAGS: usize = 0>,
map_type: BPF_MAP_TYPE_RINGBUF,
max_entries: MAX_ENTRIES,
map_flags: FLAGS,
key_type: (),
value_type: T,
value_size: *const [i32; 0] = ::core::ptr::null(),
);
impl<T, const MAX_ENTRIES: usize, const FLAGS: usize> RingBuf<T, MAX_ENTRIES, FLAGS> {
/// Reserve a dynamically sized byte buffer in the ring buffer.
///
/// Returns `None` if the ring buffer is full.
///
/// Note that using this method requires care; the verifier does not allow truly dynamic
/// allocation sizes. In other words, it is incumbent upon users of this function to convince
/// the verifier that `size` is a compile-time constant. Good luck!
pub fn reserve_bytes(&self, size: usize, flags: u64) -> Option<RingBufBytes<'_>> {
let ptr = unsafe { bpf_ringbuf_reserve(self.as_ptr(), size as u64, flags) }.cast::<u8>();
unsafe { RingBufBytes::from_raw(ptr, size) }
}
/// Reserve memory in the ring buffer that can fit the map's `T`.
///
/// Returns `None` if the ring buffer is full.
#[cfg(generic_const_exprs)]
pub fn reserve(&self, flags: u64) -> Option<RingBufEntry<T>>
where
T: 'static,
Assert<{ 8 % align_of::<T>() == 0 }>: IsTrue,
{
self.reserve_untyped::<T>(flags)
}
/// Reserve memory in the ring buffer that can fit the map's `T`.
///
/// Returns `None` if the ring buffer is full.
///
/// The kernel will reserve memory at an 8-bytes aligned boundary, so `mem::align_of<U>()` must
/// be equal or smaller than 8. If you use this with a `U` that isn't properly aligned, this
/// function will be compiled to a panic; depending on your `panic_handler`, this may make
/// the eBPF program fail to load, or it may make it have undefined behavior.
#[cfg(not(generic_const_exprs))]
pub fn reserve(&self, flags: u64) -> Option<RingBufEntry<T>>
where
T: 'static,
{
self.reserve_untyped::<T>(flags)
}
/// Reserve memory in the ring buffer that can fit `U`.
///
/// Returns `None` if the ring buffer is full.
#[cfg(generic_const_exprs)]
pub fn reserve_untyped<U: 'static>(&self, flags: u64) -> Option<RingBufEntry<U>>
where
Assert<{ 8 % align_of::<U>() == 0 }>: IsTrue,
{
self.reserve_impl::<U>(flags)
}
/// Reserve memory in the ring buffer that can fit `U`.
///
/// Returns `None` if the ring buffer is full.
#[cfg(not(generic_const_exprs))]
pub fn reserve_untyped<U: 'static>(&self, flags: u64) -> Option<RingBufEntry<U>> {
assert_eq!(8 % align_of::<U>(), 0);
self.reserve_impl::<U>(flags)
}
fn reserve_impl<U: 'static>(&self, flags: u64) -> Option<RingBufEntry<U>> {
let ptr = unsafe { bpf_ringbuf_reserve(self.as_ptr(), size_of::<U>() as u64, flags) }
.cast::<MaybeUninit<U>>();
unsafe { RingBufEntry::from_raw(ptr) }
}
/// Copy `data` to the ring buffer output using the map's `T`.
pub fn output(&self, data: impl Borrow<T>, flags: u64) -> Result<(), i32> {
self.output_untyped::<T>(data, flags)
}
/// Copy `data` to the ring buffer output.
///
/// Consider using [`reserve`] and [`submit`] if `T` is statically sized and you want to save a
/// copy from either a map buffer or the stack.
///
/// Unlike [`reserve`], this function can handle dynamically sized types (which is hard to
/// create in eBPF but still possible, e.g. by slicing an array).
///
/// Note: `T` must be aligned to no more than 8 bytes; it's not possible to fulfill larger
/// alignment requests. If you use this with a `T` that isn't properly aligned, this function will
/// be compiled to a panic and silently make your eBPF program fail to load.
/// See [here](https://github.com/torvalds/linux/blob/3f01e9fed/kernel/bpf/ringbuf.c#L418).
///
/// [`reserve`]: RingBuf::reserve
/// [`submit`]: RingBufEntry::submit
pub fn output_untyped<U: ?Sized>(&self, data: impl Borrow<U>, flags: u64) -> Result<(), i32> {
let data = data.borrow();
assert_eq!(8 % align_of_val(data), 0);
let ret = unsafe {
bpf_ringbuf_output(
self.as_ptr(),
ptr::from_ref(data).cast_mut().cast(),
size_of_val(data) as u64,
flags,
)
};
if ret < 0 { Err(ret as i32) } else { Ok(()) }
}
}