xref: /DragonOS/kernel/src/bpf/map/queue.rs (revision fae6e9ade46a52976ad5d099643d51cc20876448)
1*fae6e9adSlinfeng use super::{BpfMapCommonOps, Result};
2*fae6e9adSlinfeng use crate::bpf::map::util::{BpfMapMeta, BpfMapUpdateElemFlags};
3*fae6e9adSlinfeng use alloc::vec::Vec;
4*fae6e9adSlinfeng use core::fmt::Debug;
5*fae6e9adSlinfeng use core::ops::Deref;
6*fae6e9adSlinfeng use core::ops::DerefMut;
7*fae6e9adSlinfeng use system_error::SystemError;
8*fae6e9adSlinfeng 
9*fae6e9adSlinfeng type BpfQueueValue = Vec<u8>;
10*fae6e9adSlinfeng /// BPF_MAP_TYPE_QUEUE provides FIFO storage and BPF_MAP_TYPE_STACK provides LIFO storage for BPF programs.
11*fae6e9adSlinfeng /// These maps support peek, pop and push operations that are exposed to BPF programs through the respective helpers.
12*fae6e9adSlinfeng /// These operations are exposed to userspace applications using the existing bpf syscall in the following way:
13*fae6e9adSlinfeng /// - `BPF_MAP_LOOKUP_ELEM` -> `peek`
14*fae6e9adSlinfeng /// - `BPF_MAP_UPDATE_ELEM` -> `push`
15*fae6e9adSlinfeng /// - `BPF_MAP_LOOKUP_AND_DELETE_ELEM ` -> `pop`
16*fae6e9adSlinfeng ///
17*fae6e9adSlinfeng /// See https://docs.kernel.org/bpf/map_queue_stack.html
18*fae6e9adSlinfeng pub trait SpecialMap: Debug + Send + Sync + 'static {
19*fae6e9adSlinfeng     /// Returns the number of elements the queue can hold.
push(&mut self, value: BpfQueueValue, flags: BpfMapUpdateElemFlags) -> Result<()>20*fae6e9adSlinfeng     fn push(&mut self, value: BpfQueueValue, flags: BpfMapUpdateElemFlags) -> Result<()>;
21*fae6e9adSlinfeng     /// Removes the first element and returns it.
pop(&mut self) -> Option<BpfQueueValue>22*fae6e9adSlinfeng     fn pop(&mut self) -> Option<BpfQueueValue>;
23*fae6e9adSlinfeng     /// Returns the first element without removing it.
peek(&self) -> Option<&BpfQueueValue>24*fae6e9adSlinfeng     fn peek(&self) -> Option<&BpfQueueValue>;
25*fae6e9adSlinfeng }
26*fae6e9adSlinfeng 
27*fae6e9adSlinfeng /// The queue map type is a generic map type, resembling a FIFO (First-In First-Out) queue.
28*fae6e9adSlinfeng ///
29*fae6e9adSlinfeng /// This map type has no keys, only values. The size and type of the values can be specified by the user
30*fae6e9adSlinfeng /// to fit a large variety of use cases. The typical use-case for this map type is to keep track of
31*fae6e9adSlinfeng /// a pool of elements such as available network ports when implementing NAT (network address translation).
32*fae6e9adSlinfeng ///
33*fae6e9adSlinfeng /// As apposed to most map types, this map type uses a custom set of helpers to pop, peek and push elements.
34*fae6e9adSlinfeng ///
35*fae6e9adSlinfeng /// See https://ebpf-docs.dylanreimerink.nl/linux/map-type/BPF_MAP_TYPE_QUEUE/
36*fae6e9adSlinfeng #[derive(Debug)]
37*fae6e9adSlinfeng pub struct QueueMap {
38*fae6e9adSlinfeng     max_entries: u32,
39*fae6e9adSlinfeng     data: Vec<BpfQueueValue>,
40*fae6e9adSlinfeng }
41*fae6e9adSlinfeng 
42*fae6e9adSlinfeng impl QueueMap {
new(attr: &BpfMapMeta) -> Result<Self>43*fae6e9adSlinfeng     pub fn new(attr: &BpfMapMeta) -> Result<Self> {
44*fae6e9adSlinfeng         if attr.value_size == 0 || attr.max_entries == 0 || attr.key_size != 0 {
45*fae6e9adSlinfeng             return Err(SystemError::EINVAL);
46*fae6e9adSlinfeng         }
47*fae6e9adSlinfeng         let data = Vec::with_capacity(attr.max_entries as usize);
48*fae6e9adSlinfeng         Ok(Self {
49*fae6e9adSlinfeng             max_entries: attr.max_entries,
50*fae6e9adSlinfeng             data,
51*fae6e9adSlinfeng         })
52*fae6e9adSlinfeng     }
53*fae6e9adSlinfeng }
54*fae6e9adSlinfeng 
55*fae6e9adSlinfeng impl SpecialMap for QueueMap {
push(&mut self, value: BpfQueueValue, flags: BpfMapUpdateElemFlags) -> Result<()>56*fae6e9adSlinfeng     fn push(&mut self, value: BpfQueueValue, flags: BpfMapUpdateElemFlags) -> Result<()> {
57*fae6e9adSlinfeng         if self.data.len() == self.max_entries as usize {
58*fae6e9adSlinfeng             if flags.contains(BpfMapUpdateElemFlags::BPF_EXIST) {
59*fae6e9adSlinfeng                 // remove the first element
60*fae6e9adSlinfeng                 self.data.remove(0);
61*fae6e9adSlinfeng             } else {
62*fae6e9adSlinfeng                 return Err(SystemError::ENOSPC);
63*fae6e9adSlinfeng             }
64*fae6e9adSlinfeng         }
65*fae6e9adSlinfeng         self.data.push(value);
66*fae6e9adSlinfeng         Ok(())
67*fae6e9adSlinfeng     }
pop(&mut self) -> Option<BpfQueueValue>68*fae6e9adSlinfeng     fn pop(&mut self) -> Option<BpfQueueValue> {
69*fae6e9adSlinfeng         if self.data.is_empty() {
70*fae6e9adSlinfeng             return None;
71*fae6e9adSlinfeng         }
72*fae6e9adSlinfeng         Some(self.data.remove(0))
73*fae6e9adSlinfeng     }
peek(&self) -> Option<&BpfQueueValue>74*fae6e9adSlinfeng     fn peek(&self) -> Option<&BpfQueueValue> {
75*fae6e9adSlinfeng         self.data.first()
76*fae6e9adSlinfeng     }
77*fae6e9adSlinfeng }
78*fae6e9adSlinfeng /// The stack map type is a generic map type, resembling a stack data structure.
79*fae6e9adSlinfeng ///
80*fae6e9adSlinfeng /// See https://ebpf-docs.dylanreimerink.nl/linux/map-type/BPF_MAP_TYPE_STACK/
81*fae6e9adSlinfeng #[derive(Debug)]
82*fae6e9adSlinfeng pub struct StackMap(QueueMap);
83*fae6e9adSlinfeng 
84*fae6e9adSlinfeng impl StackMap {
new(attr: &BpfMapMeta) -> Result<Self>85*fae6e9adSlinfeng     pub fn new(attr: &BpfMapMeta) -> Result<Self> {
86*fae6e9adSlinfeng         QueueMap::new(attr).map(StackMap)
87*fae6e9adSlinfeng     }
88*fae6e9adSlinfeng }
89*fae6e9adSlinfeng 
90*fae6e9adSlinfeng impl Deref for StackMap {
91*fae6e9adSlinfeng     type Target = QueueMap;
deref(&self) -> &Self::Target92*fae6e9adSlinfeng     fn deref(&self) -> &Self::Target {
93*fae6e9adSlinfeng         &self.0
94*fae6e9adSlinfeng     }
95*fae6e9adSlinfeng }
96*fae6e9adSlinfeng 
97*fae6e9adSlinfeng impl DerefMut for StackMap {
deref_mut(&mut self) -> &mut Self::Target98*fae6e9adSlinfeng     fn deref_mut(&mut self) -> &mut Self::Target {
99*fae6e9adSlinfeng         &mut self.0
100*fae6e9adSlinfeng     }
101*fae6e9adSlinfeng }
102*fae6e9adSlinfeng 
103*fae6e9adSlinfeng impl SpecialMap for StackMap {
push(&mut self, value: BpfQueueValue, flags: BpfMapUpdateElemFlags) -> Result<()>104*fae6e9adSlinfeng     fn push(&mut self, value: BpfQueueValue, flags: BpfMapUpdateElemFlags) -> Result<()> {
105*fae6e9adSlinfeng         if self.data.len() == self.max_entries as usize {
106*fae6e9adSlinfeng             if flags.contains(BpfMapUpdateElemFlags::BPF_EXIST) {
107*fae6e9adSlinfeng                 // remove the last element
108*fae6e9adSlinfeng                 self.data.pop();
109*fae6e9adSlinfeng             } else {
110*fae6e9adSlinfeng                 return Err(SystemError::ENOSPC);
111*fae6e9adSlinfeng             }
112*fae6e9adSlinfeng         }
113*fae6e9adSlinfeng         self.data.push(value);
114*fae6e9adSlinfeng         Ok(())
115*fae6e9adSlinfeng     }
pop(&mut self) -> Option<BpfQueueValue>116*fae6e9adSlinfeng     fn pop(&mut self) -> Option<BpfQueueValue> {
117*fae6e9adSlinfeng         self.data.pop()
118*fae6e9adSlinfeng     }
peek(&self) -> Option<&BpfQueueValue>119*fae6e9adSlinfeng     fn peek(&self) -> Option<&BpfQueueValue> {
120*fae6e9adSlinfeng         self.data.last()
121*fae6e9adSlinfeng     }
122*fae6e9adSlinfeng }
123*fae6e9adSlinfeng 
124*fae6e9adSlinfeng impl<T: SpecialMap> BpfMapCommonOps for T {
125*fae6e9adSlinfeng     /// Equal to [QueueMap::peek]
lookup_elem(&mut self, _key: &[u8]) -> Result<Option<&[u8]>>126*fae6e9adSlinfeng     fn lookup_elem(&mut self, _key: &[u8]) -> Result<Option<&[u8]>> {
127*fae6e9adSlinfeng         Ok(self.peek().map(|v| v.as_slice()))
128*fae6e9adSlinfeng     }
129*fae6e9adSlinfeng     /// Equal to [QueueMap::push]
update_elem(&mut self, _key: &[u8], value: &[u8], flags: u64) -> Result<()>130*fae6e9adSlinfeng     fn update_elem(&mut self, _key: &[u8], value: &[u8], flags: u64) -> Result<()> {
131*fae6e9adSlinfeng         let flag = BpfMapUpdateElemFlags::from_bits_truncate(flags);
132*fae6e9adSlinfeng         self.push(value.to_vec(), flag)
133*fae6e9adSlinfeng     }
134*fae6e9adSlinfeng     /// Equal to [QueueMap::pop]
lookup_and_delete_elem(&mut self, _key: &[u8], value: &mut [u8]) -> Result<()>135*fae6e9adSlinfeng     fn lookup_and_delete_elem(&mut self, _key: &[u8], value: &mut [u8]) -> Result<()> {
136*fae6e9adSlinfeng         if let Some(v) = self.pop() {
137*fae6e9adSlinfeng             value.copy_from_slice(&v);
138*fae6e9adSlinfeng             Ok(())
139*fae6e9adSlinfeng         } else {
140*fae6e9adSlinfeng             Err(SystemError::ENOENT)
141*fae6e9adSlinfeng         }
142*fae6e9adSlinfeng     }
push_elem(&mut self, value: &[u8], flags: u64) -> Result<()>143*fae6e9adSlinfeng     fn push_elem(&mut self, value: &[u8], flags: u64) -> Result<()> {
144*fae6e9adSlinfeng         self.update_elem(&[], value, flags)
145*fae6e9adSlinfeng     }
pop_elem(&mut self, value: &mut [u8]) -> Result<()>146*fae6e9adSlinfeng     fn pop_elem(&mut self, value: &mut [u8]) -> Result<()> {
147*fae6e9adSlinfeng         self.lookup_and_delete_elem(&[], value)
148*fae6e9adSlinfeng     }
peek_elem(&self, value: &mut [u8]) -> Result<()>149*fae6e9adSlinfeng     fn peek_elem(&self, value: &mut [u8]) -> Result<()> {
150*fae6e9adSlinfeng         self.peek()
151*fae6e9adSlinfeng             .map(|v| value.copy_from_slice(v))
152*fae6e9adSlinfeng             .ok_or(SystemError::ENOENT)
153*fae6e9adSlinfeng     }
154*fae6e9adSlinfeng }
155