Repository: aya-rs/aya (language: rust)

Issue:
Add support for map and program pinning
Program pinning looks simple enough:
It should be possible to add `pub fn pin(&mut self, path: Path )` to the `Link` trait.
Where `Path` would be a relative path on `/sys/fs/bpf` or an absolute path to `/sys/fs/bpf`.
The implementation would call the `bpf` syscall and `BPF_OBJ_PIN` (see: `bpf_obj_pin` in libbpf for reference).

For Map pinning, the API depends on where the maps are defined.
Aya seems to rely on the eBPF program to define the map, which is the created when the program is loaded:
As such I propose we use the following API:
```rust
pub fn load_file<P: AsRef<Path>>(path: P, map_pin_directory: Option<P>) -> Result<Bpf, BpfError>
```
The `map_pin_directory` is then passed down through function calls so it's available when the maps are created.
The usage of Path in this API is the same as the one on the `Link` trait.


Discussion / hints:

Fix patch:
diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs
index 5ed1beba7..7a107ac85 100644
--- a/aya/src/bpf.rs
+++ b/aya/src/bpf.rs
@@ -2,7 +2,7 @@ use std::{
     collections::HashMap,
     error::Error,
     fs, io,
-    os::raw::c_int,
+    os::{raw::c_int, unix::io::RawFd},
     path::{Path, PathBuf},
 };
 
@@ -58,64 +58,92 @@ pub(crate) struct bpf_map_def {
     pub(crate) map_flags: u32,
     // optional features
     pub(crate) id: u32,
-    pub(crate) pinning: u32,
+    pub(crate) pinning: PinningType,
 }
 
-/// The main entry point into the library, used to work with eBPF programs and maps.
-#[derive(Debug)]
-pub struct Bpf {
-    maps: HashMap<String, MapLock>,
-    programs: HashMap<String, Program>,
+#[repr(u32)]
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub(crate) enum PinningType {
+    None = 0,
+    #[allow(dead_code)] // ByName is constructed from the BPF side
+    ByName = 1,
 }
 
-impl Bpf {
+impl Default for PinningType {
+    fn default() -> Self {
+        PinningType::None
+    }
+}
+
+#[derive(Default, Debug)]
+pub struct BpfLoader {
+    btf: Option<Btf>,
+    map_pin_path: Option<PathBuf>,
+}
+
+impl BpfLoader {
+    pub fn new() -> BpfLoader {
+        BpfLoader {
+            btf: None,
+            map_pin_path: None,
+        }
+    }
+
+    // Set the target BTF
+    pub fn btf(&mut self, btf: Btf) -> &mut BpfLoader {
+        self.btf = Some(btf);
+        self
+    }
+
+    // Set the map pin path
+    pub fn map_pin_path<P: AsRef<Path>>(&mut self, path: P) -> &mut BpfLoader {
+        self.map_pin_path = Some(path.as_ref().to_owned());
+        self
+    }
+
     /// Loads eBPF bytecode from a file.
     ///
-    /// Parses the given object code file and initializes the [maps](crate::maps) defined in it. If
-    /// the kernel supports [BTF](Btf) debug info, it is automatically loaded from
-    /// `/sys/kernel/btf/vmlinux`.
+    /// Parses the given object code file and initializes the [maps](crate::maps) defined in it.
     ///
     /// # Examples
     ///
     /// ```no_run
-    /// use aya::Bpf;
+    /// use aya::BpfLoader;
     ///
-    /// let bpf = Bpf::load_file("file.o")?;
+    /// let bpf = BpfLoader::new().load_file("file.o")?;
     /// # Ok::<(), aya::BpfError>(())
     /// ```
-    pub fn load_file<P: AsRef<Path>>(path: P) -> Result<Bpf, BpfError> {
+    pub fn load_file<P: AsRef<Path>>(&mut self, path: P) -> Result<Bpf, BpfError> {
         let path = path.as_ref();
-        Bpf::load(
-            &fs::read(path).map_err(|error| BpfError::FileError {
-                path: path.to_owned(),
-                error,
-            })?,
-            Btf::from_sys_fs().ok().as_ref(),
-        )
+        self.load(&fs::read(path).map_err(|error| BpfError::FileError {
+            path: path.to_owned(),
+            error,
+        })?)
     }
 
     /// Load eBPF bytecode.
     ///
     /// Parses the object code contained in `data` and initializes the [maps](crate::maps) defined
-    /// in it. If `target_btf` is not `None` and `data` includes BTF debug info, [BTF](Btf) relocations
-    /// are applied as well. In order to allow sharing of a single [BTF](Btf) object among multiple
-    /// eBPF programs, `target_btf` is passed by reference.
+    /// in it. If `BpfLoader.btf` is not `None` and `data` includes BTF debug info, [BTF](Btf) relocations
+    /// are applied as well. Any maps that require pinning will be pinned to `BpfLoader.map_pin_path`
     ///
     /// # Examples
     ///
     /// ```no_run
-    /// use aya::{Bpf, Btf};
+    /// use aya::{BpfLoader, Btf};
     /// use std::fs;
     ///
     /// let data = fs::read("file.o").unwrap();
     /// // load the BTF data from /sys/kernel/btf/vmlinux
-    /// let bpf = Bpf::load(&data, Btf::from_sys_fs().ok().as_ref());
+
+    /// let target_btf = Btf::from_sys_fs().unwrap();
+    /// let bpf = BpfLoader::new().btf(target_btf).load(&data);
     /// # Ok::<(), aya::BpfError>(())
     /// ```
-    pub fn load(data: &[u8], target_btf: Option<&Btf>) -> Result<Bpf, BpfError> {
+    pub fn load(&mut self, data: &[u8]) -> Result<Bpf, BpfError> {
         let mut obj = Object::parse(data)?;
 
-        if let Some(btf) = target_btf {
+        if let Some(btf) = &self.btf {
             obj.relocate_btf(btf)?;
         }
 
@@ -130,8 +158,32 @@ impl Bpf {
                     })?
                     .len() as u32;
             }
-            let mut map = Map { obj, fd: None };
-            let fd = map.create()?;
+            let mut map = Map {
+                obj,
+                fd: None,
+                pinned: false,
+            };
+            let fd = match map.obj.def.pinning {
+                PinningType::ByName => {
+                    let path = match &self.map_pin_path {
+                        Some(p) => p,
+                        None => return Err(BpfError::NoPinPath),
+                    };
+                    // try to open map in case it's already pinned
+                    match map.from_pinned(path) {
+                        Ok(fd) => {
+                            map.pinned = true;
+                            fd as RawFd
+                        }
+                        Err(_) => {
+                            let fd = map.create()?;
+                            map.pin(path)?;
+                            fd
+                        }
+                    }
+                }
+                PinningType::None => map.create()?,
+            };
             if !map.obj.data.is_empty() && map.obj.name != ".bss" {
                 bpf_map_update_elem_ptr(fd, &0 as *const _, map.obj.data.as_mut_ptr(), 0).map_err(
                     |(code, io_error)| MapError::SyscallError {
@@ -208,7 +260,6 @@ impl Bpf {
                 (name, program)
             })
             .collect();
-
         Ok(Bpf {
             maps: maps
                 .drain(..)
@@ -217,6 +268,64 @@ impl Bpf {
             programs,
         })
     }
+}
+
+/// The main entry point into the library, used to work with eBPF programs and maps.
+#[derive(Debug)]
+pub struct Bpf {
+    maps: HashMap<String, MapLock>,
+    programs: HashMap<String, Program>,
+}
+
+impl Bpf {
+    /// Loads eBPF bytecode from a file.
+    ///
+    /// Parses the given object code file and initializes the [maps](crate::maps) defined in it. If
+    /// the kernel supports [BTF](Btf) debug info, it is automatically loaded from
+    /// `/sys/kernel/btf/vmlinux`.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// use aya::Bpf;
+    ///
+    /// let bpf = Bpf::load_file("file.o")?;
+    /// # Ok::<(), aya::BpfError>(())
+    /// ```
+    pub fn load_file<P: AsRef<Path>>(path: P) -> Result<Bpf, BpfError> {
+        let mut loader = BpfLoader::new();
+        let path = path.as_ref();
+        if let Ok(btf) = Btf::from_sys_fs() {
+            loader.btf(btf);
+        };
+        loader.load_file(path)
+    }
+
+    /// Load eBPF bytecode.
+    ///
+    /// Parses the object code contained in `data` and initializes the [maps](crate::maps) defined
+    /// in it. If `target_btf` is not `None` and `data` includes BTF debug info, [BTF](Btf) relocations
+    /// are applied as well. In order to allow sharing of a single [BTF](Btf) object among multiple
+    /// eBPF programs, `target_btf` is passed by reference.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// use aya::{Bpf, Btf};
+    /// use std::fs;
+    ///
+    /// let data = fs::read("file.o").unwrap();
+    /// // load the BTF data from /sys/kernel/btf/vmlinux
+    /// let bpf = Bpf::load(&data);
+    /// # Ok::<(), aya::BpfError>(())
+    /// ```
+    pub fn load(data: &[u8]) -> Result<Bpf, BpfError> {
+        let mut loader = BpfLoader::new();
+        if let Ok(btf) = Btf::from_sys_fs() {
+            loader.btf(btf);
+        };
+        loader.load(data)
+    }
 
     /// Returns a reference to the map with the given name.
     ///
@@ -272,7 +381,7 @@ impl Bpf {
     ///
     /// # Examples
     /// ```no_run
-    /// # let mut bpf = aya::Bpf::load(&[], None)?;
+    /// # let mut bpf = aya::Bpf::load(&[])?;
     /// for (name, map) in bpf.maps() {
     ///     println!(
     ///         "found map `{}` of type `{:?}`",
@@ -308,7 +417,7 @@ impl Bpf {
     /// # Examples
     ///
     /// ```no_run
-    /// # let bpf = aya::Bpf::load(&[], None)?;
+    /// # let bpf = aya::Bpf::load(&[])?;
     /// let program = bpf.program("SSL_read")?;
     /// println!("program SSL_read is of type {:?}", program.prog_type());
     /// # Ok::<(), aya::BpfError>(())
@@ -333,7 +442,7 @@ impl Bpf {
     /// # Examples
     ///
     /// ```no_run
-    /// # let mut bpf = aya::Bpf::load(&[], None)?;
+    /// # let mut bpf = aya::Bpf::load(&[])?;
     /// use aya::programs::UProbe;
     /// use std::convert::TryInto;
     ///
@@ -354,7 +463,7 @@ impl Bpf {
     ///
     /// # Examples
     /// ```no_run
-    /// # let mut bpf = aya::Bpf::load(&[], None)?;
+    /// # let mut bpf = aya::Bpf::load(&[])?;
     /// for program in bpf.programs() {
     ///     println!(
     ///         "found program `{}` of type `{:?}`",
@@ -379,6 +488,15 @@ pub enum BpfError {
         error: io::Error,
     },
 
+    #[error("pinning requested but no path provided")]
+    NoPinPath,
+
+    #[error("unexpected pinning type {name}")]
+    UnexpectedPinningType { name: u32 },
+
+    #[error("invalid path `{error}`")]
+    InvalidPath { error: String },
+
     #[error("error parsing BPF object")]
     ParseError(#[from] ParseError),
 
diff --git a/aya/src/maps/array/array.rs b/aya/src/maps/array/array.rs
index 8aa9abce6..6b41cc5d7 100644
--- a/aya/src/maps/array/array.rs
+++ b/aya/src/maps/array/array.rs
@@ -23,7 +23,7 @@ use crate::{
 ///
 /// # Examples
 /// ```no_run
-/// # let bpf = aya::Bpf::load(&[], None)?;
+/// # let bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::Array;
 /// use std::convert::TryFrom;
 ///
diff --git a/aya/src/maps/array/per_cpu_array.rs b/aya/src/maps/array/per_cpu_array.rs
index 400553b88..099778e84 100644
--- a/aya/src/maps/array/per_cpu_array.rs
+++ b/aya/src/maps/array/per_cpu_array.rs
@@ -32,7 +32,7 @@ use crate::{
 /// #     #[error(transparent)]
 /// #     Bpf(#[from] aya::BpfError)
 /// # }
-/// # let bpf = aya::Bpf::load(&[], None)?;
+/// # let bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::{PerCpuArray, PerCpuValues};
 /// use aya::util::nr_cpus;
 /// use std::convert::TryFrom;
diff --git a/aya/src/maps/array/program_array.rs b/aya/src/maps/array/program_array.rs
index 840033a23..9b2ae51dc 100644
--- a/aya/src/maps/array/program_array.rs
+++ b/aya/src/maps/array/program_array.rs
@@ -26,7 +26,7 @@ use crate::{
 ///
 /// # Examples
 /// ```no_run
-/// # let bpf = aya::Bpf::load(&[], None)?;
+/// # let bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::ProgramArray;
 /// use aya::programs::CgroupSkb;
 /// use std::convert::{TryFrom, TryInto};
diff --git a/aya/src/maps/hash_map/hash_map.rs b/aya/src/maps/hash_map/hash_map.rs
index f9d20c9d5..fc841f342 100644
--- a/aya/src/maps/hash_map/hash_map.rs
+++ b/aya/src/maps/hash_map/hash_map.rs
@@ -20,7 +20,7 @@ use crate::{
 /// # Examples
 ///
 /// ```no_run
-/// # let bpf = aya::Bpf::load(&[], None)?;
+/// # let bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::HashMap;
 /// use std::convert::TryFrom;
 ///
@@ -182,6 +182,7 @@ mod tests {
         let map = Map {
             obj: new_obj_map("TEST"),
             fd: None,
+            pinned: false,
         };
         assert!(matches!(
             HashMap::<_, u8, u32>::new(&map),
@@ -197,6 +198,7 @@ mod tests {
         let map = Map {
             obj: new_obj_map("TEST"),
             fd: None,
+            pinned: false,
         };
         assert!(matches!(
             HashMap::<_, u32, u16>::new(&map),
@@ -223,6 +225,7 @@ mod tests {
                 data: Vec::new(),
             },
             fd: None,
+            pinned: false,
         };
 
         assert!(matches!(
@@ -236,6 +239,7 @@ mod tests {
         let mut map = Map {
             obj: new_obj_map("TEST"),
             fd: None,
+            pinned: false,
         };
 
         assert!(matches!(
@@ -249,6 +253,7 @@ mod tests {
         let mut map = Map {
             obj: new_obj_map("TEST"),
             fd: Some(42),
+            pinned: false,
         };
 
         assert!(HashMap::<_, u32, u32>::new(&mut map).is_ok());
@@ -259,6 +264,7 @@ mod tests {
         let map = Map {
             obj: new_obj_map("TEST"),
             fd: Some(42),
+            pinned: false,
         };
         assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok())
     }
@@ -279,6 +285,7 @@ mod tests {
                 data: Vec::new(),
             },
             fd: Some(42),
+            pinned: false,
         };
 
         assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok())
@@ -291,6 +298,7 @@ mod tests {
         let mut map = Map {
             obj: new_obj_map("TEST"),
             fd: Some(42),
+            pinned: false,
         };
         let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
 
@@ -313,6 +321,7 @@ mod tests {
         let mut map = Map {
             obj: new_obj_map("TEST"),
             fd: Some(42),
+            pinned: false,
         };
         let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
 
@@ -326,6 +335,7 @@ mod tests {
         let mut map = Map {
             obj: new_obj_map("TEST"),
             fd: Some(42),
+            pinned: false,
         };
         let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
 
@@ -348,6 +358,7 @@ mod tests {
         let mut map = Map {
             obj: new_obj_map("TEST"),
             fd: Some(42),
+            pinned: false,
         };
         let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
 
@@ -360,6 +371,7 @@ mod tests {
         let map = Map {
             obj: new_obj_map("TEST"),
             fd: Some(42),
+            pinned: false,
         };
         let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
 
@@ -381,6 +393,7 @@ mod tests {
         let map = Map {
             obj: new_obj_map("TEST"),
             fd: Some(42),
+            pinned: false,
         };
         let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
 
@@ -419,6 +432,7 @@ mod tests {
         let map = Map {
             obj: new_obj_map("TEST"),
             fd: Some(42),
+            pinned: false,
         };
         let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
         let keys = unsafe { hm.keys() }.collect::<Result<Vec<_>, _>>();
@@ -462,6 +476,7 @@ mod tests {
         let map = Map {
             obj: new_obj_map("TEST"),
             fd: Some(42),
+            pinned: false,
         };
         let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
 
@@ -489,6 +504,7 @@ mod tests {
         let map = Map {
             obj: new_obj_map("TEST"),
             fd: Some(42),
+            pinned: false,
         };
         let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
 
@@ -518,6 +534,7 @@ mod tests {
         let map = Map {
             obj: new_obj_map("TEST"),
             fd: Some(42),
+            pinned: false,
         };
         let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
         let items = unsafe { hm.iter() }.collect::<Result<Vec<_>, _>>().unwrap();
@@ -550,6 +567,7 @@ mod tests {
         let map = Map {
             obj: new_obj_map("TEST"),
             fd: Some(42),
+            pinned: false,
         };
         let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
 
@@ -583,6 +601,7 @@ mod tests {
         let map = Map {
             obj: new_obj_map("TEST"),
             fd: Some(42),
+            pinned: false,
         };
         let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
 
@@ -622,6 +641,7 @@ mod tests {
         let map = Map {
             obj: new_obj_map("TEST"),
             fd: Some(42),
+            pinned: false,
         };
         let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
 
diff --git a/aya/src/maps/hash_map/per_cpu_hash_map.rs b/aya/src/maps/hash_map/per_cpu_hash_map.rs
index 991282ed0..34b8453c4 100644
--- a/aya/src/maps/hash_map/per_cpu_hash_map.rs
+++ b/aya/src/maps/hash_map/per_cpu_hash_map.rs
@@ -27,7 +27,7 @@ use crate::{
 /// # Examples
 ///
 /// ```no_run
-/// # let bpf = aya::Bpf::load(&[], None)?;
+/// # let bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::PerCpuHashMap;
 /// use std::convert::TryFrom;
 ///
@@ -113,7 +113,7 @@ impl<T: DerefMut<Target = Map>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
     /// #     #[error(transparent)]
     /// #     Bpf(#[from] aya::BpfError)
     /// # }
-    /// # let bpf = aya::Bpf::load(&[], None)?;
+    /// # let bpf = aya::Bpf::load(&[])?;
     /// use aya::maps::{PerCpuHashMap, PerCpuValues};
     /// use aya::util::nr_cpus;
     /// use std::convert::TryFrom;
diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs
index bf7b1293b..e481cb7de 100644
--- a/aya/src/maps/mod.rs
+++ b/aya/src/maps/mod.rs
@@ -15,7 +15,7 @@
 //! *typed maps* using the [`TryFrom`](std::convert::TryFrom) trait. For example:
 //!
 //! ```no_run
-//! # let mut bpf = aya::Bpf::load(&[], None)?;
+//! # let mut bpf = aya::Bpf::load(&[])?;
 //! use std::convert::{TryFrom, TryInto};
 //! use aya::maps::SockMap;
 //! use aya::programs::SkMsg;
@@ -34,14 +34,14 @@
 //! implement the [Pod] trait.
 use std::{
     convert::TryFrom, ffi::CString, io, marker::PhantomData, mem, ops::Deref, os::unix::io::RawFd,
-    ptr,
+    path::Path, ptr,
 };
 use thiserror::Error;
 
 use crate::{
     generated::bpf_map_type,
     obj,
-    sys::{bpf_create_map, bpf_map_get_next_key},
+    sys::{bpf_create_map, bpf_get_object, bpf_map_get_next_key, bpf_pin_object},
     util::nr_cpus,
     Pod,
 };
@@ -76,12 +76,18 @@ pub enum MapError {
     #[error("invalid map name `{name}`")]
     InvalidName { name: String },
 
+    #[error("invalid map path `{error}`")]
+    InvalidPinPath { error: String },
+
     #[error("the map `{name}` has not been created")]
     NotCreated { name: String },
 
     #[error("the map `{name}` has already been created")]
     AlreadyCreated { name: String },
 
+    #[error("the map `{name}` has already been pinned")]
+    AlreadyPinned { name: String },
+
     #[error("failed to create map `{name}`: {code}")]
     CreateError {
         name: String,
@@ -89,6 +95,13 @@ pub enum MapError {
         io_error: io::Error,
     },
 
+    #[error("failed to pin map `{name}`: {code}")]
+    PinError {
+        name: String,
+        code: libc::c_long,
+        io_error: io::Error,
+    },
+
     #[error("invalid key size {size}, expected {expected}")]
     InvalidKeySize { size: usize, expected: usize },
 
@@ -128,6 +141,7 @@ pub enum MapError {
 pub struct Map {
     pub(crate) obj: obj::Map,
     pub(crate) fd: Option<RawFd>,
+    pub pinned: bool,
 }
 
 impl Map {
@@ -153,6 +167,31 @@ impl Map {
         Ok(fd)
     }
 
+    pub(crate) fn from_pinned<P: AsRef<Path>>(&mut self, path: P) -> Result<RawFd, MapError> {
+        let name = self.obj.name.clone();
+        if self.fd.is_some() {
+            return Err(MapError::AlreadyCreated { name });
+        }
+        let map_path = path.as_ref().join(self.name());
+        let path_string = match CString::new(map_path.to_str().unwrap()) {
+            Ok(s) => s,
+            Err(e) => {
+                return Err(MapError::InvalidPinPath {
+                    error: e.to_string(),
+                })
+            }
+        };
+        let fd = bpf_get_object(&path_string).map_err(|(code, io_error)| MapError::PinError {
+            name,
+            code,
+            io_error,
+        })? as RawFd;
+
+        self.fd = Some(fd);
+
+        Ok(fd)
+    }
+
     pub fn name(&self) -> &str {
         &self.obj.name
     }
@@ -166,6 +205,28 @@ impl Map {
             name: self.obj.name.clone(),
         })
     }
+
+    pub(crate) fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), MapError> {
+        if self.pinned {
+            return Err(MapError::AlreadyPinned {
+                name: self.name().to_string(),
+            });
+        }
+        let map_path = path.as_ref().join(self.name());
+        let fd = self.fd_or_err()?;
+        let path_string = CString::new(map_path.to_string_lossy().into_owned()).map_err(|e| {
+            MapError::InvalidPinPath {
+                error: e.to_string(),
+            }
+        })?;
+        bpf_pin_object(fd, &path_string).map_err(|(code, io_error)| MapError::SyscallError {
+            call: "BPF_OBJ_PIN".to_string(),
+            code,
+            io_error,
+        })?;
+        self.pinned = true;
+        Ok(())
+    }
 }
 
 pub(crate) trait IterableMap<K: Pod, V> {
@@ -334,7 +395,7 @@ impl PerCpuKernelMem {
 /// #     #[error(transparent)]
 /// #     Bpf(#[from] aya::BpfError)
 /// # }
-/// # let bpf = aya::Bpf::load(&[], None)?;
+/// # let bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::PerCpuValues;
 /// use aya::util::nr_cpus;
 /// use std::convert::TryFrom;
@@ -438,6 +499,7 @@ mod tests {
         Map {
             obj: new_obj_map(name),
             fd: None,
+            pinned: false,
         }
     }
 
diff --git a/aya/src/maps/perf/async_perf_event_array.rs b/aya/src/maps/perf/async_perf_event_array.rs
index 623191c4a..fdea6db5a 100644
--- a/aya/src/maps/perf/async_perf_event_array.rs
+++ b/aya/src/maps/perf/async_perf_event_array.rs
@@ -46,7 +46,7 @@ use crate::maps::{
 /// # }
 /// # #[cfg(feature = "async_tokio")]
 /// # async fn try_main() -> Result<(), Error> {
-/// # let bpf = aya::Bpf::load(&[], None)?;
+/// # let bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::perf::{AsyncPerfEventArray, PerfBufferError};
 /// use aya::util::online_cpus;
 /// use std::convert::TryFrom;
diff --git a/aya/src/maps/perf/perf_event_array.rs b/aya/src/maps/perf/perf_event_array.rs
index 240cde182..9695ea60f 100644
--- a/aya/src/maps/perf/perf_event_array.rs
+++ b/aya/src/maps/perf/perf_event_array.rs
@@ -109,7 +109,7 @@ impl<T: DerefMut<Target = Map>> AsRawFd for PerfEventArrayBuffer<T> {
 /// #    #[error(transparent)]
 /// #    PerfBuf(#[from] aya::maps::perf::PerfBufferError),
 /// # }
-/// # let bpf = aya::Bpf::load(&[], None)?;
+/// # let bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::PerfEventArray;
 /// use aya::util::online_cpus;
 /// use std::convert::{TryFrom, TryInto};
diff --git a/aya/src/maps/queue.rs b/aya/src/maps/queue.rs
index f4b53e052..a62578071 100644
--- a/aya/src/maps/queue.rs
+++ b/aya/src/maps/queue.rs
@@ -21,7 +21,7 @@ use crate::{
 ///
 /// # Examples
 /// ```no_run
-/// # let bpf = aya::Bpf::load(&[], None)?;
+/// # let bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::Queue;
 /// use std::convert::TryFrom;
 ///
diff --git a/aya/src/maps/sock/sock_hash.rs b/aya/src/maps/sock/sock_hash.rs
index d3b02e009..7e06173c1 100644
--- a/aya/src/maps/sock/sock_hash.rs
+++ b/aya/src/maps/sock/sock_hash.rs
@@ -41,7 +41,7 @@ use crate::{
 /// #     #[error(transparent)]
 /// #     Bpf(#[from] aya::BpfError)
 /// # }
-/// # let mut bpf = aya::Bpf::load(&[], None)?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use std::convert::{TryFrom, TryInto};
 /// use std::io::Write;
 /// use std::net::TcpStream;
diff --git a/aya/src/maps/sock/sock_map.rs b/aya/src/maps/sock/sock_map.rs
index ae00769d8..d39f449cf 100644
--- a/aya/src/maps/sock/sock_map.rs
+++ b/aya/src/maps/sock/sock_map.rs
@@ -29,7 +29,7 @@ use crate::{
 /// # Examples
 ///
 /// ```no_run
-/// # let mut bpf = aya::Bpf::load(&[], None)?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use std::convert::{TryFrom, TryInto};
 /// use aya::maps::SockMap;
 /// use aya::programs::SkSkb;
diff --git a/aya/src/maps/stack.rs b/aya/src/maps/stack.rs
index b86828ee4..c8f36083c 100644
--- a/aya/src/maps/stack.rs
+++ b/aya/src/maps/stack.rs
@@ -21,7 +21,7 @@ use crate::{
 ///
 /// # Examples
 /// ```no_run
-/// # let bpf = aya::Bpf::load(&[], None)?;
+/// # let bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::Stack;
 /// use std::convert::TryFrom;
 ///
diff --git a/aya/src/maps/stack_trace.rs b/aya/src/maps/stack_trace.rs
index d00189d9f..aa006b24d 100644
--- a/aya/src/maps/stack_trace.rs
+++ b/aya/src/maps/stack_trace.rs
@@ -34,7 +34,7 @@ use crate::{
 /// #     #[error(transparent)]
 /// #     Bpf(#[from] aya::BpfError)
 /// # }
-/// # let bpf = aya::Bpf::load(&[], None)?;
+/// # let bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::StackTraceMap;
 /// use aya::util::kernel_symbols;
 /// use std::convert::TryFrom;
diff --git a/aya/src/obj/mod.rs b/aya/src/obj/mod.rs
index 1849abcf3..edf6341ed 100644
--- a/aya/src/obj/mod.rs
+++ b/aya/src/obj/mod.rs
@@ -586,6 +586,7 @@ mod tests {
     use std::slice;
 
     use super::*;
+    use crate::PinningType;
 
     fn fake_section<'a>(name: &'a str, data: &'a [u8]) -> Section<'a> {
         Section {
@@ -688,7 +689,7 @@ mod tests {
             max_entries: 4,
             map_flags: 5,
             id: 0,
-            pinning: 0,
+            pinning: PinningType::None,
         };
 
         assert_eq!(
@@ -706,7 +707,7 @@ mod tests {
             max_entries: 4,
             map_flags: 5,
             id: 6,
-            pinning: 7,
+            pinning: PinningType::ByName,
         };
 
         assert_eq!(parse_map_def("foo", bytes_of(&def)).unwrap(), def);
@@ -721,7 +722,7 @@ mod tests {
             max_entries: 4,
             map_flags: 5,
             id: 6,
-            pinning: 7,
+            pinning: PinningType::ByName,
         };
         let mut buf = [0u8; 128];
         unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, def) };
@@ -750,7 +751,7 @@ mod tests {
                         max_entries: 4,
                         map_flags: 5,
                         id: 0,
-                        pinning: 0
+                        pinning: PinningType::None,
                     })
                 ),
                 "foo"
@@ -765,7 +766,7 @@ mod tests {
                     max_entries: 4,
                     map_flags: 5,
                     id: 0,
-                    pinning: 0
+                    pinning: PinningType::None,
                 },
                 data
             }) if name == "foo" && data.is_empty()
@@ -793,7 +794,7 @@ mod tests {
                     max_entries: 1,
                     map_flags: 0,
                     id: 0,
-                    pinning: 0,
+                    pinning: PinningType::None,
                 },
                 data
             }) if name == ".bss" && data == map_data && value_size == map_data.len() as u32
diff --git a/aya/src/programs/cgroup_skb.rs b/aya/src/programs/cgroup_skb.rs
index 68971886c..6b798ad19 100644
--- a/aya/src/programs/cgroup_skb.rs
+++ b/aya/src/programs/cgroup_skb.rs
@@ -37,7 +37,7 @@ use super::FdLink;
 /// #     #[error(transparent)]
 /// #     Bpf(#[from] aya::BpfError)
 /// # }
-/// # let mut bpf = aya::Bpf::load(&[], None)?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use std::fs::File;
 /// use std::convert::TryInto;
 /// use aya::programs::{CgroupSkb, CgroupSkbAttachType};
diff --git a/aya/src/programs/lirc_mode2.rs b/aya/src/programs/lirc_mode2.rs
index 8ae1ccd23..06d4cd8ac 100644
--- a/aya/src/programs/lirc_mode2.rs
+++ b/aya/src/programs/lirc_mode2.rs
@@ -33,7 +33,7 @@ use libc::{close, dup};
 /// #     #[error(transparent)]
 /// #     Bpf(#[from] aya::BpfError)
 /// # }
-/// # let mut bpf = aya::Bpf::load(&[], None)?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use std::fs::File;
 /// use std::convert::TryInto;
 /// use aya::programs::LircMode2;
diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs
index 6179f3898..8d39c4e8a 100644
--- a/aya/src/programs/mod.rs
+++ b/aya/src/programs/mod.rs
@@ -56,9 +56,10 @@ use std::{
     cell::RefCell,
     cmp,
     convert::TryFrom,
-    ffi::CStr,
+    ffi::{CStr, CString},
     io,
     os::unix::io::{AsRawFd, RawFd},
+    path::Path,
     rc::Rc,
 };
 use thiserror::Error;
@@ -82,7 +83,7 @@ use crate::{
     generated::{bpf_attach_type, bpf_prog_info, bpf_prog_type},
     maps::MapError,
     obj::{self, Function},
-    sys::{bpf_load_program, bpf_prog_detach, bpf_prog_query},
+    sys::{bpf_load_program, bpf_pin_object, bpf_prog_detach, bpf_prog_query},
 };
 
 /// Error type returned when working with programs.
@@ -136,6 +137,9 @@ pub enum ProgramError {
     #[error("unexpected program type")]
     UnexpectedProgramType,
 
+    #[error("invalid pin path `{error}`")]
+    InvalidPinPath { error: String },
+
     /// A map error occurred while loading or attaching a program.
     #[error(transparent)]
     MapError(#[from] MapError),
@@ -225,6 +229,11 @@ impl Program {
         &self.data().name
     }
 
+    /// Pin the program to the provided path
+    pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), ProgramError> {
+        self.data_mut().pin(path)
+    }
+
     fn data(&self) -> &ProgramData {
         match self {
             Program::KProbe(p) => &p.data,
@@ -278,6 +287,23 @@ impl ProgramData {
         self.links.push(Rc::clone(&link));
         LinkRef::new(link)
     }
+
+    pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), ProgramError> {
+        let fd = self.fd_or_err()?;
+        let path_string =
+            CString::new(path.as_ref().to_string_lossy().into_owned()).map_err(|e| {
+                MapError::InvalidPinPath {
+                    error: e.to_string(),
+                }
+            })?;
+        bpf_pin_object(fd, &path_string).map_err(|(_code, io_error)| {
+            ProgramError::SyscallError {
+                call: "BPF_OBJ_PIN".to_string(),
+                io_error,
+            }
+        })?;
+        Ok(())
+    }
 }
 
 const MIN_LOG_BUF_SIZE: usize = 1024 * 10;
diff --git a/aya/src/programs/perf_event.rs b/aya/src/programs/perf_event.rs
index 56f3ed673..209e2ddc7 100644
--- a/aya/src/programs/perf_event.rs
+++ b/aya/src/programs/perf_event.rs
@@ -59,7 +59,7 @@ pub enum PerfEventScope {
 /// #     #[error(transparent)]
 /// #     Bpf(#[from] aya::BpfError)
 /// # }
-/// # let mut bpf = aya::Bpf::load(&[], None)?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use std::convert::TryInto;
 /// use aya::util::online_cpus;
 /// use aya::programs::perf_event::{
diff --git a/aya/src/programs/sk_msg.rs b/aya/src/programs/sk_msg.rs
index 4e42136ef..9298a478b 100644
--- a/aya/src/programs/sk_msg.rs
+++ b/aya/src/programs/sk_msg.rs
@@ -29,7 +29,7 @@ use crate::{
 /// #     #[error(transparent)]
 /// #     Bpf(#[from] aya::BpfError)
 /// # }
-/// # let mut bpf = aya::Bpf::load(&[], None)?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use std::convert::{TryFrom, TryInto};
 /// use std::io::Write;
 /// use std::net::TcpStream;
diff --git a/aya/src/programs/sk_skb.rs b/aya/src/programs/sk_skb.rs
index ffaebf6ac..6ab8a0fbc 100644
--- a/aya/src/programs/sk_skb.rs
+++ b/aya/src/programs/sk_skb.rs
@@ -28,7 +28,7 @@ pub enum SkSkbKind {
 /// # Examples
 ///
 /// ```no_run
-/// # let mut bpf = aya::Bpf::load(&[], None)?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use std::convert::{TryFrom, TryInto};
 /// use aya::maps::SockMap;
 /// use aya::programs::SkSkb;
diff --git a/aya/src/programs/sock_ops.rs b/aya/src/programs/sock_ops.rs
index 048bb9508..5852c4d73 100644
--- a/aya/src/programs/sock_ops.rs
+++ b/aya/src/programs/sock_ops.rs
@@ -30,7 +30,7 @@ use crate::{
 /// #     #[error(transparent)]
 /// #     Bpf(#[from] aya::BpfError)
 /// # }
-/// # let mut bpf = aya::Bpf::load(&[], None)?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use std::fs::File;
 /// use std::convert::TryInto;
 /// use aya::programs::SockOps;
diff --git a/aya/src/programs/socket_filter.rs b/aya/src/programs/socket_filter.rs
index c01dc1ded..1509b22bc 100644
--- a/aya/src/programs/socket_filter.rs
+++ b/aya/src/programs/socket_filter.rs
@@ -44,7 +44,7 @@ pub enum SocketFilterError {
 /// #     #[error(transparent)]
 /// #     Bpf(#[from] aya::BpfError)
 /// # }
-/// # let mut bpf = aya::Bpf::load(&[], None)?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use std::convert::TryInto;
 /// use std::net::TcpStream;
 /// use std::os::unix::io::AsRawFd;
diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs
index 1d6fae6b7..4d16e0d49 100644
--- a/aya/src/programs/tc.rs
+++ b/aya/src/programs/tc.rs
@@ -51,7 +51,7 @@ pub enum TcAttachType {
 /// #     #[error(transparent)]
 /// #     Bpf(#[from] aya::BpfError)
 /// # }
-/// # let mut bpf = aya::Bpf::load(&[], None)?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use std::convert::TryInto;
 /// use aya::programs::{tc, SchedClassifier, TcAttachType};
 ///
diff --git a/aya/src/programs/trace_point.rs b/aya/src/programs/trace_point.rs
index a3cb52f63..331be6cad 100644
--- a/aya/src/programs/trace_point.rs
+++ b/aya/src/programs/trace_point.rs
@@ -40,7 +40,7 @@ pub enum TracePointError {
 /// #     #[error(transparent)]
 /// #     Bpf(#[from] aya::BpfError)
 /// # }
-/// # let mut bpf = aya::Bpf::load(&[], None)?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use std::convert::TryInto;
 /// use aya::programs::TracePoint;
 ///
diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs
index 86b38e0ff..a9902015b 100644
--- a/aya/src/sys/bpf.rs
+++ b/aya/src/sys/bpf.rs
@@ -38,6 +38,21 @@ pub(crate) fn bpf_create_map(name: &CStr, def: &bpf_map_def) -> SysResult {
     sys_bpf(bpf_cmd::BPF_MAP_CREATE, &attr)
 }
 
+pub(crate) fn bpf_pin_object(fd: RawFd, path: &CStr) -> SysResult {
+    let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
+    let u = unsafe { &mut attr.__bindgen_anon_4 };
+    u.bpf_fd = fd as u32;
+    u.pathname = path.as_ptr() as u64;
+    sys_bpf(bpf_cmd::BPF_OBJ_PIN, &attr)
+}
+
+pub(crate) fn bpf_get_object(path: &CStr) -> SysResult {
+    let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
+    let u = unsafe { &mut attr.__bindgen_anon_4 };
+    u.pathname = path.as_ptr() as u64;
+    sys_bpf(bpf_cmd::BPF_OBJ_GET, &attr)
+}
+
 pub(crate) fn bpf_load_program(
     ty: bpf_prog_type,
     insns: &[bpf_insn],
diff --git a/bpf/aya-bpf/src/maps/array.rs b/bpf/aya-bpf/src/maps/array.rs
index bbee2f753..349e7efe5 100644
--- a/bpf/aya-bpf/src/maps/array.rs
+++ b/bpf/aya-bpf/src/maps/array.rs
@@ -5,6 +5,7 @@ use aya_bpf_cty::c_void;
 use crate::{
     bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_ARRAY},
     helpers::bpf_map_lookup_elem,
+    maps::PinningType,
 };
 
 #[repr(transparent)]
@@ -23,7 +24,22 @@ impl<T> Array<T> {
                 max_entries,
                 map_flags: flags,
                 id: 0,
-                pinning: 0,
+                pinning: PinningType::None as u32,
+            },
+            _t: PhantomData,
+        }
+    }
+
+    pub const fn pinned(max_entries: u32, flags: u32) -> Array<T> {
+        Array {
+            def: bpf_map_def {
+                type_: BPF_MAP_TYPE_ARRAY,
+                key_size: mem::size_of::<u32>() as u32,
+                value_size: mem::size_of::<T>() as u32,
+                max_entries,
+                map_flags: flags,
+                id: 0,
+                pinning: PinningType::ByName as u32,
             },
             _t: PhantomData,
         }
diff --git a/bpf/aya-bpf/src/maps/hash_map.rs b/bpf/aya-bpf/src/maps/hash_map.rs
index 3f3a803db..b2fc50ef1 100644
--- a/bpf/aya-bpf/src/maps/hash_map.rs
+++ b/bpf/aya-bpf/src/maps/hash_map.rs
@@ -5,6 +5,7 @@ use aya_bpf_cty::{c_long, c_void};
 use crate::{
     bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_HASH},
     helpers::{bpf_map_delete_elem, bpf_map_lookup_elem, bpf_map_update_elem},
+    maps::PinningType,
 };
 
 #[repr(transparent)]
@@ -24,14 +25,14 @@ impl<K, V> HashMap<K, V> {
                 max_entries,
                 map_flags: flags,
                 id: 0,
-                pinning: 0,
+                pinning: PinningType::None as u32,
             },
             _k: PhantomData,
             _v: PhantomData,
         }
     }
 
-    pub const fn pinned(max_entries: u32, flags: u32, pinning: u32) -> HashMap<K, V> {
+    pub const fn pinned(max_entries: u32, flags: u32) -> HashMap<K, V> {
         HashMap {
             def: bpf_map_def {
                 type_: BPF_MAP_TYPE_HASH,
@@ -40,7 +41,7 @@ impl<K, V> HashMap<K, V> {
                 max_entries,
                 map_flags: flags,
                 id: 0,
-                pinning,
+                pinning: PinningType::ByName as u32,
             },
             _k: PhantomData,
             _v: PhantomData,
diff --git a/bpf/aya-bpf/src/maps/mod.rs b/bpf/aya-bpf/src/maps/mod.rs
index 71db366a7..b6cd3c65a 100644
--- a/bpf/aya-bpf/src/maps/mod.rs
+++ b/bpf/aya-bpf/src/maps/mod.rs
@@ -1,3 +1,10 @@
+#[repr(u32)]
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub(crate) enum PinningType {
+    None = 0,
+    ByName = 1,
+}
+
 pub mod array;
 pub mod hash_map;
 pub mod per_cpu_array;
diff --git a/bpf/aya-bpf/src/maps/per_cpu_array.rs b/bpf/aya-bpf/src/maps/per_cpu_array.rs
index cfa264ade..5019e93f5 100644
--- a/bpf/aya-bpf/src/maps/per_cpu_array.rs
+++ b/bpf/aya-bpf/src/maps/per_cpu_array.rs
@@ -5,6 +5,7 @@ use aya_bpf_cty::c_void;
 use crate::{
     bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_PERCPU_ARRAY},
     helpers::bpf_map_lookup_elem,
+    maps::PinningType,
 };
 
 #[repr(transparent)]
@@ -23,7 +24,22 @@ impl<T> PerCpuArray<T> {
                 max_entries,
                 map_flags: flags,
                 id: 0,
-                pinning: 0,
+                pinning: PinningType::None as u32,
+            },
+            _t: PhantomData,
+        }
+    }
+
+    pub const fn pinned(max_entries: u32, flags: u32) -> PerCpuArray<T> {
+        PerCpuArray {
+            def: bpf_map_def {
+                type_: BPF_MAP_TYPE_PERCPU_ARRAY,
+                key_size: mem::size_of::<u32>() as u32,
+                value_size: mem::size_of::<T>() as u32,
+                max_entries,
+                map_flags: flags,
+                id: 0,
+                pinning: PinningType::ByName as u32,
             },
             _t: PhantomData,
         }
diff --git a/bpf/aya-bpf/src/maps/perf_map.rs b/bpf/aya-bpf/src/maps/perf_map.rs
index 3776f2f23..bd3d7fd43 100644
--- a/bpf/aya-bpf/src/maps/perf_map.rs
+++ b/bpf/aya-bpf/src/maps/perf_map.rs
@@ -3,6 +3,7 @@ use core::{marker::PhantomData, mem};
 use crate::{
     bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY, BPF_F_CURRENT_CPU},
     helpers::bpf_perf_event_output,
+    maps::PinningType,
     BpfContext,
 };
 
@@ -26,7 +27,22 @@ impl<T> PerfMap<T> {
                 max_entries,
                 map_flags: flags,
                 id: 0,
-                pinning: 0,
+                pinning: PinningType::None as u32,
+            },
+            _t: PhantomData,
+        }
+    }
+
+    pub const fn pinned(max_entries: u32, flags: u32) -> PerfMap<T> {
+        PerfMap {
+            def: bpf_map_def {
+                type_: BPF_MAP_TYPE_PERF_EVENT_ARRAY,
+                key_size: mem::size_of::<u32>() as u32,
+                value_size: mem::size_of::<u32>() as u32,
+                max_entries,
+                map_flags: flags,
+                id: 0,
+                pinning: PinningType::ByName as u32,
             },
             _t: PhantomData,
         }
diff --git a/bpf/aya-bpf/src/maps/queue.rs b/bpf/aya-bpf/src/maps/queue.rs
index b0d86a403..783c5bbda 100644
--- a/bpf/aya-bpf/src/maps/queue.rs
+++ b/bpf/aya-bpf/src/maps/queue.rs
@@ -3,6 +3,7 @@ use core::{marker::PhantomData, mem};
 use crate::{
     bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_QUEUE},
     helpers::{bpf_map_pop_elem, bpf_map_push_elem},
+    maps::PinningType,
 };
 
 #[repr(transparent)]
@@ -21,7 +22,22 @@ impl<T> Queue<T> {
                 max_entries,
                 map_flags: flags,
                 id: 0,
-                pinning: 0,
+                pinning: PinningType::None as u32,
+            },
+            _t: PhantomData,
+        }
+    }
+
+    pub const fn pinned(max_entries: u32, flags: u32) -> Queue<T> {
+        Queue {
+            def: bpf_map_def {
+                type_: BPF_MAP_TYPE_QUEUE,
+                key_size: 0,
+                value_size: mem::size_of::<T>() as u32,
+                max_entries,
+                map_flags: flags,
+                id: 0,
+                pinning: PinningType::ByName as u32,
             },
             _t: PhantomData,
         }
diff --git a/bpf/aya-bpf/src/maps/sock_hash.rs b/bpf/aya-bpf/src/maps/sock_hash.rs
index 6b2c30b53..d381b3cb3 100644
--- a/bpf/aya-bpf/src/maps/sock_hash.rs
+++ b/bpf/aya-bpf/src/maps/sock_hash.rs
@@ -5,6 +5,7 @@ use aya_bpf_cty::c_void;
 use crate::{
     bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_SOCKHASH, bpf_sock_ops},
     helpers::{bpf_msg_redirect_hash, bpf_sock_hash_update},
+    maps::PinningType,
     programs::SkMsgContext,
     BpfContext,
 };
@@ -25,7 +26,22 @@ impl<K> SockHash<K> {
                 max_entries,
                 map_flags: flags,
                 id: 0,
-                pinning: 0,
+                pinning: PinningType::None as u32,
+            },
+            _k: PhantomData,
+        }
+    }
+
+    pub const fn pinned(max_entries: u32, flags: u32) -> SockHash<K> {
+        SockHash {
+            def: bpf_map_def {
+                type_: BPF_MAP_TYPE_SOCKHASH,
+                key_size: mem::size_of::<K>() as u32,
+                value_size: mem::size_of::<u32>() as u32,
+                max_entries,
+                map_flags: flags,
+                id: 0,
+                pinning: PinningType::ByName as u32,
             },
             _k: PhantomData,
         }
diff --git a/bpf/aya-bpf/src/maps/sock_map.rs b/bpf/aya-bpf/src/maps/sock_map.rs
index 2952ad37e..97ddd008a 100644
--- a/bpf/aya-bpf/src/maps/sock_map.rs
+++ b/bpf/aya-bpf/src/maps/sock_map.rs
@@ -5,6 +5,7 @@ use aya_bpf_cty::c_void;
 use crate::{
     bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_SOCKMAP, bpf_sock_ops},
     helpers::{bpf_msg_redirect_map, bpf_sock_map_update},
+    maps::PinningType,
     programs::SkMsgContext,
     BpfContext,
 };
@@ -24,7 +25,21 @@ impl SockMap {
                 max_entries,
                 map_flags: flags,
                 id: 0,
-                pinning: 0,
+                pinning: PinningType::None as u32,
+            },
+        }
+    }
+
+    pub const fn pinned(max_entries: u32, flags: u32) -> SockMap {
+        SockMap {
+            def: bpf_map_def {
+                type_: BPF_MAP_TYPE_SOCKMAP,
+                key_size: mem::size_of::<u32>() as u32,
+                value_size: mem::size_of::<u32>() as u32,
+                max_entries,
+                map_flags: flags,
+                id: 0,
+                pinning: PinningType::ByName as u32,
             },
         }
     }
diff --git a/bpf/aya-bpf/src/maps/stack_trace.rs b/bpf/aya-bpf/src/maps/stack_trace.rs
index 9557976d2..d87e66670 100644
--- a/bpf/aya-bpf/src/maps/stack_trace.rs
+++ b/bpf/aya-bpf/src/maps/stack_trace.rs
@@ -2,42 +2,53 @@ use core::mem;
 
 use crate::{
     bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_STACK_TRACE},
-    helpers::{bpf_get_stackid},
+    helpers::bpf_get_stackid,
+    maps::PinningType,
     BpfContext,
 };
 
 #[repr(transparent)]
 pub struct StackTrace {
-	def: bpf_map_def,
+    def: bpf_map_def,
 }
 
 const PERF_MAX_STACK_DEPTH: u32 = 127;
 
 impl StackTrace {
-	pub const fn with_max_entries(max_entries: u32, flags: u32) -> StackTrace {
-		StackTrace {
-			def: bpf_map_def {
-				type_: BPF_MAP_TYPE_STACK_TRACE,
-				key_size: mem::size_of::<u32>() as u32,
-				value_size: mem::size_of::<u64>() as u32 * PERF_MAX_STACK_DEPTH,
-				max_entries,
-				map_flags: flags,
-				id: 0,
-				pinning: 0,
-			},
-		}
-	}
+    pub const fn with_max_entries(max_entries: u32, flags: u32) -> StackTrace {
+        StackTrace {
+            def: bpf_map_def {
+                type_: BPF_MAP_TYPE_STACK_TRACE,
+                key_size: mem::size_of::<u32>() as u32,
+                value_size: mem::size_of::<u64>() as u32 * PERF_MAX_STACK_DEPTH,
+                max_entries,
+                map_flags: flags,
+                id: 0,
+                pinning: PinningType::None as u32,
+            },
+        }
+    }
 
-	pub unsafe fn get_stackid<C: BpfContext>(&mut self, ctx: &C, flags: u64) -> Result<i64, i64> {
-		let ret = bpf_get_stackid(
-			ctx.as_ptr(),
-			&mut self.def as *mut _ as *mut _,
-			flags,
-		);
-		if ret < 0 {
-			Err(ret)
-		} else {
-			Ok(ret)
-		}
-	}
-}
\ No newline at end of file
+    pub const fn pinned(max_entries: u32, flags: u32) -> StackTrace {
+        StackTrace {
+            def: bpf_map_def {
+                type_: BPF_MAP_TYPE_STACK_TRACE,
+                key_size: mem::size_of::<u32>() as u32,
+                value_size: mem::size_of::<u64>() as u32 * PERF_MAX_STACK_DEPTH,
+                max_entries,
+                map_flags: flags,
+                id: 0,
+                pinning: PinningType::ByName as u32,
+            },
+        }
+    }
+
+    pub unsafe fn get_stackid<C: BpfContext>(&mut self, ctx: &C, flags: u64) -> Result<i64, i64> {
+        let ret = bpf_get_stackid(ctx.as_ptr(), &mut self.def as *mut _ as *mut _, flags);
+        if ret < 0 {
+            Err(ret)
+        } else {
+            Ok(ret)
+        }
+    }
+}


Test patch: