Rust-for-linux archive mirror
 help / color / mirror / Atom feed
From: Andreas Hindborg <nmi@metaspace.dk>
To: Miguel Ojeda <ojeda@kernel.org>,
	Alex Gaynor <alex.gaynor@gmail.com>,
	Wedson Almeida Filho <wedsonaf@gmail.com>,
	Anna-Maria Behnsen <anna-maria@linutronix.de>,
	Frederic Weisbecker <frederic@kernel.org>,
	Thomas Gleixner <tglx@linutronix.de>
Cc: "Andreas Hindborg" <a.hindborg@samsung.com>,
	"Boqun Feng" <boqun.feng@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <benno.lossin@proton.me>,
	"Alice Ryhl" <aliceryhl@google.com>,
	rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH] rust: hrtimer: introduce hrtimer support
Date: Thu, 25 Apr 2024 11:46:34 +0200	[thread overview]
Message-ID: <20240425094634.262674-1-nmi@metaspace.dk> (raw)

From: Andreas Hindborg <a.hindborg@samsung.com>

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@samsung.com>

---

This patch is a dependency for the Rust null block driver [1].

Link: https://lore.kernel.org/rust-for-linux/20240313110515.70088-1-nmi@metaspace.dk/T/#me0990150b9ba9f5b3d00293ec9a473c7bc3cc506 [1]

 rust/kernel/hrtimer.rs | 283 +++++++++++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs     |   1 +
 2 files changed, 284 insertions(+)
 create mode 100644 rust/kernel/hrtimer.rs

diff --git a/rust/kernel/hrtimer.rs b/rust/kernel/hrtimer.rs
new file mode 100644
index 000000000000..1e282608e70c
--- /dev/null
+++ b/rust/kernel/hrtimer.rs
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Intrusive high resolution timers.
+//!
+//! Allows scheduling timer callbacks without doing allocations at the time of
+//! scheduling. For now, only one timer per type is allowed.
+//!
+//! # Example
+//!
+//! ```rust
+//! use kernel::{
+//!     sync::Arc, hrtimer::{RawTimer, Timer, TimerCallback},
+//!     impl_has_timer, prelude::*, stack_pin_init
+//! };
+//! use core::sync::atomic::AtomicBool;
+//! use core::sync::atomic::Ordering;
+//!
+//! #[pin_data]
+//! struct IntrusiveTimer {
+//!     #[pin]
+//!     timer: Timer<Self>,
+//!     flag: AtomicBool,
+//! }
+//!
+//! impl IntrusiveTimer {
+//!     fn new() -> impl PinInit<Self> {
+//!         pin_init!(Self {
+//!             timer <- Timer::new(),
+//!             flag: AtomicBool::new(false),
+//!         })
+//!     }
+//! }
+//!
+//! impl TimerCallback for IntrusiveTimer {
+//!     type Receiver = Arc<IntrusiveTimer>;
+//!
+//!     fn run(this: Self::Receiver) {
+//!         pr_info!("Timer called\n");
+//!         this.flag.store(true, Ordering::Relaxed);
+//!     }
+//! }
+//!
+//! impl_has_timer! {
+//!     impl HasTimer<Self> for IntrusiveTimer { self.timer }
+//! }
+//!
+//! let has_timer = Arc::pin_init(IntrusiveTimer::new())?;
+//! has_timer.clone().schedule(200_000_000);
+//! while !has_timer.flag.load(Ordering::Relaxed) { core::hint::spin_loop() }
+//!
+//! pr_info!("Flag raised\n");
+//!
+//! # Ok::<(), kernel::error::Error>(())
+//! ```
+//!
+//! C header: [`include/linux/hrtimer.h`](srctree/include/linux/hrtimer.h)
+
+use core::{marker::PhantomData, pin::Pin};
+
+use crate::{init::PinInit, prelude::*, sync::Arc, types::Opaque};
+
+/// A timer backed by a C `struct hrtimer`
+///
+/// # Invariants
+///
+/// * `self.timer` is initialized by `bindings::hrtimer_init`.
+#[repr(transparent)]
+#[pin_data(PinnedDrop)]
+pub struct Timer<T> {
+    #[pin]
+    timer: Opaque<bindings::hrtimer>,
+    _t: PhantomData<T>,
+}
+
+// SAFETY: A `Timer` can be moved to other threads and used from there.
+unsafe impl<T> Send for Timer<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 Timer<T> {}
+
+impl<T: TimerCallback> Timer<T> {
+    /// Return an initializer for a new timer instance.
+    pub fn new() -> impl PinInit<Self> {
+        crate::pin_init!( Self {
+            timer <- Opaque::ffi_init(move |place: *mut bindings::hrtimer| {
+                // SAFETY: By design of `pin_init!`, `place` is a pointer live
+                // allocation. hrtimer_init will initialize `place` and does not
+                // require `place` to be initialized prior to the call.
+                unsafe {
+                    bindings::hrtimer_init(
+                        place,
+                        bindings::CLOCK_MONOTONIC as i32,
+                        bindings::hrtimer_mode_HRTIMER_MODE_REL,
+                    );
+                }
+
+                // SAFETY: `place` is pointing to a live allocation, so the deref
+                // is safe. The `function` field might not be initialized, but
+                // `addr_of_mut` does not create a reference to the field.
+                let function: *mut Option<_> = unsafe { core::ptr::addr_of_mut!((*place).function) };
+
+                // SAFETY: `function` points to a valid allocation.
+                unsafe { core::ptr::write(function, Some(T::Receiver::run)) };
+            }),
+            _t: PhantomData,
+        })
+    }
+}
+
+#[pinned_drop]
+impl<T> PinnedDrop for Timer<T> {
+    fn drop(self: Pin<&mut Self>) {
+        // SAFETY: By struct invariant `self.timer` was initialized by
+        // `hrtimer_init` so by C API contract it is safe to call
+        // `hrtimer_cancel`.
+        unsafe {
+            bindings::hrtimer_cancel(self.timer.get());
+        }
+    }
+}
+
+/// Implemented by pointer types to structs that embed a [`Timer`]. This trait
+/// facilitates queueing the timer through the pointer that implements the
+/// trait.
+///
+/// Typical implementers would be [`Box<T>`], [`Arc<T>`], [`ARef<T>`] where `T`
+/// has a field of type `Timer`.
+///
+/// Target must be [`Sync`] because timer callbacks happen in another thread of
+/// execution.
+///
+/// [`Box<T>`]: Box
+/// [`Arc<T>`]: Arc
+/// [`ARef<T>`]: crate::types::ARef
+pub trait RawTimer: Sync {
+    /// Schedule the timer after `expires` time units
+    fn schedule(self, expires: u64);
+}
+
+/// Implemented by structs that contain timer nodes.
+///
+/// Clients of the timer API would usually safely implement this trait by using
+/// the [`impl_has_timer`] macro.
+///
+/// # Safety
+///
+/// Implementers of this trait must ensure that the implementer has a [`Timer`]
+/// field at the offset specified by `OFFSET` and that all trait methods are
+/// implemented according to their documentation.
+///
+/// [`impl_has_timer`]: crate::impl_has_timer
+pub unsafe trait HasTimer<T> {
+    /// Offset of the [`Timer`] field within `Self`
+    const OFFSET: usize;
+
+    /// Return a pointer to the [`Timer`] within `Self`.
+    ///
+    /// # Safety
+    ///
+    /// `ptr` must point to a valid struct of type `Self`.
+    unsafe fn raw_get_timer(ptr: *const Self) -> *const Timer<T> {
+        // SAFETY: By the safety requirement of this trait, the trait
+        // implementor will have a `Timer` field at the specified offset.
+        unsafe { ptr.cast::<u8>().add(Self::OFFSET).cast::<Timer<T>>() }
+    }
+
+    /// Return a pointer to the struct that is embedding the [`Timer`] pointed
+    /// to by `ptr`.
+    ///
+    /// # Safety
+    ///
+    /// `ptr` must point to a [`Timer<T>`] field in a struct of type `Self`.
+    unsafe fn timer_container_of(ptr: *mut Timer<T>) -> *mut Self
+    where
+        Self: Sized,
+    {
+        // SAFETY: By the safety requirement of this trait, the trait
+        // implementor will have a `Timer` field at the specified offset.
+        unsafe { ptr.cast::<u8>().sub(Self::OFFSET).cast::<Self>() }
+    }
+}
+
+/// Implemented by pointer types that can be the target of a C timer callback.
+pub trait RawTimerCallback: RawTimer {
+    /// Callback to be called from C.
+    ///
+    /// # Safety
+    ///
+    /// Only to be called by C code in `hrtimer`subsystem.
+    unsafe extern "C" fn run(ptr: *mut bindings::hrtimer) -> bindings::hrtimer_restart;
+}
+
+/// Implemented by pointers to structs that can the target of a timer callback
+pub trait TimerCallback {
+    /// Type of `this` argument for `run()`.
+    type Receiver: RawTimerCallback;
+
+    /// Called by the timer logic when the timer fires
+    fn run(this: Self::Receiver);
+}
+
+impl<T> RawTimer for Arc<T>
+where
+    T: Send + Sync,
+    T: HasTimer<T>,
+{
+    fn schedule(self, expires: u64) {
+        let self_ptr = Arc::into_raw(self);
+
+        // SAFETY: `self_ptr` is a valid pointer to a `T`
+        let timer_ptr = unsafe { T::raw_get_timer(self_ptr) };
+
+        // `Timer` is `repr(transparent)`
+        let c_timer_ptr = timer_ptr.cast::<bindings::hrtimer>();
+
+        // Schedule the timer - if it is already scheduled it is removed and
+        // inserted
+
+        // SAFETY: c_timer_ptr points to a valid hrtimer instance that was
+        // initialized by `hrtimer_init`
+        unsafe {
+            bindings::hrtimer_start_range_ns(
+                c_timer_ptr.cast_mut(),
+                expires as i64,
+                0,
+                bindings::hrtimer_mode_HRTIMER_MODE_REL,
+            );
+        }
+    }
+}
+
+impl<T> kernel::hrtimer::RawTimerCallback for Arc<T>
+where
+    T: Send + Sync,
+    T: HasTimer<T>,
+    T: TimerCallback<Receiver = Self>,
+{
+    unsafe extern "C" fn run(ptr: *mut bindings::hrtimer) -> bindings::hrtimer_restart {
+        // `Timer` is `repr(transparent)`
+        let timer_ptr = ptr.cast::<kernel::hrtimer::Timer<T>>();
+
+        // SAFETY: By C API contract `ptr` is the pointer we passed when
+        // enqueing the timer, so it is a `Timer<T>` embedded in a `T`
+        let data_ptr = unsafe { T::timer_container_of(timer_ptr) };
+
+        // SAFETY: This `Arc` comes from a call to `Arc::into_raw()`
+        let receiver = unsafe { Arc::from_raw(data_ptr) };
+
+        T::run(receiver);
+
+        bindings::hrtimer_restart_HRTIMER_NORESTART
+    }
+}
+
+/// Use to implement the [`HasTimer<T>`] trait.
+///
+/// See [`module`] documentation for an example.
+///
+/// [`module`]: crate::hrtimer
+#[macro_export]
+macro_rules! impl_has_timer {
+    ($(impl$(<$($implarg:ident),*>)?
+       HasTimer<$timer_type:ty $(, $id:tt)?>
+       for $self:ident $(<$($selfarg:ident),*>)?
+       { self.$field:ident }
+    )*) => {$(
+        // SAFETY: This implementation of `raw_get_timer` only compiles if the
+        // field has the right type.
+        unsafe impl$(<$($implarg),*>)? $crate::hrtimer::HasTimer<$timer_type> for $self $(<$($selfarg),*>)? {
+            const OFFSET: usize = ::core::mem::offset_of!(Self, $field) as usize;
+
+            #[inline]
+            unsafe fn raw_get_timer(ptr: *const Self) -> *const $crate::hrtimer::Timer<$timer_type $(, $id)?> {
+                // SAFETY: The caller promises that the pointer is not dangling.
+                unsafe {
+                    ::core::ptr::addr_of!((*ptr).$field)
+                }
+            }
+
+        }
+    )*};
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index be68d5e567b1..7af3ca601167 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -33,6 +33,7 @@
 mod allocator;
 mod build_assert;
 pub mod error;
+pub mod hrtimer;
 pub mod init;
 pub mod ioctl;
 #[cfg(CONFIG_KUNIT)]

base-commit: ed30a4a51bb196781c8058073ea720133a65596f
-- 
2.44.0


             reply	other threads:[~2024-04-25  9:46 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-04-25  9:46 Andreas Hindborg [this message]
2024-04-26  7:52 ` [PATCH] rust: hrtimer: introduce hrtimer support Benno Lossin
2024-04-26  9:27   ` Andreas Hindborg
2024-04-29 17:31     ` Boqun Feng
2024-04-30 12:33       ` Andreas Hindborg
2024-04-30 15:17         ` Boqun Feng
2024-04-30 18:22           ` Andreas Hindborg
2024-04-30 17:05   ` Thomas Gleixner
2024-04-29 12:49 ` Alice Ryhl
2024-04-29 13:47   ` Andreas Hindborg
2024-04-30 17:02 ` Thomas Gleixner
2024-04-30 18:18   ` Andreas Hindborg
2024-04-30 22:25     ` Thomas Gleixner
2024-05-01 11:37       ` Andreas Hindborg

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=20240425094634.262674-1-nmi@metaspace.dk \
    --to=nmi@metaspace.dk \
    --cc=a.hindborg@samsung.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=frederic@kernel.org \
    --cc=gary@garyguo.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tglx@linutronix.de \
    --cc=wedsonaf@gmail.com \
    /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 read-only IMAP folder(s) and NNTP newsgroup(s).