xref: /DragonOS/kernel/src/bpf/map/array_map.rs (revision 7b0ef10895108a0de5ff5ef3d2f93f40cf2e33a5)
1 //! BPF_MAP_TYPE_ARRAY and BPF_MAP_TYPE_PERCPU_ARRAY
2 //!
3 //!
4 //! See https://docs.kernel.org/bpf/map_array.html
5 
6 use super::super::Result;
7 use crate::bpf::map::util::round_up;
8 use crate::bpf::map::{BpfCallBackFn, BpfMapCommonOps, BpfMapMeta};
9 use crate::mm::percpu::{PerCpu, PerCpuVar};
10 use crate::smp::cpu::{smp_cpu_manager, ProcessorId};
11 use alloc::{vec, vec::Vec};
12 use core::{
13     fmt::{Debug, Formatter},
14     ops::{Index, IndexMut},
15 };
16 use log::info;
17 use system_error::SystemError;
18 
19 /// The array map type is a generic map type with no restrictions on the structure of the value.
20 /// Like a normal array, the array map has a numeric key starting at 0 and incrementing.
21 ///
22 /// See https://ebpf-docs.dylanreimerink.nl/linux/map-type/BPF_MAP_TYPE_ARRAY/
23 #[derive(Debug)]
24 pub struct ArrayMap {
25     max_entries: u32,
26     data: ArrayMapData,
27 }
28 
29 struct ArrayMapData {
30     elem_size: u32,
31     /// The data is stored in a Vec<u8> with the size of elem_size * max_entries.
32     data: Vec<u8>,
33 }
34 
35 impl Debug for ArrayMapData {
36     fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
37         f.debug_struct("ArrayMapData")
38             .field("elem_size", &self.elem_size)
39             .field("data_len", &self.data.len())
40             .finish()
41     }
42 }
43 
44 impl ArrayMapData {
45     pub fn new(elem_size: u32, max_entries: u32) -> Self {
46         debug_assert!(elem_size % 8 == 0);
47         let total_size = elem_size * max_entries;
48         let data = vec![0; total_size as usize];
49         ArrayMapData { elem_size, data }
50     }
51 }
52 
53 impl Index<u32> for ArrayMapData {
54     type Output = [u8];
55     fn index(&self, index: u32) -> &Self::Output {
56         let start = index * self.elem_size;
57         &self.data[start as usize..(start + self.elem_size) as usize]
58     }
59 }
60 
61 impl IndexMut<u32> for ArrayMapData {
62     fn index_mut(&mut self, index: u32) -> &mut Self::Output {
63         let start = index * self.elem_size;
64         &mut self.data[start as usize..(start + self.elem_size) as usize]
65     }
66 }
67 
68 impl ArrayMap {
69     pub fn new(attr: &BpfMapMeta) -> Result<Self> {
70         if attr.value_size == 0 || attr.max_entries == 0 || attr.key_size != 4 {
71             return Err(SystemError::EINVAL);
72         }
73         let elem_size = round_up(attr.value_size as usize, 8);
74         let data = ArrayMapData::new(elem_size as u32, attr.max_entries);
75         Ok(ArrayMap {
76             max_entries: attr.max_entries,
77             data,
78         })
79     }
80 }
81 
82 impl BpfMapCommonOps for ArrayMap {
83     fn lookup_elem(&mut self, key: &[u8]) -> Result<Option<&[u8]>> {
84         if key.len() != 4 {
85             return Err(SystemError::EINVAL);
86         }
87         let index = u32::from_ne_bytes(key.try_into().map_err(|_| SystemError::EINVAL)?);
88         if index >= self.max_entries {
89             return Err(SystemError::EINVAL);
90         }
91         let val = self.data.index(index);
92         Ok(Some(val))
93     }
94     fn update_elem(&mut self, key: &[u8], value: &[u8], _flags: u64) -> Result<()> {
95         if key.len() != 4 {
96             return Err(SystemError::EINVAL);
97         }
98         let index = u32::from_ne_bytes(key.try_into().map_err(|_| SystemError::EINVAL)?);
99         if index >= self.max_entries {
100             return Err(SystemError::EINVAL);
101         }
102         if value.len() > self.data.elem_size as usize {
103             return Err(SystemError::EINVAL);
104         }
105         let old_value = self.data.index_mut(index);
106         old_value[..value.len()].copy_from_slice(value);
107         Ok(())
108     }
109     /// For ArrayMap, delete_elem is not supported.
110     fn delete_elem(&mut self, _key: &[u8]) -> Result<()> {
111         Err(SystemError::EINVAL)
112     }
113     fn for_each_elem(&mut self, cb: BpfCallBackFn, ctx: *const u8, flags: u64) -> Result<u32> {
114         if flags != 0 {
115             return Err(SystemError::EINVAL);
116         }
117         let mut total_used = 0;
118         for i in 0..self.max_entries {
119             let key = i.to_ne_bytes();
120             let value = self.data.index(i);
121             total_used += 1;
122             let res = cb(&key, value, ctx);
123             // return value: 0 - continue, 1 - stop and return
124             if res != 0 {
125                 break;
126             }
127         }
128         Ok(total_used)
129     }
130 
131     fn lookup_and_delete_elem(&mut self, _key: &[u8], _value: &mut [u8]) -> Result<()> {
132         Err(SystemError::EINVAL)
133     }
134 
135     fn get_next_key(&self, key: Option<&[u8]>, next_key: &mut [u8]) -> Result<()> {
136         if let Some(key) = key {
137             if key.len() != 4 {
138                 return Err(SystemError::EINVAL);
139             }
140             let index = u32::from_ne_bytes(key.try_into().map_err(|_| SystemError::EINVAL)?);
141             if index == self.max_entries - 1 {
142                 return Err(SystemError::ENOENT);
143             }
144             let next_index = index + 1;
145             next_key.copy_from_slice(&next_index.to_ne_bytes());
146         } else {
147             next_key.copy_from_slice(&0u32.to_ne_bytes());
148         }
149         Ok(())
150     }
151 
152     fn freeze(&self) -> Result<()> {
153         info!("fake freeze done for ArrayMap");
154         Ok(())
155     }
156     fn first_value_ptr(&self) -> Result<*const u8> {
157         Ok(self.data.data.as_ptr())
158     }
159 }
160 
161 /// This is the per-CPU variant of the [ArrayMap] map type.
162 ///
163 /// See https://ebpf-docs.dylanreimerink.nl/linux/map-type/BPF_MAP_TYPE_PERCPU_ARRAY/
164 pub struct PerCpuArrayMap {
165     per_cpu_data: PerCpuVar<ArrayMap>,
166 }
167 
168 impl Debug for PerCpuArrayMap {
169     fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
170         f.debug_struct("PerCpuArrayMap")
171             .field("data", &self.per_cpu_data)
172             .finish()
173     }
174 }
175 
176 impl PerCpuArrayMap {
177     pub fn new(attr: &BpfMapMeta) -> Result<Self> {
178         let num_cpus = PerCpu::MAX_CPU_NUM;
179         let mut data = Vec::with_capacity(num_cpus as usize);
180         for _ in 0..num_cpus {
181             let array_map = ArrayMap::new(attr)?;
182             data.push(array_map);
183         }
184         let per_cpu_data = PerCpuVar::new(data).ok_or(SystemError::EINVAL)?;
185         Ok(PerCpuArrayMap { per_cpu_data })
186     }
187 }
188 
189 impl BpfMapCommonOps for PerCpuArrayMap {
190     fn lookup_elem(&mut self, key: &[u8]) -> Result<Option<&[u8]>> {
191         self.per_cpu_data.get_mut().lookup_elem(key)
192     }
193     fn update_elem(&mut self, key: &[u8], value: &[u8], flags: u64) -> Result<()> {
194         self.per_cpu_data.get_mut().update_elem(key, value, flags)
195     }
196     fn delete_elem(&mut self, key: &[u8]) -> Result<()> {
197         self.per_cpu_data.get_mut().delete_elem(key)
198     }
199     fn for_each_elem(&mut self, cb: BpfCallBackFn, ctx: *const u8, flags: u64) -> Result<u32> {
200         self.per_cpu_data.get_mut().for_each_elem(cb, ctx, flags)
201     }
202     fn lookup_and_delete_elem(&mut self, _key: &[u8], _value: &mut [u8]) -> Result<()> {
203         Err(SystemError::EINVAL)
204     }
205     fn lookup_percpu_elem(&mut self, key: &[u8], cpu: u32) -> Result<Option<&[u8]>> {
206         unsafe {
207             self.per_cpu_data
208                 .force_get_mut(ProcessorId::new(cpu))
209                 .lookup_elem(key)
210         }
211     }
212     fn get_next_key(&self, key: Option<&[u8]>, next_key: &mut [u8]) -> Result<()> {
213         self.per_cpu_data.get_mut().get_next_key(key, next_key)
214     }
215     fn first_value_ptr(&self) -> Result<*const u8> {
216         self.per_cpu_data.get_mut().first_value_ptr()
217     }
218 }
219 
220 /// See https://ebpf-docs.dylanreimerink.nl/linux/map-type/BPF_MAP_TYPE_PERF_EVENT_ARRAY/
221 pub struct PerfEventArrayMap {
222     // The value is the file descriptor of the perf event.
223     fds: ArrayMapData,
224 }
225 
226 impl Debug for PerfEventArrayMap {
227     fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
228         f.debug_struct("PerfEventArrayMap")
229             .field("fds", &self.fds)
230             .finish()
231     }
232 }
233 
234 impl PerfEventArrayMap {
235     pub fn new(attr: &BpfMapMeta) -> Result<Self> {
236         let num_cpus = smp_cpu_manager().possible_cpus_count();
237         if attr.key_size != 4 || attr.value_size != 4 || attr.max_entries != num_cpus {
238             return Err(SystemError::EINVAL);
239         }
240         let fds = ArrayMapData::new(4, num_cpus);
241         Ok(PerfEventArrayMap { fds })
242     }
243 }
244 
245 impl BpfMapCommonOps for PerfEventArrayMap {
246     fn lookup_elem(&mut self, key: &[u8]) -> Result<Option<&[u8]>> {
247         let cpu_id = u32::from_ne_bytes(key.try_into().map_err(|_| SystemError::EINVAL)?);
248         let value = self.fds.index(cpu_id);
249         Ok(Some(value))
250     }
251     fn update_elem(&mut self, key: &[u8], value: &[u8], _flags: u64) -> Result<()> {
252         assert_eq!(value.len(), 4);
253         let cpu_id = u32::from_ne_bytes(key.try_into().map_err(|_| SystemError::EINVAL)?);
254         let old_value = self.fds.index_mut(cpu_id);
255         old_value.copy_from_slice(value);
256         Ok(())
257     }
258     fn delete_elem(&mut self, key: &[u8]) -> Result<()> {
259         let cpu_id = u32::from_ne_bytes(key.try_into().map_err(|_| SystemError::EINVAL)?);
260         self.fds.index_mut(cpu_id).copy_from_slice(&[0; 4]);
261         Ok(())
262     }
263     fn for_each_elem(&mut self, cb: BpfCallBackFn, ctx: *const u8, _flags: u64) -> Result<u32> {
264         let mut total_used = 0;
265         let num_cpus = smp_cpu_manager().possible_cpus_count();
266         for i in 0..num_cpus {
267             let key = i.to_ne_bytes();
268             let value = self.fds.index(i);
269             total_used += 1;
270             let res = cb(&key, value, ctx);
271             if res != 0 {
272                 break;
273             }
274         }
275         Ok(total_used)
276     }
277     fn lookup_and_delete_elem(&mut self, _key: &[u8], _value: &mut [u8]) -> Result<()> {
278         Err(SystemError::EINVAL)
279     }
280     fn first_value_ptr(&self) -> Result<*const u8> {
281         Ok(self.fds.data.as_ptr())
282     }
283 }
284