From a984da24e7ac411c95bab7b6c127bae4927f9d03 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 21 Aug 2025 15:32:41 -0400 Subject: rust: hrtimer: Document the return value for HrTimerHandle::cancel() Just a drive-by fix I noticed: we don't actually document what the return value from cancel() does, so do that. Signed-off-by: Lyude Paul Reviewed-by: Andreas Hindborg Reviewed-by: Daniel Almeida Link: https://lore.kernel.org/r/20250821193259.964504-2-lyude@redhat.com Signed-off-by: Andreas Hindborg --- rust/kernel/time/hrtimer.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/time/hrtimer.rs b/rust/kernel/time/hrtimer.rs index 144e3b57cc78..6bfc0223f4f5 100644 --- a/rust/kernel/time/hrtimer.rs +++ b/rust/kernel/time/hrtimer.rs @@ -324,6 +324,8 @@ pub unsafe trait HrTimerHandle { /// Note that the timer might be started by a concurrent start operation. If /// so, the timer might not be in the **stopped** state when this function /// returns. + /// + /// Returns `true` if the timer was running. fn cancel(&mut self) -> bool; } -- cgit v1.2.3 From 0e2aab67f2d5716e8db73b6d0719208b44086ed5 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 21 Aug 2025 15:32:42 -0400 Subject: rust: hrtimer: Add HrTimerInstant Since we want to add HrTimer methods that can accept Instants, we will want to make sure that for each method we are using the correct Clocksource for the given HrTimer. This would get a bit overly-verbose, so add a simple HrTimerInstant type-alias to handle this for us. Signed-off-by: Lyude Paul Reviewed-by: Andreas Hindborg Reviewed-by: Daniel Almeida Link: https://lore.kernel.org/r/20250821193259.964504-3-lyude@redhat.com Signed-off-by: Andreas Hindborg --- rust/kernel/time/hrtimer.rs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/time/hrtimer.rs b/rust/kernel/time/hrtimer.rs index 6bfc0223f4f5..be1bad4aacaa 100644 --- a/rust/kernel/time/hrtimer.rs +++ b/rust/kernel/time/hrtimer.rs @@ -72,6 +72,11 @@ use crate::{prelude::*, types::Opaque}; use core::marker::PhantomData; use pin_init::PinInit; +/// A type-alias to refer to the [`Instant`] for a given `T` from [`HrTimer`]. +/// +/// Where `C` is the [`ClockSource`] of the [`HrTimer`]. +pub type HrTimerInstant = Instant<<>::TimerMode as HrTimerMode>::Clock>; + /// A timer backed by a C `struct hrtimer`. /// /// # Invariants -- cgit v1.2.3 From 3efb9ce91c5279d7ea73563d1fb136077f52dd2e Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 21 Aug 2025 15:32:43 -0400 Subject: rust: hrtimer: Add HrTimer::raw_forward() and forward() Within the hrtimer API there are quite a number of functions that can only be safely called from one of two contexts: * When we have exclusive access to the hrtimer and the timer is not active. * When we're within the hrtimer's callback context as it is being executed. This commit adds bindings for hrtimer_forward() for the first such context, along with HrTimer::raw_forward() for later use in implementing the hrtimer_forward() in the latter context. Signed-off-by: Lyude Paul Reviewed-by: Daniel Almeida Reviewed-by: Andreas Hindborg Link: https://lore.kernel.org/r/20250821193259.964504-4-lyude@redhat.com Signed-off-by: Andreas Hindborg --- rust/kernel/time/hrtimer.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/time/hrtimer.rs b/rust/kernel/time/hrtimer.rs index be1bad4aacaa..79fed14b2d98 100644 --- a/rust/kernel/time/hrtimer.rs +++ b/rust/kernel/time/hrtimer.rs @@ -168,6 +168,46 @@ impl HrTimer { // handled on the C side. unsafe { bindings::hrtimer_cancel(c_timer_ptr) != 0 } } + + /// Forward the timer expiry for a given timer pointer. + /// + /// # Safety + /// + /// - `self_ptr` must point to a valid `Self`. + /// - The caller must either have exclusive access to the data pointed at by `self_ptr`, or be + /// within the context of the timer callback. + #[inline] + unsafe fn raw_forward(self_ptr: *mut Self, now: HrTimerInstant, interval: Delta) -> u64 + where + T: HasHrTimer, + { + // SAFETY: + // * The C API requirements for this function are fulfilled by our safety contract. + // * `self_ptr` is guaranteed to point to a valid `Self` via our safety contract + unsafe { + bindings::hrtimer_forward(Self::raw_get(self_ptr), now.as_nanos(), interval.as_nanos()) + } + } + + /// Conditionally forward the timer. + /// + /// If the timer expires after `now`, this function does nothing and returns 0. If the timer + /// expired at or before `now`, this function forwards the timer by `interval` until the timer + /// expires after `now` and then returns the number of times the timer was forwarded by + /// `interval`. + /// + /// Returns the number of overruns that occurred as a result of the timer expiry change. + pub fn forward(self: Pin<&mut Self>, now: HrTimerInstant, interval: Delta) -> u64 + where + T: HasHrTimer, + { + // SAFETY: `raw_forward` does not move `Self` + let this = unsafe { self.get_unchecked_mut() }; + + // SAFETY: By existence of `Pin<&mut Self>`, the pointer passed to `raw_forward` points to a + // valid `Self` that we have exclusive access to. + unsafe { Self::raw_forward(this, now, interval) } + } } /// Implemented by pointer types that point to structs that contain a [`HrTimer`]. -- cgit v1.2.3 From 3f2a5ba784b808109cac0aac921213e43143a216 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 21 Aug 2025 15:32:44 -0400 Subject: rust: hrtimer: Add HrTimerCallbackContext and ::forward() With Linux's hrtimer API, there's a number of methods that can only be called in two situations: * When we have exclusive access to the hrtimer and it is not currently active * When we're within the context of an hrtimer callback context This commit handles the second situation and implements hrtimer_forward() support in the context of a timer callback. We do this by introducing a HrTimerCallbackContext type which is provided to users during the RawHrTimerCallback::run() callback, and then add a forward() function to the type. Signed-off-by: Lyude Paul Reviewed-by: Daniel Almeida Reviewed-by: Andreas Hindborg Link: https://lore.kernel.org/r/20250821193259.964504-5-lyude@redhat.com Signed-off-by: Andreas Hindborg --- rust/kernel/time/hrtimer.rs | 63 +++++++++++++++++++++++++++++++++++-- rust/kernel/time/hrtimer/arc.rs | 9 +++++- rust/kernel/time/hrtimer/pin.rs | 9 +++++- rust/kernel/time/hrtimer/pin_mut.rs | 12 +++++-- rust/kernel/time/hrtimer/tbox.rs | 9 +++++- 5 files changed, 93 insertions(+), 9 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/time/hrtimer.rs b/rust/kernel/time/hrtimer.rs index 79fed14b2d98..1e8839d27729 100644 --- a/rust/kernel/time/hrtimer.rs +++ b/rust/kernel/time/hrtimer.rs @@ -69,7 +69,7 @@ use super::{ClockSource, Delta, Instant}; use crate::{prelude::*, types::Opaque}; -use core::marker::PhantomData; +use core::{marker::PhantomData, ptr::NonNull}; use pin_init::PinInit; /// A type-alias to refer to the [`Instant`] for a given `T` from [`HrTimer`]. @@ -196,6 +196,10 @@ impl HrTimer { /// expires after `now` and then returns the number of times the timer was forwarded by /// `interval`. /// + /// This function is mainly useful for timer types which can provide exclusive access to the + /// timer when the timer is not running. For forwarding the timer from within the timer callback + /// context, see [`HrTimerCallbackContext::forward()`]. + /// /// Returns the number of overruns that occurred as a result of the timer expiry change. pub fn forward(self: Pin<&mut Self>, now: HrTimerInstant, interval: Delta) -> u64 where @@ -345,9 +349,13 @@ pub trait HrTimerCallback { type Pointer<'a>: RawHrTimerCallback; /// Called by the timer logic when the timer fires. - fn run(this: as RawHrTimerCallback>::CallbackTarget<'_>) -> HrTimerRestart + fn run( + this: as RawHrTimerCallback>::CallbackTarget<'_>, + ctx: HrTimerCallbackContext<'_, Self>, + ) -> HrTimerRestart where - Self: Sized; + Self: Sized, + Self: HasHrTimer; } /// A handle representing a potentially running timer. @@ -632,6 +640,55 @@ impl HrTimerMode for RelativePinnedHardMode { type Expires = Delta; } +/// Privileged smart-pointer for a [`HrTimer`] callback context. +/// +/// Many [`HrTimer`] methods can only be called in two situations: +/// +/// * When the caller has exclusive access to the `HrTimer` and the `HrTimer` is guaranteed not to +/// be running. +/// * From within the context of an `HrTimer`'s callback method. +/// +/// This type provides access to said methods from within a timer callback context. +/// +/// # Invariants +/// +/// * The existence of this type means the caller is currently within the callback for an +/// [`HrTimer`]. +/// * `self.0` always points to a live instance of [`HrTimer`]. +pub struct HrTimerCallbackContext<'a, T: HasHrTimer>(NonNull>, PhantomData<&'a ()>); + +impl<'a, T: HasHrTimer> HrTimerCallbackContext<'a, T> { + /// Create a new [`HrTimerCallbackContext`]. + /// + /// # Safety + /// + /// This function relies on the caller being within the context of a timer callback, so it must + /// not be used anywhere except for within implementations of [`RawHrTimerCallback::run`]. The + /// caller promises that `timer` points to a valid initialized instance of + /// [`bindings::hrtimer`]. + /// + /// The returned `Self` must not outlive the function context of [`RawHrTimerCallback::run`] + /// where this function is called. + pub(crate) unsafe fn from_raw(timer: *mut HrTimer) -> Self { + // SAFETY: The caller guarantees `timer` is a valid pointer to an initialized + // `bindings::hrtimer` + // INVARIANT: Our safety contract ensures that we're within the context of a timer callback + // and that `timer` points to a live instance of `HrTimer`. + Self(unsafe { NonNull::new_unchecked(timer) }, PhantomData) + } + + /// Conditionally forward the timer. + /// + /// This function is identical to [`HrTimer::forward()`] except that it may only be used from + /// within the context of a [`HrTimer`] callback. + pub fn forward(&mut self, now: HrTimerInstant, interval: Delta) -> u64 { + // SAFETY: + // - We are guaranteed to be within the context of a timer callback by our type invariants + // - By our type invariants, `self.0` always points to a valid `HrTimer` + unsafe { HrTimer::::raw_forward(self.0.as_ptr(), now, interval) } + } +} + /// Use to implement the [`HasHrTimer`] trait. /// /// See [`module`] documentation for an example. diff --git a/rust/kernel/time/hrtimer/arc.rs b/rust/kernel/time/hrtimer/arc.rs index ed490a7a8950..7be82bcb352a 100644 --- a/rust/kernel/time/hrtimer/arc.rs +++ b/rust/kernel/time/hrtimer/arc.rs @@ -3,6 +3,7 @@ use super::HasHrTimer; use super::HrTimer; use super::HrTimerCallback; +use super::HrTimerCallbackContext; use super::HrTimerHandle; use super::HrTimerMode; use super::HrTimerPointer; @@ -99,6 +100,12 @@ where // allocation from other `Arc` clones. let receiver = unsafe { ArcBorrow::from_raw(data_ptr) }; - T::run(receiver).into_c() + // SAFETY: + // - By C API contract `timer_ptr` is the pointer that we passed when queuing the timer, so + // it is a valid pointer to a `HrTimer` embedded in a `T`. + // - We are within `RawHrTimerCallback::run` + let context = unsafe { HrTimerCallbackContext::from_raw(timer_ptr) }; + + T::run(receiver, context).into_c() } } diff --git a/rust/kernel/time/hrtimer/pin.rs b/rust/kernel/time/hrtimer/pin.rs index aef16d9ee2f0..4d39ef781697 100644 --- a/rust/kernel/time/hrtimer/pin.rs +++ b/rust/kernel/time/hrtimer/pin.rs @@ -3,6 +3,7 @@ use super::HasHrTimer; use super::HrTimer; use super::HrTimerCallback; +use super::HrTimerCallbackContext; use super::HrTimerHandle; use super::HrTimerMode; use super::RawHrTimerCallback; @@ -103,6 +104,12 @@ where // here. let receiver_pin = unsafe { Pin::new_unchecked(receiver_ref) }; - T::run(receiver_pin).into_c() + // SAFETY: + // - By C API contract `timer_ptr` is the pointer that we passed when queuing the timer, so + // it is a valid pointer to a `HrTimer` embedded in a `T`. + // - We are within `RawHrTimerCallback::run` + let context = unsafe { HrTimerCallbackContext::from_raw(timer_ptr) }; + + T::run(receiver_pin, context).into_c() } } diff --git a/rust/kernel/time/hrtimer/pin_mut.rs b/rust/kernel/time/hrtimer/pin_mut.rs index 767d0a4e8a2c..9d9447d4d57e 100644 --- a/rust/kernel/time/hrtimer/pin_mut.rs +++ b/rust/kernel/time/hrtimer/pin_mut.rs @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 use super::{ - HasHrTimer, HrTimer, HrTimerCallback, HrTimerHandle, HrTimerMode, RawHrTimerCallback, - UnsafeHrTimerPointer, + HasHrTimer, HrTimer, HrTimerCallback, HrTimerCallbackContext, HrTimerHandle, HrTimerMode, + RawHrTimerCallback, UnsafeHrTimerPointer, }; use core::{marker::PhantomData, pin::Pin, ptr::NonNull}; @@ -107,6 +107,12 @@ where // here. let receiver_pin = unsafe { Pin::new_unchecked(receiver_ref) }; - T::run(receiver_pin).into_c() + // SAFETY: + // - By C API contract `timer_ptr` is the pointer that we passed when queuing the timer, so + // it is a valid pointer to a `HrTimer` embedded in a `T`. + // - We are within `RawHrTimerCallback::run` + let context = unsafe { HrTimerCallbackContext::from_raw(timer_ptr) }; + + T::run(receiver_pin, context).into_c() } } diff --git a/rust/kernel/time/hrtimer/tbox.rs b/rust/kernel/time/hrtimer/tbox.rs index ec08303315f2..aa1ee31a7195 100644 --- a/rust/kernel/time/hrtimer/tbox.rs +++ b/rust/kernel/time/hrtimer/tbox.rs @@ -3,6 +3,7 @@ use super::HasHrTimer; use super::HrTimer; use super::HrTimerCallback; +use super::HrTimerCallbackContext; use super::HrTimerHandle; use super::HrTimerMode; use super::HrTimerPointer; @@ -119,6 +120,12 @@ where // `data_ptr` exist. let data_mut_ref = unsafe { Pin::new_unchecked(&mut *data_ptr) }; - T::run(data_mut_ref).into_c() + // SAFETY: + // - By C API contract `timer_ptr` is the pointer that we passed when queuing the timer, so + // it is a valid pointer to a `HrTimer` embedded in a `T`. + // - We are within `RawHrTimerCallback::run` + let context = unsafe { HrTimerCallbackContext::from_raw(timer_ptr) }; + + T::run(data_mut_ref, context).into_c() } } -- cgit v1.2.3 From ac0a7bd27f6593dfe9ea6b65538bebbbd8322241 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 21 Aug 2025 15:32:45 -0400 Subject: rust: hrtimer: Add forward_now() to HrTimer and HrTimerCallbackContext Signed-off-by: Lyude Paul Reviewed-by: Daniel Almeida Reviewed-by: Andreas Hindborg Link: https://lore.kernel.org/r/20250821193259.964504-6-lyude@redhat.com Signed-off-by: Andreas Hindborg --- rust/kernel/time/hrtimer.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/time/hrtimer.rs b/rust/kernel/time/hrtimer.rs index 1e8839d27729..e0d78a885990 100644 --- a/rust/kernel/time/hrtimer.rs +++ b/rust/kernel/time/hrtimer.rs @@ -212,6 +212,17 @@ impl HrTimer { // valid `Self` that we have exclusive access to. unsafe { Self::raw_forward(this, now, interval) } } + + /// Conditionally forward the timer. + /// + /// This is a variant of [`forward()`](Self::forward) that uses an interval after the current + /// time of the base clock for the [`HrTimer`]. + pub fn forward_now(self: Pin<&mut Self>, interval: Delta) -> u64 + where + T: HasHrTimer, + { + self.forward(HrTimerInstant::::now(), interval) + } } /// Implemented by pointer types that point to structs that contain a [`HrTimer`]. @@ -687,6 +698,14 @@ impl<'a, T: HasHrTimer> HrTimerCallbackContext<'a, T> { // - By our type invariants, `self.0` always points to a valid `HrTimer` unsafe { HrTimer::::raw_forward(self.0.as_ptr(), now, interval) } } + + /// Conditionally forward the timer. + /// + /// This is a variant of [`HrTimerCallbackContext::forward()`] that uses an interval after the + /// current time of the base clock for the [`HrTimer`]. + pub fn forward_now(&mut self, duration: Delta) -> u64 { + self.forward(HrTimerInstant::::now(), duration) + } } /// Use to implement the [`HasHrTimer`] trait. -- cgit v1.2.3 From 583802cc99bdac95d173aaf1fc58e99993aa1d1d Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 21 Aug 2025 15:32:46 -0400 Subject: rust: time: Add Instant::from_ktime() For implementing Rust bindings which can return a point in time. Signed-off-by: Lyude Paul Reviewed-by: Andreas Hindborg Reviewed-by: Alice Ryhl Reviewed-by: FUJITA Tomonori Link: https://lore.kernel.org/r/20250821193259.964504-7-lyude@redhat.com Signed-off-by: Andreas Hindborg --- rust/kernel/time.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index 64c8dcf548d6..874a1023dcdf 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -200,6 +200,29 @@ impl Instant { pub(crate) fn as_nanos(&self) -> i64 { self.inner } + + /// Create an [`Instant`] from a `ktime_t` without checking if it is non-negative. + /// + /// # Panics + /// + /// On debug builds, this function will panic if `ktime` is not in the range from 0 to + /// `KTIME_MAX`. + /// + /// # Safety + /// + /// The caller promises that `ktime` is in the range from 0 to `KTIME_MAX`. + #[expect(unused)] + #[inline] + pub(crate) unsafe fn from_ktime(ktime: bindings::ktime_t) -> Self { + debug_assert!(ktime >= 0); + + // INVARIANT: Our safety contract ensures that `ktime` is in the range from 0 to + // `KTIME_MAX`. + Self { + inner: ktime, + _c: PhantomData, + } + } } impl core::ops::Sub for Instant { -- cgit v1.2.3 From 4b0147494275fdbea98305f4ddad7e2def7b556f Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 21 Aug 2025 15:32:47 -0400 Subject: rust: hrtimer: Add HrTimer::expires() Add a simple callback for retrieving the current expiry time for an HrTimer. In rvkms, we use the HrTimer expiry value in order to calculate the approximate vblank timestamp during each emulated vblank interrupt. Signed-off-by: Lyude Paul Reviewed-by: Andreas Hindborg Link: https://lore.kernel.org/r/20250821193259.964504-8-lyude@redhat.com Signed-off-by: Andreas Hindborg --- rust/kernel/time.rs | 1 - rust/kernel/time/hrtimer.rs | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index 874a1023dcdf..7320d8715bcc 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -211,7 +211,6 @@ impl Instant { /// # Safety /// /// The caller promises that `ktime` is in the range from 0 to `KTIME_MAX`. - #[expect(unused)] #[inline] pub(crate) unsafe fn from_ktime(ktime: bindings::ktime_t) -> Self { debug_assert!(ktime >= 0); diff --git a/rust/kernel/time/hrtimer.rs b/rust/kernel/time/hrtimer.rs index e0d78a885990..856d2d929a00 100644 --- a/rust/kernel/time/hrtimer.rs +++ b/rust/kernel/time/hrtimer.rs @@ -223,6 +223,29 @@ impl HrTimer { { self.forward(HrTimerInstant::::now(), interval) } + + /// Return the time expiry for this [`HrTimer`]. + /// + /// This value should only be used as a snapshot, as the actual expiry time could change after + /// this function is called. + pub fn expires(&self) -> HrTimerInstant + where + T: HasHrTimer, + { + // SAFETY: `self` is an immutable reference and thus always points to a valid `HrTimer`. + let c_timer_ptr = unsafe { HrTimer::raw_get(self) }; + + // SAFETY: + // - Timers cannot have negative ktime_t values as their expiration time. + // - There's no actual locking here, a racy read is fine and expected + unsafe { + Instant::from_ktime( + // This `read_volatile` is intended to correspond to a READ_ONCE call. + // FIXME(read_once): Replace with `read_once` when available on the Rust side. + core::ptr::read_volatile(&raw const ((*c_timer_ptr).node.expires)), + ) + } + } } /// Implemented by pointer types that point to structs that contain a [`HrTimer`]. -- cgit v1.2.3 From 22b65a40574e597e1919ca22cc3535c2b541f764 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Wed, 20 Aug 2025 16:26:43 -0400 Subject: rust: time: Implement Add/Sub for Instant In order to copy the behavior rust currently follows for basic arithmetic operations and panic if the result of an addition or subtraction results in a value that would violate the invariants of Instant, but only if the kernel has overflow checking for rust enabled. Signed-off-by: Lyude Paul Reviewed-by: Alice Ryhl Reviewed-by: FUJITA Tomonori Reviewed-by: Andreas Hindborg Reviewed-by: Daniel Almeida Link: https://lore.kernel.org/r/20250820203704.731588-2-lyude@redhat.com Signed-off-by: Andreas Hindborg --- rust/kernel/time.rs | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index 7320d8715bcc..642c42a520a8 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -25,6 +25,7 @@ //! C header: [`include/linux/ktime.h`](srctree/include/linux/ktime.h). use core::marker::PhantomData; +use core::ops; pub mod delay; pub mod hrtimer; @@ -224,7 +225,7 @@ impl Instant { } } -impl core::ops::Sub for Instant { +impl ops::Sub for Instant { type Output = Delta; // By the type invariant, it never overflows. @@ -236,6 +237,46 @@ impl core::ops::Sub for Instant { } } +impl ops::Add for Instant { + type Output = Self; + + #[inline] + fn add(self, rhs: Delta) -> Self::Output { + // INVARIANT: With arithmetic over/underflow checks enabled, this will panic if we overflow + // (e.g. go above `KTIME_MAX`) + let res = self.inner + rhs.nanos; + + // INVARIANT: With overflow checks enabled, we verify here that the value is >= 0 + #[cfg(CONFIG_RUST_OVERFLOW_CHECKS)] + assert!(res >= 0); + + Self { + inner: res, + _c: PhantomData, + } + } +} + +impl ops::Sub for Instant { + type Output = Self; + + #[inline] + fn sub(self, rhs: Delta) -> Self::Output { + // INVARIANT: With arithmetic over/underflow checks enabled, this will panic if we overflow + // (e.g. go above `KTIME_MAX`) + let res = self.inner - rhs.nanos; + + // INVARIANT: With overflow checks enabled, we verify here that the value is >= 0 + #[cfg(CONFIG_RUST_OVERFLOW_CHECKS)] + assert!(res >= 0); + + Self { + inner: res, + _c: PhantomData, + } + } +} + /// A span of time. /// /// This struct represents a span of time, with its value stored as nanoseconds. -- cgit v1.2.3 From 4521438fb076f8a6a52f45b0e508f6ef10ac0c49 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Wed, 20 Aug 2025 16:26:44 -0400 Subject: rust: time: Implement basic arithmetic operations for Delta While rvkms is only going to be using a few of these, since Deltas are basically the same as i64 it's easy enough to just implement all of the basic arithmetic operations for Delta types. Keep in mind there's one quirk here - the kernel has no support for i64 % i64 on 32 bit platforms, the closest we have is i64 % i32 through div_s64_rem(). So, instead of implementing ops::Rem or ops::RemAssign we simply provide Delta::rem_nanos(). Signed-off-by: Lyude Paul Reviewed-by: Daniel Almeida Reviewed-by: FUJITA Tomonori Reviewed-by: Andreas Hindborg Link: https://lore.kernel.org/r/20250820203704.731588-3-lyude@redhat.com Signed-off-by: Andreas Hindborg --- rust/kernel/time.rs | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index 642c42a520a8..6ea98dfcd027 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -287,6 +287,78 @@ pub struct Delta { nanos: i64, } +impl ops::Add for Delta { + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self { + Self { + nanos: self.nanos + rhs.nanos, + } + } +} + +impl ops::AddAssign for Delta { + #[inline] + fn add_assign(&mut self, rhs: Self) { + self.nanos += rhs.nanos; + } +} + +impl ops::Sub for Delta { + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + Self { + nanos: self.nanos - rhs.nanos, + } + } +} + +impl ops::SubAssign for Delta { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + self.nanos -= rhs.nanos; + } +} + +impl ops::Mul for Delta { + type Output = Self; + + #[inline] + fn mul(self, rhs: i64) -> Self::Output { + Self { + nanos: self.nanos * rhs, + } + } +} + +impl ops::MulAssign for Delta { + #[inline] + fn mul_assign(&mut self, rhs: i64) { + self.nanos *= rhs; + } +} + +impl ops::Div for Delta { + type Output = i64; + + #[inline] + fn div(self, rhs: Self) -> Self::Output { + #[cfg(CONFIG_64BIT)] + { + self.nanos / rhs.nanos + } + + #[cfg(not(CONFIG_64BIT))] + { + // SAFETY: This function is always safe to call regardless of the input values + unsafe { bindings::div64_s64(self.nanos, rhs.nanos) } + } + } +} + impl Delta { /// A span of time equal to zero. pub const ZERO: Self = Self { nanos: 0 }; @@ -375,4 +447,30 @@ impl Delta { bindings::ktime_to_ms(self.as_nanos()) } } + + /// Return `self % dividend` where `dividend` is in nanoseconds. + /// + /// The kernel doesn't have any emulation for `s64 % s64` on 32 bit platforms, so this is + /// limited to 32 bit dividends. + #[inline] + pub fn rem_nanos(self, dividend: i32) -> Self { + #[cfg(CONFIG_64BIT)] + { + Self { + nanos: self.as_nanos() % i64::from(dividend), + } + } + + #[cfg(not(CONFIG_64BIT))] + { + let mut rem = 0; + + // SAFETY: `rem` is in the stack, so we can always provide a valid pointer to it. + unsafe { bindings::div_s64_rem(self.as_nanos(), dividend, &mut rem) }; + + Self { + nanos: i64::from(rem), + } + } + } } -- cgit v1.2.3