summaryrefslogtreecommitdiff
path: root/vendor/rustix/src/backend/linux_raw/event/syscalls.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/rustix/src/backend/linux_raw/event/syscalls.rs')
-rw-r--r--vendor/rustix/src/backend/linux_raw/event/syscalls.rs358
1 files changed, 358 insertions, 0 deletions
diff --git a/vendor/rustix/src/backend/linux_raw/event/syscalls.rs b/vendor/rustix/src/backend/linux_raw/event/syscalls.rs
new file mode 100644
index 00000000..8a4e24bd
--- /dev/null
+++ b/vendor/rustix/src/backend/linux_raw/event/syscalls.rs
@@ -0,0 +1,358 @@
+//! linux_raw syscalls supporting `rustix::event`.
+//!
+//! # Safety
+//!
+//! See the `rustix::backend` module documentation for details.
+#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)]
+
+use crate::backend::conv::{
+ by_ref, c_int, c_uint, opt_mut, opt_ref, pass_usize, ret, ret_c_int, ret_error, ret_owned_fd,
+ ret_usize, size_of, slice_mut, zero,
+};
+use crate::event::{epoll, EventfdFlags, FdSetElement, PollFd, Timespec};
+use crate::fd::{BorrowedFd, OwnedFd};
+use crate::io;
+use core::ptr::null_mut;
+use linux_raw_sys::general::{kernel_sigset_t, EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD};
+
+#[inline]
+pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: Option<&Timespec>) -> io::Result<usize> {
+ let (fds_addr_mut, fds_len) = slice_mut(fds);
+
+ #[cfg(target_pointer_width = "32")]
+ unsafe {
+ // If we don't have Linux 5.1, and the timeout fits in a
+ // `__kernel_old_timespec`, use plain `ppoll`.
+ //
+ // We do this unconditionally, rather than trying `ppoll_time64` and
+ // falling back on `Errno::NOSYS`, because seccomp configurations will
+ // sometimes abort the process on syscalls they don't recognize.
+ #[cfg(not(feature = "linux_5_1"))]
+ {
+ use linux_raw_sys::general::__kernel_old_timespec;
+
+ // If we don't have a timeout, or if we can convert the timeout to
+ // a `__kernel_old_timespec`, the use `__NR_ppoll`.
+ fn convert(timeout: &Timespec) -> Option<__kernel_old_timespec> {
+ Some(__kernel_old_timespec {
+ tv_sec: timeout.tv_sec.try_into().ok()?,
+ tv_nsec: timeout.tv_nsec.try_into().ok()?,
+ })
+ }
+ let old_timeout = if let Some(timeout) = timeout {
+ match convert(timeout) {
+ // Could not convert timeout.
+ None => None,
+ // Could convert timeout. Ok!
+ Some(old_timeout) => Some(Some(old_timeout)),
+ }
+ } else {
+ // No timeout. Ok!
+ Some(None)
+ };
+ if let Some(mut old_timeout) = old_timeout {
+ // Call `ppoll`.
+ //
+ // Linux's `ppoll` mutates the timeout argument. Our public
+ // interface does not do this, because it's not portable to other
+ // platforms, so we create a temporary value to hide this behavior.
+ return ret_usize(syscall!(
+ __NR_ppoll,
+ fds_addr_mut,
+ fds_len,
+ opt_mut(old_timeout.as_mut()),
+ zero(),
+ size_of::<kernel_sigset_t, _>()
+ ));
+ }
+ }
+
+ // We either have Linux 5.1 or the timeout didn't fit in
+ // `__kernel_old_timespec` so `__NR_ppoll_time64` will either
+ // succeed or fail due to our having no other options.
+
+ // Call `ppoll_time64`.
+ //
+ // Linux's `ppoll_time64` mutates the timeout argument. Our public
+ // interface does not do this, because it's not portable to other
+ // platforms, so we create a temporary value to hide this behavior.
+ ret_usize(syscall!(
+ __NR_ppoll_time64,
+ fds_addr_mut,
+ fds_len,
+ opt_mut(timeout.copied().as_mut()),
+ zero(),
+ size_of::<kernel_sigset_t, _>()
+ ))
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ unsafe {
+ // Call `ppoll`.
+ //
+ // Linux's `ppoll` mutates the timeout argument. Our public interface
+ // does not do this, because it's not portable to other platforms, so
+ // we create a temporary value to hide this behavior.
+ ret_usize(syscall!(
+ __NR_ppoll,
+ fds_addr_mut,
+ fds_len,
+ opt_mut(timeout.copied().as_mut()),
+ zero(),
+ size_of::<kernel_sigset_t, _>()
+ ))
+ }
+}
+
+pub(crate) unsafe fn select(
+ nfds: i32,
+ readfds: Option<&mut [FdSetElement]>,
+ writefds: Option<&mut [FdSetElement]>,
+ exceptfds: Option<&mut [FdSetElement]>,
+ timeout: Option<&crate::timespec::Timespec>,
+) -> io::Result<i32> {
+ let len = crate::event::fd_set_num_elements_for_bitvector(nfds);
+
+ let readfds = match readfds {
+ Some(readfds) => {
+ assert!(readfds.len() >= len);
+ readfds.as_mut_ptr()
+ }
+ None => null_mut(),
+ };
+ let writefds = match writefds {
+ Some(writefds) => {
+ assert!(writefds.len() >= len);
+ writefds.as_mut_ptr()
+ }
+ None => null_mut(),
+ };
+ let exceptfds = match exceptfds {
+ Some(exceptfds) => {
+ assert!(exceptfds.len() >= len);
+ exceptfds.as_mut_ptr()
+ }
+ None => null_mut(),
+ };
+
+ #[cfg(target_pointer_width = "32")]
+ {
+ // If we don't have Linux 5.1, and the timeout fits in a
+ // `__kernel_old_timespec`, use plain `pselect6`.
+ //
+ // We do this unconditionally, rather than trying `pselect6_time64` and
+ // falling back on `Errno::NOSYS`, because seccomp configurations will
+ // sometimes abort the process on syscalls they don't recognize.
+ #[cfg(not(feature = "linux_5_1"))]
+ {
+ use linux_raw_sys::general::__kernel_old_timespec;
+
+ // If we don't have a timeout, or if we can convert the timeout to
+ // a `__kernel_old_timespec`, the use `__NR_pselect6`.
+ fn convert(timeout: &Timespec) -> Option<__kernel_old_timespec> {
+ Some(__kernel_old_timespec {
+ tv_sec: timeout.tv_sec.try_into().ok()?,
+ tv_nsec: timeout.tv_nsec.try_into().ok()?,
+ })
+ }
+ let old_timeout = if let Some(timeout) = timeout {
+ match convert(timeout) {
+ // Could not convert timeout.
+ None => None,
+ // Could convert timeout. Ok!
+ Some(old_timeout) => Some(Some(old_timeout)),
+ }
+ } else {
+ // No timeout. Ok!
+ Some(None)
+ };
+ if let Some(mut old_timeout) = old_timeout {
+ // Call `pselect6`.
+ //
+ // Linux's `pselect6` mutates the timeout argument. Our public
+ // interface does not do this, because it's not portable to other
+ // platforms, so we create a temporary value to hide this behavior.
+ return ret_c_int(syscall!(
+ __NR_pselect6,
+ c_int(nfds),
+ readfds,
+ writefds,
+ exceptfds,
+ opt_mut(old_timeout.as_mut()),
+ zero()
+ ));
+ }
+ }
+
+ // We either have Linux 5.1 or the timeout didn't fit in
+ // `__kernel_old_timespec` so `__NR_pselect6_time64` will either
+ // succeed or fail due to our having no other options.
+
+ // Call `pselect6_time64`.
+ //
+ // Linux's `pselect6_time64` mutates the timeout argument. Our public
+ // interface does not do this, because it's not portable to other
+ // platforms, so we create a temporary value to hide this behavior.
+ ret_c_int(syscall!(
+ __NR_pselect6_time64,
+ c_int(nfds),
+ readfds,
+ writefds,
+ exceptfds,
+ opt_mut(timeout.copied().as_mut()),
+ zero()
+ ))
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ {
+ // Call `pselect6`.
+ //
+ // Linux's `pselect6` mutates the timeout argument. Our public interface
+ // does not do this, because it's not portable to other platforms, so we
+ // create a temporary value to hide this behavior.
+ ret_c_int(syscall!(
+ __NR_pselect6,
+ c_int(nfds),
+ readfds,
+ writefds,
+ exceptfds,
+ opt_mut(timeout.copied().as_mut()),
+ zero()
+ ))
+ }
+}
+
+#[inline]
+pub(crate) fn epoll_create(flags: epoll::CreateFlags) -> io::Result<OwnedFd> {
+ // SAFETY: `__NR_epoll_create1` doesn't access any user memory.
+ unsafe { ret_owned_fd(syscall_readonly!(__NR_epoll_create1, flags)) }
+}
+
+#[inline]
+pub(crate) fn epoll_add(
+ epfd: BorrowedFd<'_>,
+ fd: BorrowedFd<'_>,
+ event: &epoll::Event,
+) -> io::Result<()> {
+ // SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_ADD` doesn't modify any user
+ // memory, and it only reads from `event`.
+ unsafe {
+ ret(syscall_readonly!(
+ __NR_epoll_ctl,
+ epfd,
+ c_uint(EPOLL_CTL_ADD),
+ fd,
+ by_ref(event)
+ ))
+ }
+}
+
+#[inline]
+pub(crate) fn epoll_mod(
+ epfd: BorrowedFd<'_>,
+ fd: BorrowedFd<'_>,
+ event: &epoll::Event,
+) -> io::Result<()> {
+ // SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_MOD` doesn't modify any user
+ // memory, and it only reads from `event`.
+ unsafe {
+ ret(syscall_readonly!(
+ __NR_epoll_ctl,
+ epfd,
+ c_uint(EPOLL_CTL_MOD),
+ fd,
+ by_ref(event)
+ ))
+ }
+}
+
+#[inline]
+pub(crate) fn epoll_del(epfd: BorrowedFd<'_>, fd: BorrowedFd<'_>) -> io::Result<()> {
+ // SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_DEL` doesn't access any user
+ // memory.
+ unsafe {
+ ret(syscall_readonly!(
+ __NR_epoll_ctl,
+ epfd,
+ c_uint(EPOLL_CTL_DEL),
+ fd,
+ zero()
+ ))
+ }
+}
+
+#[inline]
+pub(crate) unsafe fn epoll_wait(
+ epfd: BorrowedFd<'_>,
+ events: (*mut crate::event::epoll::Event, usize),
+ timeout: Option<&Timespec>,
+) -> io::Result<usize> {
+ // If we don't have Linux 5.1, and the timeout fits in an `i32`, use plain
+ // `epoll_pwait`.
+ //
+ // We do this unconditionally, rather than trying `epoll_pwait2` and
+ // falling back on `Errno::NOSYS`, because seccomp configurations will
+ // sometimes abort the process on syscalls they don't recognize.
+ #[cfg(not(feature = "linux_5_11"))]
+ {
+ // If we don't have a timeout, or if we can convert the timeout to an
+ // `i32`, the use `__NR_epoll_pwait`.
+ let old_timeout = if let Some(timeout) = timeout {
+ // Try to convert the timeout; if this is `Some`, we're ok!
+ timeout.as_c_int_millis()
+ } else {
+ // No timeout. Ok!
+ Some(-1)
+ };
+ if let Some(old_timeout) = old_timeout {
+ // Call `epoll_pwait`.
+ return ret_usize(syscall!(
+ __NR_epoll_pwait,
+ epfd,
+ events.0,
+ pass_usize(events.1),
+ c_int(old_timeout),
+ zero()
+ ));
+ }
+ }
+
+ // Call `epoll_pwait2`.
+ //
+ // We either have Linux 5.1 or the timeout didn't fit in an `i32`, so
+ // `__NR_epoll_pwait2` will either succeed or fail due to our having no
+ // other options.
+ ret_usize(syscall!(
+ __NR_epoll_pwait2,
+ epfd,
+ events.0,
+ pass_usize(events.1),
+ opt_ref(timeout),
+ zero()
+ ))
+}
+
+#[inline]
+pub(crate) fn eventfd(initval: u32, flags: EventfdFlags) -> io::Result<OwnedFd> {
+ unsafe { ret_owned_fd(syscall_readonly!(__NR_eventfd2, c_uint(initval), flags)) }
+}
+
+#[inline]
+pub(crate) fn pause() {
+ unsafe {
+ #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
+ let error = ret_error(syscall_readonly!(
+ __NR_ppoll,
+ zero(),
+ zero(),
+ zero(),
+ zero()
+ ));
+
+ #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
+ let error = ret_error(syscall_readonly!(__NR_pause));
+
+ debug_assert_eq!(error, io::Errno::INTR);
+ }
+}