Skip to content

Commit 3f2a5ba

Browse files
LyudeAndreas Hindborg
authored andcommitted
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 <lyude@redhat.com> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com> Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org> Link: https://lore.kernel.org/r/20250821193259.964504-5-lyude@redhat.com Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
1 parent 3efb9ce commit 3f2a5ba

5 files changed

Lines changed: 93 additions & 9 deletions

File tree

rust/kernel/time/hrtimer.rs

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
7070
use super::{ClockSource, Delta, Instant};
7171
use crate::{prelude::*, types::Opaque};
72-
use core::marker::PhantomData;
72+
use core::{marker::PhantomData, ptr::NonNull};
7373
use pin_init::PinInit;
7474

7575
/// A type-alias to refer to the [`Instant<C>`] for a given `T` from [`HrTimer<T>`].
@@ -196,6 +196,10 @@ impl<T> HrTimer<T> {
196196
/// expires after `now` and then returns the number of times the timer was forwarded by
197197
/// `interval`.
198198
///
199+
/// This function is mainly useful for timer types which can provide exclusive access to the
200+
/// timer when the timer is not running. For forwarding the timer from within the timer callback
201+
/// context, see [`HrTimerCallbackContext::forward()`].
202+
///
199203
/// Returns the number of overruns that occurred as a result of the timer expiry change.
200204
pub fn forward(self: Pin<&mut Self>, now: HrTimerInstant<T>, interval: Delta) -> u64
201205
where
@@ -345,9 +349,13 @@ pub trait HrTimerCallback {
345349
type Pointer<'a>: RawHrTimerCallback;
346350

347351
/// Called by the timer logic when the timer fires.
348-
fn run(this: <Self::Pointer<'_> as RawHrTimerCallback>::CallbackTarget<'_>) -> HrTimerRestart
352+
fn run(
353+
this: <Self::Pointer<'_> as RawHrTimerCallback>::CallbackTarget<'_>,
354+
ctx: HrTimerCallbackContext<'_, Self>,
355+
) -> HrTimerRestart
349356
where
350-
Self: Sized;
357+
Self: Sized,
358+
Self: HasHrTimer<Self>;
351359
}
352360

353361
/// A handle representing a potentially running timer.
@@ -632,6 +640,55 @@ impl<C: ClockSource> HrTimerMode for RelativePinnedHardMode<C> {
632640
type Expires = Delta;
633641
}
634642

643+
/// Privileged smart-pointer for a [`HrTimer`] callback context.
644+
///
645+
/// Many [`HrTimer`] methods can only be called in two situations:
646+
///
647+
/// * When the caller has exclusive access to the `HrTimer` and the `HrTimer` is guaranteed not to
648+
/// be running.
649+
/// * From within the context of an `HrTimer`'s callback method.
650+
///
651+
/// This type provides access to said methods from within a timer callback context.
652+
///
653+
/// # Invariants
654+
///
655+
/// * The existence of this type means the caller is currently within the callback for an
656+
/// [`HrTimer`].
657+
/// * `self.0` always points to a live instance of [`HrTimer<T>`].
658+
pub struct HrTimerCallbackContext<'a, T: HasHrTimer<T>>(NonNull<HrTimer<T>>, PhantomData<&'a ()>);
659+
660+
impl<'a, T: HasHrTimer<T>> HrTimerCallbackContext<'a, T> {
661+
/// Create a new [`HrTimerCallbackContext`].
662+
///
663+
/// # Safety
664+
///
665+
/// This function relies on the caller being within the context of a timer callback, so it must
666+
/// not be used anywhere except for within implementations of [`RawHrTimerCallback::run`]. The
667+
/// caller promises that `timer` points to a valid initialized instance of
668+
/// [`bindings::hrtimer`].
669+
///
670+
/// The returned `Self` must not outlive the function context of [`RawHrTimerCallback::run`]
671+
/// where this function is called.
672+
pub(crate) unsafe fn from_raw(timer: *mut HrTimer<T>) -> Self {
673+
// SAFETY: The caller guarantees `timer` is a valid pointer to an initialized
674+
// `bindings::hrtimer`
675+
// INVARIANT: Our safety contract ensures that we're within the context of a timer callback
676+
// and that `timer` points to a live instance of `HrTimer<T>`.
677+
Self(unsafe { NonNull::new_unchecked(timer) }, PhantomData)
678+
}
679+
680+
/// Conditionally forward the timer.
681+
///
682+
/// This function is identical to [`HrTimer::forward()`] except that it may only be used from
683+
/// within the context of a [`HrTimer`] callback.
684+
pub fn forward(&mut self, now: HrTimerInstant<T>, interval: Delta) -> u64 {
685+
// SAFETY:
686+
// - We are guaranteed to be within the context of a timer callback by our type invariants
687+
// - By our type invariants, `self.0` always points to a valid `HrTimer<T>`
688+
unsafe { HrTimer::<T>::raw_forward(self.0.as_ptr(), now, interval) }
689+
}
690+
}
691+
635692
/// Use to implement the [`HasHrTimer<T>`] trait.
636693
///
637694
/// See [`module`] documentation for an example.

