Commit 1664a671 authored by FUJITA Tomonori's avatar FUJITA Tomonori Committed by Andreas Hindborg
Browse files

rust: time: Replace ClockId enum with ClockSource trait



Replace the ClockId enum with a trait-based abstraction called
ClockSource. This change enables expressing clock sources as types and
leveraging the Rust type system to enforce clock correctness at
compile time.

This also sets the stage for future generic abstractions over Instant
types such as Instant<C>.

Reviewed-by: default avatarAndreas Hindborg <a.hindborg@kernel.org>
Reviewed-by: default avatarBoqun Feng <boqun.feng@gmail.com>
Signed-off-by: default avatarFUJITA Tomonori <fujita.tomonori@gmail.com>
Link: https://lore.kernel.org/r/20250610093258.3435874-2-fujita.tomonori@gmail.com


Signed-off-by: default avatarAndreas Hindborg <a.hindborg@kernel.org>
parent 1b7bbd59
Loading
Loading
Loading
Loading
+81 −66
Original line number Diff line number Diff line
@@ -49,59 +49,37 @@ pub fn msecs_to_jiffies(msecs: Msecs) -> Jiffies {
    unsafe { bindings::__msecs_to_jiffies(msecs) }
}

/// A specific point in time.
/// Trait for clock sources.
///
/// # Invariants
/// Selection of the clock source depends on the use case. In some cases the usage of a
/// particular clock is mandatory, e.g. in network protocols, filesystems. In other
/// cases the user of the clock has to decide which clock is best suited for the
/// purpose. In most scenarios clock [`Monotonic`] is the best choice as it
/// provides a accurate monotonic notion of time (leap second smearing ignored).
pub trait ClockSource {
    /// The kernel clock ID associated with this clock source.
    ///
/// The `inner` value is in the range from 0 to `KTIME_MAX`.
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct Instant {
    inner: bindings::ktime_t,
}

impl Instant {
    /// Get the current time using `CLOCK_MONOTONIC`.
    #[inline]
    pub fn now() -> Self {
        // INVARIANT: The `ktime_get()` function returns a value in the range
        // from 0 to `KTIME_MAX`.
        Self {
            // SAFETY: It is always safe to call `ktime_get()` outside of NMI context.
            inner: unsafe { bindings::ktime_get() },
        }
    }

    /// Return the amount of time elapsed since the [`Instant`].
    #[inline]
    pub fn elapsed(&self) -> Delta {
        Self::now() - *self
    }
    /// This constant corresponds to the C side `clockid_t` value.
    const ID: bindings::clockid_t;
}

impl core::ops::Sub for Instant {
    type Output = Delta;
/// A monotonically increasing clock.
///
/// A nonsettable system-wide clock that represents monotonic time since as
/// described by POSIX, "some unspecified point in the past". On Linux, that
/// point corresponds to the number of seconds that the system has been
/// running since it was booted.
///
/// The CLOCK_MONOTONIC clock is not affected by discontinuous jumps in the
/// CLOCK_REAL (e.g., if the system administrator manually changes the
/// clock), but is affected by frequency adjustments. This clock does not
/// count time that the system is suspended.
pub struct Monotonic;

