فهرست منبع

Implement Pinning For Programs and Maps

This commit adds 2 new methods to aya::sys
- bpf_pin_object
- bpf_get_object

Which allow the pinning and retrieval of programs/maps to bpffs.

It adds a `Program.pin` API, such that a loaded program can be pinned.
For map pinning, the user must ensure the `pinning u32` in the
`bpf_map_def` is set to 1, maps will be pinned using a new builder API.

BpfLoader::new().map_pin_path("/sys/fs/bpf/myapp").load_file("myapp.o")

This will pin all maps whose definition requests pinning to path + name.

Signed-off-by: Dave Tucker <dave@dtucker.co.uk>
Dave Tucker 3 سال پیش
والد
کامیت
9426f36f79

+ 153 - 35
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),
 

+ 1 - 1
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;
 ///

+ 1 - 1
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;

+ 1 - 1
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};

+ 21 - 1
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();
 

+ 2 - 2
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;

+ 66 - 4
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,
         }
     }
 

+ 1 - 1
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;

+ 1 - 1
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};

+ 1 - 1
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;
 ///

+ 1 - 1
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;

+ 1 - 1
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;

+ 1 - 1
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;
 ///

+ 1 - 1
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;

+ 7 - 6
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

+ 1 - 1
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};

+ 1 - 1
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;

+ 28 - 2
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;

+ 1 - 1
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::{

+ 1 - 1
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;

+ 1 - 1
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;

+ 1 - 1
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;

+ 1 - 1
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;

+ 1 - 1
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};
 ///

+ 1 - 1
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;
 ///

+ 15 - 0
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],