]>
Commit | Line | Data |
---|---|---|
fe692bf9 FG |
1 | //! linux_raw syscalls supporting `rustix::time`. |
2 | //! | |
3 | //! # Safety | |
4 | //! | |
5 | //! See the `rustix::backend` module documentation for details. | |
6 | #![allow(unsafe_code)] | |
7 | #![allow(clippy::undocumented_unsafe_blocks)] | |
8 | ||
781aab86 FG |
9 | #[cfg(feature = "time")] |
10 | use super::super::conv::{by_ref, ret_owned_fd}; | |
fe692bf9 FG |
11 | use super::super::conv::{ret, ret_infallible}; |
12 | use super::types::ClockId; | |
781aab86 FG |
13 | #[cfg(feature = "time")] |
14 | use crate::fd::BorrowedFd; | |
15 | #[cfg(feature = "time")] | |
16 | use crate::fd::OwnedFd; | |
fe692bf9 | 17 | use crate::io; |
781aab86 FG |
18 | #[cfg(feature = "time")] |
19 | use crate::time::{Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags}; | |
fe692bf9 FG |
20 | use core::mem::MaybeUninit; |
21 | use linux_raw_sys::general::__kernel_timespec; | |
fe692bf9 | 22 | #[cfg(feature = "time")] |
781aab86 | 23 | #[cfg(target_pointer_width = "32")] |
fe692bf9 | 24 | use {core::convert::TryInto, linux_raw_sys::general::itimerspec as __kernel_old_itimerspec}; |
781aab86 FG |
25 | #[cfg(target_pointer_width = "32")] |
26 | use {core::ptr, linux_raw_sys::general::timespec as __kernel_old_timespec}; | |
fe692bf9 FG |
27 | |
28 | // `clock_gettime` has special optimizations via the vDSO. | |
29 | #[cfg(feature = "time")] | |
30 | pub(crate) use super::super::vdso_wrappers::{clock_gettime, clock_gettime_dynamic}; | |
31 | ||
32 | #[inline] | |
33 | pub(crate) fn clock_getres(which_clock: ClockId) -> __kernel_timespec { | |
34 | #[cfg(target_pointer_width = "32")] | |
35 | unsafe { | |
36 | let mut result = MaybeUninit::<__kernel_timespec>::uninit(); | |
37 | if let Err(err) = ret(syscall!(__NR_clock_getres_time64, which_clock, &mut result)) { | |
38 | // See the comments in `rustix_clock_gettime_via_syscall` about | |
39 | // emulation. | |
40 | debug_assert_eq!(err, io::Errno::NOSYS); | |
41 | clock_getres_old(which_clock, &mut result); | |
42 | } | |
43 | result.assume_init() | |
44 | } | |
45 | #[cfg(target_pointer_width = "64")] | |
46 | unsafe { | |
47 | let mut result = MaybeUninit::<__kernel_timespec>::uninit(); | |
48 | ret_infallible(syscall!(__NR_clock_getres, which_clock, &mut result)); | |
49 | result.assume_init() | |
50 | } | |
51 | } | |
52 | ||
53 | #[cfg(target_pointer_width = "32")] | |
54 | unsafe fn clock_getres_old(which_clock: ClockId, result: &mut MaybeUninit<__kernel_timespec>) { | |
55 | let mut old_result = MaybeUninit::<__kernel_old_timespec>::uninit(); | |
56 | ret_infallible(syscall!(__NR_clock_getres, which_clock, &mut old_result)); | |
57 | let old_result = old_result.assume_init(); | |
58 | // TODO: With Rust 1.55, we can use MaybeUninit::write here. | |
781aab86 FG |
59 | ptr::write( |
60 | result.as_mut_ptr(), | |
61 | __kernel_timespec { | |
62 | tv_sec: old_result.tv_sec.into(), | |
63 | tv_nsec: old_result.tv_nsec.into(), | |
64 | }, | |
65 | ); | |
fe692bf9 FG |
66 | } |
67 | ||
68 | #[cfg(feature = "time")] | |
69 | #[inline] | |
70 | pub(crate) fn clock_settime(which_clock: ClockId, timespec: __kernel_timespec) -> io::Result<()> { | |
fe692bf9 FG |
71 | #[cfg(target_pointer_width = "32")] |
72 | unsafe { | |
73 | match ret(syscall_readonly!( | |
74 | __NR_clock_settime64, | |
75 | which_clock, | |
76 | by_ref(×pec) | |
77 | )) { | |
78 | Err(io::Errno::NOSYS) => clock_settime_old(which_clock, timespec), | |
79 | otherwise => otherwise, | |
80 | } | |
81 | } | |
82 | #[cfg(target_pointer_width = "64")] | |
83 | unsafe { | |
84 | ret(syscall_readonly!( | |
85 | __NR_clock_settime, | |
86 | which_clock, | |
87 | by_ref(×pec) | |
88 | )) | |
89 | } | |
90 | } | |
91 | ||
92 | #[cfg(feature = "time")] | |
93 | #[cfg(target_pointer_width = "32")] | |
94 | unsafe fn clock_settime_old(which_clock: ClockId, timespec: __kernel_timespec) -> io::Result<()> { | |
95 | let old_timespec = __kernel_old_timespec { | |
96 | tv_sec: timespec | |
97 | .tv_sec | |
98 | .try_into() | |
99 | .map_err(|_| io::Errno::OVERFLOW)?, | |
100 | tv_nsec: timespec.tv_nsec as _, | |
101 | }; | |
102 | ret(syscall_readonly!( | |
103 | __NR_clock_settime, | |
104 | which_clock, | |
105 | by_ref(&old_timespec) | |
106 | )) | |
107 | } | |
108 | ||
109 | #[cfg(feature = "time")] | |
110 | #[inline] | |
111 | pub(crate) fn timerfd_create(clockid: TimerfdClockId, flags: TimerfdFlags) -> io::Result<OwnedFd> { | |
112 | unsafe { ret_owned_fd(syscall!(__NR_timerfd_create, clockid, flags)) } | |
113 | } | |
114 | ||
115 | #[cfg(feature = "time")] | |
116 | #[inline] | |
117 | pub(crate) fn timerfd_settime( | |
118 | fd: BorrowedFd<'_>, | |
119 | flags: TimerfdTimerFlags, | |
120 | new_value: &Itimerspec, | |
121 | ) -> io::Result<Itimerspec> { | |
122 | let mut result = MaybeUninit::<Itimerspec>::uninit(); | |
123 | ||
124 | #[cfg(target_pointer_width = "64")] | |
125 | unsafe { | |
126 | ret(syscall!( | |
127 | __NR_timerfd_settime, | |
128 | fd, | |
129 | flags, | |
130 | by_ref(new_value), | |
131 | &mut result | |
132 | ))?; | |
133 | Ok(result.assume_init()) | |
134 | } | |
135 | ||
136 | #[cfg(target_pointer_width = "32")] | |
137 | unsafe { | |
138 | ret(syscall!( | |
139 | __NR_timerfd_settime64, | |
140 | fd, | |
141 | flags, | |
142 | by_ref(new_value), | |
143 | &mut result | |
144 | )) | |
145 | .or_else(|err| { | |
146 | // See the comments in `rustix_clock_gettime_via_syscall` about | |
147 | // emulation. | |
148 | if err == io::Errno::NOSYS { | |
149 | timerfd_settime_old(fd, flags, new_value, &mut result) | |
150 | } else { | |
151 | Err(err) | |
152 | } | |
153 | })?; | |
154 | Ok(result.assume_init()) | |
155 | } | |
156 | } | |
157 | ||
158 | #[cfg(feature = "time")] | |
159 | #[cfg(target_pointer_width = "32")] | |
160 | unsafe fn timerfd_settime_old( | |
161 | fd: BorrowedFd<'_>, | |
162 | flags: TimerfdTimerFlags, | |
163 | new_value: &Itimerspec, | |
164 | result: &mut MaybeUninit<Itimerspec>, | |
165 | ) -> io::Result<()> { | |
166 | let mut old_result = MaybeUninit::<__kernel_old_itimerspec>::uninit(); | |
167 | ||
168 | // Convert `new_value` to the old `__kernel_old_itimerspec` format. | |
169 | let old_new_value = __kernel_old_itimerspec { | |
170 | it_interval: __kernel_old_timespec { | |
171 | tv_sec: new_value | |
172 | .it_interval | |
173 | .tv_sec | |
174 | .try_into() | |
175 | .map_err(|_| io::Errno::OVERFLOW)?, | |
176 | tv_nsec: new_value | |
177 | .it_interval | |
178 | .tv_nsec | |
179 | .try_into() | |
180 | .map_err(|_| io::Errno::INVAL)?, | |
181 | }, | |
182 | it_value: __kernel_old_timespec { | |
183 | tv_sec: new_value | |
184 | .it_value | |
185 | .tv_sec | |
186 | .try_into() | |
187 | .map_err(|_| io::Errno::OVERFLOW)?, | |
188 | tv_nsec: new_value | |
189 | .it_value | |
190 | .tv_nsec | |
191 | .try_into() | |
192 | .map_err(|_| io::Errno::INVAL)?, | |
193 | }, | |
194 | }; | |
195 | ret(syscall!( | |
196 | __NR_timerfd_settime, | |
197 | fd, | |
198 | flags, | |
199 | by_ref(&old_new_value), | |
200 | &mut old_result | |
201 | ))?; | |
202 | let old_result = old_result.assume_init(); | |
203 | // TODO: With Rust 1.55, we can use MaybeUninit::write here. | |
781aab86 FG |
204 | ptr::write( |
205 | result.as_mut_ptr(), | |
206 | Itimerspec { | |
207 | it_interval: __kernel_timespec { | |
208 | tv_sec: old_result.it_interval.tv_sec.into(), | |
209 | tv_nsec: old_result.it_interval.tv_nsec.into(), | |
210 | }, | |
211 | it_value: __kernel_timespec { | |
212 | tv_sec: old_result.it_value.tv_sec.into(), | |
213 | tv_nsec: old_result.it_value.tv_nsec.into(), | |
214 | }, | |
fe692bf9 | 215 | }, |
781aab86 | 216 | ); |
fe692bf9 FG |
217 | Ok(()) |
218 | } | |
219 | ||
220 | #[cfg(feature = "time")] | |
221 | #[inline] | |
222 | pub(crate) fn timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> { | |
223 | let mut result = MaybeUninit::<Itimerspec>::uninit(); | |
224 | ||
225 | #[cfg(target_pointer_width = "64")] | |
226 | unsafe { | |
227 | ret(syscall!(__NR_timerfd_gettime, fd, &mut result))?; | |
228 | Ok(result.assume_init()) | |
229 | } | |
230 | ||
231 | #[cfg(target_pointer_width = "32")] | |
232 | unsafe { | |
233 | ret(syscall!(__NR_timerfd_gettime64, fd, &mut result)).or_else(|err| { | |
234 | // See the comments in `rustix_clock_gettime_via_syscall` about | |
235 | // emulation. | |
236 | if err == io::Errno::NOSYS { | |
237 | timerfd_gettime_old(fd, &mut result) | |
238 | } else { | |
239 | Err(err) | |
240 | } | |
241 | })?; | |
242 | Ok(result.assume_init()) | |
243 | } | |
244 | } | |
245 | ||
246 | #[cfg(feature = "time")] | |
247 | #[cfg(target_pointer_width = "32")] | |
248 | unsafe fn timerfd_gettime_old( | |
249 | fd: BorrowedFd<'_>, | |
250 | result: &mut MaybeUninit<Itimerspec>, | |
251 | ) -> io::Result<()> { | |
252 | let mut old_result = MaybeUninit::<__kernel_old_itimerspec>::uninit(); | |
253 | ret(syscall!(__NR_timerfd_gettime, fd, &mut old_result))?; | |
254 | let old_result = old_result.assume_init(); | |
255 | // TODO: With Rust 1.55, we can use MaybeUninit::write here. | |
781aab86 FG |
256 | ptr::write( |
257 | result.as_mut_ptr(), | |
258 | Itimerspec { | |
259 | it_interval: __kernel_timespec { | |
260 | tv_sec: old_result.it_interval.tv_sec.into(), | |
261 | tv_nsec: old_result.it_interval.tv_nsec.into(), | |
262 | }, | |
263 | it_value: __kernel_timespec { | |
264 | tv_sec: old_result.it_value.tv_sec.into(), | |
265 | tv_nsec: old_result.it_value.tv_nsec.into(), | |
266 | }, | |
fe692bf9 | 267 | }, |
781aab86 | 268 | ); |
fe692bf9 FG |
269 | Ok(()) |
270 | } |