    // By the type invariant, it never overflows.
    #[inline]
    fn sub(self, other: Instant) -> Delta {
        Delta {
            nanos: self.inner - other.inner,
        }
    }
impl ClockSource for Monotonic {
    const ID: bindings::clockid_t = bindings::CLOCK_MONOTONIC as bindings::clockid_t;
}

/// An identifier for a clock. Used when specifying clock sources.
///
///
/// Selection of the clock depends on the use case. In some cases the usage of a
/// particular clock is mandatory, e.g. in network protocols, filesystems.In other
/// cases the user of the clock has to decide which clock is best suited for the
/// purpose. In most scenarios clock [`ClockId::Monotonic`] is the best choice as it
/// provides a accurate monotonic notion of time (leap second smearing ignored).
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u32)]
pub enum ClockId {
/// A settable system-wide clock that measures real (i.e., wall-clock) time.
///
/// Setting this clock requires appropriate privileges. This clock is
@@ -116,19 +94,12 @@ pub enum ClockId {
/// or slow down the clock to account for the leap second without
/// discontinuities in the clock. If leap second smearing is not applied,
/// the clock will experience discontinuity around leap second adjustment.
    RealTime = bindings::CLOCK_REALTIME,
    /// A monotonically increasing clock.
    ///
    /// A nonsettable system-wide clock that represents monotonic time since—as
    /// described by POSIX—"some unspecified point in the past". On Linux, that
    /// point corresponds to the number of seconds that the system has been
    /// running since it was booted.
    ///
    /// The CLOCK_MONOTONIC clock is not affected by discontinuous jumps in the
    /// CLOCK_REAL (e.g., if the system administrator manually changes the
    /// clock), but is affected by frequency adjustments. This clock does not
    /// count time that the system is suspended.
    Monotonic = bindings::CLOCK_MONOTONIC,
pub struct RealTime;

impl ClockSource for RealTime {
    const ID: bindings::clockid_t = bindings::CLOCK_REALTIME as bindings::clockid_t;
}

/// A monotonic that ticks while system is suspended.
///
/// A nonsettable system-wide clock that is identical to CLOCK_MONOTONIC,
@@ -136,7 +107,12 @@ pub enum ClockId {
/// allows applications to get a suspend-aware monotonic clock without
/// having to deal with the complications of CLOCK_REALTIME, which may have
/// discontinuities if the time is changed using settimeofday(2) or similar.
    BootTime = bindings::CLOCK_BOOTTIME,
pub struct BootTime;

impl ClockSource for BootTime {
    const ID: bindings::clockid_t = bindings::CLOCK_BOOTTIME as bindings::clockid_t;
}

/// International Atomic Time.
///
/// A system-wide clock derived from wall-clock time but counting leap seconds.
@@ -148,12 +124,51 @@ pub enum ClockId {
/// smearing, this clock will not be precise during leap second smearing.
///
/// The acronym TAI refers to International Atomic Time.
    TAI = bindings::CLOCK_TAI,
pub struct Tai;

impl ClockSource for Tai {
    const ID: bindings::clockid_t = bindings::CLOCK_TAI as bindings::clockid_t;
}

impl ClockId {
    fn into_c(self) -> bindings::clockid_t {
        self as bindings::clockid_t
/// A specific point in time.
///
/// # Invariants
///
/// The `inner` value is in the range from 0 to `KTIME_MAX`.
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct Instant {
    inner: bindings::ktime_t,
}

impl Instant {
    /// Get the current time using `CLOCK_MONOTONIC`.
    #[inline]
    pub fn now() -> Self {
        // INVARIANT: The `ktime_get()` function returns a value in the range
        // from 0 to `KTIME_MAX`.
        Self {
            // SAFETY: It is always safe to call `ktime_get()` outside of NMI context.
            inner: unsafe { bindings::ktime_get() },
        }
    }

    /// Return the amount of time elapsed since the [`Instant`].
    #[inline]
    pub fn elapsed(&self) -> Delta {
        Self::now() - *self
    }
}

impl core::ops::Sub for Instant {
    type Output = Delta;

    // By the type invariant, it never overflows.
    #[inline]
    fn sub(self, other: Instant) -> Delta {
        Delta {
            nanos: self.inner - other.inner,
        }
    }
}

+3 −3
Original line number Diff line number Diff line
@@ -67,7 +67,7 @@
//! A `restart` operation on a timer in the **stopped** state is equivalent to a
//! `start` operation.

use super::ClockId;
use super::ClockSource;
use crate::{prelude::*, types::Opaque};
use core::marker::PhantomData;
use pin_init::PinInit;
@@ -112,7 +112,7 @@ unsafe impl<T> Sync for HrTimer<T> {}

impl<T> HrTimer<T> {
    /// Return an initializer for a new timer instance.
    pub fn new(mode: HrTimerMode, clock: ClockId) -> impl PinInit<Self>
    pub fn new<U: ClockSource>(mode: HrTimerMode) -> impl PinInit<Self>
    where
        T: HrTimerCallback,
    {
@@ -126,7 +126,7 @@ pub fn new(mode: HrTimerMode, clock: ClockId) -> impl PinInit<Self>
                    bindings::hrtimer_setup(
                        place,
                        Some(T::Pointer::run),
                        clock.into_c(),
                        U::ID,
                        mode.into_c(),
                    );
                }