starnix_core/vfs/
timer.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::fs::fuchsia::{BootZxTimer, MonotonicZxTimer};
6use crate::power::OnWakeOps;
7use crate::task::{
8    CurrentTask, EventHandler, GenericDuration, HrTimer, Kernel, SignalHandler, SignalHandlerInner,
9    TargetTime, Timeline, TimerWakeup, WaitCanceler, Waiter,
10};
11use crate::vfs::buffers::{InputBuffer, OutputBuffer};
12use crate::vfs::{
13    Anon, FileHandle, FileObject, FileObjectState, FileOps, fileops_impl_nonseekable,
14    fileops_impl_noop_sync,
15};
16use futures::channel::mpsc;
17use starnix_logging::{log_debug, log_warn};
18use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Mutex};
19use starnix_types::time::{duration_from_timespec, timespec_from_duration, timespec_is_zero};
20use starnix_uapi::errors::Errno;
21use starnix_uapi::open_flags::OpenFlags;
22use starnix_uapi::vfs::FdEvents;
23use starnix_uapi::{TFD_TIMER_ABSTIME, TFD_TIMER_CANCEL_ON_SET, error, itimerspec};
24use std::sync::{Arc, Weak};
25use zerocopy::IntoBytes;
26use zx::HandleRef;
27
28pub trait TimerOps: Send + Sync + 'static {
29    /// Starts the timer with the specified `deadline`.
30    ///
31    /// This method should start the timer and schedule it to trigger at the specified `deadline`.
32    /// The timer should be cancelled if it is already running.
33    fn start(
34        &self,
35        current_task: &CurrentTask,
36        source: Option<Weak<dyn OnWakeOps>>,
37        deadline: TargetTime,
38    ) -> Result<(), Errno>;
39
40    /// Stops the timer.
41    ///
42    /// This method should stop the timer and prevent it from triggering.
43    fn stop(&self, kernel: &Arc<Kernel>) -> Result<(), Errno>;
44
45    /// Returns a reference to the underlying Zircon handle.
46    fn as_handle_ref(&self) -> HandleRef<'_>;
47
48    /// For TimerOps that support monitoring timeline changes (e.g. timers on the
49    /// UTC timeline), this returns a an object that counts the number of timeline
50    /// changes since last reset.
51    ///
52    /// The caller must reset this value to restart the counting.
53    fn get_timeline_change_observer(
54        &self,
55        current_task: &CurrentTask,
56    ) -> Option<TimelineChangeObserver>;
57}
58
59/// Used to observe timeline changes from within TimerOps.
60#[derive(Debug)]
61pub struct TimelineChangeObserver {
62    // Stores the number of timeline changes observed since initial watch, or
63    // timeline reset.
64    timeline_change_counter: zx::Counter,
65    // Used to indicate timeline change interest.
66    timeline_change_registration: mpsc::UnboundedSender<bool>,
67}
68
69impl Drop for TimelineChangeObserver {
70    // Ensure that a lingering registration does not get retained.
71    fn drop(&mut self) {
72        self.set_timeline_change_interest(false);
73    }
74}
75
76impl TimelineChangeObserver {
77    pub fn new(
78        timeline_change_counter: zx::Counter,
79        timeline_change_registration: mpsc::UnboundedSender<bool>,
80    ) -> Self {
81        Self { timeline_change_counter, timeline_change_registration }
82    }
83
84    /// Resets the change counter, and returns the observed value.
85    ///
86    /// The reset is done in such a way that any "sets" that come while "reset" is running does not
87    /// lose a "set".
88    ///
89    /// What is expected here is that at the end either (1) COUNTER_POSITIVE is cleared, or (2) if
90    /// it happens not to be cleared due to a race, that Starnix has a way to probe the
91    /// COUNTER_POSITIVE and react *again* if it remains asserted (without being strobed to zero)
92    /// after this call returns.
93    ///
94    /// (2) happens to be true today due to how file ops work, and will likely continue to be so.
95    /// But it's a tad bit disconcerting that the correct operation of this counter depends on two
96    /// bits of code that are somewhat far away from each other. A safer alternative would be
97    /// a "counter swap" operation that would write a value *and* return the old value atomically,
98    /// but that does not exist today.
99    pub fn reset_timeline_change_counter(&self) -> i64 {
100        let counter = &self.timeline_change_counter;
101        let value = counter.read().expect("it is possible to read the counter");
102        if value != 0 {
103            // We do not write a zero here, but write a number that neutralizes the effect of
104            // `value`. Writing a zero would lose a concurrent "add" from the producer side if
105            // the add came between the `read` and `write zero`.
106            //
107            // This approach will, for the same reason, not necessarily strobe the
108            // COUNTER_POSITIVE signal, but the way Starnix handles async waits will ensure
109            // that the signal value will not be missed.
110            counter.add(-value).expect("it is possible to set the counter to zero");
111        }
112        return value;
113    }
114
115    pub fn get_timeline_change_counter_ref(&self) -> &zx::Counter {
116        &self.timeline_change_counter
117    }
118
119    pub fn set_timeline_change_interest(&mut self, is_interested: bool) {
120        // Unbounded send on an async channel is OK.
121        self.timeline_change_registration.unbounded_send(is_interested).expect("can send");
122    }
123}
124
125/// Deadline interval information for this [TimerFile].
126///
127/// When the file is read, the deadline is recomputed based on the current time and the set
128/// interval. If the interval is 0, `self.timer` is cancelled after the file is read.
129#[derive(Debug)]
130pub struct TimerFileInfo {
131    /// The timeout, expressed as a target value in the chosen timeline.
132    deadline: TargetTime,
133    /// The period for interval timer repeat. `0` for timers that do not repeat.
134    interval: zx::MonotonicDuration,
135    /// Incremented when a timeline change occurs. We must set this to zero manually
136    /// if we want to get repeated signaling. This timer is not shared with any
137    /// other `TimerFileInfo`s, so the signaling protocol should not depend on
138    /// how many timers are active.
139    timeline_change_observer: Option<TimelineChangeObserver>,
140    /// If set, this timer must return ECANCELED when read. (The read unblocks
141    /// when there are UTC timeline changes.)
142    cancel_on_set: bool,
143}
144
145impl TimerFileInfo {
146    pub fn new(next_deadline: TargetTime, interval_period: zx::MonotonicDuration) -> Self {
147        Self {
148            deadline: next_deadline,
149            interval: interval_period,
150            timeline_change_observer: None,
151            cancel_on_set: false,
152        }
153    }
154
155    pub fn reset_timeline_change_counter(&self) -> i64 {
156        if let Some(observer) = self.timeline_change_observer.as_ref() {
157            return observer.reset_timeline_change_counter();
158        } else {
159            0
160        }
161    }
162
163    pub fn set_deadline(&mut self, new_deadline: TargetTime) -> &mut Self {
164        self.deadline = new_deadline;
165        self
166    }
167
168    pub fn set_interval(&mut self, new_interval: zx::MonotonicDuration) -> &mut Self {
169        self.interval = new_interval;
170        self
171    }
172
173    /// Set the counter used for tracking changes to the underlying timeline. This counter gets
174    /// incremented on each timeline change by Starnix from within `HrTimerManager`.
175    pub fn set_timeline_change_observer(
176        &mut self,
177        observer: Option<TimelineChangeObserver>,
178    ) -> &mut Self {
179        self.timeline_change_observer = observer;
180        self
181    }
182
183    /// Mark the timer as "cancel on set". Such a timer will report ECANCELED on read if armed with
184    /// a zero valued realtime timeline, but will report data available for read when used in
185    /// epoll.
186    pub fn set_cancel_on_set(&mut self, value: bool) -> &mut Self {
187        self.cancel_on_set = value;
188        self.timeline_change_observer
189            .as_mut()
190            .map(|observer| observer.set_timeline_change_interest(value));
191        self
192    }
193}
194
195/// A `TimerFile` represents a file created by `timerfd_create`.
196///
197/// Clients can read the number of times the timer has triggered from the file. The file supports
198/// blocking reads, waiting for the timer to trigger.
199pub struct TimerFile {
200    /// The timer that is used to wait for blocking reads.
201    timer: Arc<dyn TimerOps>,
202
203    /// The type of clock this file was created with.
204    timeline: Timeline,
205
206    /// Details about the timeline, deadline and cancel behavior requested from this
207    /// [TimerFile].
208    timer_file_info: Arc<Mutex<TimerFileInfo>>,
209}
210
211impl TimerFile {
212    /// Creates a new anonymous `TimerFile` in `kernel`.
213    ///
214    /// Returns an error if the `zx::Timer` could not be created.
215    pub fn new_file<L>(
216        locked: &mut Locked<L>,
217        current_task: &CurrentTask,
218        wakeup_type: TimerWakeup,
219        timeline: Timeline,
220        flags: OpenFlags,
221    ) -> Result<FileHandle, Errno>
222    where
223        L: LockEqualOrBefore<FileOpsCore>,
224    {
225        let timer: Arc<dyn TimerOps> = match (wakeup_type, timeline) {
226            (TimerWakeup::Regular, Timeline::Monotonic) => Arc::new(MonotonicZxTimer::new()),
227            (TimerWakeup::Regular, Timeline::BootInstant) => Arc::new(BootZxTimer::new()),
228            (TimerWakeup::Regular, Timeline::RealTime)
229            | (TimerWakeup::Alarm, Timeline::BootInstant | Timeline::RealTime) => {
230                Arc::new(HrTimer::new())
231            }
232            (TimerWakeup::Alarm, Timeline::Monotonic) => {
233                unreachable!("monotonic times cannot be alarm deadlines")
234            }
235        };
236
237        let mut timer_file_info =
238            TimerFileInfo::new(timeline.zero_time(), zx::MonotonicDuration::default());
239
240        if timeline.is_realtime() {
241            // Realtime timers must also track changes of the UTC timeline, so we configure
242            // that here. In theory we could only do this when timerfd_settime is called.
243            // However, it turns out that the appropriate wait_asyncs can be requested before
244            // the timer is started with proper flags, such as when the timer is used in
245            // `epoll`. This means we have to create this setup at timer creation.
246            timer_file_info
247                .set_timeline_change_observer(timer.get_timeline_change_observer(current_task));
248        }
249
250        Ok(Anon::new_private_file(
251            locked,
252            current_task,
253            Box::new(TimerFile {
254                timer,
255                timeline,
256                timer_file_info: Arc::new(Mutex::new(timer_file_info)),
257            }),
258            flags,
259            "[timerfd]",
260        ))
261    }
262
263    /// Returns the current `itimerspec` for the file.
264    ///
265    /// The returned `itimerspec.it_value` contains the amount of time remaining until the
266    /// next timer trigger.
267    pub fn current_timer_spec(&self) -> itimerspec {
268        let (deadline, interval) = {
269            let guard = self.timer_file_info.lock();
270            (guard.deadline, guard.interval)
271        };
272        let now = self.timeline.now();
273        let remaining_time = if interval == zx::MonotonicDuration::default() && deadline <= now {
274            timespec_from_duration(zx::MonotonicDuration::default())
275        } else {
276            timespec_from_duration(
277                *deadline.delta(&now).expect("deadline and now come from same timeline"),
278            )
279        };
280
281        itimerspec { it_interval: timespec_from_duration(interval), it_value: remaining_time }
282    }
283
284    /// Sets the `itimerspec` for the timer, which will either update the associated `zx::Timer`'s
285    /// scheduled trigger or cancel the timer.
286    ///
287    /// Returns the previous `itimerspec` on success.
288    pub fn set_timer_spec(
289        &self,
290        current_task: &CurrentTask,
291        file_object: &FileObject,
292        timer_spec: itimerspec,
293        flags: u32,
294    ) -> Result<itimerspec, Errno> {
295        let mut tfi = self.timer_file_info.lock();
296        // On each timer "set", we need to figure out if we want to set this flag again, since each
297        // timer can be used in both "cancel or set" or "regular" flavors over its lifetim.  Reset
298        // it here first unconditionally, then set again below, if the right combination of
299        // settings comes along.
300        tfi.set_cancel_on_set(false);
301        let old_itimerspec = tfi.deadline.itimerspec(tfi.interval);
302
303        if timespec_is_zero(timer_spec.it_value) {
304            // Sayeth timerfd_settime(2):
305            // Setting both fields of new_value.it_value to zero disarms the timer.
306            tfi.set_deadline(self.timeline.zero_time()).set_interval(zx::MonotonicDuration::ZERO);
307            self.timer.stop(current_task.kernel())?;
308
309            // Also sayeth timerfd_settime(2):
310            // TFD_TIMER_CANCEL_ON_SET
311            // If this flag is specified along with TFD_TIMER_ABSTIME and
312            // the clock for this timer is CLOCK_REALTIME or
313            // CLOCK_REALTIME_ALARM, then mark this timer as cancelable if
314            // the real-time clock undergoes a discontinuous change
315            // (settimeofday(2), clock_settime(2), or similar).  When such
316            // changes occur, a current or future read(2) from the file
317            // descriptor will fail with the error ECANCELED.
318            if (flags & TFD_TIMER_ABSTIME != 0)
319                && (flags & TFD_TIMER_CANCEL_ON_SET != 0)
320                && self.timeline.is_realtime()
321            {
322                // This timer is configured as "cancel on set", so mark it as such
323                // to allow wait_async to be configured properly. "Cancel on set"
324                // timers don't get scheduled anywhere, they just monitor timeline
325                // changes, so we don't need to start anything here.
326                tfi.set_cancel_on_set(true);
327            }
328        } else {
329            let new_deadline = if flags & TFD_TIMER_ABSTIME != 0 {
330                // If the time_spec represents an absolute time, then treat the
331                // `it_value` as the deadline..
332                self.timeline.target_from_timespec(timer_spec.it_value)?
333            } else {
334                // .. otherwise the deadline is computed relative to the current time.
335                self.timeline.now()
336                    + GenericDuration::from(duration_from_timespec::<zx::SyntheticTimeline>(
337                        timer_spec.it_value,
338                    )?)
339            };
340            let new_interval = duration_from_timespec(timer_spec.it_interval)?;
341
342            self.timer.start(current_task, Some(file_object.weak_handle.clone()), new_deadline)?;
343            tfi.set_deadline(new_deadline).set_interval(new_interval);
344        }
345
346        Ok(old_itimerspec)
347    }
348
349    /// Returns the `zx::Signals` to listen for given `events`. Used to wait on the `TimerOps`
350    /// associated with a `TimerFile`.
351    fn get_signals_from_events(events: FdEvents) -> zx::Signals {
352        if events.contains(FdEvents::POLLIN) {
353            zx::Signals::TIMER_SIGNALED
354        } else {
355            zx::Signals::NONE
356        }
357    }
358
359    fn get_events_from_signals(signals: zx::Signals) -> FdEvents {
360        let mut events = FdEvents::empty();
361
362        if signals.contains(zx::Signals::TIMER_SIGNALED) {
363            events |= FdEvents::POLLIN;
364        }
365        events
366    }
367
368    /// Converts the events that can happen on a `zx::Counter` to
369    /// corresponding [FdEvents] on a timer. This is used when polling
370    /// for timeline changes on the timers.
371    fn get_counter_events_from_signals(signals: zx::Signals) -> FdEvents {
372        let mut events = FdEvents::empty();
373
374        if signals.contains(zx::Signals::COUNTER_POSITIVE) {
375            events |= FdEvents::POLLIN;
376        }
377        events
378    }
379}
380
381impl FileOps for TimerFile {
382    fileops_impl_nonseekable!();
383    fileops_impl_noop_sync!();
384
385    fn close(
386        self: Box<Self>,
387        _locked: &mut Locked<FileOpsCore>,
388        _file: &FileObjectState,
389        current_task: &CurrentTask,
390    ) {
391        if let Err(e) = self.timer.stop(current_task.kernel()) {
392            log_warn!("Failed to stop the timer when closing the timerfd: {e:?}");
393        }
394    }
395
396    fn write(
397        &self,
398        _locked: &mut Locked<FileOpsCore>,
399        file: &FileObject,
400        _current_task: &CurrentTask,
401        offset: usize,
402        _data: &mut dyn InputBuffer,
403    ) -> Result<usize, Errno> {
404        debug_assert!(offset == 0);
405        // The expected error seems to vary depending on the open flags..
406        if file.flags().contains(OpenFlags::NONBLOCK) { error!(EINVAL) } else { error!(ESPIPE) }
407    }
408
409    fn read(
410        &self,
411        locked: &mut Locked<FileOpsCore>,
412        file: &FileObject,
413        current_task: &CurrentTask,
414        offset: usize,
415        data: &mut dyn OutputBuffer,
416    ) -> Result<usize, Errno> {
417        debug_assert!(offset == 0);
418        file.blocking_op(locked, current_task, FdEvents::POLLIN | FdEvents::POLLHUP, None, |_| {
419            let mut tfi = self.timer_file_info.lock();
420            let is_cancel_on_set = tfi.cancel_on_set;
421            // A "cancel-on-set" timer, this was prepared in `set_timer_spec`. It is handled
422            // specially.
423            if is_cancel_on_set {
424                if tfi.reset_timeline_change_counter() != 0 {
425                    // Timeline has changed, we communicate that by returning ECANCELED
426                    // to the reader. `data` is ignored.
427                    return error!(ECANCELED);
428                }
429                // The timer state has not changed, tell the caller to try again later.
430                return error!(EAGAIN);
431            }
432
433            if tfi.deadline.is_zero() {
434                return error!(EAGAIN);
435            }
436
437            let now = self.timeline.now();
438            log_debug!(
439                "read:\n\tnow={now:?}\n\ttfi={:?}\n\tnow_boot={:?}",
440                zx::MonotonicInstant::get(),
441                tfi.deadline
442            );
443            if tfi.deadline > now {
444                // The next deadline has not yet passed.
445                return error!(EAGAIN);
446            }
447
448            let count: i64 = if tfi.interval > zx::MonotonicDuration::default() {
449                let elapsed_nanos =
450                    now.delta(&tfi.deadline).expect("timelines must match").into_nanos();
451                // The number of times the timer has triggered is written to `data`.
452                let num_intervals = elapsed_nanos / tfi.interval.into_nanos() + 1;
453                let new_deadline =
454                    tfi.deadline + GenericDuration::from(tfi.interval * num_intervals);
455
456                // The timer is set to clear the `ZX_TIMER_SIGNALED` signal until the next deadline
457                // is reached.
458                self.timer.start(current_task, Some(file.weak_handle.clone()), new_deadline)?;
459                tfi.set_deadline(new_deadline);
460
461                /*count=*/
462                num_intervals
463            } else {
464                tfi.set_deadline(self.timeline.zero_time())
465                    .set_interval(zx::MonotonicDuration::ZERO)
466                    .set_cancel_on_set(false);
467                // The timer is non-repeating, so cancel the timer to clear the `ZX_TIMER_SIGNALED`
468                // signal.
469                self.timer.stop(current_task.kernel())?;
470
471                /*count=*/
472                1
473            };
474
475            data.write(count.as_bytes())
476        })
477    }
478
479    fn wait_async(
480        &self,
481        _locked: &mut Locked<FileOpsCore>,
482        _file: &FileObject,
483        _current_task: &CurrentTask,
484        waiter: &Waiter,
485        events: FdEvents,
486        event_handler: EventHandler,
487    ) -> Option<WaitCanceler> {
488        let signal_handler = SignalHandler {
489            inner: SignalHandlerInner::ZxHandle(TimerFile::get_events_from_signals),
490            event_handler: event_handler.clone(),
491            err_code: None,
492        };
493        let canceler = waiter
494            .wake_on_zircon_signals(
495                &self.timer.as_handle_ref(),
496                TimerFile::get_signals_from_events(events),
497                signal_handler,
498            )
499            .expect("TODO return error");
500        //
501        let cancel_timeline_change = {
502            // For timers that support timeline change notifications, set up an additional wake
503            // option on the counter that tallies the occurrences of timeline changes.
504            //
505            // Note that such a counter will not get notified unless the timer is also configured
506            // to receive such notifications.
507            if !self.timeline.is_realtime() {
508                None
509            } else {
510                let guard = self.timer_file_info.lock();
511                guard.timeline_change_observer.as_ref().map(|obs| {
512                    let handler = SignalHandler {
513                        inner: SignalHandlerInner::ZxHandle(
514                            TimerFile::get_counter_events_from_signals,
515                        ),
516                        event_handler,
517                        err_code: None,
518                    };
519                    waiter
520                        .wake_on_zircon_signals(
521                            obs.get_timeline_change_counter_ref(),
522                            zx::Signals::COUNTER_POSITIVE,
523                            handler,
524                        )
525                        .expect("TODO return error")
526                })
527            }
528        };
529        let mut cancel = WaitCanceler::new_port(canceler);
530        if let Some(cancel_timeline_change) = cancel_timeline_change {
531            let cancel_timeline_change = WaitCanceler::new_port(cancel_timeline_change);
532            cancel = cancel.merge(cancel_timeline_change);
533        }
534
535        Some(cancel)
536    }
537
538    fn query_events(
539        &self,
540        _locked: &mut Locked<FileOpsCore>,
541        _file: &FileObject,
542        _current_task: &CurrentTask,
543    ) -> Result<FdEvents, Errno> {
544        let guard = self.timer_file_info.lock();
545        let counter_signals = guard
546            .timeline_change_observer
547            .as_ref()
548            .map(|observer| {
549                observer
550                    .get_timeline_change_counter_ref()
551                    .wait_one(zx::Signals::COUNTER_POSITIVE, zx::Instant::ZERO)
552                    .to_result()
553            })
554            // It seems that translating errors into empty signal sets is OK.
555            .unwrap_or_else(|| Ok(zx::Signals::empty()))
556            .unwrap_or_else(|_| zx::Signals::empty());
557        let events_from_counter = TimerFile::get_counter_events_from_signals(counter_signals);
558
559        let timer_signals = match self
560            .timer
561            .as_handle_ref()
562            .wait_one(zx::Signals::TIMER_SIGNALED, zx::MonotonicInstant::ZERO)
563            .to_result()
564        {
565            Err(zx::Status::TIMED_OUT) => zx::Signals::empty(),
566            res => res.unwrap(),
567        };
568        let events_from_timer = TimerFile::get_events_from_signals(timer_signals);
569        Ok(events_from_timer | events_from_counter)
570    }
571}