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::{
perf_event_header, perf_event_mmap_page,
perf_event_type::{PERF_RECORD_LOST, PERF_RECORD_SAMPLE},
PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE,
};
use bytes::BytesMut;
use libc::{MAP_SHARED, PROT_READ, PROT_WRITE};
@ -16,7 +15,7 @@ use thiserror::Error;
use crate::{
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.
@ -120,8 +119,8 @@ impl PerfBuffer {
fd,
};
perf_event_ioctl(perf_buf.fd.as_fd(), PERF_EVENT_IOC_ENABLE, 0)
.map_err(|(_, io_error)| PerfBufferError::PerfEventEnableError { io_error })?;
perf_event_ioctl(perf_buf.fd.as_fd(), PerfEventIoctlRequest::Enable)
.map_err(|io_error| PerfBufferError::PerfEventEnableError { io_error })?;
Ok(perf_buf)
}
@ -260,7 +259,7 @@ impl AsFd for PerfBuffer {
impl Drop for PerfBuffer {
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) {
let buf: *mut _ = buf;
override_syscall(|call| match call {
Syscall::PerfEventOpen { .. } | Syscall::PerfEventIoctl { .. } => {
Ok(crate::MockableFd::mock_signed_fd().into())
}
Syscall::PerfEventOpen { .. } => Ok(crate::MockableFd::mock_signed_fd().into()),
Syscall::PerfEventIoctl { .. } => Ok(0),
call => panic!("unexpected syscall: {:?}", call),
});
TEST_MMAP_RET.with(|ret| *ret.borrow_mut() = buf.cast());

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

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

@ -13,7 +13,7 @@ use aya_obj::generated::{
};
use libc::pid_t;
use super::{syscall, SysResult, Syscall};
use super::{syscall, PerfEventIoctlRequest, Syscall};
#[allow(clippy::too_many_arguments)]
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)
}
pub(crate) fn perf_event_ioctl(fd: BorrowedFd<'_>, request: u32, arg: c_int) -> SysResult {
syscall(Syscall::PerfEventIoctl { fd, request, arg })
pub(crate) fn perf_event_ioctl(
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(

Loading…
Cancel
Save