Skip to main content

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, Kernel, SignalHandler, SignalHandlerInner, WaitCanceler, Waiter,
9};
10use crate::time::{GenericDuration, HrTimer, TargetTime, Timeline, TimerWakeup};
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::{LockDepMutex, TimerFileInfoLock};
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    /// Whether this timer can wake up the system.
207    wakeup_type: TimerWakeup,
208
209    /// Details about the timeline, deadline and cancel behavior requested from this
210    /// [TimerFile].
211    timer_file_info: Arc<LockDepMutex<TimerFileInfo, TimerFileInfoLock>>,
212}
213
214impl TimerFile {
215    /// Creates a new anonymous `TimerFile` in `kernel`.
216    ///
217    /// Returns an error if the `zx::Timer` could not be created.
218    pub fn new_file(
219        current_task: &CurrentTask,
220        wakeup_type: TimerWakeup,
221        timeline: Timeline,
222        flags: OpenFlags,
223    ) -> Result<FileHandle, Errno> {
224        let timer: Arc<dyn TimerOps> = match (wakeup_type, timeline) {
225            (TimerWakeup::Regular, Timeline::Monotonic) => Arc::new(MonotonicZxTimer::new()),
226            (TimerWakeup::Regular, Timeline::BootInstant) => Arc::new(BootZxTimer::new()),
227            (TimerWakeup::Regular, Timeline::RealTime)
228            | (TimerWakeup::Alarm, Timeline::BootInstant | Timeline::RealTime) => {
229                Arc::new(HrTimer::new())
230            }
231            (TimerWakeup::Alarm, Timeline::Monotonic) => {
232                unreachable!("monotonic times cannot be alarm deadlines")
233            }
234        };
235
236        let mut timer_file_info =
237            TimerFileInfo::new(timeline.zero_time(), zx::MonotonicDuration::default());
238
239        if timeline.is_realtime() {
240            // Realtime timers must also track changes of the UTC timeline, so we configure
241            // that here. In theory we could only do this when timerfd_settime is called.
242            // However, it turns out that the appropriate wait_asyncs can be requested before
243            // the timer is started with proper flags, such as when the timer is used in
244            // `epoll`. This means we have to create this setup at timer creation.
245            timer_file_info
246                .set_timeline_change_observer(timer.get_timeline_change_observer(current_task));
247        }
248
249        Ok(Anon::new_private_file(
250            current_task,
251            Box::new(TimerFile {
252                timer,
253                timeline,
254                wakeup_type,
255                timer_file_info: Arc::new(timer_file_info.into()),
256            }),
257            flags,
258            "[timerfd]",
259        ))
260    }
261
262    pub fn wakeup_type(&self) -> TimerWakeup {
263        self.wakeup_type
264    }
265
266    /// Returns the current `itimerspec` for the file.
267    ///
268    /// The returned `itimerspec.it_value` contains the amount of time remaining until the
269    /// next timer trigger.
270    pub fn current_timer_spec(&self) -> itimerspec {
271        let (deadline, interval) = {
272            let guard = self.timer_file_info.lock();
273            (guard.deadline, guard.interval)
274        };
275        let now = self.timeline.now();
276        let remaining_time = if interval == zx::MonotonicDuration::default() && deadline <= now {
277            timespec_from_duration(zx::MonotonicDuration::default())
278        } else {
279            timespec_from_duration(
280                *deadline.delta(&now).expect("deadline and now come from same timeline"),
281            )
282        };
283
284        itimerspec { it_interval: timespec_from_duration(interval), it_value: remaining_time }
285    }
286
287    /// Sets the `itimerspec` for the timer, which will either update the associated `zx::Timer`'s
288    /// scheduled trigger or cancel the timer.
289    ///
290    /// Returns the previous `itimerspec` on success.
291    pub fn set_timer_spec(
292        &self,
293        current_task: &CurrentTask,
294        file_object: &FileObject,
295        timer_spec: itimerspec,
296        flags: u32,
297    ) -> Result<itimerspec, Errno> {
298        let mut tfi = self.timer_file_info.lock();
299        // On each timer "set", we need to figure out if we want to set this flag again, since each
300        // timer can be used in both "cancel or set" or "regular" flavors over its lifetim.  Reset
301        // it here first unconditionally, then set again below, if the right combination of
302        // settings comes along.
303        tfi.set_cancel_on_set(false);
304        let old_itimerspec = tfi.deadline.itimerspec(tfi.interval);
305
306        if timespec_is_zero(timer_spec.it_value) {
307            // Sayeth timerfd_settime(2):
308            // Setting both fields of new_value.it_value to zero disarms the timer.
309            tfi.set_deadline(self.timeline.zero_time()).set_interval(zx::MonotonicDuration::ZERO);
310            self.timer.stop(current_task.kernel())?;
311
312            // Also sayeth timerfd_settime(2):
313            // TFD_TIMER_CANCEL_ON_SET
314            // If this flag is specified along with TFD_TIMER_ABSTIME and
315            // the clock for this timer is CLOCK_REALTIME or
316            // CLOCK_REALTIME_ALARM, then mark this timer as cancelable if
317            // the real-time clock undergoes a discontinuous change
318            // (settimeofday(2), clock_settime(2), or similar).  When such
319            // changes occur, a current or future read(2) from the file
320            // descriptor will fail with the error ECANCELED.
321            if (flags & TFD_TIMER_ABSTIME != 0)
322                && (flags & TFD_TIMER_CANCEL_ON_SET != 0)
323                && self.timeline.is_realtime()
324            {
325                // This timer is configured as "cancel on set", so mark it as such
326                // to allow wait_async to be configured properly. "Cancel on set"
327                // timers don't get scheduled anywhere, they just monitor timeline
328                // changes, so we don't need to start anything here.
329                tfi.set_cancel_on_set(true);
330            }
331        } else {
332            let new_deadline = if flags & TFD_TIMER_ABSTIME != 0 {
333                // If the time_spec represents an absolute time, then treat the
334                // `it_value` as the deadline..
335                self.timeline.target_from_timespec(timer_spec.it_value)?
336            } else {
337                // .. otherwise the deadline is computed relative to the current time.
338                self.timeline.now()
339                    + GenericDuration::from(duration_from_timespec::<zx::SyntheticTimeline>(
340                        timer_spec.it_value,
341                    )?)
342            };
343            let new_interval = duration_from_timespec(timer_spec.it_interval)?;
344
345            self.timer.start(current_task, Some(file_object.weak_handle.clone()), new_deadline)?;
346            tfi.set_deadline(new_deadline).set_interval(new_interval);
347        }
348
349        Ok(old_itimerspec)
350    }
351
352    /// Returns the `zx::Signals` to listen for given `events`. Used to wait on the `TimerOps`
353    /// associated with a `TimerFile`.
354    fn get_signals_from_events(events: FdEvents) -> zx::Signals {
355        if events.contains(FdEvents::POLLIN) {
356            zx::Signals::TIMER_SIGNALED
357        } else {
358            zx::Signals::NONE
359        }
360    }
361
362    fn get_events_from_signals(signals: zx::Signals) -> FdEvents {
363        let mut events = FdEvents::empty();
364
365        if signals.contains(zx::Signals::TIMER_SIGNALED) {
366            events |= FdEvents::POLLIN;
367        }
368        events
369    }
370
371    /// Converts the events that can happen on a `zx::Counter` to
372    /// corresponding [FdEvents] on a timer. This is used when polling
373    /// for timeline changes on the timers.
374    fn get_counter_events_from_signals(signals: zx::Signals) -> FdEvents {
375        let mut events = FdEvents::empty();
376
377        if signals.contains(zx::Signals::COUNTER_POSITIVE) {
378            events |= FdEvents::POLLIN;
379        }
380        events
381    }
382}
383
384impl FileOps for TimerFile {
385    fileops_impl_nonseekable!();
386    fileops_impl_noop_sync!();
387
388    fn close(self: Box<Self>, _file: &FileObjectState, current_task: &CurrentTask) {
389        if let Err(e) = self.timer.stop(current_task.kernel()) {
390            log_warn!("Failed to stop the timer when closing the timerfd: {e:?}");
391        }
392    }
393
394    fn write(
395        &self,
396        file: &FileObject,
397        _current_task: &CurrentTask,
398        offset: usize,
399        _data: &mut dyn InputBuffer,
400    ) -> Result<usize, Errno> {
401        debug_assert!(offset == 0);
402        // The expected error seems to vary depending on the open flags..
403        if file.flags().contains(OpenFlags::NONBLOCK) { error!(EINVAL) } else { error!(ESPIPE) }
404    }
405
406    fn read(
407        &self,
408        file: &FileObject,
409        current_task: &CurrentTask,
410        offset: usize,
411        data: &mut dyn OutputBuffer,
412    ) -> Result<usize, Errno> {
413        debug_assert!(offset == 0);
414        file.blocking_op(current_task, FdEvents::POLLIN | FdEvents::POLLHUP, None, || {
415            let mut tfi = self.timer_file_info.lock();
416            let is_cancel_on_set = tfi.cancel_on_set;
417            // A "cancel-on-set" timer, this was prepared in `set_timer_spec`. It is handled
418            // specially.
419            if is_cancel_on_set {
420                if tfi.reset_timeline_change_counter() != 0 {
421                    // Timeline has changed, we communicate that by returning ECANCELED
422                    // to the reader. `data` is ignored.
423                    return error!(ECANCELED);
424                }
425                // The timer state has not changed, tell the caller to try again later.
426                return error!(EAGAIN);
427            }
428
429            if tfi.deadline.is_zero() {
430                return error!(EAGAIN);
431            }
432
433            let now = self.timeline.now();
434            log_debug!(
435                "read:\n\tnow={now:?}\n\ttfi={:?}\n\tnow_boot={:?}",
436                zx::MonotonicInstant::get(),
437                tfi.deadline
438            );
439            if tfi.deadline > now {
440                // The next deadline has not yet passed.
441                return error!(EAGAIN);
442            }
443
444            let count: i64 = if tfi.interval > zx::MonotonicDuration::default() {
445                let elapsed_nanos =
446                    now.delta(&tfi.deadline).expect("timelines must match").into_nanos();
447                // The number of times the timer has triggered is written to `data`.
448                let num_intervals = elapsed_nanos / tfi.interval.into_nanos() + 1;
449                let new_deadline =
450                    tfi.deadline + GenericDuration::from(tfi.interval * num_intervals);
451
452                // The timer is set to clear the `ZX_TIMER_SIGNALED` signal until the next deadline
453                // is reached.
454                self.timer.start(current_task, Some(file.weak_handle.clone()), new_deadline)?;
455                tfi.set_deadline(new_deadline);
456
457                /*count=*/
458                num_intervals
459            } else {
460                tfi.set_deadline(self.timeline.zero_time())
461                    .set_interval(zx::MonotonicDuration::ZERO)
462                    .set_cancel_on_set(false);
463                // The timer is non-repeating, so cancel the timer to clear the `ZX_TIMER_SIGNALED`
464                // signal.
465                self.timer.stop(current_task.kernel())?;
466
467                /*count=*/
468                1
469            };
470
471            data.write(count.as_bytes())
472        })
473    }
474
475    fn wait_async(
476        &self,
477        _file: &FileObject,
478        _current_task: &CurrentTask,
479        waiter: &Waiter,
480        events: FdEvents,
481        event_handler: EventHandler,
482    ) -> Option<WaitCanceler> {
483        let signal_handler = SignalHandler {
484            inner: SignalHandlerInner::ZxHandle(TimerFile::get_events_from_signals),
485            event_handler: event_handler.clone(),
486            err_code: None,
487        };
488        let canceler = waiter
489            .wake_on_zircon_signals(
490                &self.timer.as_handle_ref(),
491                TimerFile::get_signals_from_events(events),
492                signal_handler,
493            )
494            .expect("TODO return error");
495        //
496        let cancel_timeline_change = {
497            // For timers that support timeline change notifications, set up an additional wake
498            // option on the counter that tallies the occurrences of timeline changes.
499            //
500            // Note that such a counter will not get notified unless the timer is also configured
501            // to receive such notifications.
502            if !self.timeline.is_realtime() {
503                None
504            } else {
505                let guard = self.timer_file_info.lock();
506                guard.timeline_change_observer.as_ref().map(|obs| {
507                    let handler = SignalHandler {
508                        inner: SignalHandlerInner::ZxHandle(
509                            TimerFile::get_counter_events_from_signals,
510                        ),
511                        event_handler,
512                        err_code: None,
513                    };
514                    waiter
515                        .wake_on_zircon_signals(
516                            obs.get_timeline_change_counter_ref(),
517                            zx::Signals::COUNTER_POSITIVE,
518                            handler,
519                        )
520                        .expect("TODO return error")
521                })
522            }
523        };
524        let mut cancel = WaitCanceler::new_port(canceler);
525        if let Some(cancel_timeline_change) = cancel_timeline_change {
526            let cancel_timeline_change = WaitCanceler::new_port(cancel_timeline_change);
527            cancel = cancel.merge(cancel_timeline_change);
528        }
529
530        Some(cancel)
531    }
532
533    fn query_events(
534        &self,
535        _file: &FileObject,
536        _current_task: &CurrentTask,
537    ) -> Result<FdEvents, Errno> {
538        let guard = self.timer_file_info.lock();
539        let counter_signals = guard
540            .timeline_change_observer
541            .as_ref()
542            .map(|observer| {
543                observer
544                    .get_timeline_change_counter_ref()
545                    .wait_one(zx::Signals::COUNTER_POSITIVE, zx::Instant::ZERO)
546                    .to_result()
547            })
548            // It seems that translating errors into empty signal sets is OK.
549            .unwrap_or_else(|| Ok(zx::Signals::empty()))
550            .unwrap_or_else(|_| zx::Signals::empty());
551        let events_from_counter = TimerFile::get_counter_events_from_signals(counter_signals);
552
553        let timer_signals = match self
554            .timer
555            .as_handle_ref()
556            .wait_one(zx::Signals::TIMER_SIGNALED, zx::MonotonicInstant::ZERO)
557            .to_result()
558        {
559            Err(zx::Status::TIMED_OUT) => zx::Signals::empty(),
560            res => res.unwrap(),
561        };
562        let events_from_timer = TimerFile::get_events_from_signals(timer_signals);
563        Ok(events_from_timer | events_from_counter)
564    }
565}