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 { new(attr: &BpfMapMeta) -> Result<Self>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 { lookup_elem(&mut self, key: &[u8]) -> Result<Option<&[u8]>>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 } update_elem(&mut self, key: &[u8], value: &[u8], flags: u64) -> Result<()>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 } delete_elem(&mut self, key: &[u8]) -> Result<()>50 fn delete_elem(&mut self, key: &[u8]) -> Result<()> { 51 self.data.remove(key); 52 Ok(()) 53 } for_each_elem(&mut self, cb: BpfCallBackFn, ctx: *const u8, flags: u64) -> Result<u32>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 } lookup_and_delete_elem(&mut self, key: &[u8], value: &mut [u8]) -> Result<()>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 } get_next_key(&self, key: Option<&[u8]>, next_key: &mut [u8]) -> Result<()>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 { fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result107 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 { new(attr: &BpfMapMeta) -> Result<Self>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 { lookup_elem(&mut self, key: &[u8]) -> Result<Option<&[u8]>>126 fn lookup_elem(&mut self, key: &[u8]) -> Result<Option<&[u8]>> { 127 self.per_cpu_maps.get_mut().lookup_elem(key) 128 } update_elem(&mut self, key: &[u8], value: &[u8], flags: u64) -> Result<()>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 } delete_elem(&mut self, key: &[u8]) -> Result<()>132 fn delete_elem(&mut self, key: &[u8]) -> Result<()> { 133 self.per_cpu_maps.get_mut().delete_elem(key) 134 } for_each_elem(&mut self, cb: BpfCallBackFn, ctx: *const u8, flags: u64) -> Result<u32>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 } lookup_and_delete_elem(&mut self, key: &[u8], value: &mut [u8]) -> Result<()>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 } lookup_percpu_elem(&mut self, key: &[u8], cpu: u32) -> Result<Option<&[u8]>>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 } get_next_key(&self, key: Option<&[u8]>, next_key: &mut [u8]) -> Result<()>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 } first_value_ptr(&self) -> Result<*const u8>153 fn first_value_ptr(&self) -> Result<*const u8> { 154 self.per_cpu_maps.get_mut().first_value_ptr() 155 } 156 } 157