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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.