Received: by 2002:ab2:3c46:0:b0:1f5:f2ab:c469 with SMTP id x6csp112742lqf; Fri, 26 Apr 2024 00:53:04 -0700 (PDT) X-Forwarded-Encrypted: i=3; AJvYcCVW0SyIpcys46sCVlbVqpmnhLd3iB310mFhzSUE+Rw2KWLDmtvGRwdTWMLi3+TCJ7oeFrSB4Yof4Qc7wYohWPMvmYOzzWbs20IFnVQNIg== X-Google-Smtp-Source: AGHT+IGOgWvD863hbph0DHpzu1XDQxT0b91l87mBusRDNsn0y2I+33rMDxrykm0oGgw5wEdNPMWD X-Received: by 2002:a17:902:a715:b0:1e3:e256:44e0 with SMTP id w21-20020a170902a71500b001e3e25644e0mr1667491plq.31.1714117984011; Fri, 26 Apr 2024 00:53:04 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1714117983; cv=pass; d=google.com; s=arc-20160816; b=ZTU42d+9QIqHbYuEhUNPNeL+rij0hiXyKBME+rtH5f8H5lRIdbRtGmgnPnx0RttAj/ n4all6XsmTFQk8HMlHMi/ALZb0dMHTwKzLZoP6bFBffMt4cvsw8nHilfgmay9e778IGW pwVAcbqEv7ognAdmiyy/ufZd+Pki0Xpo+i+vJUrkqgvMxixWZ0nPmq+swJaeT9pQ6Noe fcpr/Qf5z+aX96xYqXOF02w67BrCDvh11kOECr0tQCOxWseKFjKnjdb/iOcXfxRyYMjU epcMEhs/f0nvG7Y/JInL5ONPp5EfRzYdRfsOyZCTMkx7TCsV0WMGfVUcZMbhMI+hVLwc S4/w== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:feedback-id:references :in-reply-to:message-id:subject:cc:from:to:date:dkim-signature; bh=DfDlyCs+TTi5EAirKlmzSmca/Ny5QX0ZbJpvr/HMJDU=; fh=gS4l5fwOJdgdM0h7gxlhAH3fv3hL+2RxnPTA0yNO1hI=; b=eYdOrkEkSpW4txADupvCvTds24olNytkibyvcBPFy8pLiQaNcqdNyps8vjt9sd/rHl +YZkRNWVBqPcHmy+OOyxeQY8OHbGnnDSObD2A0O5w5OEMdiSrJHgWe7M/rSZCXzs2tPP 7TQPUcsLK0JF4GGuIyos8nHcC4w0h6orCh30iSQl+O+gNuqOeTdMS04tBcrBvztZDFIm FAmySuMG1juqTRJbG/Snm0y5A+5TWP683m26LvzRQfEVri2Ylk2K0jHBDkQ3gAzSvWLm Pm9kGwbaRLCV/Zy4VUWbsH8pmtYEe3bOHgBfo9P3MH2K2+Bdqx77gkOoyfP99ejqfZbW 6hFQ==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=eoqfjgFc; arc=pass (i=1 spf=pass spfdomain=proton.me dkim=pass dkdomain=proton.me dmarc=pass fromdomain=proton.me); spf=pass (google.com: domain of linux-kernel+bounces-159662-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) smtp.mailfrom="linux-kernel+bounces-159662-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Return-Path: Received: from sy.mirrors.kernel.org (sy.mirrors.kernel.org. [147.75.48.161]) by mx.google.com with ESMTPS id y9-20020a17090322c900b001eb144f95f9si556799plg.613.2024.04.26.00.53.03 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Apr 2024 00:53:03 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel+bounces-159662-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) client-ip=147.75.48.161; Authentication-Results: mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=eoqfjgFc; arc=pass (i=1 spf=pass spfdomain=proton.me dkim=pass dkdomain=proton.me dmarc=pass fromdomain=proton.me); spf=pass (google.com: domain of linux-kernel+bounces-159662-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) smtp.mailfrom="linux-kernel+bounces-159662-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sy.mirrors.kernel.org (Postfix) with ESMTPS id 1B821B223E7 for ; Fri, 26 Apr 2024 07:53:02 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 915F113B298; Fri, 26 Apr 2024 07:52:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=proton.me header.i=@proton.me header.b="eoqfjgFc" Received: from mail-4322.protonmail.ch (mail-4322.protonmail.ch [185.70.43.22]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4A7AA13A271 for ; Fri, 26 Apr 2024 07:52:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.70.43.22 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714117970; cv=none; b=P2sKpYgjK6YeR+Axqqxkp3UPa9eHiiWLuJJE3xygRPxYKdlLX1wYH0eDimeZXVORCFvGBTLZ4mULmpkDa38Mr9f5jo98CJf8M0pbGhKNlX5hVBC7LLhTWlEcOFVPbnZSyrMgc4JeDQet3bHbfe2AKOSkBDqTOCxudjPSjOjpvMs= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714117970; c=relaxed/simple; bh=f066WrpDI7eQ/wBEZOaNc1YtPmsY1fNb2G4frBU8rF0=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=GR15231F2o11sQfCWDIGpVJzPCFJ9TjTZZSPfosn+g/ztbP5/91dOTN87zYCoyjGRmUZaKiAFhB5OxaVU5tMYOM5U/FH+nBM8tp8Y5j1ijCQu8kW+qgpNfwefRvprUxSBOn4Xeb0/4E7hmXJ+vZXGTd33DVj7umlkslLPdRr14E= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=proton.me; spf=pass smtp.mailfrom=proton.me; dkim=pass (2048-bit key) header.d=proton.me header.i=@proton.me header.b=eoqfjgFc; arc=none smtp.client-ip=185.70.43.22 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=proton.me Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=proton.me DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=proton.me; s=protonmail; t=1714117966; x=1714377166; bh=DfDlyCs+TTi5EAirKlmzSmca/Ny5QX0ZbJpvr/HMJDU=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=eoqfjgFcI+7DuxB9fqJsvCyRKODBXbNE0CtW9Vygwd8yadBv8vmFdFgvWGwAJiwyO yQBikWgws9XVLzm1WXynIdgSYEGCW0VAVBmgnRswg5rTFYzqOc0SDqKVVh/6FbZSeB pLo29tykoy+pUIIrRB4K1xKRfmVt/DLNQvPv7pvrSBKxqsdQlLERJYa9PTACbbdqU8 vuMJwKiUYh1dmzEJcC/nNVUg3B+2xiV47EDJlTNDqHO1LRY3mGF6/N9iA6V//Oh+zU /DSk33LvyfVK97Q7JMBOpip5NFP67W5bEUF87U0p0LiFoTaIRV/xhNXaio9BwVAmfd 1ieFd651cuU9A== Date: Fri, 26 Apr 2024 07:52:41 +0000 To: Andreas Hindborg , Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Anna-Maria Behnsen , Frederic Weisbecker , Thomas Gleixner From: Benno Lossin Cc: Andreas Hindborg , Boqun Feng , Gary Guo , =?utf-8?Q?Bj=C3=B6rn_Roy_Baron?= , Alice Ryhl , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org Subject: Re: [PATCH] rust: hrtimer: introduce hrtimer support Message-ID: In-Reply-To: <20240425094634.262674-1-nmi@metaspace.dk> References: <20240425094634.262674-1-nmi@metaspace.dk> Feedback-ID: 71780778:user:proton X-Pm-Message-ID: 54bf713338d951f9d261fee021aaf7d56b0e8061 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On 25.04.24 11:46, Andreas Hindborg wrote: > From: Andreas Hindborg >=20 > This patch adds support for intrusive use of the hrtimer system. For now,= only > one timer can be embedded in a Rust struct. >=20 > The hrtimer Rust API is based on the intrusive style pattern introduced b= y the > Rust workqueue API. >=20 > Signed-off-by: Andreas Hindborg >=20 > --- >=20 > This patch is a dependency for the Rust null block driver [1]. >=20 > Link: https://lore.kernel.org/rust-for-linux/20240313110515.70088-1-nmi@m= etaspace.dk/T/#me0990150b9ba9f5b3d00293ec9a473c7bc3cc506 [1] >=20 > rust/kernel/hrtimer.rs | 283 +++++++++++++++++++++++++++++++++++++++++ > rust/kernel/lib.rs | 1 + > 2 files changed, 284 insertions(+) > create mode 100644 rust/kernel/hrtimer.rs >=20 > diff --git a/rust/kernel/hrtimer.rs b/rust/kernel/hrtimer.rs Hmm is this the right place? I imagine there are other timers, does this fit better into the `time` module (ie make `hrtimer` a submodule of `time`) or should we later introduce a `timer` parent module? > 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 t= ime 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, > +//! flag: AtomicBool, > +//! } > +//! > +//! impl IntrusiveTimer { > +//! fn new() -> impl PinInit { > +//! pin_init!(Self { > +//! timer <- Timer::new(), > +//! flag: AtomicBool::new(false), > +//! }) > +//! } > +//! } > +//! > +//! impl TimerCallback for IntrusiveTimer { > +//! type Receiver =3D Arc; > +//! > +//! fn run(this: Self::Receiver) { > +//! pr_info!("Timer called\n"); > +//! this.flag.store(true, Ordering::Relaxed); > +//! } > +//! } > +//! > +//! impl_has_timer! { > +//! impl HasTimer for IntrusiveTimer { self.timer } > +//! } > +//! > +//! let has_timer =3D Arc::pin_init(IntrusiveTimer::new())?; I would not name this variable `has_timer`. Maybe `my_timer` is better? > +//! has_timer.clone().schedule(200_000_000); > +//! while !has_timer.flag.load(Ordering::Relaxed) { core::hint::spin_loo= p() } Weird formatting, we should also use `rustfmt` in examples. > +//! > +//! 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` Missing `.` at the end, this also occurs below. > +/// > +/// # Invariants > +/// > +/// * `self.timer` is initialized by `bindings::hrtimer_init`. > +#[repr(transparent)] > +#[pin_data(PinnedDrop)] > +pub struct Timer { > + #[pin] > + timer: Opaque, > + _t: PhantomData, > +} > + > +// SAFETY: A `Timer` can be moved to other threads and used from there. > +unsafe impl Send for Timer {} > + > +// SAFETY: Timer operations are locked on C side, so it is safe to opera= te on a > +// timer from multiple threads > +unsafe impl Sync for Timer {} > + > +impl Timer { > + /// Return an initializer for a new timer instance. > + pub fn new() -> impl PinInit { > + crate::pin_init!( Self { `pin_init!` is in the prelude, no need to prefix with `crate`. > + timer <- Opaque::ffi_init(move |place: *mut bindings::hrtime= r| { > + // SAFETY: By design of `pin_init!`, `place` is a pointe= r 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 initialize= d, but > + // `addr_of_mut` does not create a reference to the fiel= d. > + let function: *mut Option<_> =3D unsafe { core::ptr::add= r_of_mut!((*place).function) }; > + > + // SAFETY: `function` points to a valid allocation. > + unsafe { core::ptr::write(function, Some(T::Receiver::ru= n)) }; > + }), > + _t: PhantomData, > + }) > + } > +} > + > +#[pinned_drop] > +impl PinnedDrop for Timer { > + 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()); > + } > + } > +} Why is this needed? The only way to schedule a timer using this API is by having an `Arc` with a timer-containing struct inside. But to schedule the `Arc`, you consume one refcount which is then sent to the timer subsystem. So it is impossible for the refcount to drop below zero while the timer is scheduled, but not yet running. Do you need to call `hrtimer_cancel` after/while a timer is running? Also is it ok to call `hrtimer_cancel` inside the timer callback? Since that can happen when the timer callback owns the last refcount. > + > +/// Implemented by pointer types to structs that embed a [`Timer`]. This= trait > +/// facilitates queueing the timer through the pointer that implements t= he > +/// trait. > +/// > +/// Typical implementers would be [`Box`], [`Arc`], [`ARef`] wh= ere `T` > +/// has a field of type `Timer`. > +/// > +/// Target must be [`Sync`] because timer callbacks happen in another th= read of > +/// execution. > +/// > +/// [`Box`]: Box > +/// [`Arc`]: Arc > +/// [`ARef`]: 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 b= y 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 { > + /// 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 { > + // SAFETY: By the safety requirement of this trait, the trait > + // implementor will have a `Timer` field at the specified offset= . > + unsafe { ptr.cast::().add(Self::OFFSET).cast::>() } > + } > + > + /// Return a pointer to the struct that is embedding the [`Timer`] p= ointed > + /// to by `ptr`. > + /// > + /// # Safety > + /// > + /// `ptr` must point to a [`Timer`] field in a struct of type `Se= lf`. > + unsafe fn timer_container_of(ptr: *mut Timer) -> *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::().sub(Self::OFFSET).cast::() } > + } > +} > + > +/// Implemented by pointer types that can be the target of a C timer cal= lback. > +pub trait RawTimerCallback: RawTimer { Do you really need two different traits? Can't we have a single `TimerPointer` trait that does both `RawTimerCallback` and `RawTimer`? (I am also wondering why we did this for workqueue) > + /// 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::h= rtimer_restart; > +} > + > +/// Implemented by pointers to structs that can the target of a timer ca= llback > +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 RawTimer for Arc > +where > + T: Send + Sync, > + T: HasTimer, > +{ > + fn schedule(self, expires: u64) { > + let self_ptr =3D Arc::into_raw(self); > + > + // SAFETY: `self_ptr` is a valid pointer to a `T` > + let timer_ptr =3D unsafe { T::raw_get_timer(self_ptr) }; > + > + // `Timer` is `repr(transparent)` > + let c_timer_ptr =3D timer_ptr.cast::(); > + > + // Schedule the timer - if it is already scheduled it is removed= and > + // inserted > + > + // SAFETY: c_timer_ptr points to a valid hrtimer instance that w= as > + // 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 kernel::hrtimer::RawTimerCallback for Arc Why are you spelling out the whole path? > +where > + T: Send + Sync, > + T: HasTimer, > + T: TimerCallback, > +{ > + unsafe extern "C" fn run(ptr: *mut bindings::hrtimer) -> bindings::h= rtimer_restart { > + // `Timer` is `repr(transparent)` > + let timer_ptr =3D ptr.cast::>(); > + > + // SAFETY: By C API contract `ptr` is the pointer we passed when > + // enqueing the timer, so it is a `Timer` embedded in a `T` > + let data_ptr =3D unsafe { T::timer_container_of(timer_ptr) }; > + > + // SAFETY: This `Arc` comes from a call to `Arc::into_raw()` > + let receiver =3D unsafe { Arc::from_raw(data_ptr) }; > + > + T::run(receiver); > + > + bindings::hrtimer_restart_HRTIMER_NORESTART > + } > +} > + > +/// Use to implement the [`HasTimer`] trait. > +/// > +/// See [`module`] documentation for an example. > +/// > +/// [`module`]: crate::hrtimer > +#[macro_export] > +macro_rules! impl_has_timer { > + ($(impl$(<$($implarg:ident),*>)? Doing it this way makes it impossible to use more complex types with eg lifetimes or const generic arguments. There is a workaround, see Alice's linked list patch [1]: macro_rules! impl_list_item { ( impl$({$($generics:tt)*})? ListItem<$num:tt> for $t:ty { using ListLinks; } $($rest:tt)* ) =3D> { [1]: https://lore.kernel.org/rust-for-linux/20240402-linked-list-v1-4-b1c59= ba7ae3b@google.com/ > + HasTimer<$timer_type:ty $(, $id:tt)?> `HasTimer` currently doesn't have an `id`, so the macro also shouldn't have it. --=20 Cheers, Benno > + for $self:ident $(<$($selfarg:ident),*>)? > + { self.$field:ident } > + )*) =3D> {$( > + // 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 =3D ::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 d= angling. > + 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)] >=20 > base-commit: ed30a4a51bb196781c8058073ea720133a65596f > -- > 2.44.0 >=20