]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | //! linux_raw syscalls supporting `rustix::thread`. |
2 | //! | |
3 | //! # Safety | |
4 | //! | |
487cf647 | 5 | //! See the `rustix::backend` module documentation for details. |
064997fb FG |
6 | #![allow(unsafe_code)] |
7 | #![allow(clippy::undocumented_unsafe_blocks)] | |
8 | ||
487cf647 FG |
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; | |
064997fb FG |
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] | |
25 | 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")] | |
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] | |
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")] | |
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] | |
149 | 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")] | |
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] | |
209 | 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] | |
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")] | |
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 | } | |
487cf647 FG |
285 | |
286 | #[cfg(any(target_os = "android", target_os = "linux"))] | |
287 | #[inline] | |
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 | } | |
f25598a0 FG |
291 | |
292 | #[cfg(any(target_os = "android", target_os = "linux"))] | |
293 | #[inline] | |
294 | pub(crate) fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> { | |
295 | unsafe { ret(syscall_readonly!(__NR_unshare, c_uint(flags.bits()))) } | |
296 | } |