1use 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 fn start(
34 &self,
35 current_task: &CurrentTask,
36 source: Option<Weak<dyn OnWakeOps>>,
37 deadline: TargetTime,
38 ) -> Result<(), Errno>;
39
40 fn stop(&self, kernel: &Arc<Kernel>) -> Result<(), Errno>;
44
45 fn as_handle_ref(&self) -> HandleRef<'_>;
47
48 fn get_timeline_change_observer(
54 &self,
55 current_task: &CurrentTask,
56 ) -> Option<TimelineChangeObserver>;
57}
58
59#[derive(Debug)]
61pub struct TimelineChangeObserver {
62 timeline_change_counter: zx::Counter,
65 timeline_change_registration: mpsc::UnboundedSender<bool>,
67}
68
69impl Drop for TimelineChangeObserver {
70 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 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 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 self.timeline_change_registration.unbounded_send(is_interested).expect("can send");
122 }
123}
124
125#[derive(Debug)]
130pub struct TimerFileInfo {
131 deadline: TargetTime,
133 interval: zx::MonotonicDuration,
135 timeline_change_observer: Option<TimelineChangeObserver>,
140 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 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 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
195pub struct TimerFile {
200 timer: Arc<dyn TimerOps>,
202
203 timeline: Timeline,
205
206 wakeup_type: TimerWakeup,
208
209 timer_file_info: Arc<LockDepMutex<TimerFileInfo, TimerFileInfoLock>>,
212}
213
214impl TimerFile {
215 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 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 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 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 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 tfi.set_deadline(self.timeline.zero_time()).set_interval(zx::MonotonicDuration::ZERO);
310 self.timer.stop(current_task.kernel())?;
311
312 if (flags & TFD_TIMER_ABSTIME != 0)
322 && (flags & TFD_TIMER_CANCEL_ON_SET != 0)
323 && self.timeline.is_realtime()
324 {
325 tfi.set_cancel_on_set(true);
330 }
331 } else {
332 let new_deadline = if flags & TFD_TIMER_ABSTIME != 0 {
333 self.timeline.target_from_timespec(timer_spec.it_value)?
336 } else {
337 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 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 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 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 if is_cancel_on_set {
420 if tfi.reset_timeline_change_counter() != 0 {
421 return error!(ECANCELED);
424 }
425 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 if self
448 .timer
449 .as_handle_ref()
450 .wait_one(zx::Signals::TIMER_SIGNALED, zx::MonotonicInstant::ZERO)
451 .is_ok()
452 {
453 self.timer.start(current_task, Some(file.weak_handle.clone()), tfi.deadline)?;
454 }
455 return error!(EAGAIN);
457 }
458
459 let count: i64 = if tfi.interval > zx::MonotonicDuration::default() {
460 let elapsed_nanos =
461 now.delta(&tfi.deadline).expect("timelines must match").into_nanos();
462 let num_intervals = elapsed_nanos / tfi.interval.into_nanos() + 1;
464 let new_deadline =
465 tfi.deadline + GenericDuration::from(tfi.interval * num_intervals);
466
467 self.timer.start(current_task, Some(file.weak_handle.clone()), new_deadline)?;
470 tfi.set_deadline(new_deadline);
471
472 num_intervals
474 } else {
475 tfi.set_deadline(self.timeline.zero_time())
476 .set_interval(zx::MonotonicDuration::ZERO)
477 .set_cancel_on_set(false);
478 self.timer.stop(current_task.kernel())?;
481
482 1
484 };
485
486 data.write(count.as_bytes())
487 })
488 }
489
490 fn wait_async(
491 &self,
492 _file: &FileObject,
493 _current_task: &CurrentTask,
494 waiter: &Waiter,
495 events: FdEvents,
496 event_handler: EventHandler,
497 ) -> Option<WaitCanceler> {
498 let signal_handler = SignalHandler {
499 inner: SignalHandlerInner::ZxHandle(TimerFile::get_events_from_signals),
500 event_handler: event_handler.clone(),
501 err_code: None,
502 };
503 let canceler = waiter
504 .wake_on_zircon_signals(
505 &self.timer.as_handle_ref(),
506 TimerFile::get_signals_from_events(events),
507 signal_handler,
508 )
509 .expect("TODO return error");
510 let cancel_timeline_change = {
512 if !self.timeline.is_realtime() {
518 None
519 } else {
520 let guard = self.timer_file_info.lock();
521 guard.timeline_change_observer.as_ref().map(|obs| {
522 let handler = SignalHandler {
523 inner: SignalHandlerInner::ZxHandle(
524 TimerFile::get_counter_events_from_signals,
525 ),
526 event_handler,
527 err_code: None,
528 };
529 waiter
530 .wake_on_zircon_signals(
531 obs.get_timeline_change_counter_ref(),
532 zx::Signals::COUNTER_POSITIVE,
533 handler,
534 )
535 .expect("TODO return error")
536 })
537 }
538 };
539 let mut cancel = WaitCanceler::new_port(canceler);
540 if let Some(cancel_timeline_change) = cancel_timeline_change {
541 let cancel_timeline_change = WaitCanceler::new_port(cancel_timeline_change);
542 cancel = cancel.merge(cancel_timeline_change);
543 }
544
545 Some(cancel)
546 }
547
548 fn query_events(
549 &self,
550 file: &FileObject,
551 current_task: &CurrentTask,
552 ) -> Result<FdEvents, Errno> {
553 let guard = self.timer_file_info.lock();
554 let counter_signals = guard
555 .timeline_change_observer
556 .as_ref()
557 .map(|observer| {
558 observer
559 .get_timeline_change_counter_ref()
560 .wait_one(zx::Signals::COUNTER_POSITIVE, zx::Instant::ZERO)
561 .to_result()
562 })
563 .unwrap_or_else(|| Ok(zx::Signals::empty()))
565 .unwrap_or_else(|_| zx::Signals::empty());
566 let events_from_counter = TimerFile::get_counter_events_from_signals(counter_signals);
567
568 let mut timer_signals = match self
569 .timer
570 .as_handle_ref()
571 .wait_one(zx::Signals::TIMER_SIGNALED, zx::MonotonicInstant::ZERO)
572 .to_result()
573 {
574 Err(zx::Status::TIMED_OUT) => zx::Signals::empty(),
575 res => res.unwrap(),
576 };
577 if timer_signals.contains(zx::Signals::TIMER_SIGNALED)
582 && !guard.deadline.is_zero()
583 && guard.deadline > self.timeline.now()
584 {
585 match self.timer.start(current_task, Some(file.weak_handle.clone()), guard.deadline) {
586 Ok(()) => timer_signals.remove(zx::Signals::TIMER_SIGNALED),
587 Err(e) => log_warn!("timerfd: failed to re-arm prematurely signaled timer: {e:?}"),
588 }
589 }
590 let events_from_timer = TimerFile::get_events_from_signals(timer_signals);
591 Ok(events_from_timer | events_from_counter)
592 }
593}
594
595#[cfg(test)]
596mod tests {
597 use super::*;
598 use crate::testing::spawn_kernel_and_run;
599 use crate::vfs::buffers::VecOutputBuffer;
600 use crate::vfs::epoll::EpollFileObject;
601 use starnix_uapi::timespec;
602 use starnix_uapi::vfs::EpollEvent;
603
604 #[::fuchsia::test]
605 async fn test_zx_timers_use_late_slack() {
606 let mono = MonotonicZxTimer::new();
607 let mono_dup = mono
608 .as_handle_ref()
609 .duplicate_handle(zx::Rights::SAME_RIGHTS)
610 .expect("duplicate handle");
611 let mono_info = zx::MonotonicTimer::from(mono_dup).info().expect("timer info");
612 assert_eq!(mono_info.options, zx::sys::ZX_TIMER_SLACK_LATE);
613
614 let boot = BootZxTimer::new();
615 let boot_dup = boot
616 .as_handle_ref()
617 .duplicate_handle(zx::Rights::SAME_RIGHTS)
618 .expect("duplicate handle");
619 let boot_info = zx::BootTimer::from(boot_dup).info().expect("timer info");
620 assert_eq!(boot_info.options, zx::sys::ZX_TIMER_SLACK_LATE);
621 }
622
623 #[::fuchsia::test]
624 async fn test_timerfd_blocking_read_when_signaled_before_deadline() {
625 spawn_kernel_and_run(async |current_task| {
626 let timer_file_handle = TimerFile::new_file(
627 ¤t_task,
628 TimerWakeup::Regular,
629 Timeline::Monotonic,
630 OpenFlags::RDONLY,
631 )
632 .unwrap();
633 let timer_file = timer_file_handle.downcast_file::<TimerFile>().unwrap();
634
635 timer_file
639 .timer
640 .start(¤t_task, None, TargetTime::Monotonic(zx::MonotonicInstant::ZERO))
641 .unwrap();
642 assert!(
643 timer_file
644 .timer
645 .as_handle_ref()
646 .wait_one(zx::Signals::TIMER_SIGNALED, zx::MonotonicInstant::INFINITE)
647 .is_ok()
648 );
649 timer_file.timer_file_info.lock().set_deadline(TargetTime::Monotonic(
650 zx::MonotonicInstant::after(zx::MonotonicDuration::from_hours(1)),
651 ));
652 assert_eq!(timer_file_handle.query_events(¤t_task).unwrap(), FdEvents::empty());
653 assert_eq!(
654 timer_file
655 .timer
656 .as_handle_ref()
657 .wait_one(zx::Signals::TIMER_SIGNALED, zx::MonotonicInstant::ZERO)
658 .to_result(),
659 Err(zx::Status::TIMED_OUT)
660 );
661
662 timer_file
675 .timer
676 .start(¤t_task, None, TargetTime::Monotonic(zx::MonotonicInstant::ZERO))
677 .unwrap();
678 assert!(
679 timer_file
680 .timer
681 .as_handle_ref()
682 .wait_one(zx::Signals::TIMER_SIGNALED, zx::MonotonicInstant::INFINITE)
683 .is_ok()
684 );
685 let future_deadline = TargetTime::Monotonic(zx::MonotonicInstant::after(
686 zx::MonotonicDuration::from_millis(50),
687 ));
688 timer_file.timer_file_info.lock().set_deadline(future_deadline);
689
690 let mut buf = VecOutputBuffer::new(8);
691 let bytes_read = timer_file_handle.read(¤t_task, &mut buf).unwrap();
692 assert_eq!(bytes_read, 8);
693 assert_eq!(i64::from_ne_bytes(buf.data().try_into().unwrap()), 1);
694 assert!(Timeline::Monotonic.now() >= future_deadline);
695 })
696 .await;
697 }
698
699 #[::fuchsia::test]
700 async fn test_timerfd_epoll_and_blocking_read_with_timerslack() {
701 spawn_kernel_and_run(async |current_task| {
702 const DEADLINE_DURATION: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(50);
703
704 let epoll_handle = EpollFileObject::new_file(¤t_task);
705 let epoll_file = epoll_handle.downcast_file::<EpollFileObject>().unwrap();
706
707 let timer_file_handle = TimerFile::new_file(
708 ¤t_task,
709 TimerWakeup::Regular,
710 Timeline::Monotonic,
711 OpenFlags::RDONLY | OpenFlags::NONBLOCK,
712 )
713 .unwrap();
714
715 epoll_file
716 .add(
717 ¤t_task,
718 &timer_file_handle,
719 &epoll_handle,
720 EpollEvent::new(FdEvents::POLLIN, 3),
721 )
722 .unwrap();
723
724 current_task.write().set_timerslack_ns(DEADLINE_DURATION.into_nanos() as u64);
726
727 let early_timer = zx::MonotonicTimer::create();
740 let now = zx::MonotonicInstant::get();
741 early_timer
742 .set(now + zx::MonotonicDuration::from_millis(20), zx::MonotonicDuration::ZERO)
743 .unwrap();
744
745 let timer_file = timer_file_handle.downcast_file::<TimerFile>().unwrap();
746 let spec = itimerspec {
747 it_interval: timespec { tv_sec: 0, tv_nsec: 0 },
748 it_value: timespec_from_duration(DEADLINE_DURATION),
749 };
750 timer_file.set_timer_spec(¤t_task, &timer_file_handle, spec, 0).unwrap();
751
752 timer_file
755 .timer
756 .as_handle_ref()
757 .wait_one(zx::Signals::TIMER_SIGNALED, zx::MonotonicInstant::INFINITE)
758 .to_result()
759 .unwrap();
760 let signaled_after = zx::MonotonicInstant::get() - now;
761 assert!(
762 signaled_after >= DEADLINE_DURATION,
763 "ZX_TIMER_SIGNALED asserted early after {signaled_after:?}"
764 );
765
766 let events = epoll_file.wait(¤t_task, 1, zx::MonotonicInstant::INFINITE).unwrap();
767 assert_eq!(events.len(), 1);
768
769 let mut buf = VecOutputBuffer::new(8);
770 let bytes_read = timer_file_handle.read(¤t_task, &mut buf).unwrap();
771 let elapsed = zx::MonotonicInstant::get() - now;
772 assert_eq!(bytes_read, 8);
773 assert!(elapsed >= DEADLINE_DURATION, "timerfd expired early after {elapsed:?}");
774 })
775 .await;
776 }
777}