1 //! linux_raw syscalls supporting `rustix::thread`.
2 //!
3 //! # Safety
4 //!
5 //! See the `rustix::backend` module documentation for details.
6 #![allow(unsafe_code)]
7 #![allow(clippy::undocumented_unsafe_blocks)]
8
9 use super::super::c;
10 use super::super::conv::{
11 by_ref, c_int, c_uint, ret, ret_c_int, ret_usize, ret_usize_infallible, zero,
12 };
13 use crate::fd::BorrowedFd;
14 use crate::io;
15 use crate::process::{Pid, RawNonZeroPid};
16 use crate::thread::{ClockId, FutexFlags, FutexOperation, NanosleepRelativeResult, Timespec};
17 use core::mem::MaybeUninit;
18 use linux_raw_sys::general::{__kernel_pid_t, __kernel_timespec, TIMER_ABSTIME};
19 #[cfg(target_pointer_width = "32")]
20 use {
21 core::convert::TryInto, core::ptr, linux_raw_sys::general::timespec as __kernel_old_timespec,
22 };
23
24 #[inline]
clock_nanosleep_relative( id: ClockId, req: &__kernel_timespec, ) -> NanosleepRelativeResult25 pub(crate) fn clock_nanosleep_relative(
26 id: ClockId,
27 req: &__kernel_timespec,
28 ) -> NanosleepRelativeResult {
29 #[cfg(target_pointer_width = "32")]
30 unsafe {
31 let mut rem = MaybeUninit::<__kernel_timespec>::uninit();
32 match ret(syscall!(
33 __NR_clock_nanosleep_time64,
34 id,
35 c_int(0),
36 by_ref(req),
37 &mut rem
38 ))
39 .or_else(|err| {
40 // See the comments in `rustix_clock_gettime_via_syscall` about
41 // emulation.
42 if err == io::Errno::NOSYS {
43 clock_nanosleep_relative_old(id, req, &mut rem)
44 } else {
45 Err(err)
46 }
47 }) {
48 Ok(()) => NanosleepRelativeResult::Ok,
49 Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(rem.assume_init()),
50 Err(err) => NanosleepRelativeResult::Err(err),
51 }
52 }
53 #[cfg(target_pointer_width = "64")]
54 unsafe {
55 let mut rem = MaybeUninit::<__kernel_timespec>::uninit();
56 match ret(syscall!(
57 __NR_clock_nanosleep,
58 id,
59 c_int(0),
60 by_ref(req),
61 &mut rem
62 )) {
63 Ok(()) => NanosleepRelativeResult::Ok,
64 Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(rem.assume_init()),
65 Err(err) => NanosleepRelativeResult::Err(err),
66 }
67 }
68 }
69
70 #[cfg(target_pointer_width = "32")]
clock_nanosleep_relative_old( id: ClockId, req: &__kernel_timespec, rem: &mut MaybeUninit<__kernel_timespec>, ) -> io::Result<()>71 unsafe fn clock_nanosleep_relative_old(
72 id: ClockId,
73 req: &__kernel_timespec,
74 rem: &mut MaybeUninit<__kernel_timespec>,
75 ) -> io::Result<()> {
76 let old_req = __kernel_old_timespec {
77 tv_sec: req.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
78 tv_nsec: req.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
79 };
80 let mut old_rem = MaybeUninit::<__kernel_old_timespec>::uninit();
81 ret(syscall!(
82 __NR_clock_nanosleep,
83 id,
84 c_int(0),
85 by_ref(&old_req),
86 &mut old_rem
87 ))?;
88 let old_rem = old_rem.assume_init();
89 // TODO: With Rust 1.55, we can use MaybeUninit::write here.
90 ptr::write(
91 rem.as_mut_ptr(),
92 __kernel_timespec {
93 tv_sec: old_rem.tv_sec.into(),
94 tv_nsec: old_rem.tv_nsec.into(),
95 },
96 );
97 Ok(())
98 }
99
100 #[inline]
clock_nanosleep_absolute(id: ClockId, req: &__kernel_timespec) -> io::Result<()>101 pub(crate) fn clock_nanosleep_absolute(id: ClockId, req: &__kernel_timespec) -> io::Result<()> {
102 #[cfg(target_pointer_width = "32")]
103 unsafe {
104 ret(syscall_readonly!(
105 __NR_clock_nanosleep_time64,
106 id,
107 c_uint(TIMER_ABSTIME),
108 by_ref(req),
109 zero()
110 ))
111 .or_else(|err| {
112 // See the comments in `rustix_clock_gettime_via_syscall` about
113 // emulation.
114 if err == io::Errno::NOSYS {
115 clock_nanosleep_absolute_old(id, req)
116 } else {
117 Err(err)
118 }
119 })
120 }
121 #[cfg(target_pointer_width = "64")]
122 unsafe {
123 ret(syscall_readonly!(
124 __NR_clock_nanosleep,
125 id,
126 c_uint(TIMER_ABSTIME),
127 by_ref(req),
128 zero()
129 ))
130 }
131 }
132
133 #[cfg(target_pointer_width = "32")]
clock_nanosleep_absolute_old(id: ClockId, req: &__kernel_timespec) -> io::Result<()>134 unsafe fn clock_nanosleep_absolute_old(id: ClockId, req: &__kernel_timespec) -> io::Result<()> {
135 let old_req = __kernel_old_timespec {
136 tv_sec: req.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
137 tv_nsec: req.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
138 };
139 ret(syscall_readonly!(
140 __NR_clock_nanosleep,
141 id,
142 c_int(0),
143 by_ref(&old_req),
144 zero()
145 ))
146 }
147
148 #[inline]
nanosleep(req: &__kernel_timespec) -> NanosleepRelativeResult149 pub(crate) fn nanosleep(req: &__kernel_timespec) -> NanosleepRelativeResult {
150 #[cfg(target_pointer_width = "32")]
151 unsafe {
152 let mut rem = MaybeUninit::<__kernel_timespec>::uninit();
153 match ret(syscall!(
154 __NR_clock_nanosleep_time64,
155 ClockId::Realtime,
156 c_int(0),
157 by_ref(req),
158 &mut rem
159 ))
160 .or_else(|err| {
161 // See the comments in `rustix_clock_gettime_via_syscall` about
162 // emulation.
163 if err == io::Errno::NOSYS {
164 nanosleep_old(req, &mut rem)
165 } else {
166 Err(err)
167 }
168 }) {
169 Ok(()) => NanosleepRelativeResult::Ok,
170 Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(rem.assume_init()),
171 Err(err) => NanosleepRelativeResult::Err(err),
172 }
173 }
174 #[cfg(target_pointer_width = "64")]
175 unsafe {
176 let mut rem = MaybeUninit::<__kernel_timespec>::uninit();
177 match ret(syscall!(__NR_nanosleep, by_ref(req), &mut rem)) {
178 Ok(()) => NanosleepRelativeResult::Ok,
179 Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(rem.assume_init()),
180 Err(err) => NanosleepRelativeResult::Err(err),
181 }
182 }
183 }
184
185 #[cfg(target_pointer_width = "32")]
nanosleep_old( req: &__kernel_timespec, rem: &mut MaybeUninit<__kernel_timespec>, ) -> io::Result<()>186 unsafe fn nanosleep_old(
187 req: &__kernel_timespec,
188 rem: &mut MaybeUninit<__kernel_timespec>,
189 ) -> io::Result<()> {
190 let old_req = __kernel_old_timespec {
191 tv_sec: req.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
192 tv_nsec: req.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
193 };
194 let mut old_rem = MaybeUninit::<__kernel_old_timespec>::uninit();
195 ret(syscall!(__NR_nanosleep, by_ref(&old_req), &mut old_rem))?;
196 let old_rem = old_rem.assume_init();
197 // TODO: With Rust 1.55, we can use MaybeUninit::write here.
198 ptr::write(
199 rem.as_mut_ptr(),
200 __kernel_timespec {
201 tv_sec: old_rem.tv_sec.into(),
202 tv_nsec: old_rem.tv_nsec.into(),
203 },
204 );
205 Ok(())
206 }
207
208 #[inline]
gettid() -> Pid209 pub(crate) fn gettid() -> Pid {
210 unsafe {
211 let tid: i32 = ret_usize_infallible(syscall_readonly!(__NR_gettid)) as __kernel_pid_t;
212 debug_assert_ne!(tid, 0);
213 Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(tid as u32))
214 }
215 }
216
217 // TODO: This could be de-multiplexed.
218 #[inline]
futex( uaddr: *mut u32, op: FutexOperation, flags: FutexFlags, val: u32, utime: *const Timespec, uaddr2: *mut u32, val3: u32, ) -> io::Result<usize>219 pub(crate) unsafe fn futex(
220 uaddr: *mut u32,
221 op: FutexOperation,
222 flags: FutexFlags,
223 val: u32,
224 utime: *const Timespec,
225 uaddr2: *mut u32,
226 val3: u32,
227 ) -> io::Result<usize> {
228 #[cfg(target_pointer_width = "32")]
229 {
230 ret_usize(syscall!(
231 __NR_futex_time64,
232 uaddr,
233 (op, flags),
234 c_uint(val),
235 utime,
236 uaddr2,
237 c_uint(val3)
238 ))
239 .or_else(|err| {
240 // See the comments in `rustix_clock_gettime_via_syscall` about
241 // emulation.
242 if err == io::Errno::NOSYS {
243 futex_old(uaddr, op, flags, val, utime, uaddr2, val3)
244 } else {
245 Err(err)
246 }
247 })
248 }
249 #[cfg(target_pointer_width = "64")]
250 ret_usize(syscall!(
251 __NR_futex,
252 uaddr,
253 (op, flags),
254 c_uint(val),
255 utime,
256 uaddr2,
257 c_uint(val3)
258 ))
259 }
260
261 #[cfg(target_pointer_width = "32")]
futex_old( uaddr: *mut u32, op: FutexOperation, flags: FutexFlags, val: u32, utime: *const Timespec, uaddr2: *mut u32, val3: u32, ) -> io::Result<usize>262 unsafe fn futex_old(
263 uaddr: *mut u32,
264 op: FutexOperation,
265 flags: FutexFlags,
266 val: u32,
267 utime: *const Timespec,
268 uaddr2: *mut u32,
269 val3: u32,
270 ) -> io::Result<usize> {
271 let old_utime = __kernel_old_timespec {
272 tv_sec: (*utime).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
273 tv_nsec: (*utime).tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
274 };
275 ret_usize(syscall!(
276 __NR_futex,
277 uaddr,
278 (op, flags),
279 c_uint(val),
280 by_ref(&old_utime),
281 uaddr2,
282 c_uint(val3)
283 ))
284 }
285
286 #[cfg(any(target_os = "android", target_os = "linux"))]
287 #[inline]
setns(fd: BorrowedFd, nstype: c::c_int) -> io::Result<c::c_int>288 pub(crate) fn setns(fd: BorrowedFd, nstype: c::c_int) -> io::Result<c::c_int> {
289 unsafe { ret_c_int(syscall_readonly!(__NR_setns, fd, c_int(nstype))) }
290 }
291
292 #[cfg(any(target_os = "android", target_os = "linux"))]
293 #[inline]
unshare(flags: crate::thread::UnshareFlags) -> io::Result<()>294 pub(crate) fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> {
295 unsafe { ret(syscall_readonly!(__NR_unshare, c_uint(flags.bits()))) }
296 }
297