1#![allow(non_upper_case_globals)]
6
7mod array;
8mod buffer;
9mod hashmap;
10mod lock;
11mod lpm_trie;
12mod ring_buffer;
13mod vmar;
14
15pub use ring_buffer::RINGBUF_SIGNAL;
16pub(crate) use ring_buffer::{RingBuffer, RingBufferWakeupPolicy};
17
18use ebpf::{BpfValue, EbpfBufferPtr, MapFlags, MapReference, MapSchema};
19use fidl_fuchsia_ebpf as febpf;
20use inspect_stubs::track_stub;
21use linux_uapi::{
22 BPF_EXIST, BPF_NOEXIST, bpf_map_type, bpf_map_type_BPF_MAP_TYPE_ARENA,
23 bpf_map_type_BPF_MAP_TYPE_ARRAY, bpf_map_type_BPF_MAP_TYPE_ARRAY_OF_MAPS,
24 bpf_map_type_BPF_MAP_TYPE_BLOOM_FILTER, bpf_map_type_BPF_MAP_TYPE_CGROUP_ARRAY,
25 bpf_map_type_BPF_MAP_TYPE_CGROUP_STORAGE, bpf_map_type_BPF_MAP_TYPE_CGRP_STORAGE,
26 bpf_map_type_BPF_MAP_TYPE_CPUMAP, bpf_map_type_BPF_MAP_TYPE_DEVMAP,
27 bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH, bpf_map_type_BPF_MAP_TYPE_HASH,
28 bpf_map_type_BPF_MAP_TYPE_HASH_OF_MAPS, bpf_map_type_BPF_MAP_TYPE_INODE_STORAGE,
29 bpf_map_type_BPF_MAP_TYPE_LPM_TRIE, bpf_map_type_BPF_MAP_TYPE_LRU_HASH,
30 bpf_map_type_BPF_MAP_TYPE_LRU_PERCPU_HASH, bpf_map_type_BPF_MAP_TYPE_PERCPU_ARRAY,
31 bpf_map_type_BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, bpf_map_type_BPF_MAP_TYPE_PERCPU_HASH,
32 bpf_map_type_BPF_MAP_TYPE_PERF_EVENT_ARRAY, bpf_map_type_BPF_MAP_TYPE_PROG_ARRAY,
33 bpf_map_type_BPF_MAP_TYPE_QUEUE, bpf_map_type_BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
34 bpf_map_type_BPF_MAP_TYPE_RINGBUF, bpf_map_type_BPF_MAP_TYPE_SK_STORAGE,
35 bpf_map_type_BPF_MAP_TYPE_SOCKHASH, bpf_map_type_BPF_MAP_TYPE_SOCKMAP,
36 bpf_map_type_BPF_MAP_TYPE_STACK, bpf_map_type_BPF_MAP_TYPE_STACK_TRACE,
37 bpf_map_type_BPF_MAP_TYPE_STRUCT_OPS, bpf_map_type_BPF_MAP_TYPE_TASK_STORAGE,
38 bpf_map_type_BPF_MAP_TYPE_UNSPEC, bpf_map_type_BPF_MAP_TYPE_USER_RINGBUF,
39 bpf_map_type_BPF_MAP_TYPE_XSKMAP,
40};
41use std::fmt::Debug;
42use std::ops::Deref;
43use std::pin::Pin;
44use std::sync::Arc;
45use zx::HandleBased;
46
47use crate::maps::buffer::VmoOrName;
48
49#[derive(Debug, Eq, PartialEq)]
50pub enum MapError {
51 InvalidParam,
53
54 InvalidKey,
56
57 EntryExists,
59
60 SizeLimit,
62
63 NoMemory,
65
66 InvalidVmo,
68
69 MapTypeNotSupported,
71
72 NotSupported,
74
75 Internal,
77}
78const SUPPORTED_FLAGS: MapFlags = MapFlags::NoPrealloc
79 .union(MapFlags::SyscallReadOnly)
80 .union(MapFlags::SyscallWriteOnly)
81 .union(MapFlags::Mmapable);
82
83fn map_flags_from_fidl(flags: febpf::MapFlags) -> MapFlags {
84 let mut r = MapFlags::empty();
85 if flags.contains(febpf::MapFlags::NO_PREALLOC) {
86 r = r | MapFlags::NoPrealloc;
87 }
88 if flags.contains(febpf::MapFlags::SYSCALL_READ_ONLY) {
89 r = r | MapFlags::SyscallReadOnly;
90 }
91 if flags.contains(febpf::MapFlags::SYSCALL_WRITE_ONLY) {
92 r = r | MapFlags::SyscallWriteOnly;
93 }
94 if flags.contains(febpf::MapFlags::MMAPABLE) {
95 r = r | MapFlags::Mmapable;
96 }
97 r
98}
99
100fn map_flags_to_fidl(flags: MapFlags) -> Result<febpf::MapFlags, MapError> {
101 if flags.contains(!SUPPORTED_FLAGS) {
102 return Err(MapError::NotSupported);
103 }
104
105 let mut r = febpf::MapFlags::empty();
106 if flags.contains(MapFlags::NoPrealloc) {
107 r = r | febpf::MapFlags::NO_PREALLOC;
108 }
109 if flags.contains(MapFlags::SyscallReadOnly) {
110 r = r | febpf::MapFlags::SYSCALL_READ_ONLY;
111 }
112 if flags.contains(MapFlags::SyscallWriteOnly) {
113 r = r | febpf::MapFlags::SYSCALL_WRITE_ONLY;
114 }
115 if flags.contains(MapFlags::Mmapable) {
116 r = r | febpf::MapFlags::MMAPABLE;
117 }
118 Ok(r)
119}
120
121fn validate_map_flags(schema: &MapSchema) -> Result<(), MapError> {
122 let flags = schema.flags;
123 if flags.contains(!SUPPORTED_FLAGS) {
124 return Err(MapError::InvalidParam);
125 }
126
127 if flags.contains(MapFlags::SyscallReadOnly) && flags.contains(MapFlags::SyscallWriteOnly) {
129 return Err(MapError::InvalidParam);
130 }
131
132 if flags.contains(MapFlags::Mmapable) && schema.map_type != bpf_map_type_BPF_MAP_TYPE_ARRAY {
134 return Err(MapError::InvalidParam);
135 }
136
137 Ok(())
138}
139
140trait MapImpl: Send + Sync + Debug {
141 fn lookup<'a>(&'a self, key: &[u8]) -> Option<MapValueRef<'a>>;
142 fn update(&self, key: &[u8], value: EbpfBufferPtr<'_>, flags: u64) -> Result<(), MapError>;
143 fn delete(&self, key: &[u8]) -> Result<(), MapError>;
144 fn get_next_key(&self, key: Option<&[u8]>) -> Result<MapKey, MapError>;
145 fn vmo(&self) -> &Arc<zx::Vmo>;
146
147 fn can_read(&self) -> Option<bool> {
150 None
151 }
152
153 fn ringbuf_reserve(&self, _size: u32, _flags: u64) -> Result<usize, MapError> {
154 Err(MapError::InvalidParam)
155 }
156}
157
158#[derive(Debug)]
160pub struct Map {
161 pub schema: MapSchema,
162
163 map_impl: Pin<Box<dyn MapImpl + Sync>>,
166}
167
168#[derive(Debug, Clone)]
171pub struct PinnedMap(Pin<Arc<Map>>);
172
173impl Deref for PinnedMap {
174 type Target = Map;
175 fn deref(&self) -> &Self::Target {
176 self.0.deref()
177 }
178}
179
180impl MapReference for PinnedMap {
181 fn schema(&self) -> &MapSchema {
182 &self.0.schema
183 }
184
185 fn as_bpf_value(&self) -> BpfValue {
186 BpfValue::from(self.deref() as *const Map)
187 }
188
189 fn get_data_ptr(&self) -> Option<BpfValue> {
190 assert!(self.0.schema.map_type == bpf_map_type_BPF_MAP_TYPE_ARRAY);
191
192 let key = [0u8; 4];
193 self.0.lookup(&key).map(|v| BpfValue::from(v.ptr().raw_ptr()))
194 }
195}
196
197pub type MapKey = smallvec::SmallVec<[u8; 16]>;
199
200pub type MapValue = smallvec::SmallVec<[u8; 64]>;
202
203const BASE_MAP_RIGHTS: zx::Rights = zx::Rights::READ
208 .union(zx::Rights::WRITE)
209 .union(zx::Rights::MAP)
210 .union(zx::Rights::SIGNAL)
211 .union(zx::Rights::WAIT);
212const SHARED_MAP_RIGHTS: zx::Rights = BASE_MAP_RIGHTS.union(zx::Rights::TRANSFER);
216
217impl Map {
218 pub fn new(schema: MapSchema, name: &str) -> Result<PinnedMap, MapError> {
219 validate_map_flags(&schema)?;
220 let map_impl = create_map_impl(&schema, name.to_string())?;
221 Ok(PinnedMap(Arc::pin(Self { schema, map_impl })))
222 }
223
224 pub fn new_shared(shared: febpf::Map) -> Result<PinnedMap, MapError> {
225 let febpf::Map { schema: Some(fidl_schema), vmo: Some(vmo), .. } = shared else {
226 return Err(MapError::InvalidParam);
227 };
228
229 let vmo_info = vmo.basic_info().map_err(|_| MapError::InvalidVmo)?;
231 if !vmo_info.rights.contains(BASE_MAP_RIGHTS) {
232 return Err(MapError::InvalidVmo);
233 }
234
235 let schema = MapSchema {
236 map_type: fidl_map_type_to_bpf_map_type(fidl_schema.type_),
237 key_size: fidl_schema.key_size,
238 value_size: fidl_schema.value_size,
239 max_entries: fidl_schema.max_entries,
240 flags: map_flags_from_fidl(fidl_schema.flags),
241 };
242
243 let map_impl = create_map_impl(&schema, vmo)?;
244 Ok(PinnedMap(Arc::pin(Self { schema, map_impl })))
245 }
246
247 pub fn share(&self) -> Result<febpf::Map, MapError> {
248 Ok(febpf::Map {
249 schema: Some(febpf::MapSchema {
250 type_: bpf_map_type_to_fidl_map_type(self.schema.map_type),
251 key_size: self.schema.key_size,
252 value_size: self.schema.value_size,
253 max_entries: self.schema.max_entries,
254 flags: map_flags_to_fidl(self.schema.flags)?,
255 }),
256 vmo: Some(
257 self.map_impl
258 .vmo()
259 .duplicate_handle(SHARED_MAP_RIGHTS)
260 .map_err(|_| MapError::Internal)?,
261 ),
262 ..Default::default()
263 })
264 }
265
266 pub fn lookup<'a>(&'a self, key: &[u8]) -> Option<MapValueRef<'a>> {
267 self.map_impl.lookup(key)
268 }
269
270 pub fn load(&self, key: &[u8]) -> Option<MapValue> {
271 self.lookup(key).map(|v| v.ptr().load())
272 }
273
274 pub fn update(&self, key: &[u8], value: EbpfBufferPtr<'_>, flags: u64) -> Result<(), MapError> {
275 if flags & (BPF_EXIST as u64) > 0 && flags & (BPF_NOEXIST as u64) > 0 {
276 return Err(MapError::InvalidParam);
277 }
278
279 self.map_impl.update(key, value, flags)
280 }
281
282 pub fn delete(&self, key: &[u8]) -> Result<(), MapError> {
283 self.map_impl.delete(key)
284 }
285
286 pub fn get_next_key(&self, key: Option<&[u8]>) -> Result<MapKey, MapError> {
287 self.map_impl.get_next_key(key)
288 }
289
290 pub fn vmo(&self) -> &Arc<zx::Vmo> {
291 self.map_impl.vmo()
292 }
293
294 pub fn can_read(&self) -> Option<bool> {
295 self.map_impl.can_read()
296 }
297
298 pub fn ringbuf_reserve(&self, size: u32, flags: u64) -> Result<usize, MapError> {
299 self.map_impl.ringbuf_reserve(size, flags)
300 }
301
302 pub fn uses_locks(&self) -> bool {
303 self.schema.map_type != bpf_map_type_BPF_MAP_TYPE_ARRAY
304 }
305}
306
307pub enum MapValueRef<'a> {
308 PlainRef(EbpfBufferPtr<'a>),
309 HashMapRef(hashmap::HashMapEntryRef<'a>),
310 LpmTrieRef(lpm_trie::LpmTrieEntryRef<'a>),
311}
312
313impl<'a> MapValueRef<'a> {
314 fn new(buf: EbpfBufferPtr<'a>) -> Self {
315 Self::PlainRef(buf)
316 }
317
318 fn new_from_hashmap(hash_map_ref: hashmap::HashMapEntryRef<'a>) -> Self {
319 Self::HashMapRef(hash_map_ref)
320 }
321
322 fn new_from_lpm_trie(lpm_trie_ref: lpm_trie::LpmTrieEntryRef<'a>) -> Self {
323 Self::LpmTrieRef(lpm_trie_ref)
324 }
325
326 pub fn is_ref_counted(&self) -> bool {
327 match self {
328 Self::PlainRef(_) => false,
329 Self::HashMapRef(_) | Self::LpmTrieRef(_) => true,
330 }
331 }
332
333 pub fn ptr(&self) -> EbpfBufferPtr<'a> {
334 match self {
335 Self::PlainRef(buf) => *buf,
336 Self::HashMapRef(hash_map_ref) => hash_map_ref.ptr(),
337 Self::LpmTrieRef(lpm_trie_ref) => lpm_trie_ref.ptr(),
338 }
339 }
340}
341
342const SK_STORAGE_MAX_ENTRIES: u32 = 8192;
343
344fn create_map_impl(
345 schema: &MapSchema,
346 vmo: impl Into<VmoOrName>,
347) -> Result<Pin<Box<dyn MapImpl>>, MapError> {
348 match schema.map_type {
351 bpf_map_type_BPF_MAP_TYPE_ARRAY => Ok(Box::pin(array::Array::new(schema, vmo)?)),
353 bpf_map_type_BPF_MAP_TYPE_HASH => Ok(Box::pin(hashmap::HashMap::new(schema, vmo)?)),
354 bpf_map_type_BPF_MAP_TYPE_RINGBUF => Ok(ring_buffer::RingBuffer::new(schema, vmo)?),
355 bpf_map_type_BPF_MAP_TYPE_LPM_TRIE => Ok(Box::pin(lpm_trie::LpmTrie::new(schema, vmo)?)),
356 bpf_map_type_BPF_MAP_TYPE_SK_STORAGE => {
357 if schema.key_size != 4
358 || schema.max_entries != 0
359 || schema.flags != MapFlags::NoPrealloc
360 {
361 return Err(MapError::InvalidParam);
362 }
363
364 let schema = MapSchema {
366 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
367 key_size: 8,
368 max_entries: SK_STORAGE_MAX_ENTRIES,
369 value_size: schema.value_size,
370 flags: MapFlags::NoPrealloc,
371 };
372 Ok(Box::pin(hashmap::HashMap::new(&schema, vmo)?))
373 }
374
375 bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH => {
378 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_DEVMAP_HASH");
379 let schema =
382 MapSchema { flags: schema.flags.difference(MapFlags::ProgReadOnly), ..*schema };
383 Ok(Box::pin(hashmap::HashMap::new(&schema, vmo)?))
384 }
385 bpf_map_type_BPF_MAP_TYPE_PERCPU_HASH => {
386 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_PERCPU_HASH");
387 Ok(Box::pin(hashmap::HashMap::new(schema, vmo)?))
388 }
389 bpf_map_type_BPF_MAP_TYPE_PERCPU_ARRAY => {
390 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_PERCPU_ARRAY");
391 Ok(Box::pin(array::Array::new(schema, vmo)?))
392 }
393 bpf_map_type_BPF_MAP_TYPE_LRU_HASH => {
394 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_LRU_HASH");
395 Ok(Box::pin(hashmap::HashMap::new(schema, vmo)?))
396 }
397 bpf_map_type_BPF_MAP_TYPE_UNSPEC => {
401 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_UNSPEC");
402 Err(MapError::MapTypeNotSupported)
403 }
404 bpf_map_type_BPF_MAP_TYPE_PROG_ARRAY => {
405 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_PROG_ARRAY");
406 Err(MapError::MapTypeNotSupported)
407 }
408 bpf_map_type_BPF_MAP_TYPE_PERF_EVENT_ARRAY => {
409 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_PERF_EVENT_ARRAY");
410 Err(MapError::MapTypeNotSupported)
411 }
412 bpf_map_type_BPF_MAP_TYPE_STACK_TRACE => {
413 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_STACK_TRACE");
414 Err(MapError::MapTypeNotSupported)
415 }
416 bpf_map_type_BPF_MAP_TYPE_CGROUP_ARRAY => {
417 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_CGROUP_ARRAY");
418 Err(MapError::MapTypeNotSupported)
419 }
420 bpf_map_type_BPF_MAP_TYPE_LRU_PERCPU_HASH => {
421 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_LRU_PERCPU_HASH");
422 Err(MapError::MapTypeNotSupported)
423 }
424 bpf_map_type_BPF_MAP_TYPE_ARRAY_OF_MAPS => {
425 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_ARRAY_OF_MAPS");
426 Err(MapError::MapTypeNotSupported)
427 }
428 bpf_map_type_BPF_MAP_TYPE_HASH_OF_MAPS => {
429 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_HASH_OF_MAPS");
430 Err(MapError::MapTypeNotSupported)
431 }
432 bpf_map_type_BPF_MAP_TYPE_DEVMAP => {
433 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_DEVMAP");
434 Err(MapError::MapTypeNotSupported)
435 }
436 bpf_map_type_BPF_MAP_TYPE_SOCKMAP => {
437 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_SOCKMAP");
438 Err(MapError::MapTypeNotSupported)
439 }
440 bpf_map_type_BPF_MAP_TYPE_CPUMAP => {
441 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_CPUMAP");
442 Err(MapError::MapTypeNotSupported)
443 }
444 bpf_map_type_BPF_MAP_TYPE_XSKMAP => {
445 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_XSKMAP");
446 Err(MapError::MapTypeNotSupported)
447 }
448 bpf_map_type_BPF_MAP_TYPE_SOCKHASH => {
449 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_SOCKHASH");
450 Err(MapError::MapTypeNotSupported)
451 }
452 bpf_map_type_BPF_MAP_TYPE_CGROUP_STORAGE => {
453 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_CGROUP_STORAGE");
454 Err(MapError::MapTypeNotSupported)
455 }
456 bpf_map_type_BPF_MAP_TYPE_REUSEPORT_SOCKARRAY => {
457 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_REUSEPORT_SOCKARRAY");
458 Err(MapError::MapTypeNotSupported)
459 }
460 bpf_map_type_BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE => {
461 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE");
462 Err(MapError::MapTypeNotSupported)
463 }
464 bpf_map_type_BPF_MAP_TYPE_QUEUE => {
465 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_QUEUE");
466 Err(MapError::MapTypeNotSupported)
467 }
468 bpf_map_type_BPF_MAP_TYPE_STACK => {
469 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_STACK");
470 Err(MapError::MapTypeNotSupported)
471 }
472 bpf_map_type_BPF_MAP_TYPE_STRUCT_OPS => {
473 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_STRUCT_OPS");
474 Err(MapError::MapTypeNotSupported)
475 }
476 bpf_map_type_BPF_MAP_TYPE_INODE_STORAGE => {
477 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_INODE_STORAGE");
478 Err(MapError::MapTypeNotSupported)
479 }
480 bpf_map_type_BPF_MAP_TYPE_TASK_STORAGE => {
481 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_TASK_STORAGE");
482 Err(MapError::MapTypeNotSupported)
483 }
484 bpf_map_type_BPF_MAP_TYPE_BLOOM_FILTER => {
485 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_BLOOM_FILTER");
486 Err(MapError::MapTypeNotSupported)
487 }
488 bpf_map_type_BPF_MAP_TYPE_USER_RINGBUF => {
489 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_USER_RINGBUF");
490 Err(MapError::MapTypeNotSupported)
491 }
492 bpf_map_type_BPF_MAP_TYPE_CGRP_STORAGE => {
493 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_CGRP_STORAGE");
494 Err(MapError::MapTypeNotSupported)
495 }
496 bpf_map_type_BPF_MAP_TYPE_ARENA => {
497 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_ARENA");
498 Err(MapError::MapTypeNotSupported)
499 }
500 _ => {
501 track_stub!(
502 TODO("https://fxbug.dev/323847465"),
503 "unknown bpf map type",
504 schema.map_type
505 );
506 Err(MapError::InvalidParam)
507 }
508 }
509}
510
511pub fn compute_map_storage_size(schema: &MapSchema) -> Result<usize, MapError> {
512 schema.value_size.checked_mul(schema.max_entries).map(|v| v as usize).ok_or(MapError::NoMemory)
513}
514
515fn bpf_map_type_to_fidl_map_type(map_type: bpf_map_type) -> febpf::MapType {
517 match map_type {
518 bpf_map_type_BPF_MAP_TYPE_ARRAY => febpf::MapType::Array,
519 bpf_map_type_BPF_MAP_TYPE_HASH => febpf::MapType::HashMap,
520 bpf_map_type_BPF_MAP_TYPE_RINGBUF => febpf::MapType::RingBuffer,
521 bpf_map_type_BPF_MAP_TYPE_PERCPU_ARRAY => febpf::MapType::PercpuArray,
522 bpf_map_type_BPF_MAP_TYPE_PERCPU_HASH => febpf::MapType::PercpuHash,
523 bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH => febpf::MapType::DevmapHash,
524 bpf_map_type_BPF_MAP_TYPE_LPM_TRIE => febpf::MapType::LpmTrie,
525 bpf_map_type_BPF_MAP_TYPE_LRU_HASH => febpf::MapType::LruHash,
526 bpf_map_type_BPF_MAP_TYPE_SK_STORAGE => febpf::MapType::SkStorage,
527 _ =>
528 {
530 unreachable!("unsupported map type {:?}", map_type)
531 }
532 }
533}
534
535fn fidl_map_type_to_bpf_map_type(map_type: febpf::MapType) -> bpf_map_type {
536 match map_type {
537 febpf::MapType::Array => bpf_map_type_BPF_MAP_TYPE_ARRAY,
538 febpf::MapType::HashMap => bpf_map_type_BPF_MAP_TYPE_HASH,
539 febpf::MapType::RingBuffer => bpf_map_type_BPF_MAP_TYPE_RINGBUF,
540 febpf::MapType::PercpuArray => bpf_map_type_BPF_MAP_TYPE_PERCPU_ARRAY,
541 febpf::MapType::PercpuHash => bpf_map_type_BPF_MAP_TYPE_PERCPU_HASH,
542 febpf::MapType::DevmapHash => bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH,
543 febpf::MapType::LpmTrie => bpf_map_type_BPF_MAP_TYPE_LPM_TRIE,
544 febpf::MapType::LruHash => bpf_map_type_BPF_MAP_TYPE_LRU_HASH,
545 febpf::MapType::SkStorage => bpf_map_type_BPF_MAP_TYPE_SK_STORAGE,
546 }
547}
548#[cfg(test)]
551mod test {
552 use super::*;
553
554 #[fuchsia::test]
555 fn test_sharing_array() {
556 let schema = MapSchema {
557 map_type: bpf_map_type_BPF_MAP_TYPE_ARRAY,
558 key_size: 4,
559 value_size: 4,
560 max_entries: 10,
561 flags: MapFlags::empty(),
562 };
563
564 let map1 = Map::new(schema, "test").unwrap();
566 let map2 = Map::new_shared(map1.share().unwrap()).unwrap();
567
568 let key = vec![0, 0, 0, 0];
570 let mut value = [0, 1, 2, 3];
571 map1.update(&MapKey::from_vec(key.clone()), (&mut value).into(), 0).unwrap();
572 assert_eq!(&map2.load(&key).unwrap()[..], &value);
573 }
574
575 #[fuchsia::test]
576 fn test_sharing_hash_map() {
577 let schema = MapSchema {
578 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
579 key_size: 4,
580 value_size: 4,
581 max_entries: 10,
582 flags: MapFlags::empty(),
583 };
584
585 let map1 = Map::new(schema, "test").unwrap();
587 let map2 = Map::new_shared(map1.share().unwrap()).unwrap();
588
589 let key = vec![0, 0, 0, 0];
591 let mut value = [0, 1, 2, 3];
592 map1.update(&MapKey::from_vec(key.clone()), (&mut value).into(), 0).unwrap();
593 assert_eq!(&map2.load(&key).unwrap()[..], &value);
594 }
595
596 #[fuchsia::test]
597 fn test_hash_map() {
598 let schema = MapSchema {
599 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
600 key_size: 5,
601 value_size: 25,
602 max_entries: 10000,
603 flags: MapFlags::empty(),
604 };
605
606 let get_key = |i| {
607 MapKey::from_vec(vec![
608 (i & 0xffusize) as u8,
609 0,
610 ((i >> 4) & 0xffusize) as u8,
611 0,
612 ((i >> 8) & 0xffusize) as u8,
613 ])
614 };
615 let get_value = |i, v| format!("--{:010} {:010}--", i, v).into_bytes();
616
617 let map = Map::new(schema, "test").unwrap();
618
619 for i in 0..10000 {
620 assert!(map.update(&get_key(i), (&mut get_value(i, 0)).into(), 0).is_ok());
621 }
622
623 assert_eq!(
625 map.update(&get_key(10001), (&mut get_value(10001, 1)).into(), 0),
626 Err(MapError::SizeLimit)
627 );
628
629 for i in 0..10000 {
630 assert_eq!(&map.load(&get_key(i)).unwrap()[..], &get_value(i, 0));
631 }
632
633 for i in 8000..9000 {
635 assert!(map.update(&get_key(i), (&mut get_value(i, 1)).into(), 0).is_ok());
636 }
637 for i in 8000..9000 {
638 assert_eq!(&map.load(&get_key(i)).unwrap()[..], &get_value(i, 1));
639 }
640
641 for i in 5000..10000 {
643 assert!(map.delete(&get_key(i)).is_ok());
644 }
645 for i in 5000..10000 {
646 assert_eq!(map.load(&get_key(i)), None);
647 }
648
649 for i in 10000..15000 {
651 assert!(map.update(&get_key(i), (&mut get_value(i, 2)).into(), 0).is_ok());
652 }
653
654 for i in 0..5000 {
655 assert_eq!(&map.load(&get_key(i)).unwrap()[..], &get_value(i, 0));
656 }
657 for i in 10000..15000 {
658 assert_eq!(&map.load(&get_key(i)).unwrap()[..], &get_value(i, 2));
659 }
660 }
661
662 #[fuchsia::test]
663 fn test_hash_map_update_direct() {
664 let schema = MapSchema {
665 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
666 key_size: 5,
667 value_size: 11,
668 max_entries: 10,
669 flags: MapFlags::empty(),
670 };
671
672 let map = Map::new(schema, "test").unwrap();
673 let key = MapKey::from_vec("12345".to_string().into_bytes());
674 let mut value = (0..11).collect::<Vec<u8>>();
675 assert!(map.update(&key.clone(), (&mut value).into(), 0).is_ok());
676
677 let value_ref = map.lookup(&key).unwrap();
679 #[allow(
680 clippy::undocumented_unsafe_blocks,
681 reason = "Force documented unsafe blocks in Starnix"
682 )]
683 unsafe {
684 *value_ref.ptr().get_ptr::<u32>(0).unwrap().deref_mut() = 0xabacadae;
685 }
686
687 assert_eq!(&map.load(&key).unwrap()[..], &[0xae, 0xad, 0xac, 0xab, 4, 5, 6, 7, 8, 9, 10]);
688 }
689
690 #[fuchsia::test]
691 fn test_hash_map_ref_counting() {
692 let schema = MapSchema {
693 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
694 key_size: 5,
695 value_size: 11,
696 max_entries: 2,
697 flags: MapFlags::empty(),
698 };
699
700 let map = Map::new(schema, "test").unwrap();
701 let key = MapKey::from_vec("12345".to_string().into_bytes());
702 let key2 = MapKey::from_vec("24122".to_string().into_bytes());
703 let mut value = (0..11).collect::<Vec<u8>>();
704 assert!(map.update(&key.clone(), (&mut value).into(), 0).is_ok());
705 assert!(map.update(&key2.clone(), (&mut value).into(), 0).is_ok());
706
707 let value_ref = map.lookup(&key).unwrap();
708
709 assert!(map.delete(&key).is_ok());
712 assert_eq!(map.update(&key.clone(), (&mut value).into(), 0), Err(MapError::SizeLimit));
713 drop(value_ref);
714 assert!(map.update(&key.clone(), (&mut value).into(), 0).is_ok());
715 }
716
717 #[fuchsia::test]
718 fn test_ringbug_sharing() {
719 let schema = MapSchema {
720 map_type: bpf_map_type_BPF_MAP_TYPE_RINGBUF,
721 key_size: 0,
722 value_size: 0,
723 max_entries: 4096 * 2,
724 flags: MapFlags::empty(),
725 };
726
727 let map = Map::new(schema, "test").unwrap();
728 map.ringbuf_reserve(8000, 0).expect("ringbuf_reserve failed");
729
730 let map2 = Map::new_shared(map.share().unwrap()).unwrap();
731
732 map2.ringbuf_reserve(2000, 0).expect_err("ringbuf_reserve expected to fail");
734 }
735
736 #[fuchsia::test]
738 fn test_all_maps_shareable() {
739 for map_type in 1..linux_uapi::bpf_map_type___MAX_BPF_MAP_TYPE {
740 let (key_size, value_size, max_entries, flags) = match map_type {
741 bpf_map_type_BPF_MAP_TYPE_RINGBUF => (0, 0, 4096, MapFlags::empty()),
742 bpf_map_type_BPF_MAP_TYPE_LPM_TRIE => (8, 4, 4096, MapFlags::NoPrealloc),
743 bpf_map_type_BPF_MAP_TYPE_SK_STORAGE => (4, 4, 0, MapFlags::NoPrealloc),
744 _ => (4, 4, 1, MapFlags::empty()),
745 };
746 let schema = MapSchema { map_type, key_size, value_size, max_entries, flags };
747
748 let map = match Map::new(schema, "test") {
749 Ok(map) => map,
750 Err(MapError::MapTypeNotSupported) => {
751 continue;
752 }
753 Err(e) => {
754 panic!("Failed to create map of type {:?}: {:?}", map_type, e);
755 }
756 };
757
758 let map_fidl = map.share().expect("Failed to share map");
759 let _: PinnedMap = Map::new_shared(map_fidl).expect("Failed to initialize shared map");
760 }
761 }
762}