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