mirror of https://github.com/aya-rs/aya
aya: rework links
Remove LinkRef and remove the Rc<RefCell<_>> that was used to store type-erased link values in ProgramData. Among other things, this allows `Bpf` to be `Send`, which makes it easier to use it with async runtimes. Change the link API to: let link_id = prog.attach(...)?; ... prog.detach(link_id)?; Link ids are strongly typed, so it's impossible to eg: let link_id = uprobe.attach(...)?; xdp.detach(link_id); As it would result in a compile time error. Links are still stored inside ProgramData, and unless detached explicitly, they are automatically detached when the parent program gets dropped.pull/249/head
parent
29d539751a
commit
cb57d10d25
@ -0,0 +1,260 @@
|
||||
use libc::{close, dup};
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
os::unix::prelude::RawFd,
|
||||
};
|
||||
|
||||
use crate::{generated::bpf_attach_type, programs::ProgramError, sys::bpf_prog_detach};
|
||||
|
||||
pub(crate) trait Link: std::fmt::Debug + 'static {
|
||||
type Id: std::fmt::Debug + std::hash::Hash + Eq + PartialEq;
|
||||
|
||||
fn id(&self) -> Self::Id;
|
||||
|
||||
fn detach(self) -> Result<(), ProgramError>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct LinkMap<T: Link> {
|
||||
links: HashMap<T::Id, T>,
|
||||
}
|
||||
|
||||
impl<T: Link> LinkMap<T> {
|
||||
pub(crate) fn new() -> LinkMap<T> {
|
||||
LinkMap {
|
||||
links: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn insert(&mut self, link: T) -> Result<T::Id, ProgramError> {
|
||||
let id = link.id();
|
||||
|
||||
match self.links.entry(link.id()) {
|
||||
Entry::Occupied(_) => return Err(ProgramError::AlreadyAttached),
|
||||
Entry::Vacant(e) => e.insert(link),
|
||||
};
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub(crate) fn remove(&mut self, link_id: T::Id) -> Result<(), ProgramError> {
|
||||
self.links
|
||||
.remove(&link_id)
|
||||
.ok_or(ProgramError::NotAttached)?
|
||||
.detach()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Link> Drop for LinkMap<T> {
|
||||
fn drop(&mut self) {
|
||||
for (_, link) in self.links.drain() {
|
||||
let _ = link.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, Eq, PartialEq)]
|
||||
pub(crate) struct FdLinkId(pub(crate) RawFd);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FdLink {
|
||||
fd: RawFd,
|
||||
}
|
||||
|
||||
impl FdLink {
|
||||
pub(crate) fn new(fd: RawFd) -> FdLink {
|
||||
FdLink { fd }
|
||||
}
|
||||
}
|
||||
|
||||
impl Link for FdLink {
|
||||
type Id = FdLinkId;
|
||||
|
||||
fn id(&self) -> Self::Id {
|
||||
FdLinkId(self.fd)
|
||||
}
|
||||
|
||||
fn detach(self) -> Result<(), ProgramError> {
|
||||
unsafe { close(self.fd) };
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, Eq, PartialEq)]
|
||||
pub(crate) struct ProgAttachLinkId(RawFd, RawFd, bpf_attach_type);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ProgAttachLink {
|
||||
prog_fd: RawFd,
|
||||
target_fd: RawFd,
|
||||
attach_type: bpf_attach_type,
|
||||
}
|
||||
|
||||
impl ProgAttachLink {
|
||||
pub(crate) fn new(
|
||||
prog_fd: RawFd,
|
||||
target_fd: RawFd,
|
||||
attach_type: bpf_attach_type,
|
||||
) -> ProgAttachLink {
|
||||
ProgAttachLink {
|
||||
prog_fd,
|
||||
target_fd: unsafe { dup(target_fd) },
|
||||
attach_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Link for ProgAttachLink {
|
||||
type Id = ProgAttachLinkId;
|
||||
|
||||
fn id(&self) -> Self::Id {
|
||||
ProgAttachLinkId(self.prog_fd, self.target_fd, self.attach_type)
|
||||
}
|
||||
|
||||
fn detach(self) -> Result<(), ProgramError> {
|
||||
let _ = bpf_prog_detach(self.prog_fd, self.target_fd, self.attach_type);
|
||||
unsafe { close(self.target_fd) };
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_link_wrapper {
|
||||
($wrapper:ident, #[$doc:meta] $wrapper_id:ident, $base:ident, $base_id:ident) => {
|
||||
#[$doc]
|
||||
#[derive(Debug, Hash, Eq, PartialEq)]
|
||||
pub struct $wrapper_id($base_id);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct $wrapper($base);
|
||||
|
||||
impl crate::programs::Link for $wrapper {
|
||||
type Id = $wrapper_id;
|
||||
|
||||
fn id(&self) -> Self::Id {
|
||||
$wrapper_id(self.0.id())
|
||||
}
|
||||
|
||||
fn detach(self) -> Result<(), ProgramError> {
|
||||
self.0.detach()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$base> for $wrapper {
|
||||
fn from(b: $base) -> $wrapper {
|
||||
$wrapper(b)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use define_link_wrapper;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::programs::ProgramError;
|
||||
|
||||
use super::{Link, LinkMap};
|
||||
|
||||
#[derive(Debug, Hash, Eq, PartialEq)]
|
||||
struct TestLinkId(u8, u8);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestLink {
|
||||
id: (u8, u8),
|
||||
detached: Rc<RefCell<u8>>,
|
||||
}
|
||||
|
||||
impl TestLink {
|
||||
fn new(a: u8, b: u8) -> TestLink {
|
||||
TestLink {
|
||||
id: (a, b),
|
||||
detached: Rc::new(RefCell::new(0)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Link for TestLink {
|
||||
type Id = TestLinkId;
|
||||
|
||||
fn id(&self) -> Self::Id {
|
||||
TestLinkId(self.id.0, self.id.1)
|
||||
}
|
||||
|
||||
fn detach(self) -> Result<(), ProgramError> {
|
||||
*self.detached.borrow_mut() += 1;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_link_map() {
|
||||
let mut links = LinkMap::new();
|
||||
let l1 = TestLink::new(1, 2);
|
||||
let l1_detached = Rc::clone(&l1.detached);
|
||||
let l2 = TestLink::new(1, 3);
|
||||
let l2_detached = Rc::clone(&l2.detached);
|
||||
|
||||
let id1 = links.insert(l1).unwrap();
|
||||
let id2 = links.insert(l2).unwrap();
|
||||
|
||||
assert!(*l1_detached.borrow() == 0);
|
||||
assert!(*l2_detached.borrow() == 0);
|
||||
|
||||
assert!(links.remove(id1).is_ok());
|
||||
assert!(*l1_detached.borrow() == 1);
|
||||
assert!(*l2_detached.borrow() == 0);
|
||||
|
||||
assert!(links.remove(id2).is_ok());
|
||||
assert!(*l1_detached.borrow() == 1);
|
||||
assert!(*l2_detached.borrow() == 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_already_attached() {
|
||||
let mut links = LinkMap::new();
|
||||
|
||||
links.insert(TestLink::new(1, 2)).unwrap();
|
||||
assert!(matches!(
|
||||
links.insert(TestLink::new(1, 2)),
|
||||
Err(ProgramError::AlreadyAttached)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_attached() {
|
||||
let mut links = LinkMap::new();
|
||||
|
||||
let l1 = TestLink::new(1, 2);
|
||||
let l1_id1 = l1.id();
|
||||
let l1_id2 = l1.id();
|
||||
links.insert(TestLink::new(1, 2)).unwrap();
|
||||
links.remove(l1_id1).unwrap();
|
||||
assert!(matches!(
|
||||
links.remove(l1_id2),
|
||||
Err(ProgramError::NotAttached)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drop_detach() {
|
||||
let l1 = TestLink::new(1, 2);
|
||||
let l1_detached = Rc::clone(&l1.detached);
|
||||
let l2 = TestLink::new(1, 3);
|
||||
let l2_detached = Rc::clone(&l2.detached);
|
||||
|
||||
{
|
||||
let mut links = LinkMap::new();
|
||||
let id1 = links.insert(l1).unwrap();
|
||||
links.insert(l2).unwrap();
|
||||
// manually remove one link
|
||||
assert!(links.remove(id1).is_ok());
|
||||
assert!(*l1_detached.borrow() == 1);
|
||||
assert!(*l2_detached.borrow() == 0);
|
||||
}
|
||||
// remove the other on drop
|
||||
assert!(*l1_detached.borrow() == 1);
|
||||
assert!(*l2_detached.borrow() == 1);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue