shadow_rs/host/syscall/handler/
timerfd.rs
1use std::sync::Arc;
2
3use atomic_refcell::AtomicRefCell;
4use linux_api::errno::Errno;
5use linux_api::fcntl::DescriptorFlags;
6use linux_api::time::{ClockId, itimerspec};
7use nix::sys::timerfd::{TimerFlags, TimerSetTimeFlags};
8use shadow_shim_helper_rs::{
9 emulated_time::EmulatedTime, simulation_time::SimulationTime, syscall_types::ForeignPtr,
10};
11
12use crate::core::worker::Worker;
13use crate::host::descriptor::descriptor_table::DescriptorHandle;
14use crate::host::descriptor::{
15 CompatFile, Descriptor, File, FileStatus, OpenFile, timerfd::TimerFd,
16};
17use crate::host::{
18 syscall::handler::{SyscallContext, SyscallHandler},
19 syscall::types::SyscallError,
20};
21use crate::utility::callback_queue::CallbackQueue;
22
23impl SyscallHandler {
24 log_syscall!(
25 timerfd_create,
26 std::ffi::c_int,
27 linux_api::time::ClockId,
28 std::ffi::c_int,
29 );
30 pub fn timerfd_create(
31 ctx: &mut SyscallContext,
32 clockid: std::ffi::c_int,
33 flags: std::ffi::c_int,
34 ) -> Result<DescriptorHandle, SyscallError> {
35 let Ok(clockid) = ClockId::try_from(clockid) else {
36 log::debug!("Invalid clockid: {clockid}");
37 return Err(Errno::EINVAL.into());
38 };
39
40 check_clockid(clockid)?;
42
43 let Some(flags) = TimerFlags::from_bits(flags) else {
44 log::debug!("Invalid timerfd_create flags: {flags}");
45 return Err(Errno::EINVAL.into());
46 };
47
48 let mut file_flags = FileStatus::empty();
49 let mut desc_flags = DescriptorFlags::empty();
50
51 if flags.contains(TimerFlags::TFD_NONBLOCK) {
52 file_flags.insert(FileStatus::NONBLOCK);
53 }
54
55 if flags.contains(TimerFlags::TFD_CLOEXEC) {
56 desc_flags.insert(DescriptorFlags::FD_CLOEXEC);
57 }
58
59 let file = TimerFd::new(file_flags);
60 let mut desc = Descriptor::new(CompatFile::New(OpenFile::new(File::TimerFd(file))));
61 desc.set_flags(desc_flags);
62
63 let fd = ctx
64 .objs
65 .thread
66 .descriptor_table_borrow_mut(ctx.objs.host)
67 .register_descriptor(desc)
68 .or(Err(Errno::ENFILE))?;
69
70 log::trace!("timerfd_create() returning fd {fd}");
71
72 Ok(fd)
73 }
74
75 log_syscall!(
76 timerfd_gettime,
77 std::ffi::c_int,
78 std::ffi::c_int,
79 *const std::ffi::c_void,
80 );
81 pub fn timerfd_gettime(
82 ctx: &mut SyscallContext,
83 fd: std::ffi::c_int,
84 curr_value_ptr: ForeignPtr<linux_api::time::itimerspec>,
85 ) -> Result<(), SyscallError> {
86 let file = get_cloned_file(ctx, fd)?;
88 let File::TimerFd(ref timerfd) = file else {
89 return Err(Errno::EINVAL.into());
90 };
91
92 Self::timerfd_gettime_helper(ctx, timerfd, curr_value_ptr)?;
93
94 Ok(())
95 }
96
97 fn timerfd_gettime_helper(
98 ctx: &mut SyscallContext,
99 timerfd: &Arc<AtomicRefCell<TimerFd>>,
100 value_ptr: ForeignPtr<linux_api::time::itimerspec>,
101 ) -> Result<(), Errno> {
102 let (remaining, interval) = {
104 let borrowed = timerfd.borrow();
105
106 let remaining = borrowed
108 .get_timer_remaining()
109 .unwrap_or(SimulationTime::ZERO);
110
111 let interval = borrowed
114 .get_timer_interval()
115 .unwrap_or(SimulationTime::ZERO);
116
117 (remaining, interval)
118 };
119
120 let result = itimerspec {
122 it_value: remaining.try_into().unwrap(),
123 it_interval: interval.try_into().unwrap(),
124 };
125
126 ctx.objs
128 .process
129 .memory_borrow_mut()
130 .write(value_ptr, &result)?;
131
132 Ok(())
133 }
134
135 log_syscall!(
136 timerfd_settime,
137 std::ffi::c_int,
138 std::ffi::c_int,
139 std::ffi::c_int,
140 *const std::ffi::c_void,
141 *const std::ffi::c_void,
142 );
143 pub fn timerfd_settime(
144 ctx: &mut SyscallContext,
145 fd: std::ffi::c_int,
146 flags: std::ffi::c_int,
147 new_value_ptr: ForeignPtr<linux_api::time::itimerspec>,
148 old_value_ptr: ForeignPtr<linux_api::time::itimerspec>,
149 ) -> Result<(), SyscallError> {
150 let Some(flags) = TimerSetTimeFlags::from_bits(flags) else {
151 log::debug!("Invalid timerfd_settime flags: {flags}");
152 return Err(Errno::EINVAL.into());
153 };
154
155 let file = get_cloned_file(ctx, fd)?;
157 let File::TimerFd(ref timerfd) = file else {
158 return Err(Errno::EINVAL.into());
159 };
160
161 let new_value = ctx.objs.process.memory_borrow().read(new_value_ptr)?;
163
164 let value = SimulationTime::try_from(new_value.it_value).or(Err(Errno::EINVAL))?;
166 let interval = SimulationTime::try_from(new_value.it_interval).or(Err(Errno::EINVAL))?;
167
168 if !old_value_ptr.is_null() {
170 Self::timerfd_gettime_helper(ctx, timerfd, old_value_ptr)?;
172 }
173
174 if value.is_zero() {
176 CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
178 timerfd.borrow_mut().disarm_timer(cb_queue);
179 });
180 log::trace!("TimerFd {fd} disarmed");
181 } else {
182 let now = Worker::current_time().unwrap();
184
185 let expire_time = {
186 let base = match flags.contains(TimerSetTimeFlags::TFD_TIMER_ABSTIME) {
187 true => EmulatedTime::UNIX_EPOCH,
188 false => now,
189 };
190 EmulatedTime::max(base + value, now)
193 };
194
195 CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
196 timerfd.borrow_mut().arm_timer(
197 ctx.objs.host,
198 expire_time,
199 interval.is_positive().then_some(interval),
200 cb_queue,
201 );
202 });
203
204 log::trace!(
205 "TimerFd {fd} armed to expire at {expire_time:?} (in {:?})",
206 expire_time.duration_since(&now)
207 );
208 }
209
210 Ok(())
211 }
212}
213
214fn check_clockid(clockid: ClockId) -> Result<(), Errno> {
218 if clockid == ClockId::CLOCK_MONOTONIC || clockid == ClockId::CLOCK_REALTIME {
219 return Ok(());
220 }
221
222 warn_once_then_debug!("Unsupported clockid {clockid:?}");
223 Err(Errno::EINVAL)
224}
225
226fn get_cloned_file(ctx: &mut SyscallContext, fd: std::ffi::c_int) -> Result<File, Errno> {
227 let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
229 let desc = SyscallHandler::get_descriptor(&desc_table, fd)?;
230
231 let CompatFile::New(file) = desc.file() else {
233 return Err(Errno::EINVAL);
234 };
235
236 Ok(file.inner_file().clone())
237}