From: FUJITA Tomonori <tomo@flapping.org>
To: a.hindborg@kernel.org, ojeda@kernel.org
Cc: acourbot@nvidia.com, aliceryhl@google.com,
anna-maria@linutronix.de, bjorn3_gh@protonmail.com,
boqun@kernel.org, dakr@kernel.org, daniel.almeida@collabora.com,
frederic@kernel.org, gary@garyguo.net, jstultz@google.com,
lossin@kernel.org, lyude@redhat.com, sboyd@kernel.org,
tamird@kernel.org, tglx@kernel.org, tmgross@umich.edu,
work@onurozkan.dev, rust-for-linux@vger.kernel.org,
FUJITA Tomonori <fujita.tomonori@gmail.com>
Subject: [PATCH v1 2/4] rust: hrtimer: Introduce HrTimerPin to make arming exclusive
Date: Thu, 13 Aug 2026 22:48:32 +0900 [thread overview]
Message-ID: <20260813134834.1562995-3-tomo@flapping.org> (raw)
In-Reply-To: <20260813134834.1562995-1-tomo@flapping.org>
From: FUJITA Tomonori <fujita.tomonori@gmail.com>
Pin<&T> has the same hole that Arc<T> had: it is Copy and
ScopedHrTimerPointer::start_scoped() is safe, so a copy captured by the
closure can arm a timer while it is already armed and its callback may
be running.
Split the right to arm out of Pin<&T> into HrTimerPin<'a, T>, which is
created from a Pin<&'a mut T> and consumed by start_scoped(). The borrow
checker supplies the exclusivity here, and the closure keeps reading the
object through the shared pinned reference returned by
HrTimerPin::as_ref().
All four pointer types now separate sharing an object from arming its
timer, so the restart operation no longer exists in the safe API. Drop
it from the documentation.
Fixes: 3f2a5ba784b8 ("rust: hrtimer: Add HrTimerCallbackContext and ::forward()")
Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
rust/kernel/time/hrtimer.rs | 50 +++++++--------
rust/kernel/time/hrtimer/pin.rs | 105 +++++++++++++++++++++++---------
2 files changed, 98 insertions(+), 57 deletions(-)
diff --git a/rust/kernel/time/hrtimer.rs b/rust/kernel/time/hrtimer.rs
index a7587db1d552..d94275f2e93f 100644
--- a/rust/kernel/time/hrtimer.rs
+++ b/rust/kernel/time/hrtimer.rs
@@ -9,15 +9,15 @@
//!
//! States:
//!
-//! - Stopped: initialized but not started, or cancelled, or not restarted.
-//! - Started: initialized and started or restarted.
+//! - Stopped: initialized but not started, cancelled, or the callback returned
+//! `NoRestart`.
+//! - Started: initialized and started, or the callback returned `Restart`.
//! - Running: executing the callback.
//!
//! Operations:
//!
//! * Start
//! * Cancel
-//! * Restart
//!
//! Events:
//!
@@ -42,11 +42,7 @@
//! --------->| Stopped | | Started +---------->| Running |
//! | | Cancel | | | |
//! | |<----------------+ | | |
-//! +-----------------+ +---------------+--+ +-----------------+
-//! ^ |
-//! | |
-//! +---------+
-//! Restart
+//! +-----------------+ +------------------+ +-----------------+
//! ```
//!
//!
@@ -60,16 +56,13 @@
//! by the `cancel` operation. A timer that is cancelled enters the **stopped**
//! state.
//!
-//! A `cancel` or `restart` operation on a timer in the **running** state takes
-//! effect after the handler has returned and the timer has transitioned
-//! out of the **running** state.
+//! A `cancel` operation on a timer in the **running** state takes effect after
+//! the handler has returned and the timer has transitioned out of the
+//! **running** state.
//!
-//! A `restart` operation on a timer in the **stopped** state is equivalent to a
-//! `start` operation.
-//!
-//! When a type implements both `HrTimerPointer` and `Clone`, it is possible to
-//! issue the `start` operation while the timer is in the **started** state. In
-//! this case the `start` operation is equivalent to the `restart` operation.
+//! The `start` operation consumes the pointer it is called on, so a timer in the
+//! **started** or **running** state cannot be started again. It has to be
+//! **cancelled** first.
//!
//! # Examples
//!
@@ -253,8 +246,8 @@
//! # },
//! # time::{
//! # hrtimer::{
-//! # ScopedHrTimerPointer, HrTimer, HrTimerCallback, HrTimerPointer, HrTimerRestart,
-//! # HasHrTimer, RelativeMode, HrTimerCallbackContext
+//! # ScopedHrTimerPointer, HrTimer, HrTimerCallback, HrTimerPin, HrTimerPointer,
+//! # HrTimerRestart, HasHrTimer, RelativeMode, HrTimerCallbackContext
//! # },
//! # Delta, Monotonic,
//! # },
@@ -282,7 +275,7 @@
//! }
//!
//! impl HrTimerCallback for IntrusiveHrTimer {
-//! type Pointer<'a> = Pin<&'a Self>;
+//! type Pointer<'a> = HrTimerPin<'a, Self>;
//!
//! fn run(this: Pin<&Self>, _ctx: HrTimerCallbackContext<'_, Self>) -> HrTimerRestart {
//! pr_info!("Timer called\n");
@@ -301,9 +294,12 @@
//! }
//!
//! stack_pin_init!( let has_timer = IntrusiveHrTimer::new() );
-//! has_timer.as_ref().start_scoped(Delta::from_micros(200), || {
-//! while has_timer.flag.load(ordering::Relaxed) != 1 {
-//! has_timer.cond.wait_for_completion();
+//! let timer_pin = HrTimerPin::new(has_timer);
+//! let shared = timer_pin.as_ref();
+//!
+//! timer_pin.start_scoped(Delta::from_micros(200), || {
+//! while shared.flag.load(ordering::Relaxed) != 1 {
+//! shared.cond.wait_for_completion();
//! }
//! });
//!
@@ -618,7 +614,8 @@ pub trait HrTimerPointer: Sync + Sized {
/// [`HrTimerHandle`] returned by `start` would be unsound. This is the case for
/// stack allocated timers.
///
-/// Typical implementers are pinned references such as [`Pin<&T>`].
+/// Typical implementers are [`HrTimerPin`] and pinned references such as
+/// [`Pin<&mut T>`].
///
/// # Safety
///
@@ -640,8 +637,7 @@ pub unsafe trait UnsafeHrTimerPointer: Sync + Sized {
/// until the timer is stopped and the callback has completed.
type TimerHandle: HrTimerHandle;
- /// Start the timer after `expires` time units. If the timer was already
- /// running, it is restarted at the new expiry time.
+ /// Start the timer after `expires` time units.
///
/// # Safety
///
@@ -1111,7 +1107,7 @@ unsafe fn timer_container_of(
mod arc;
pub use arc::{ArcHrTimerHandle, HrTimerArc};
mod pin;
-pub use pin::PinHrTimerHandle;
+pub use pin::{HrTimerPin, PinHrTimerHandle};
mod pin_mut;
pub use pin_mut::PinMutHrTimerHandle;
// `box` is a reserved keyword, so prefix with `t` for timer
diff --git a/rust/kernel/time/hrtimer/pin.rs b/rust/kernel/time/hrtimer/pin.rs
index 4d39ef781697..f44ac07cb722 100644
--- a/rust/kernel/time/hrtimer/pin.rs
+++ b/rust/kernel/time/hrtimer/pin.rs
@@ -10,50 +10,58 @@
use super::UnsafeHrTimerPointer;
use core::pin::Pin;
-/// A handle for a `Pin<&HasHrTimer>`. When the handle exists, the timer might be
-/// running.
-pub struct PinHrTimerHandle<'a, T>
+/// A wrapper around a pinned shared reference that's guaranteed unique.
+///
+/// The `HrTimerPin` type can be thought of as a special pinned reference to an object that
+/// owns the permission to arm the [`HrTimer`] stored in the object. By ensuring that each
+/// object has only one `HrTimerPin`, the owner of it is assured exclusive access to the arming
+/// operation. Starting a timer consumes the `HrTimerPin`, and the returned
+/// [`PinHrTimerHandle`] keeps the object borrowed, so the timer cannot be armed again until the
+/// handle is dropped.
+///
+/// While this `HrTimerPin` is unique, shared pinned references to the object can still be
+/// obtained with [`HrTimerPin::as_ref`].
+///
+/// # Invariants
+///
+/// * Each object has at most one `HrTimerPin`.
+pub struct HrTimerPin<'a, T>
where
T: HasHrTimer<T>,
{
- pub(crate) inner: Pin<&'a T>,
+ pin: Pin<&'a T>,
}
-// SAFETY: We cancel the timer when the handle is dropped. The implementation of
-// the `cancel` method will block if the timer handler is running.
-unsafe impl<'a, T> HrTimerHandle for PinHrTimerHandle<'a, T>
+impl<'a, T> HrTimerPin<'a, T>
where
T: HasHrTimer<T>,
{
- fn cancel(&mut self) -> bool {
- let self_ptr: *const T = self.inner.get_ref();
-
- // SAFETY: As we got `self_ptr` from a reference above, it must point to
- // a valid `T`.
- let timer_ptr = unsafe { <T as HasHrTimer<T>>::raw_get_timer(self_ptr) };
-
- // SAFETY: As `timer_ptr` is derived from a reference, it must point to
- // a valid and initialized `HrTimer`.
- unsafe { HrTimer::<T>::raw_cancel(timer_ptr) }
+ /// Create a `HrTimerPin` from an exclusive pinned reference to a `T`.
+ #[inline]
+ pub fn new(inner: Pin<&'a mut T>) -> Self {
+ // INVARIANT: We have an exclusive reference, so there is no `HrTimerPin` for this
+ // object.
+ Self {
+ pin: inner.into_ref(),
+ }
}
-}
-impl<'a, T> Drop for PinHrTimerHandle<'a, T>
-where
- T: HasHrTimer<T>,
-{
- fn drop(&mut self) {
- self.cancel();
+ /// Get a shared pinned reference to the object.
+ ///
+ /// The returned reference can be used to access the object, but not to arm its timer.
+ #[inline]
+ pub fn as_ref(&self) -> Pin<&'a T> {
+ self.pin
}
}
// SAFETY: We capture the lifetime of `Self` when we create a `PinHrTimerHandle`,
// so `Self` will outlive the handle.
-unsafe impl<'a, T> UnsafeHrTimerPointer for Pin<&'a T>
+unsafe impl<'a, T> UnsafeHrTimerPointer for HrTimerPin<'a, T>
where
T: Send + Sync,
T: HasHrTimer<T>,
- T: HrTimerCallback<Pointer<'a> = Self>,
+ T: HrTimerCallback<Pointer<'a> = HrTimerPin<'a, T>>,
{
type TimerMode = <T as HasHrTimer<T>>::TimerMode;
type TimerHandle = PinHrTimerHandle<'a, T>;
@@ -63,7 +71,7 @@ unsafe fn start(
expires: <<T as HasHrTimer<T>>::TimerMode as HrTimerMode>::Expires,
) -> Self::TimerHandle {
// Cast to pointer
- let self_ptr: *const T = self.get_ref();
+ let self_ptr: *const T = self.pin.get_ref();
// SAFETY:
// - As we derive `self_ptr` from a reference above, it must point to a
@@ -71,16 +79,53 @@ unsafe fn start(
// - We keep `self` alive by wrapping it in a handle below.
unsafe { T::start(self_ptr, expires) };
- PinHrTimerHandle { inner: self }
+ PinHrTimerHandle { inner: self.pin }
+ }
+}
+
+/// A handle for a `Pin<&HasHrTimer>`. When the handle exists, the timer might be
+/// running.
+pub struct PinHrTimerHandle<'a, T>
+where
+ T: HasHrTimer<T>,
+{
+ pub(crate) inner: Pin<&'a T>,
+}
+
+// SAFETY: We cancel the timer when the handle is dropped. The implementation of
+// the `cancel` method will block if the timer handler is running.
+unsafe impl<'a, T> HrTimerHandle for PinHrTimerHandle<'a, T>
+where
+ T: HasHrTimer<T>,
+{
+ fn cancel(&mut self) -> bool {
+ let self_ptr: *const T = self.inner.get_ref();
+
+ // SAFETY: As we got `self_ptr` from a reference above, it must point to
+ // a valid `T`.
+ let timer_ptr = unsafe { <T as HasHrTimer<T>>::raw_get_timer(self_ptr) };
+
+ // SAFETY: As `timer_ptr` is derived from a reference, it must point to
+ // a valid and initialized `HrTimer`.
+ unsafe { HrTimer::<T>::raw_cancel(timer_ptr) }
+ }
+}
+
+impl<'a, T> Drop for PinHrTimerHandle<'a, T>
+where
+ T: HasHrTimer<T>,
+{
+ fn drop(&mut self) {
+ self.cancel();
}
}
-impl<'a, T> RawHrTimerCallback for Pin<&'a T>
+impl<'a, T> RawHrTimerCallback for HrTimerPin<'a, T>
where
T: HasHrTimer<T>,
T: HrTimerCallback<Pointer<'a> = Self>,
{
- type CallbackTarget<'b> = Self;
+ type CallbackTarget<'b> = Pin<&'a T>;
unsafe extern "C" fn run(ptr: *mut bindings::hrtimer) -> bindings::hrtimer_restart {
// `HrTimer` is `repr(C)`
--
2.43.0
next prev parent reply other threads:[~2026-08-13 13:49 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-13 13:48 [PATCH 0/4] Fix forward()/expires() racing with concurrent arming FUJITA Tomonori
2026-08-13 13:48 ` [PATCH v1 1/4] rust: hrtimer: Introduce HrTimerArc to make arming exclusive FUJITA Tomonori
2026-08-13 13:48 ` FUJITA Tomonori [this message]
2026-08-13 13:48 ` [PATCH v1 3/4] rust: hrtimer: Restrict expires() to safe contexts FUJITA Tomonori
2026-08-13 13:48 ` [PATCH v1 4/4] rust: hrtimer: Make HrTimer repr(transparent) FUJITA Tomonori
2026-08-13 14:16 ` [PATCH 0/4] Fix forward()/expires() racing with concurrent arming Gary Guo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260813134834.1562995-3-tomo@flapping.org \
--to=tomo@flapping.org \
--cc=a.hindborg@kernel.org \
--cc=acourbot@nvidia.com \
--cc=aliceryhl@google.com \
--cc=anna-maria@linutronix.de \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=frederic@kernel.org \
--cc=fujita.tomonori@gmail.com \
--cc=gary@garyguo.net \
--cc=jstultz@google.com \
--cc=lossin@kernel.org \
--cc=lyude@redhat.com \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=sboyd@kernel.org \
--cc=tamird@kernel.org \
--cc=tglx@kernel.org \
--cc=tmgross@umich.edu \
--cc=work@onurozkan.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox