aya: encode perf_event ioctl contract

Enumerate the possible ioctls in an enum and bake in the knowledge that
they all return 0 on success and -1 on error.
reviewable/pr1194/r1
Tamir Duberstein 2 weeks ago
parent eee7975ce4
commit de1e80c1d1

@ -8,7 +8,6 @@ use std::{
use aya_obj::generated::{ use aya_obj::generated::{
perf_event_header, perf_event_mmap_page, perf_event_header, perf_event_mmap_page,
perf_event_type::{PERF_RECORD_LOST, PERF_RECORD_SAMPLE}, perf_event_type::{PERF_RECORD_LOST, PERF_RECORD_SAMPLE},
PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE,
}; };
use bytes::BytesMut; use bytes::BytesMut;
use libc::{MAP_SHARED, PROT_READ, PROT_WRITE}; use libc::{MAP_SHARED, PROT_READ, PROT_WRITE};
@ -16,7 +15,7 @@ use thiserror::Error;
use crate::{ use crate::{
maps::MMap, maps::MMap,
sys::{perf_event_ioctl, perf_event_open_bpf, SysResult, SyscallError}, sys::{perf_event_ioctl, perf_event_open_bpf, PerfEventIoctlRequest, SyscallError},
}; };
/// Perf buffer error. /// Perf buffer error.
@ -120,8 +119,8 @@ impl PerfBuffer {
fd, fd,
}; };
perf_event_ioctl(perf_buf.fd.as_fd(), PERF_EVENT_IOC_ENABLE, 0) perf_event_ioctl(perf_buf.fd.as_fd(), PerfEventIoctlRequest::Enable)
.map_err(|(_, io_error)| PerfBufferError::PerfEventEnableError { io_error })?; .map_err(|io_error| PerfBufferError::PerfEventEnableError { io_error })?;
Ok(perf_buf) Ok(perf_buf)
} }
@ -260,7 +259,7 @@ impl AsFd for PerfBuffer {
impl Drop for PerfBuffer { impl Drop for PerfBuffer {
fn drop(&mut self) { fn drop(&mut self) {
let _: SysResult = perf_event_ioctl(self.fd.as_fd(), PERF_EVENT_IOC_DISABLE, 0); let _: io::Result<()> = perf_event_ioctl(self.fd.as_fd(), PerfEventIoctlRequest::Disable);
} }
} }
@ -289,9 +288,8 @@ mod tests {
fn fake_mmap(buf: &mut MMappedBuf) { fn fake_mmap(buf: &mut MMappedBuf) {
let buf: *mut _ = buf; let buf: *mut _ = buf;
override_syscall(|call| match call { override_syscall(|call| match call {
Syscall::PerfEventOpen { .. } | Syscall::PerfEventIoctl { .. } => { Syscall::PerfEventOpen { .. } => Ok(crate::MockableFd::mock_signed_fd().into()),
Ok(crate::MockableFd::mock_signed_fd().into()) Syscall::PerfEventIoctl { .. } => Ok(0),
}
call => panic!("unexpected syscall: {:?}", call), call => panic!("unexpected syscall: {:?}", call),
}); });
TEST_MMAP_RET.with(|ret| *ret.borrow_mut() = buf.cast()); TEST_MMAP_RET.with(|ret| *ret.borrow_mut() = buf.cast());

@ -1,11 +1,11 @@
//! Perf attach links. //! Perf attach links.
use std::os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, RawFd}; use std::{
io,
use aya_obj::generated::{ os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, RawFd},
bpf_attach_type::BPF_PERF_EVENT, PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE,
PERF_EVENT_IOC_SET_BPF,
}; };
use aya_obj::generated::bpf_attach_type::BPF_PERF_EVENT;
use crate::{ use crate::{
programs::{ programs::{
id_as_key, id_as_key,
@ -14,7 +14,7 @@ use crate::{
}, },
sys::{ sys::{
bpf_link_create, is_bpf_cookie_supported, perf_event_ioctl, BpfLinkCreateArgs, LinkTarget, bpf_link_create, is_bpf_cookie_supported, perf_event_ioctl, BpfLinkCreateArgs, LinkTarget,
SysResult, SyscallError, PerfEventIoctlRequest, SyscallError,
}, },
FEATURES, FEATURES,
}; };
@ -71,7 +71,7 @@ impl Link for PerfLink {
fn detach(self) -> Result<(), ProgramError> { fn detach(self) -> Result<(), ProgramError> {
let Self { perf_fd, event } = self; let Self { perf_fd, event } = self;
let _: SysResult = perf_event_ioctl(perf_fd.as_fd(), PERF_EVENT_IOC_DISABLE, 0); let _: io::Result<()> = perf_event_ioctl(perf_fd.as_fd(), PerfEventIoctlRequest::Disable);
if let Some(event) = event { if let Some(event) = event {
let _: Result<_, _> = detach_debug_fs(event); let _: Result<_, _> = detach_debug_fs(event);
} }
@ -121,13 +121,13 @@ fn perf_attach_either(
fd: crate::MockableFd, fd: crate::MockableFd,
event: Option<ProbeEvent>, event: Option<ProbeEvent>,
) -> Result<PerfLinkInner, ProgramError> { ) -> Result<PerfLinkInner, ProgramError> {
perf_event_ioctl(fd.as_fd(), PERF_EVENT_IOC_SET_BPF, prog_fd.as_raw_fd()).map_err( perf_event_ioctl(fd.as_fd(), PerfEventIoctlRequest::SetBpf(prog_fd)).map_err(|io_error| {
|(_, io_error)| SyscallError { SyscallError {
call: "PERF_EVENT_IOC_SET_BPF", call: "PERF_EVENT_IOC_SET_BPF",
io_error, io_error,
}, }
)?; })?;
perf_event_ioctl(fd.as_fd(), PERF_EVENT_IOC_ENABLE, 0).map_err(|(_, io_error)| { perf_event_ioctl(fd.as_fd(), PerfEventIoctlRequest::Enable).map_err(|io_error| {
SyscallError { SyscallError {
call: "PERF_EVENT_IOC_ENABLE", call: "PERF_EVENT_IOC_ENABLE",
io_error, io_error,

@ -9,15 +9,14 @@ mod fake;
use std::{ use std::{
ffi::{c_int, c_long, c_void}, ffi::{c_int, c_long, c_void},
io, mem, io,
os::fd::{AsRawFd as _, BorrowedFd, OwnedFd}, os::fd::{BorrowedFd, OwnedFd},
}; };
use aya_obj::generated::{bpf_attr, bpf_cmd, perf_event_attr}; use aya_obj::generated::{bpf_attr, bpf_cmd, perf_event_attr};
pub(crate) use bpf::*; pub(crate) use bpf::*;
#[cfg(test)] #[cfg(test)]
pub(crate) use fake::*; pub(crate) use fake::*;
use libc::{pid_t, SYS_bpf, SYS_ioctl, SYS_perf_event_open};
#[doc(hidden)] #[doc(hidden)]
pub use netlink::netlink_set_link_up; pub use netlink::netlink_set_link_up;
pub(crate) use netlink::*; pub(crate) use netlink::*;
@ -26,6 +25,15 @@ use thiserror::Error;
pub(crate) type SysResult = Result<c_long, (c_long, io::Error)>; pub(crate) type SysResult = Result<c_long, (c_long, io::Error)>;
#[cfg_attr(test, expect(dead_code))]
#[derive(Debug)]
pub(crate) enum PerfEventIoctlRequest<'a> {
Enable,
Disable,
SetBpf(BorrowedFd<'a>),
}
#[cfg_attr(test, expect(dead_code))]
pub(crate) enum Syscall<'a> { pub(crate) enum Syscall<'a> {
Ebpf { Ebpf {
cmd: bpf_cmd, cmd: bpf_cmd,
@ -33,15 +41,14 @@ pub(crate) enum Syscall<'a> {
}, },
PerfEventOpen { PerfEventOpen {
attr: perf_event_attr, attr: perf_event_attr,
pid: pid_t, pid: libc::pid_t,
cpu: i32, cpu: i32,
group: i32, group: i32,
flags: u32, flags: u32,
}, },
PerfEventIoctl { PerfEventIoctl {
fd: BorrowedFd<'a>, fd: BorrowedFd<'a>,
request: u32, request: PerfEventIoctlRequest<'a>,
arg: c_int,
}, },
} }
@ -78,11 +85,10 @@ impl std::fmt::Debug for Syscall<'_> {
.field("group", group) .field("group", group)
.field("flags", flags) .field("flags", flags)
.finish(), .finish(),
Self::PerfEventIoctl { fd, request, arg } => f Self::PerfEventIoctl { fd, request } => f
.debug_struct("Syscall::PerfEventIoctl") .debug_struct("Syscall::PerfEventIoctl")
.field("fd", fd) .field("fd", fd)
.field("request", request) .field("request", request)
.field("arg", arg)
.finish(), .finish(),
} }
} }
@ -90,14 +96,16 @@ impl std::fmt::Debug for Syscall<'_> {
fn syscall(call: Syscall<'_>) -> SysResult { fn syscall(call: Syscall<'_>) -> SysResult {
#[cfg(test)] #[cfg(test)]
return TEST_SYSCALL.with(|test_impl| unsafe { test_impl.borrow()(call) }); {
TEST_SYSCALL.with(|test_impl| unsafe { test_impl.borrow()(call) })
}
#[cfg_attr(test, allow(unreachable_code))] #[cfg(not(test))]
{ {
let ret = unsafe { let ret = unsafe {
match call { match call {
Syscall::Ebpf { cmd, attr } => { Syscall::Ebpf { cmd, attr } => {
libc::syscall(SYS_bpf, cmd, attr, mem::size_of::<bpf_attr>()) libc::syscall(libc::SYS_bpf, cmd, attr, std::mem::size_of::<bpf_attr>())
} }
Syscall::PerfEventOpen { Syscall::PerfEventOpen {
attr, attr,
@ -105,9 +113,29 @@ fn syscall(call: Syscall<'_>) -> SysResult {
cpu, cpu,
group, group,
flags, flags,
} => libc::syscall(SYS_perf_event_open, &attr, pid, cpu, group, flags), } => libc::syscall(libc::SYS_perf_event_open, &attr, pid, cpu, group, flags),
Syscall::PerfEventIoctl { fd, request, arg } => { Syscall::PerfEventIoctl { fd, request } => {
libc::syscall(SYS_ioctl, fd.as_raw_fd(), request, arg) use std::os::fd::AsRawFd as _;
let fd = fd.as_raw_fd();
match request {
PerfEventIoctlRequest::Enable => libc::syscall(
libc::SYS_ioctl,
fd,
aya_obj::generated::PERF_EVENT_IOC_ENABLE,
),
PerfEventIoctlRequest::Disable => libc::syscall(
libc::SYS_ioctl,
fd,
aya_obj::generated::PERF_EVENT_IOC_DISABLE,
),
PerfEventIoctlRequest::SetBpf(bpf_fd) => libc::syscall(
libc::SYS_ioctl,
fd,
aya_obj::generated::PERF_EVENT_IOC_SET_BPF,
bpf_fd.as_raw_fd(),
),
}
} }
} }
}; };
@ -128,11 +156,17 @@ pub(crate) unsafe fn mmap(
fd: BorrowedFd<'_>, fd: BorrowedFd<'_>,
offset: libc::off_t, offset: libc::off_t,
) -> *mut c_void { ) -> *mut c_void {
#[cfg(test)]
{
TEST_MMAP_RET.with(|ret| *ret.borrow())
}
#[cfg(not(test))] #[cfg(not(test))]
return libc::mmap(addr, len, prot, flags, fd.as_raw_fd(), offset); {
use std::os::fd::AsRawFd as _;
#[cfg(test)] libc::mmap(addr, len, prot, flags, fd.as_raw_fd(), offset)
TEST_MMAP_RET.with(|ret| *ret.borrow()) }
} }
#[cfg_attr(test, allow(unused_variables))] #[cfg_attr(test, allow(unused_variables))]

@ -13,7 +13,7 @@ use aya_obj::generated::{
}; };
use libc::pid_t; use libc::pid_t;
use super::{syscall, SysResult, Syscall}; use super::{syscall, PerfEventIoctlRequest, Syscall};
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub(crate) fn perf_event_open( pub(crate) fn perf_event_open(
@ -104,8 +104,18 @@ pub(crate) fn perf_event_open_trace_point(
perf_event_sys(attr, pid, cpu, PERF_FLAG_FD_CLOEXEC) perf_event_sys(attr, pid, cpu, PERF_FLAG_FD_CLOEXEC)
} }
pub(crate) fn perf_event_ioctl(fd: BorrowedFd<'_>, request: u32, arg: c_int) -> SysResult { pub(crate) fn perf_event_ioctl(
syscall(Syscall::PerfEventIoctl { fd, request, arg }) fd: BorrowedFd<'_>,
request: PerfEventIoctlRequest<'_>,
) -> io::Result<()> {
syscall(Syscall::PerfEventIoctl { fd, request })
.map(|code| {
assert_eq!(code, 0);
})
.map_err(|(code, io_error)| {
assert_eq!(code, -1);
io_error
})
} }
fn perf_event_sys( fn perf_event_sys(

Loading…
Cancel
Save