rustix/backend/linux_raw/event/
syscalls.rs

1//! linux_raw syscalls supporting `rustix::event`.
2//!
3//! # Safety
4//!
5//! See the `rustix::backend` module documentation for details.
6#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)]
7
8use crate::backend::c;
9use crate::backend::conv::{
10    by_ref, c_int, c_uint, ret, ret_c_int, ret_error, ret_owned_fd, ret_usize, slice_mut, zero,
11};
12use crate::event::{epoll, EventfdFlags, FdSetElement, PollFd};
13use crate::fd::{BorrowedFd, OwnedFd};
14use crate::io;
15use crate::utils::as_mut_ptr;
16#[cfg(feature = "alloc")]
17use core::mem::MaybeUninit;
18use core::ptr::null_mut;
19use linux_raw_sys::general::{EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD};
20#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
21use {
22    crate::backend::conv::{opt_ref, size_of},
23    linux_raw_sys::general::{__kernel_timespec, kernel_sigset_t},
24};
25
26#[inline]
27pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: c::c_int) -> io::Result<usize> {
28    let (fds_addr_mut, fds_len) = slice_mut(fds);
29
30    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
31    unsafe {
32        let timeout = if timeout >= 0 {
33            Some(__kernel_timespec {
34                tv_sec: (timeout as i64) / 1000,
35                tv_nsec: (timeout as i64) % 1000 * 1_000_000,
36            })
37        } else {
38            None
39        };
40        ret_usize(syscall!(
41            __NR_ppoll,
42            fds_addr_mut,
43            fds_len,
44            opt_ref(timeout.as_ref()),
45            zero(),
46            size_of::<kernel_sigset_t, _>()
47        ))
48    }
49    #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
50    unsafe {
51        ret_usize(syscall!(__NR_poll, fds_addr_mut, fds_len, c_int(timeout)))
52    }
53}
54
55pub(crate) unsafe fn select(
56    nfds: i32,
57    readfds: Option<&mut [FdSetElement]>,
58    writefds: Option<&mut [FdSetElement]>,
59    exceptfds: Option<&mut [FdSetElement]>,
60    timeout: Option<&crate::timespec::Timespec>,
61) -> io::Result<i32> {
62    let len = crate::event::fd_set_num_elements_for_bitvector(nfds);
63
64    let readfds = match readfds {
65        Some(readfds) => {
66            assert!(readfds.len() >= len);
67            readfds.as_mut_ptr()
68        }
69        None => null_mut(),
70    };
71    let writefds = match writefds {
72        Some(writefds) => {
73            assert!(writefds.len() >= len);
74            writefds.as_mut_ptr()
75        }
76        None => null_mut(),
77    };
78    let exceptfds = match exceptfds {
79        Some(exceptfds) => {
80            assert!(exceptfds.len() >= len);
81            exceptfds.as_mut_ptr()
82        }
83        None => null_mut(),
84    };
85
86    // Linux's `pselect6` mutates the timeout argument. Our public interface
87    // does not do this, because it's not portable to other platforms, so we
88    // create a temporary value to hide this behavior.
89    let mut timeout_data;
90    let timeout_ptr = match timeout {
91        Some(timeout) => {
92            timeout_data = *timeout;
93            as_mut_ptr(&mut timeout_data)
94        }
95        None => null_mut(),
96    };
97
98    #[cfg(any(
99        target_arch = "arm",
100        target_arch = "powerpc",
101        target_arch = "sparc",
102        target_arch = "csky",
103        target_arch = "x86",
104        target_arch = "mips32r6",
105        target_arch = "riscv32",
106        target_arch = "mips"
107    ))]
108    {
109        ret_c_int(syscall!(
110            __NR_pselect6_time64,
111            c_int(nfds),
112            readfds,
113            writefds,
114            exceptfds,
115            timeout_ptr,
116            zero()
117        ))
118    }
119
120    #[cfg(target_pointer_width = "64")]
121    {
122        ret_c_int(syscall!(
123            __NR_pselect6,
124            c_int(nfds),
125            readfds,
126            writefds,
127            exceptfds,
128            timeout_ptr,
129            zero()
130        ))
131    }
132}
133
134#[inline]
135pub(crate) fn epoll_create(flags: epoll::CreateFlags) -> io::Result<OwnedFd> {
136    // SAFETY: `__NR_epoll_create1` doesn't access any user memory.
137    unsafe { ret_owned_fd(syscall_readonly!(__NR_epoll_create1, flags)) }
138}
139
140#[inline]
141pub(crate) fn epoll_add(
142    epfd: BorrowedFd<'_>,
143    fd: BorrowedFd<'_>,
144    event: &epoll::Event,
145) -> io::Result<()> {
146    // SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_ADD` doesn't modify any user
147    // memory, and it only reads from `event`.
148    unsafe {
149        ret(syscall_readonly!(
150            __NR_epoll_ctl,
151            epfd,
152            c_uint(EPOLL_CTL_ADD),
153            fd,
154            by_ref(event)
155        ))
156    }
157}
158
159#[inline]
160pub(crate) fn epoll_mod(
161    epfd: BorrowedFd<'_>,
162    fd: BorrowedFd<'_>,
163    event: &epoll::Event,
164) -> io::Result<()> {
165    // SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_MOD` doesn't modify any user
166    // memory, and it only reads from `event`.
167    unsafe {
168        ret(syscall_readonly!(
169            __NR_epoll_ctl,
170            epfd,
171            c_uint(EPOLL_CTL_MOD),
172            fd,
173            by_ref(event)
174        ))
175    }
176}
177
178#[inline]
179pub(crate) fn epoll_del(epfd: BorrowedFd<'_>, fd: BorrowedFd<'_>) -> io::Result<()> {
180    // SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_DEL` doesn't access any user
181    // memory.
182    unsafe {
183        ret(syscall_readonly!(
184            __NR_epoll_ctl,
185            epfd,
186            c_uint(EPOLL_CTL_DEL),
187            fd,
188            zero()
189        ))
190    }
191}
192
193#[cfg(feature = "alloc")]
194#[inline]
195pub(crate) fn epoll_wait(
196    epfd: BorrowedFd<'_>,
197    events: &mut [MaybeUninit<crate::event::epoll::Event>],
198    timeout: c::c_int,
199) -> io::Result<usize> {
200    let (buf_addr_mut, buf_len) = slice_mut(events);
201    // SAFETY: `__NR_epoll_wait` doesn't access any user memory outside of
202    // the `events` array.
203    #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
204    unsafe {
205        ret_usize(syscall!(
206            __NR_epoll_wait,
207            epfd,
208            buf_addr_mut,
209            buf_len,
210            c_int(timeout)
211        ))
212    }
213    // SAFETY: `__NR_epoll_pwait` doesn't access any user memory outside of
214    // the `events` array, as we don't pass it a `sigmask`.
215    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
216    unsafe {
217        ret_usize(syscall!(
218            __NR_epoll_pwait,
219            epfd,
220            buf_addr_mut,
221            buf_len,
222            c_int(timeout),
223            zero()
224        ))
225    }
226}
227
228#[inline]
229pub(crate) fn eventfd(initval: u32, flags: EventfdFlags) -> io::Result<OwnedFd> {
230    unsafe { ret_owned_fd(syscall_readonly!(__NR_eventfd2, c_uint(initval), flags)) }
231}
232
233#[inline]
234pub(crate) fn pause() {
235    unsafe {
236        #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
237        let error = ret_error(syscall_readonly!(
238            __NR_ppoll,
239            zero(),
240            zero(),
241            zero(),
242            zero()
243        ));
244
245        #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
246        let error = ret_error(syscall_readonly!(__NR_pause));
247
248        debug_assert_eq!(error, io::Errno::INTR);
249    }
250}