rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Andreas Hindborg <a.hindborg@kernel.org>
To: "Benno Lossin" <benno.lossin@proton.me>
Cc: "Miguel Ojeda" <ojeda@kernel.org>,
	"Anna-Maria Behnsen" <anna-maria@linutronix.de>,
	"Frederic Weisbecker" <frederic@kernel.org>,
	"Thomas Gleixner" <tglx@linutronix.de>,
	"Danilo Krummrich" <dakr@kernel.org>,
	"Alex Gaynor" <alex.gaynor@gmail.com>,
	"Boqun Feng" <boqun.feng@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Lyude Paul" <lyude@redhat.com>,
	"Guangbo Cui" <2407018371@qq.com>,
	"Dirk Behme" <dirk.behme@gmail.com>,
	"Daniel Almeida" <daniel.almeida@collabora.com>,
	"Tamir Duberstein" <tamird@gmail.com>,
	rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v8 02/14] rust: hrtimer: introduce hrtimer support
Date: Fri, 21 Feb 2025 10:03:03 +0100	[thread overview]
Message-ID: <87a5afhdq0.fsf@kernel.org> (raw)
In-Reply-To: <df748ac2-3551-460f-a16f-85d805671a3f@proton.me> (Benno Lossin's message of "Thu, 20 Feb 2025 23:46:10 +0000")

"Benno Lossin" <benno.lossin@proton.me> writes:

> On 18.02.25 14:27, Andreas Hindborg wrote:
>> This patch adds support for intrusive use of the hrtimer system. For now,
>> only one timer can be embedded in a Rust struct.
>>
>> The hrtimer Rust API is based on the intrusive style pattern introduced by
>> the Rust workqueue API.
>>
>> Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
>> ---
>>  rust/kernel/time.rs         |   2 +
>>  rust/kernel/time/hrtimer.rs | 312 ++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 314 insertions(+)
>>
>> diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs
>> index 87e47f2f5618d..2cf365cfb412e 100644
>> --- a/rust/kernel/time.rs
>> +++ b/rust/kernel/time.rs
>> @@ -10,6 +10,8 @@
>>
>>  use core::convert::Into;
>>
>> +pub mod hrtimer;
>> +
>>  /// The number of nanoseconds per millisecond.
>>  pub const NSEC_PER_MSEC: i64 = bindings::NSEC_PER_MSEC as i64;
>>
>> diff --git a/rust/kernel/time/hrtimer.rs b/rust/kernel/time/hrtimer.rs
>> new file mode 100644
>> index 0000000000000..a6332924efabd
>> --- /dev/null
>> +++ b/rust/kernel/time/hrtimer.rs
>> @@ -0,0 +1,312 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +//! Intrusive high resolution timers.
>> +//!
>> +//! Allows running timer callbacks without doing allocations at the time of
>> +//! starting the timer. For now, only one timer per type is allowed.
>> +//!
>> +//! # Vocabulary
>> +//!
>> +//! States:
>> +//!
>> +//! * Stopped
>> +//! * Running
>> +//!
>> +//! Operations:
>> +//!
>> +//! * Start
>> +//! * Cancel
>> +//! * Stop
>> +//! * Restart
>> +//!
>> +//! Events:
>> +//!
>> +//! * Expire
>> +//!
>> +//! ## State Diagram
>> +//!
>> +//! ```text
>> +//!                  <-- Stop ----
>> +//!                  <-- Cancel --
>> +//!                  --- Start -->
>> +//!        +---------+        +---------+
>> +//!   O--->| Stopped |        | Running |---o
>> +//!        +---------+        +---------+   |
>> +//!                                  ^      |
>> +//!                  <- Expire --    |      |
>> +//!                                  o------o
>> +//!                                   Restart
>> +//! ```
>> +//!
>> +//! A timer is initialized in the **stopped** state. A stopped timer can be
>> +//! **started** with an **expiry** time. After the timer is started, it is
>> +//! **running**. When the timer **expires**, the timer handler is executed.
>> +//! After the handler has executed, the timer may be **restarted** or
>> +//! **stopped**. A running timer can be **canceled** before it's handler is
>
> This confuses me a bit, in the other thread you wrote that the handler
> decides if the timer should restart or be stopped. But What happens when
> I call `cancel` on the `HrTimerHandle` while the handler is running, but
> finishes shortly after with a restart request? Will it be canceled?

Tamir had a somewhat similar question and Is added the following


//! A timer is still considered to be running when the handler is executing. The
//! return value of the handler determines if the timer will stop or restart
//! when the handler finishes executing.
//!

If a timer is canceled while the handler is running, the call to cancel
will block. After the handler has finished executing and the timer has
been restarted or stopped, the cancel call will unblock. If at the timer
is stopped cancel has no effect. If the timer was restarted because of
the handler return value, it will be canceled.

I'll add a note on that as well.


> I also have a bit of a wording issue with "the timer is running" IIUC,
> this means that the timer subsystem keeps track of the expiry time and
> when the time is elapsed, it fires the code that you registered prior.
> At first, I thought that "the timer is running" meant that the
> registered code is running. Maybe we should have two different terms for
> that? I personally would prefer "active timer" for "the timer subsystem
> is currently tracking the time and when it is elapsed, it will run the
> code" and "running timer" for "the timer's expiry time has elapsed and
> the timer callback is currently being executed".

I understand your confusion and I experienced a similar confusion when I
first dove in. Early iterations of this patch set used another
terminology, but C maintainers requested I use the terminology used in C
hrtimer subsystem. Which of course is the right thing to do.

And so in this terminology a timer is `running` when it is armed and
will run the handler when expiration occurs.

I tried to explain with the paragraph under the diagram, but perhaps it
is not enough. I can try to expand it further.

>
>> +//! executed. A timer that is cancelled enters the **stopped** state.
>> +//!
>> +
>> +use crate::{init::PinInit, prelude::*, time::Ktime, types::Opaque};
>> +use core::marker::PhantomData;
>> +
>> +/// A timer backed by a C `struct hrtimer`.
>> +///
>> +/// # Invariants
>> +///
>> +/// * `self.timer` is initialized by `bindings::hrtimer_setup`.
>> +#[pin_data]
>> +#[repr(C)]
>> +pub struct HrTimer<T> {
>> +    #[pin]
>> +    timer: Opaque<bindings::hrtimer>,
>> +    _t: PhantomData<T>,
>> +}
>> +
>> +// SAFETY: Ownership of an `HrTimer` can be moved to other threads and
>> +// used/dropped from there.
>> +unsafe impl<T> Send for HrTimer<T> {}
>> +
>> +// SAFETY: Timer operations are locked on C side, so it is safe to operate on a
>> +// timer from multiple threads
>> +unsafe impl<T> Sync for HrTimer<T> {}
>> +
>> +impl<T> HrTimer<T> {
>> +    /// Return an initializer for a new timer instance.
>> +    pub fn new() -> impl PinInit<Self>
>> +    where
>> +        T: HrTimerCallback,
>> +    {
>> +        pin_init!(Self {
>> +            // INVARIANTS: We initialize `timer` with `hrtimer_setup` below.
>> +            timer <- Opaque::ffi_init(move |place: *mut bindings::hrtimer| {
>> +                // SAFETY: By design of `pin_init!`, `place` is a pointer to a
>> +                // live allocation. hrtimer_setup will initialize `place` and
>> +                // does not require `place` to be initialized prior to the call.
>> +                unsafe {
>> +                    bindings::hrtimer_setup(
>> +                        place,
>> +                        Some(T::CallbackTarget::run),
>> +                        bindings::CLOCK_MONOTONIC as i32,
>> +                        bindings::hrtimer_mode_HRTIMER_MODE_REL,
>> +                    );
>> +                }
>> +            }),
>> +            _t: PhantomData,
>> +        })
>> +    }
>> +
>> +    /// Get a pointer to the contained `bindings::hrtimer`.
>> +    ///
>> +    /// # Safety
>> +    ///
>> +    /// `ptr` must point to a live allocation of at least the size of `Self`.
>> +    unsafe fn raw_get(ptr: *const Self) -> *mut bindings::hrtimer {
>> +        // SAFETY: The field projection to `timer` does not go out of bounds,
>> +        // because the caller of this function promises that `ptr` points to an
>> +        // allocation of at least the size of `Self`.
>> +        unsafe { Opaque::raw_get(core::ptr::addr_of!((*ptr).timer)) }
>> +    }
>> +
>> +    /// Cancel an initialized and potentially running timer.
>> +    ///
>> +    /// If the timer handler is running, this will block until the handler is
>> +    /// finished.
>> +    ///
>> +    /// Users of the `HrTimer` API would not usually call this method directly.
>> +    /// Instead they would use the safe `cancel` method on the [`HrTimerHandle`]
>
> Can you link to the `cancel` function?
>
>> +    /// returned when the timer was started.
>> +    ///
>> +    /// # Safety
>> +    ///
>> +    /// `self_ptr` must point to a valid `Self`.
>> +    #[allow(dead_code)]
>> +    pub(crate) unsafe fn raw_cancel(self_ptr: *const Self) -> bool {
>> +        // SAFETY: timer_ptr points to an allocation of at least `HrTimer` size.
>> +        let c_timer_ptr = unsafe { HrTimer::raw_get(self_ptr) };
>> +
>> +        // If the handler is running, this will wait for the handler to finish
>> +        // before returning.
>> +        // SAFETY: `c_timer_ptr` is initialized and valid. Synchronization is
>> +        // handled on C side.
>> +        unsafe { bindings::hrtimer_cancel(c_timer_ptr) != 0 }
>> +    }
>> +}
>> +
>> +/// Implemented by pointer types that point to structs that embed a [`HrTimer`].
>> +///
>> +/// Target (pointee) must be [`Sync`] because timer callbacks happen in another
>> +/// thread of execution (hard or soft interrupt context).
>> +///
>> +/// Starting a timer returns a [`HrTimerHandle`] that can be used to manipulate
>> +/// the timer. Note that it is OK to call the start function repeatedly, and
>> +/// that more than one [`HrTimerHandle`] associated with a [`HrTimerPointer`] may
>> +/// exist. A timer can be manipulated through any of the handles, and a handle
>> +/// may represent a cancelled timer.
>> +pub trait HrTimerPointer: Sync + Sized {
>> +    /// A handle representing a started or restarted timer.
>> +    ///
>> +    /// If the timer is running or if the timer callback is executing when the
>> +    /// handle is dropped, the drop method of [`HrTimerHandle`] should not return
>> +    /// until the timer is stopped and the callback has completed.
>> +    ///
>> +    /// Note: When implementing this trait, consider that it is not unsafe to
>> +    /// leak the handle.
>> +    type TimerHandle: HrTimerHandle;
>> +
>> +    /// Start the timer with expiry after `expires` time units. If the timer was
>> +    /// already running, it is restarted with the new expiry time.
>> +    fn start(self, expires: Ktime) -> Self::TimerHandle;
>> +}
>> +
>> +/// Implemented by [`HrTimerPointer`] implementers to give the C timer callback a
>> +/// function to call.
>> +// This is split from `HrTimerPointer` to make it easier to specify trait bounds.
>
> I don't understand this argument. The bounds in the other patches seem
> like they could easily be combined. Then this trait could be merged into
> the one above.

I initially wrote `HrTimerPointer` and `RawHrTimerCallback` as a single
trait. But I ran into problems when specifying trait bounds. I got some
loops in the type graph that the compiler could not figure out.

>
>> +pub trait RawHrTimerCallback {
>> +    /// Callback to be called from C when timer fires.
>> +    ///
>> +    /// # Safety
>> +    ///
>> +    /// Only to be called by C code in `hrtimer` subsystem. `ptr` must point to
>> +    /// the `bindings::hrtimer` structure that was used to start the timer.
>> +    unsafe extern "C" fn run(ptr: *mut bindings::hrtimer) -> bindings::hrtimer_restart;
>> +}
>> +
>> +/// Implemented by structs that can be the target of a timer callback.
>> +pub trait HrTimerCallback {
>> +    /// The type whose [`RawHrTimerCallback::run`] method will be invoked when
>> +    /// the timer expires.
>> +    type CallbackTarget<'a>: RawHrTimerCallback;
>> +
>> +    /// This type is passed to the timer callback function. It may be a borrow
>> +    /// of [`Self::CallbackTarget`], or it may be `Self::CallbackTarget` if the
>> +    /// implementation can guarantee exclusive access to the target during timer
>
> Technically "exclusive" access is correct if the `CallbackTarget` is
> `Pin<&Self>`, since you will get exclusive access to a `Pin<&Self>`, but
> it might confuse people, because there can be multiple `Pin<&Self>`. So
> I would just drop the word "exclusive" here.

Yes, maybe it should be "shared or exclusive access, depending on the type"?

>
>> +    /// handler execution.
>> +    type CallbackTargetParameter<'a>;
>
> Also why can't this type be an associated type of `HrTimerPointer`?
> Since this seems to always be constrained in the impls of
> `RawHrTimerCallback`.

That might be a nice improvement, I'll try that out.


Best regards,
Andreas Hindborg



  reply	other threads:[~2025-02-21  9:29 UTC|newest]

Thread overview: 73+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <aIJ0ymzdUceCN05hwJpth4erH5u2SHYzYl52wGeT3uiO9bdk92ZkEmEEq9a9NXsInJYSz9uziwq-1fvdsXoeDA==@protonmail.internalid>
2025-02-18 13:27 ` [PATCH v8 00/14] hrtimer Rust API Andreas Hindborg
2025-02-18 13:27   ` [PATCH v8 01/14] rust: time: Add Ktime::from_ns() Andreas Hindborg
2025-02-19 11:58     ` Benno Lossin
2025-02-19 14:53       ` Andreas Hindborg
2025-02-18 13:27   ` [PATCH v8 02/14] rust: hrtimer: introduce hrtimer support Andreas Hindborg
2025-02-20 17:04     ` Tamir Duberstein
2025-02-20 21:18       ` Andreas Hindborg
2025-02-20 21:37         ` Tamir Duberstein
2025-02-21  8:19           ` Andreas Hindborg
2025-02-21 13:04             ` Tamir Duberstein
2025-02-21 13:17               ` Andreas Hindborg
2025-02-21 14:19                 ` Tamir Duberstein
2025-02-21  8:36           ` Andreas Hindborg
2025-02-21 13:14             ` Tamir Duberstein
2025-02-21 13:28             ` Andreas Hindborg
2025-02-21 14:21               ` Tamir Duberstein
2025-02-22 18:25                 ` Andreas Hindborg
2025-02-22 18:41                   ` Tamir Duberstein
2025-02-21 14:40               ` Boqun Feng
2025-02-21 14:46                 ` Tamir Duberstein
2025-02-21 15:19                   ` Boqun Feng
2025-02-21 19:46                     ` Tamir Duberstein
2025-02-21 22:37                       ` Boqun Feng
2025-02-22  1:08                         ` Tamir Duberstein
2025-02-22  1:38                           ` Boqun Feng
2025-02-22  9:25                     ` Andreas Hindborg
2025-02-22 11:40                       ` Andreas Hindborg
2025-02-22 21:25                         ` Boqun Feng
2025-02-20 23:46     ` Benno Lossin
2025-02-21  9:03       ` Andreas Hindborg [this message]
2025-02-21 10:15         ` Andreas Hindborg
2025-02-21 11:05           ` Benno Lossin
2025-02-21 12:17             ` Andreas Hindborg
2025-02-22  9:37               ` Benno Lossin
2025-02-22 11:27                 ` Andreas Hindborg
2025-02-21  9:05       ` Andreas Hindborg
2025-02-21 11:29       ` Frederic Weisbecker
2025-02-21 11:44         ` Andreas Hindborg
2025-02-18 13:27   ` [PATCH v8 03/14] rust: sync: add `Arc::as_ptr` Andreas Hindborg
2025-02-20 23:18     ` Benno Lossin
2025-02-18 13:27   ` [PATCH v8 04/14] rust: hrtimer: implement `HrTimerPointer` for `Arc` Andreas Hindborg
2025-02-18 13:27   ` [PATCH v8 05/14] rust: hrtimer: allow timer restart from timer handler Andreas Hindborg
2025-02-20 23:47     ` Benno Lossin
2025-02-21  9:09       ` Andreas Hindborg
2025-02-21 10:15         ` Benno Lossin
2025-02-18 13:27   ` [PATCH v8 06/14] rust: hrtimer: add `UnsafeHrTimerPointer` Andreas Hindborg
2025-02-20 23:18     ` Benno Lossin
2025-02-18 13:27   ` [PATCH v8 07/14] rust: hrtimer: add `hrtimer::ScopedHrTimerPointer` Andreas Hindborg
2025-02-18 13:27   ` [PATCH v8 08/14] rust: hrtimer: implement `UnsafeHrTimerPointer` for `Pin<&T>` Andreas Hindborg
2025-02-18 13:27   ` [PATCH v8 09/14] rust: hrtimer: implement `UnsafeHrTimerPointer` for `Pin<&mut T>` Andreas Hindborg
2025-02-18 13:27   ` [PATCH v8 10/14] rust: alloc: add `Box::into_pin` Andreas Hindborg
2025-02-20 23:20     ` Benno Lossin
2025-02-21  9:10       ` Andreas Hindborg
2025-02-18 13:27   ` [PATCH v8 11/14] rust: hrtimer: implement `HrTimerPointer` for `Pin<Box<T>>` Andreas Hindborg
2025-02-18 13:27   ` [PATCH v8 12/14] rust: hrtimer: add `HrTimerMode` Andreas Hindborg
2025-02-20 23:23     ` Benno Lossin
2025-02-21  9:17       ` Andreas Hindborg
2025-02-21 10:19         ` Benno Lossin
2025-02-21 11:39           ` Andreas Hindborg
2025-02-22  9:34             ` Benno Lossin
2025-02-18 13:27   ` [PATCH v8 13/14] rust: hrtimer: add clocksource selection through `ClockSource` Andreas Hindborg
2025-02-20 23:27     ` Benno Lossin
2025-02-21  9:29       ` Andreas Hindborg
2025-02-21 10:22         ` Benno Lossin
2025-02-18 13:27   ` [PATCH v8 14/14] rust: hrtimer: add maintainer entry Andreas Hindborg
2025-02-19 11:02   ` [PATCH v8 00/14] hrtimer Rust API Andreas Hindborg
2025-02-20 21:03     ` Frederic Weisbecker
2025-02-21  8:40       ` Andreas Hindborg
2025-02-21 11:20         ` Frederic Weisbecker
2025-02-22 13:04           ` Miguel Ojeda
2025-02-22 13:10             ` Miguel Ojeda
2025-03-05 17:34             ` Frederic Weisbecker
2025-02-20 22:35   ` Frederic Weisbecker

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=87a5afhdq0.fsf@kernel.org \
    --to=a.hindborg@kernel.org \
    --cc=2407018371@qq.com \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=anna-maria@linutronix.de \
    --cc=benno.lossin@proton.me \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=dakr@kernel.org \
    --cc=daniel.almeida@collabora.com \
    --cc=dirk.behme@gmail.com \
    --cc=frederic@kernel.org \
    --cc=gary@garyguo.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lyude@redhat.com \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tamird@gmail.com \
    --cc=tglx@linutronix.de \
    --cc=tmgross@umich.edu \
    /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;
as well as URLs for NNTP newsgroup(s).