Browse Source

aya: allow specifying a pin path for a named map

This commit extends the EbpfLoader with set_map_pin_path that allows the
caller to associate a named map with a pin path.

One note is that this path is an absolute path, not relative to
`map_pin_path`, and it forces the map to be loaded from that path.
Andrew Werner 3 weeks ago
parent
commit
5802dc7a23
3 changed files with 58 additions and 13 deletions
  1. 51 10
      aya/src/bpf.rs
  2. 6 3
      aya/src/maps/mod.rs
  3. 1 0
      xtask/public-api/aya.txt

+ 51 - 10
aya/src/bpf.rs

@@ -123,6 +123,10 @@ pub struct EbpfLoader<'a> {
     // Max entries overrides the max_entries field of the map that matches the provided name
     // before the map is created.
     max_entries: HashMap<&'a str, u32>,
+    // Map pin path overrides the pin path of the map that matches the provided name before
+    // it is created.
+    map_pin_path_by_name: HashMap<&'a str, std::borrow::Cow<'a, Path>>,
+
     extensions: HashSet<&'a str>,
     verifier_log_level: VerifierLogLevel,
     allow_unsupported_maps: bool,
@@ -161,6 +165,7 @@ impl<'a> EbpfLoader<'a> {
             default_map_pin_directory: None,
             globals: HashMap::new(),
             max_entries: HashMap::new(),
+            map_pin_path_by_name: HashMap::new(),
             extensions: HashSet::new(),
             verifier_log_level: VerifierLogLevel::default(),
             allow_unsupported_maps: false,
@@ -218,6 +223,9 @@ impl<'a> EbpfLoader<'a> {
     /// Pinned maps will be loaded from `path/MAP_NAME`.
     /// The caller is responsible for ensuring the directory exists.
     ///
+    /// Note that if a path is provided for a specific map via [`EbpfLoader::map_pin_path`],
+    /// it will take precedence over this path.
+    ///
     /// # Example
     ///
     /// ```no_run
@@ -304,6 +312,33 @@ impl<'a> EbpfLoader<'a> {
         self
     }
 
+    /// Set the pin path for the map that matches the provided name.
+    ///
+    /// Note that this is an absolute path to the pinned map; it is not a prefix
+    /// to be combined with the map name, and it is not relative to the
+    /// configured base directory for pinned maps.
+    ///
+    /// Each call to this function with the same name overwrites the path to the
+    /// pinned map; last one wins.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// # use std::path::Path;
+    ///
+    /// # let mut loader = aya::EbpfLoader::new();
+    /// # let pin_path = Path::new("/sys/fs/bpf/my-pinned-map");
+    /// let bpf = loader
+    ///     .map_pin_path("map", pin_path)
+    ///     .load_file("file.o")?;
+    /// # Ok::<(), aya::EbpfError>(())
+    /// ```
+    ///
+    pub fn map_pin_path<P: Into<Cow<'a, Path>>>(&mut self, name: &'a str, path: P) -> &mut Self {
+        self.map_pin_path_by_name.insert(name, path.into());
+        self
+    }
+
     /// Treat the provided program as an [`Extension`]
     ///
     /// When attempting to load the program with the provided `name`
@@ -387,6 +422,7 @@ impl<'a> EbpfLoader<'a> {
             extensions,
             verifier_log_level,
             allow_unsupported_maps,
+            map_pin_path_by_name,
         } = self;
         let mut obj = Object::parse(data)?;
         obj.patch_map_data(globals.clone())?;
@@ -491,16 +527,21 @@ impl<'a> EbpfLoader<'a> {
                 _ => (),
             }
             let btf_fd = btf_fd.as_deref().map(|fd| fd.as_fd());
-            let mut map = match obj.pinning() {
-                PinningType::None => MapData::create(obj, &name, btf_fd)?,
-                PinningType::ByName => {
-                    // pin maps in /sys/fs/bpf by default to align with libbpf
-                    // behavior https://github.com/libbpf/libbpf/blob/v1.2.2/src/libbpf.c#L2161.
-                    let path = default_map_pin_directory
-                        .as_deref()
-                        .unwrap_or_else(|| Path::new("/sys/fs/bpf"));
-
-                    MapData::create_pinned_by_name(path, obj, &name, btf_fd)?
+            let mut map = if let Some(pin_path) = map_pin_path_by_name.get(name.as_str()) {
+                MapData::create_pinned_by_name(pin_path, obj, &name, btf_fd)?
+            } else {
+                match obj.pinning() {
+                    PinningType::None => MapData::create(obj, &name, btf_fd)?,
+                    PinningType::ByName => {
+                        // pin maps in /sys/fs/bpf by default to align with libbpf
+                        // behavior https://github.com/libbpf/libbpf/blob/v1.2.2/src/libbpf.c#L2161.
+                        let path = default_map_pin_directory
+                            .as_deref()
+                            .unwrap_or_else(|| Path::new("/sys/fs/bpf"));
+                        let path = path.join(&name);
+
+                        MapData::create_pinned_by_name(path, obj, &name, btf_fd)?
+                    }
                 }
             };
             map.finalize()?;

+ 6 - 3
aya/src/maps/mod.rs

@@ -616,13 +616,16 @@ impl MapData {
         use std::os::unix::ffi::OsStrExt as _;
 
         // try to open map in case it's already pinned
-        let path = path.as_ref().join(name);
+        let path = path.as_ref();
         let path_string = match CString::new(path.as_os_str().as_bytes()) {
             Ok(path) => path,
             Err(error) => {
                 return Err(MapError::PinError {
                     name: Some(name.into()),
-                    error: PinError::InvalidPinPath { path, error },
+                    error: PinError::InvalidPinPath {
+                        path: path.to_path_buf(),
+                        error,
+                    },
                 });
             }
         };
@@ -636,7 +639,7 @@ impl MapData {
             }),
             Err(_) => {
                 let map = Self::create(obj, name, btf_fd)?;
-                map.pin(&path).map_err(|error| MapError::PinError {
+                map.pin(path).map_err(|error| MapError::PinError {
                     name: Some(name.into()),
                     error,
                 })?;

+ 1 - 0
xtask/public-api/aya.txt

@@ -10746,6 +10746,7 @@ pub fn aya::EbpfLoader<'a>::default_map_pin_directory<P: core::convert::AsRef<st
 pub fn aya::EbpfLoader<'a>::extension(&mut self, name: &'a str) -> &mut Self
 pub fn aya::EbpfLoader<'a>::load(&mut self, data: &[u8]) -> core::result::Result<aya::Ebpf, aya::EbpfError>
 pub fn aya::EbpfLoader<'a>::load_file<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<aya::Ebpf, aya::EbpfError>
+pub fn aya::EbpfLoader<'a>::map_pin_path<P: core::convert::Into<alloc::borrow::Cow<'a, std::path::Path>>>(&mut self, name: &'a str, path: P) -> &mut Self
 pub fn aya::EbpfLoader<'a>::new() -> Self
 pub fn aya::EbpfLoader<'a>::set_global<T: core::convert::Into<aya::GlobalData<'a>>>(&mut self, name: &'a str, value: T, must_exist: bool) -> &mut Self
 pub fn aya::EbpfLoader<'a>::set_max_entries(&mut self, name: &'a str, size: u32) -> &mut Self