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
9 #[cfg(feature = "time")]
10 use super::super::conv::{by_ref, ret_owned_fd};
11 use super::super::conv::{ret, ret_infallible};
12 use super::types::ClockId;
13 #[cfg(feature = "time")]
14 use crate::fd::BorrowedFd;
15 #[cfg(feature = "time")]
16 use crate::fd::OwnedFd;
17 use crate::io;
18 #[cfg(feature = "time")]
19 use crate::time::{Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags};
20 use core::mem::MaybeUninit;
21 use linux_raw_sys::general::__kernel_timespec;
22 #[cfg(feature = "time")]
23 #[cfg(target_pointer_width = "32")]
24 use {core::convert::TryInto, linux_raw_sys::general::itimerspec as __kernel_old_itimerspec};
25 #[cfg(target_pointer_width = "32")]
26 use {core::ptr, linux_raw_sys::general::timespec as __kernel_old_timespec};
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]
clock_getres(which_clock: ClockId) -> __kernel_timespec33 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")]
clock_getres_old(which_clock: ClockId, result: &mut MaybeUninit<__kernel_timespec>)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.
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 );
66 }
67
68 #[cfg(feature = "time")]
69 #[inline]
timerfd_create(clockid: TimerfdClockId, flags: TimerfdFlags) -> io::Result<OwnedFd>70 pub(crate) fn timerfd_create(clockid: TimerfdClockId, flags: TimerfdFlags) -> io::Result<OwnedFd> {
71 unsafe { ret_owned_fd(syscall!(__NR_timerfd_create, clockid, flags)) }
72 }
73
74 #[cfg(feature = "time")]
75 #[inline]
timerfd_settime( fd: BorrowedFd<'_>, flags: TimerfdTimerFlags, new_value: &Itimerspec, ) -> io::Result<Itimerspec>76 pub(crate) fn timerfd_settime(
77 fd: BorrowedFd<'_>,
78 flags: TimerfdTimerFlags,
79 new_value: &Itimerspec,
80 ) -> io::Result<Itimerspec> {
81 let mut result = MaybeUninit::<Itimerspec>::uninit();
82
83 #[cfg(target_pointer_width = "64")]
84 unsafe {
85 ret(syscall!(
86 __NR_timerfd_settime,
87 fd,
88 flags,
89 by_ref(new_value),
90 &mut result
91 ))?;
92 Ok(result.assume_init())
93 }
94
95 #[cfg(target_pointer_width = "32")]
96 unsafe {
97 ret(syscall!(
98 __NR_timerfd_settime64,
99 fd,
100 flags,
101 by_ref(new_value),
102 &mut result
103 ))
104 .or_else(|err| {
105 // See the comments in `rustix_clock_gettime_via_syscall` about
106 // emulation.
107 if err == io::Errno::NOSYS {
108 timerfd_settime_old(fd, flags, new_value, &mut result)
109 } else {
110 Err(err)
111 }
112 })?;
113 Ok(result.assume_init())
114 }
115 }
116
117 #[cfg(feature = "time")]
118 #[cfg(target_pointer_width = "32")]
timerfd_settime_old( fd: BorrowedFd<'_>, flags: TimerfdTimerFlags, new_value: &Itimerspec, result: &mut MaybeUninit<Itimerspec>, ) -> io::Result<()>119 unsafe fn timerfd_settime_old(
120 fd: BorrowedFd<'_>,
121 flags: TimerfdTimerFlags,
122 new_value: &Itimerspec,
123 result: &mut MaybeUninit<Itimerspec>,
124 ) -> io::Result<()> {
125 let mut old_result = MaybeUninit::<__kernel_old_itimerspec>::uninit();
126
127 // Convert `new_value` to the old `__kernel_old_itimerspec` format.
128 let old_new_value = __kernel_old_itimerspec {
129 it_interval: __kernel_old_timespec {
130 tv_sec: new_value
131 .it_interval
132 .tv_sec
133 .try_into()
134 .map_err(|_| io::Errno::OVERFLOW)?,
135 tv_nsec: new_value
136 .it_interval
137 .tv_nsec
138 .try_into()
139 .map_err(|_| io::Errno::INVAL)?,
140 },
141 it_value: __kernel_old_timespec {
142 tv_sec: new_value
143 .it_value
144 .tv_sec
145 .try_into()
146 .map_err(|_| io::Errno::OVERFLOW)?,
147 tv_nsec: new_value
148 .it_value
149 .tv_nsec
150 .try_into()
151 .map_err(|_| io::Errno::INVAL)?,
152 },
153 };
154 ret(syscall!(
155 __NR_timerfd_settime,
156 fd,
157 flags,
158 by_ref(&old_new_value),
159 &mut old_result
160 ))?;
161 let old_result = old_result.assume_init();
162 // TODO: With Rust 1.55, we can use MaybeUninit::write here.
163 ptr::write(
164 result.as_mut_ptr(),
165 Itimerspec {
166 it_interval: __kernel_timespec {
167 tv_sec: old_result.it_interval.tv_sec.into(),
168 tv_nsec: old_result.it_interval.tv_nsec.into(),
169 },
170 it_value: __kernel_timespec {
171 tv_sec: old_result.it_value.tv_sec.into(),
172 tv_nsec: old_result.it_value.tv_nsec.into(),
173 },
174 },
175 );
176 Ok(())
177 }
178
179 #[cfg(feature = "time")]
180 #[inline]
timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result<Itimerspec>181 pub(crate) fn timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> {
182 let mut result = MaybeUninit::<Itimerspec>::uninit();
183
184 #[cfg(target_pointer_width = "64")]
185 unsafe {
186 ret(syscall!(__NR_timerfd_gettime, fd, &mut result))?;
187 Ok(result.assume_init())
188 }
189
190 #[cfg(target_pointer_width = "32")]
191 unsafe {
192 ret(syscall!(__NR_timerfd_gettime64, fd, &mut result)).or_else(|err| {
193 // See the comments in `rustix_clock_gettime_via_syscall` about
194 // emulation.
195 if err == io::Errno::NOSYS {
196 timerfd_gettime_old(fd, &mut result)
197 } else {
198 Err(err)
199 }
200 })?;
201 Ok(result.assume_init())
202 }
203 }
204
205 #[cfg(feature = "time")]
206 #[cfg(target_pointer_width = "32")]
timerfd_gettime_old( fd: BorrowedFd<'_>, result: &mut MaybeUninit<Itimerspec>, ) -> io::Result<()>207 unsafe fn timerfd_gettime_old(
208 fd: BorrowedFd<'_>,
209 result: &mut MaybeUninit<Itimerspec>,
210 ) -> io::Result<()> {
211 let mut old_result = MaybeUninit::<__kernel_old_itimerspec>::uninit();
212 ret(syscall!(__NR_timerfd_gettime, fd, &mut old_result))?;
213 let old_result = old_result.assume_init();
214 // TODO: With Rust 1.55, we can use MaybeUninit::write here.
215 ptr::write(
216 result.as_mut_ptr(),
217 Itimerspec {
218 it_interval: __kernel_timespec {
219 tv_sec: old_result.it_interval.tv_sec.into(),
220 tv_nsec: old_result.it_interval.tv_nsec.into(),
221 },
222 it_value: __kernel_timespec {
223 tv_sec: old_result.it_value.tv_sec.into(),
224 tv_nsec: old_result.it_value.tv_nsec.into(),
225 },
226 },
227 );
228 Ok(())
229 }
230