Skip to main content

ebpf_api/maps/
mod.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#![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    // Equivalent of EINVAL.
51    InvalidParam,
52
53    // No entry with the specified key,
54    InvalidKey,
55
56    // Entry already exists..
57    EntryExists,
58
59    // Map size limit has been reached.
60    SizeLimit,
61
62    // Cannot allocate memory.
63    NoMemory,
64
65    // Invalid VMO was passed for a shared map.
66    InvalidVmo,
67
68    // Specified map type is not supported.
69    MapTypeNotSupported,
70
71    // Specified map configuration is not supported.
72    NotSupported,
73
74    // An internal issue, e.g. failed to allocate VMO.
75    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    // Read-only and write-only flags are mutually exclusive.
127    if flags.contains(MapFlags::SyscallReadOnly) && flags.contains(MapFlags::SyscallWriteOnly) {
128        return Err(MapError::InvalidParam);
129    }
130
131    // `MMAPABLE` is valid only for arrays.
132    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    // Returns true if `POLLIN` is signaled for the map FD. Should be
147    // overridden only for ring buffers.
148    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/// A BPF map. This is a hashtable that can be accessed both by BPF programs and userspace.
158#[derive(Debug)]
159pub struct Map {
160    pub schema: MapSchema,
161
162    // The impl because it's required for some map implementations need to be
163    // pinned, particularly ring buffers.
164    map_impl: Pin<Box<dyn MapImpl + Sync>>,
165}
166
167/// Maps are normally kept pinned in memory since linked eBPF programs store direct pointers to
168/// the maps they depend on.
169#[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
196// Avoid allocation for eBPF keys smaller than 16 bytes.
197pub type MapKey = smallvec::SmallVec<[u8; 16]>;
198
199// Avoid allocation for eBPF values smaller than 64 bytes.
200pub type MapValue = smallvec::SmallVec<[u8; 64]>;
201
202// Access rights required for a map VMO handle. Should be consistent with the
203// rights specified in FIDL. READ, WRITE and MAP rights are required to access
204// the map contents. SIGNAL and WAIT rights are used for synchronization.
205// LINT.IfChange(map_rights)
206const 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);
211// LINT.ThenChange(//sdk/fidl/fuchsia.ebpf/ebpf.fidl:map_rights)
212
213// Rights for the VMO handle when sharing a map.
214const 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        // Check VMO rights.
229        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    // The list of supported maps should be kept in sync with the enum values in
348    // `fuchsia.ebpf.MapType`.
349    match schema.map_type {
350        // LINT.IfChange(supported_maps)
351        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            // SK_STORAGE maps are implemented as hashmaps with socket cookie used as a key.
364            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        // These types are in use, but not yet implemented. Incorrectly use Array or Hash for
375        // these
376        bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH => {
377            track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_DEVMAP_HASH");
378            // `BPF_F_RDONLY_PROG` is not yet implemented, but it's always set
379            // for `DEVMAP` maps.
380            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        // LINT.ThenChange(:fidl_map_types)
397
398        // Unimplemented types
399        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
514// LINT.IfChange(fidl_map_types)
515fn 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        // Other map types are rejected in `create_map_impl()`.
528        {
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// LINT.ThenChange(:supported_maps, //sdk/fidl/fuchsia.ebpf/ebpf.fidl:map_types)
548
549#[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        // Create two array maps sharing the content.
564        let map1 = Map::new(schema, "test").unwrap();
565        let map2 = Map::new_shared(map1.share().unwrap()).unwrap();
566
567        // Set a value in one map and check that it's updated in the other.
568        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        // Create two array maps sharing the content.
585        let map1 = Map::new(schema, "test").unwrap();
586        let map2 = Map::new_shared(map1.share().unwrap()).unwrap();
587
588        // Set a value in one map and check that it's updated in the other.
589        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        // Should fail to add another entry when the map is full.
623        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        // Update some elements.
633        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        // Delete half of the entries.
641        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        // Replace removed entries with new ones
649        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        // Access a value directly the way eBPF programs do.
677        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        // Delete an element. The corresponding data entry should not be
709        // released until `value_ref` is dropped.
710        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        // Expected to fail since there is no space left.
732        map2.ringbuf_reserve(2000, 0).expect_err("ringbuf_reserve expected to fail");
733    }
734
735    // Verifies that all supported map types are shareable.
736    #[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}