rust/kernel/time/hrtimer/arc.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use super::HasHrTimer;
44
use super::HrTimer;
55
use super::HrTimerCallback;
6+
use super::HrTimerCallbackContext;
67
use super::HrTimerHandle;
78
use super::HrTimerMode;
89
use super::HrTimerPointer;
@@ -99,6 +100,12 @@ where
99100
// allocation from other `Arc` clones.
100101
let receiver = unsafe { ArcBorrow::from_raw(data_ptr) };
101102

102-
T::run(receiver).into_c()
103+
// SAFETY:
104+
// - By C API contract `timer_ptr` is the pointer that we passed when queuing the timer, so
105+
// it is a valid pointer to a `HrTimer<T>` embedded in a `T`.
106+
// - We are within `RawHrTimerCallback::run`
107+
let context = unsafe { HrTimerCallbackContext::from_raw(timer_ptr) };
108+
109+
T::run(receiver, context).into_c()
103110
}
104111
}

rust/kernel/time/hrtimer/pin.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use super::HasHrTimer;
44
use super::HrTimer;
55
use super::HrTimerCallback;
6+
use super::HrTimerCallbackContext;
67
use super::HrTimerHandle;
78
use super::HrTimerMode;
89
use super::RawHrTimerCallback;
@@ -103,6 +104,12 @@ where
103104
// here.
104105
let receiver_pin = unsafe { Pin::new_unchecked(receiver_ref) };
105106

106-
T::run(receiver_pin).into_c()
107+
// SAFETY:
108+
// - By C API contract `timer_ptr` is the pointer that we passed when queuing the timer, so
109+
// it is a valid pointer to a `HrTimer<T>` embedded in a `T`.
110+
// - We are within `RawHrTimerCallback::run`
111+
let context = unsafe { HrTimerCallbackContext::from_raw(timer_ptr) };
112+
113+
T::run(receiver_pin, context).into_c()
107114
}
108115
}

rust/kernel/time/hrtimer/pin_mut.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// SPDX-License-Identifier: GPL-2.0
22

33
use super::{
4-
HasHrTimer, HrTimer, HrTimerCallback, HrTimerHandle, HrTimerMode, RawHrTimerCallback,
5-
UnsafeHrTimerPointer,
4+
HasHrTimer, HrTimer, HrTimerCallback, HrTimerCallbackContext, HrTimerHandle, HrTimerMode,
5+
RawHrTimerCallback, UnsafeHrTimerPointer,
66
};
77
use core::{marker::PhantomData, pin::Pin, ptr::NonNull};
88

@@ -107,6 +107,12 @@ where
107107
// here.
108108
let receiver_pin = unsafe { Pin::new_unchecked(receiver_ref) };
109109

110-
T::run(receiver_pin).into_c()
110+
// SAFETY:
111+
// - By C API contract `timer_ptr` is the pointer that we passed when queuing the timer, so
112+
// it is a valid pointer to a `HrTimer<T>` embedded in a `T`.
113+
// - We are within `RawHrTimerCallback::run`
114+
let context = unsafe { HrTimerCallbackContext::from_raw(timer_ptr) };
115+
116+
T::run(receiver_pin, context).into_c()
111117
}
112118
}

rust/kernel/time/hrtimer/tbox.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use super::HasHrTimer;
44
use super::HrTimer;
55
use super::HrTimerCallback;
6+
use super::HrTimerCallbackContext;
67
use super::HrTimerHandle;
78
use super::HrTimerMode;
89
use super::HrTimerPointer;
@@ -119,6 +120,12 @@ where
119120
// `data_ptr` exist.
120121
let data_mut_ref = unsafe { Pin::new_unchecked(&mut *data_ptr) };
121122

122-
T::run(data_mut_ref).into_c()
123+
// SAFETY:
124+
// - By C API contract `timer_ptr` is the pointer that we passed when queuing the timer, so
125+
// it is a valid pointer to a `HrTimer<T>` embedded in a `T`.
126+
// - We are within `RawHrTimerCallback::run`
127+
let context = unsafe { HrTimerCallbackContext::from_raw(timer_ptr) };
128+
129+
T::run(data_mut_ref, context).into_c()
123130
}
124131
}

0 commit comments

Comments
 (0)