1use std::io::Write;
2use std::sync::{Arc, Weak};
34use atomic_refcell::AtomicRefCell;
5use linux_api::errno::Errno;
6use linux_api::ioctls::IoctlRequest;
7use linux_api::posix_types::kernel_off_t;
8use shadow_shim_helper_rs::{
9 emulated_time::EmulatedTime, simulation_time::SimulationTime, syscall_types::ForeignPtr,
10};
1112use crate::cshadow as c;
13use crate::host::descriptor::listener::{StateEventSource, StateListenHandle, StateListenerFilter};
14use crate::host::descriptor::{FileMode, FileSignals, FileState, FileStatus};
15use crate::host::host::Host;
16use crate::host::memory_manager::MemoryManager;
17use crate::host::syscall::io::{IoVec, IoVecWriter};
18use crate::host::syscall::types::{SyscallError, SyscallResult};
19use crate::host::timer::Timer;
20use crate::utility::HostTreePointer;
21use crate::utility::callback_queue::CallbackQueue;
2223pub struct TimerFd {
24 timer: Timer,
25 event_source: StateEventSource,
26 status: FileStatus,
27 state: FileState,
28// Should only be used by `OpenFile` to make sure there is only ever one `OpenFile` instance for
29 // this file,
30has_open_file: bool,
31}
3233impl TimerFd {
34/// Creates a new [`TimerFd`] object that internally sets up a [`Timer`] that can be waited on
35 /// with poll, select, and epoll, enabling support for timerfd_create(2).
36 ///
37 /// We wrap the new [`TimerFd`] in an [`Arc<AtomicRefCell>`] because we need to use a weak
38 /// reference to internally support setting up callback functions that reference the [`TimerFd`]
39 /// on timer expiration.
40pub fn new(status: FileStatus) -> Arc<AtomicRefCell<Self>> {
41// We need a circular reference here, so that the inner Timer can refer back to the outer
42 // TimerFd when executing a callback that will mutate the TimerFd when the timer expires.
43Arc::new_cyclic(|weak| {
44let weak_cloned = weak.clone();
45 AtomicRefCell::new(Self {
46 timer: Timer::new(move |_host| Self::timer_expired(&weak_cloned)),
47 event_source: StateEventSource::new(),
48 state: FileState::ACTIVE,
49 status,
50 has_open_file: false,
51 })
52 })
53 }
5455/// Called by the inner [`Timer`] when a timer expiration occurs.
56fn timer_expired(timerfd_weak: &Weak<AtomicRefCell<TimerFd>>) {
57let Some(timerfd) = timerfd_weak.upgrade() else {
58log::trace!("Expired TimerFd no longer exists.");
59return;
60 };
6162// The TimerFd may have become readable now that a timer expired. We use the CallbackQueue
63 // here to make sure that any listeners that need to wake up and handle a readable TimerFd
64 // are not invoked until after we release the borrow.
65CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
66 timerfd.borrow_mut().refresh_state(cb_queue);
67 });
68 }
6970/// Returns the number of expirations that have occured since the timer was last armed.
71fn get_timer_count(&self) -> u64 {
72self.timer.expiration_count()
73 }
7475/// Returns the relative duration until the next expiration event occurs if the timer is armed,
76 /// and `None` if the timer is disarmed.
77pub fn get_timer_remaining(&self) -> Option<SimulationTime> {
78self.timer.remaining_time()
79 }
8081/// Returns the relative duration over which the timer has been configured to periodically
82 /// expire, or `None` if the timer is configured to expire only once.
83pub fn get_timer_interval(&self) -> Option<SimulationTime> {
84self.timer.expire_interval()
85 }
8687/// Arm the timer by setting its expiration time and interval, enabling support for
88 /// timerfd_settime(2). The readable state of the [`TimerFd`] is updated as appropriate.
89pub fn arm_timer(
90&mut self,
91 host: &Host,
92 expire_time: EmulatedTime,
93 interval: Option<SimulationTime>,
94 cb_queue: &mut CallbackQueue,
95 ) {
96// Make sure to update our READABLE status.
97self.timer.arm(host, expire_time, interval);
98self.refresh_state(cb_queue);
99 }
100101/// Disarm the timer so that it no longer fires expiration events, enabling support for
102 /// timerfd_settime(2). The readable state of the [`TimerFd`] is updated as appropriate.
103pub fn disarm_timer(&mut self, cb_queue: &mut CallbackQueue) {
104// Make sure to update our READABLE status.
105self.timer.disarm();
106self.refresh_state(cb_queue);
107 }
108109pub fn status(&self) -> FileStatus {
110self.status
111 }
112113pub fn set_status(&mut self, status: FileStatus) {
114self.status = status;
115 }
116117pub fn mode(&self) -> FileMode {
118 FileMode::READ
119 }
120121pub fn has_open_file(&self) -> bool {
122self.has_open_file
123 }
124125pub fn supports_sa_restart(&self) -> bool {
126false
127}
128129pub fn set_has_open_file(&mut self, val: bool) {
130self.has_open_file = val;
131 }
132133pub fn readv(
134&mut self,
135 iovs: &[IoVec],
136 offset: Option<kernel_off_t>,
137 _flags: std::ffi::c_int,
138 mem: &mut MemoryManager,
139 cb_queue: &mut CallbackQueue,
140 ) -> Result<isize, SyscallError> {
141// TimerFds don't support seeking
142if offset.is_some() {
143return Err(Errno::ESPIPE.into());
144 }
145146// timerfd_create(2): "read(2) returns an unsigned 8-byte integer containing the number of
147 // expirations that have occurred."
148const NUM_BYTES: usize = 8;
149150let len: usize = iovs.iter().map(|x| x.len).sum();
151152// This check doesn't guarantee that we can write all bytes since the stream length is only
153 // a hint.
154if len < NUM_BYTES {
155log::trace!("Reading from TimerFd requires a buffer of at least {NUM_BYTES} bytes",);
156return Err(Errno::EINVAL.into());
157 }
158159let expiration_count = self.timer.consume_expiration_count();
160161if expiration_count == 0 {
162log::trace!("TimerFd expiration count is 0 and cannot be read right now");
163return Err(Errno::EWOULDBLOCK.into());
164 }
165166let mut writer = IoVecWriter::new(iovs, mem);
167let to_write: [u8; NUM_BYTES] = expiration_count.to_ne_bytes();
168 writer.write_all(&to_write)?;
169170// We just read the expiration counter and so are not readable anymore.
171self.refresh_state(cb_queue);
172173Ok(NUM_BYTES.try_into().unwrap())
174 }
175176pub fn writev(
177&mut self,
178 _iovs: &[IoVec],
179 _offset: Option<kernel_off_t>,
180 _flags: std::ffi::c_int,
181 _mem: &mut MemoryManager,
182 _cb_queue: &mut CallbackQueue,
183 ) -> Result<isize, SyscallError> {
184// TimerFds don't support writing.
185Err(Errno::EINVAL.into())
186 }
187188pub fn close(&mut self, cb_queue: &mut CallbackQueue) -> Result<(), SyscallError> {
189// Set the closed flag and remove the active and readable flags.
190self.update_state(
191 FileState::CLOSED | FileState::ACTIVE | FileState::READABLE,
192 FileState::CLOSED,
193 FileSignals::empty(),
194 cb_queue,
195 );
196197Ok(())
198 }
199200pub fn ioctl(
201&mut self,
202 request: IoctlRequest,
203 _arg_ptr: ForeignPtr<()>,
204 _memory_manager: &mut MemoryManager,
205 ) -> SyscallResult {
206// The only timerfd-specific ioctl request is for `TFD_IOC_SET_TICKS`, which is available
207 // since Linux 3.17 but only if the kernel was configured with `CONFIG_CHECKPOINT_RESTORE`.
208 // See timerfd_create(2) for more details.
209warn_once_then_debug!("We do not yet handle ioctl request {request:?} on TimerFds");
210Err(Errno::EINVAL.into())
211 }
212213pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError> {
214warn_once_then_debug!("We do not yet handle stat calls on timerfds");
215Err(Errno::EINVAL.into())
216 }
217218pub fn add_listener(
219&mut self,
220 monitoring_state: FileState,
221 monitoring_signals: FileSignals,
222 filter: StateListenerFilter,
223 notify_fn: impl Fn(FileState, FileState, FileSignals, &mut CallbackQueue)
224 + Send
225 + Sync
226 + 'static,
227 ) -> StateListenHandle {
228self.event_source
229 .add_listener(monitoring_state, monitoring_signals, filter, notify_fn)
230 }
231232pub fn add_legacy_listener(&mut self, ptr: HostTreePointer<c::StatusListener>) {
233self.event_source.add_legacy_listener(ptr);
234 }
235236pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener) {
237self.event_source.remove_legacy_listener(ptr);
238 }
239240pub fn state(&self) -> FileState {
241self.state
242 }
243244fn refresh_state(&mut self, cb_queue: &mut CallbackQueue) {
245if self.state.contains(FileState::CLOSED) {
246return;
247 }
248249let mut new_state = FileState::empty();
250251// Set the descriptor as readable if we have a non-zero expiration count.
252new_state.set(FileState::READABLE, self.get_timer_count() > 0);
253254self.update_state(
255 FileState::READABLE,
256 new_state,
257 FileSignals::empty(),
258 cb_queue,
259 );
260 }
261262fn update_state(
263&mut self,
264 mask: FileState,
265 state: FileState,
266 signals: FileSignals,
267 cb_queue: &mut CallbackQueue,
268 ) {
269let old_state = self.state;
270271// Remove the mask, then copy the masked flags.
272self.state.remove(mask);
273self.state.insert(state & mask);
274275self.handle_state_change(old_state, signals, cb_queue);
276 }
277278fn handle_state_change(
279&mut self,
280 old_state: FileState,
281 signals: FileSignals,
282 cb_queue: &mut CallbackQueue,
283 ) {
284let states_changed = self.state ^ old_state;
285286// Just return if nothing changed.
287if states_changed.is_empty() && signals.is_empty() {
288return;
289 }
290291self.event_source
292 .notify_listeners(self.state, states_changed, signals, cb_queue);
293 }
294}