xref: /DragonOS/kernel/src/bpf/map/hash_map.rs (revision 55e6f0b65f91b32638fd56581f711a816eccdcd1)
1 use super::Result;
2 use crate::bpf::map::util::{round_up, BpfMapUpdateElemFlags};
3 use crate::bpf::map::{BpfCallBackFn, BpfMapCommonOps, BpfMapMeta};
4 use crate::mm::percpu::{PerCpu, PerCpuVar};
5 use crate::smp::cpu::ProcessorId;
6 use alloc::{collections::BTreeMap, vec::Vec};
7 use core::fmt::Debug;
8 use system_error::SystemError;
9 
10 type BpfHashMapKey = Vec<u8>;
11 type BpfHashMapValue = Vec<u8>;
12 
13 /// The hash map type is a generic map type with no restrictions on the structure of the key and value.
14 /// Hash-maps are implemented using a hash table, allowing for lookups with arbitrary keys.
15 ///
16 /// See https://ebpf-docs.dylanreimerink.nl/linux/map-type/BPF_MAP_TYPE_HASH/
17 #[derive(Debug)]
18 pub struct BpfHashMap {
19     _max_entries: u32,
20     _key_size: u32,
21     _value_size: u32,
22     data: BTreeMap<BpfHashMapKey, BpfHashMapValue>,
23 }
24 
25 impl BpfHashMap {
26     pub fn new(attr: &BpfMapMeta) -> Result<Self> {
27         if attr.value_size == 0 || attr.max_entries == 0 {
28             return Err(SystemError::EINVAL);
29         }
30         let value_size = round_up(attr.value_size as usize, 8);
31         Ok(Self {
32             _max_entries: attr.max_entries,
33             _key_size: attr.key_size,
34             _value_size: value_size as u32,
35             data: BTreeMap::new(),
36         })
37     }
38 }
39 
40 impl BpfMapCommonOps for BpfHashMap {
41     fn lookup_elem(&mut self, key: &[u8]) -> Result<Option<&[u8]>> {
42         let value = self.data.get(key).map(|v| v.as_slice());
43         Ok(value)
44     }
45     fn update_elem(&mut self, key: &[u8], value: &[u8], flags: u64) -> Result<()> {
46         let _flags = BpfMapUpdateElemFlags::from_bits_truncate(flags);
47         self.data.insert(key.to_vec(), value.to_vec());
48         Ok(())
49     }
50     fn delete_elem(&mut self, key: &[u8]) -> Result<()> {
51         self.data.remove(key);
52         Ok(())
53     }
54     fn for_each_elem(&mut self, cb: BpfCallBackFn, ctx: *const u8, flags: u64) -> Result<u32> {
55         if flags != 0 {
56             return Err(SystemError::EINVAL);
57         }
58         let mut total_used = 0;
59         for (key, value) in self.data.iter() {
60             let res = cb(key, value, ctx);
61             // return value: 0 - continue, 1 - stop and return
62             if res != 0 {
63                 break;
64             }
65             total_used += 1;
66         }
67         Ok(total_used)
68     }
69     fn lookup_and_delete_elem(&mut self, key: &[u8], value: &mut [u8]) -> Result<()> {
70         let v = self
71             .data
72             .get(key)
73             .map(|v| v.as_slice())
74             .ok_or(SystemError::ENOENT)?;
75         value.copy_from_slice(v);
76         self.data.remove(key);
77         Ok(())
78     }
79     fn get_next_key(&self, key: Option<&[u8]>, next_key: &mut [u8]) -> Result<()> {
80         let mut iter = self.data.iter();
81         if let Some(key) = key {
82             for (k, _) in iter.by_ref() {
83                 if k.as_slice() == key {
84                     break;
85                 }
86             }
87         }
88         let res = iter.next();
89         match res {
90             Some((k, _)) => {
91                 next_key.copy_from_slice(k.as_slice());
92                 Ok(())
93             }
94             None => Err(SystemError::ENOENT),
95         }
96     }
97 }
98 
99 /// This is the per-CPU variant of the [BpfHashMap] map type.
100 ///
101 /// See https://ebpf-docs.dylanreimerink.nl/linux/map-type/BPF_MAP_TYPE_PERCPU_HASH/
102 pub struct PerCpuHashMap {
103     per_cpu_maps: PerCpuVar<BpfHashMap>,
104 }
105 
106 impl Debug for PerCpuHashMap {
107     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
108         f.debug_struct("PerCpuHashMap")
109             .field("maps", &self.per_cpu_maps)
110             .finish()
111     }
112 }
113 impl PerCpuHashMap {
114     pub fn new(attr: &BpfMapMeta) -> Result<Self> {
115         let num_cpus = PerCpu::MAX_CPU_NUM;
116         let mut data = Vec::with_capacity(num_cpus as usize);
117         for _ in 0..num_cpus {
118             let array_map = BpfHashMap::new(attr)?;
119             data.push(array_map);
120         }
121         let per_cpu_maps = PerCpuVar::new(data).ok_or(SystemError::EINVAL)?;
122         Ok(PerCpuHashMap { per_cpu_maps })
123     }
124 }
125 impl BpfMapCommonOps for PerCpuHashMap {
126     fn lookup_elem(&mut self, key: &[u8]) -> Result<Option<&[u8]>> {
127         self.per_cpu_maps.get_mut().lookup_elem(key)
128     }
129     fn update_elem(&mut self, key: &[u8], value: &[u8], flags: u64) -> Result<()> {
130         self.per_cpu_maps.get_mut().update_elem(key, value, flags)
131     }
132     fn delete_elem(&mut self, key: &[u8]) -> Result<()> {
133         self.per_cpu_maps.get_mut().delete_elem(key)
134     }
135     fn for_each_elem(&mut self, cb: BpfCallBackFn, ctx: *const u8, flags: u64) -> Result<u32> {
136         self.per_cpu_maps.get_mut().for_each_elem(cb, ctx, flags)
137     }
138     fn lookup_and_delete_elem(&mut self, key: &[u8], value: &mut [u8]) -> Result<()> {
139         self.per_cpu_maps
140             .get_mut()
141             .lookup_and_delete_elem(key, value)
142     }
143     fn lookup_percpu_elem(&mut self, key: &[u8], cpu: u32) -> Result<Option<&[u8]>> {
144         unsafe {
145             self.per_cpu_maps
146                 .force_get_mut(ProcessorId::new(cpu))
147                 .lookup_elem(key)
148         }
149     }
150     fn get_next_key(&self, key: Option<&[u8]>, next_key: &mut [u8]) -> Result<()> {
151         self.per_cpu_maps.get_mut().get_next_key(key, next_key)
152     }
153     fn first_value_ptr(&self) -> Result<*const u8> {
154         self.per_cpu_maps.get_mut().first_value_ptr()
155     }
156 }
157