Re: [PATCH v2] rust: time: add Ktime

From: Valentin Obst
Date: Sun Mar 24 2024 - 05:47:11 EST


> Subject: [PATCH] rust: time: Add clock source reading functionality
>
> Introduce wrappers around `ktime_t` with a time duration type `KTime`
> and a timestamp type `Instant`.
>
> Rust Binder will use these bindings to compute how many milliseconds a
> transaction has been active for when dumping the current state of the
> Binder driver. This replicates the logic in C Binder [1].
>
> For a usage example in Rust Binder, see [2].
>
> The `ktime_get` method cannot be safely called in NMI context. This
> requirement is not checked by these abstractions, but it is intended
> that klint [3] or a similar tool will be used to check it in the future.
>
> Link: https://lore.kernel.org/lkml/5ac8c0d09392290be789423f0dd78a520b830fab.1682333709.git.zhangchuang3@xxxxxxxxxx/ [1]
> Link: https://r.android.com/3004103 [2]
> Link: https://rust-for-linux.com/klint [3]
> Originally-by: Heghedus Razvan <heghedus.razvan@xxxxxxxxxxxxxx>
> Originally-by: Asahi Lina <lina@xxxxxxxxxxxxx>
> Signed-off-by: Alice Ryhl <aliceryhl@xxxxxxxxxx>
> Signed-off-by: Boqun Feng <boqun.feng@xxxxxxxxx>
> ---
> rust/kernel/time.rs | 158 ++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 158 insertions(+)
>
> diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs
> index 25a896eed468..50cc063aa9b4 100644
> --- a/rust/kernel/time.rs
> +++ b/rust/kernel/time.rs
> @@ -4,6 +4,15 @@
> //!
> //! This module contains the kernel APIs related to time and timers that
> //! have been ported or wrapped for usage by Rust code in the kernel.
> +//!
> +//! C header: [`include/linux/jiffies.h`](srctree/include/linux/jiffiesh).
> +//! C header: [`include/linux/ktime.h`](srctree/include/linux/ktime.h).
> +
> +use crate::pr_err;
> +use core::marker::PhantomData;
> +
> +/// The number of nanoseconds per millisecond.
> +pub const NSEC_PER_MSEC: i64 = bindings::NSEC_PER_MSEC as i64;
>
> /// The time unit of Linux kernel. One jiffy equals (1/HZ) second.
> pub type Jiffies = core::ffi::c_ulong;
> @@ -18,3 +27,152 @@ pub fn msecs_to_jiffies(msecs: Msecs) -> Jiffies {
> // matter what the argument is.
> unsafe { bindings::__msecs_to_jiffies(msecs) }
> }
> +
> +/// A kernel time duration.
> +///
> +/// This type basically wraps the `ktime_t` with one restriction: it should only be used for
> +/// representing a time duration, in other words, it's not the type for timestamps.
> +#[repr(transparent)]
> +#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
> +pub struct KTime {
> + inner: bindings::ktime_t,
> +}
> +
> +impl KTime {
> + /// Create a [`KTime`] from a raw `ktime_t`.
> + #[inline]
> + pub fn from_raw(inner: bindings::ktime_t) -> Self {
> + Self { inner }
> + }

Eventually we might want to be able to create instances of types that
represent durations in const contexts, e.g., for fixed thresholds or
fixed offsets to relative timers. Would it make sense to '/fn/const fn/'
for the `KTime` (or `Ktime`) methods that support it?

[For that use case the naming/signature `from_raw(inner: bindings::ktime_t)`
could maybe also be changed to something like `new(duration: i64)`, i.e.,
make it sound less like an internal API.]

- Best Valentin

> +
> + /// Divide the number of nanoseconds by a compile-time constant.
> + #[inline]
> + fn divns_constant<const DIV: i64>(self) -> i64 {
> + self.to_ns() / DIV
> + }
> +
> + /// Returns the number of nanoseconds.
> + #[inline]
> + pub fn to_ns(self) -> i64 {
> + self.inner
> + }
> +
> + /// Returns the number of milliseconds.
> + #[inline]
> + pub fn to_ms(self) -> i64 {
> + self.divns_constant::<NSEC_PER_MSEC>()
> + }
> +}
> +
> +impl core::ops::Sub for KTime {
> + type Output = KTime;
> +
> + #[inline]
> + fn sub(self, other: KTime) -> KTime {
> + Self {
> + inner: self.inner - other.inner,
> + }
> + }
> +}
> +
> +/// Represents a clock, that is, a unique time source and it can be queried for the current time.
> +pub trait Clock: Sized {
> + /// Returns the current time for this clock.
> + fn now() -> Instant<Self>;
> +}
> +
> +/// Marker trait for clock sources that are guaranteed to be monotonic.
> +pub trait Monotonic {}
> +
> +/// An instant in time associated with a given clock source.
> +#[derive(Debug)]
> +pub struct Instant<T: Clock> {
> + ktime: KTime,
> + _type: PhantomData<T>,
> +}
> +
> +impl<T: Clock> Clone for Instant<T> {
> + fn clone(&self) -> Self {
> + *self
> + }
> +}
> +
> +impl<T: Clock> Copy for Instant<T> {}
> +
> +impl<T: Clock> Instant<T> {
> + fn new(ktime: KTime) -> Self {
> + Instant {
> + ktime,
> + _type: PhantomData,
> + }
> + }
> +
> + /// Returns the time elapsed since an earlier [`Instant`], or None if the argument is a later
> + /// Instant.
> + ///
> + /// # Examples
> + ///
> + /// ```
> + /// use kernel::time::{Clock, clock::KernelTime};
> + ///
> + /// let a = KernelTime::now();
> + /// let b = KernelTime::now();
> + ///
> + /// // `KernelTime` is monotonic.
> + /// assert_eq!(a.since(b), None);
> + /// assert_eq!(b.since(a).map(|d| d.to_ns() >= 0), Some(true));
> + ///
> + /// ```
> + pub fn since(&self, earlier: Instant<T>) -> Option<KTime> {
> + if self.ktime < earlier.ktime {
> + None
> + } else {
> + Some(self.ktime - earlier.ktime)
> + }
> + }
> +}
> +
> +impl<T: Clock + Monotonic> Instant<T> {
> + /// Returns the time elapsed since this [`Instant`].
> + ///
> + /// This is guaranteed to return a non-negative result, since it is only implemented for
> + /// monotonic clocks.
> + ///
> + /// # Examples
> + ///
> + /// ```
> + /// use kernel::time::{Clock, clock::KernelTime};
> + ///
> + /// let a = KernelTime::now();
> + ///
> + /// // `KernelTime` is monotonic.
> + /// assert!(a.elapsed().to_ns() >= 0);
> + ///
> + /// ```
> + pub fn elapsed(&self) -> KTime {
> + self.since(T::now()).unwrap_or_else(|| {
> + pr_err!(
> + "Monotonic clock {} went backwards!",
> + core::any::type_name::<T>()
> + );
> + KTime::from_raw(0)
> + })
> + }
> +}
> +
> +/// Contains the various clock source types available to the kernel.
> +pub mod clock {
> + use super::*;
> +
> + /// A clock representing the default kernel time source (`CLOCK_MONOTONIC`).
> + pub struct KernelTime;
> +
> + impl Monotonic for KernelTime {}

nit: blank line missing

> + impl Clock for KernelTime {
> + #[inline]
> + fn now() -> Instant<Self> {
> + // SAFETY: It is always safe to call `ktime_get` outside of NMI context.
> + Instant::<Self>::new(KTime::from_raw(unsafe { bindings::ktime_get() }))
> + }
> + }
> +}