Received: by 2002:a89:48b:0:b0:1f5:f2ab:c469 with SMTP id a11csp989494lqd; Thu, 25 Apr 2024 02:47:01 -0700 (PDT) X-Forwarded-Encrypted: i=3; AJvYcCW58cgSsCXmP/LmhvNd77FlR7FxygS7hNLm7YboyKNR+IwPGHQ2b8Xj/Y7YGn7+5rlHS79GrhQ9PJSsYeeuzTbsOntUFh/QAfe6SgqOqw== X-Google-Smtp-Source: AGHT+IETsXiBpU7MUwkEdc8RKix0RMbjhLaEIt7yc4100tWVD7AGmMQmj+6ISpcv3DOa6A46YZzc X-Received: by 2002:a17:902:ec8b:b0:1e0:e8b5:3225 with SMTP id x11-20020a170902ec8b00b001e0e8b53225mr3550733plg.12.1714038421100; Thu, 25 Apr 2024 02:47:01 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1714038421; cv=pass; d=google.com; s=arc-20160816; b=KrwKbLvQq9I3unJUnV047wMFrmmRBQBrIBMKZr4MTfUULPvQ5+BDB+3sj/KzaLmA2v 0p5QmqRZqlyB9JG25U4EzmbxktrFoaz9o8ouGvHSQW5gsIIfknbNmsavm8HAfLLi5U7Y 8pDymlm8mNW+JsdldKQ2+WZyPNNvQ3v7m2xBL+LfaDnLbzulAceDYOqkdQ8WPhK2ZlnF Shs38JF0IVTceiZXIpov+0Vh98XoM7NjhxbQZI7QHERSWpTRiJ2Z9V84HEMOsiuQcXQb Dm0NtSCktebyZNssqRW6sHInRaYnWzBkodPh1J3q2CA9k6RwybWNox3+VHKoRempOsIB wB1Q== 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:message-id:date:subject:cc:to :from:dkim-signature; bh=eRa9kc42TXW2jUhWRVQQ1tURo8DR/aWzs48K8hurW1Q=; fh=A/n3lMyENFudR6dLeCVlxwRzsAK6MTJzG6lt4R//xbo=; b=0d/VjLpUbmI2nS2vMMTg7p4JAKGMU6Q2aFQJvr4HT58PhOgc0IL3HLacxoSie3rx2i MHVIBpEMmXk1O4V7WPN14W8nfWBptOKTiubGTd8ZqCTxXaK0OVCX+CHmVz/ln7ZYRsOQ 5RxWYlU9THl1vaJBxNg+OK74n3KyXqcr/OR341Ql71jgIZ3LnbTm6tQmz+5c2cVNhK0n f2aebF97nXXZVL/PsZUTTH5G0GbuASlUiM4CjHTqH16Mr63KUiSHMNRON9+G5Z+XzQju qzzESzVfpmDweQAi41Oyj5blPZB6Rt6E5tIuKkZYv0K3QgdIfLYKYDNkER/jk8jAMHog Gjgg==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@metaspace-dk.20230601.gappssmtp.com header.s=20230601 header.b=k6zgdYNM; arc=pass (i=1 dkim=pass dkdomain=metaspace-dk.20230601.gappssmtp.com); spf=pass (google.com: domain of linux-kernel+bounces-158324-linux.lists.archive=gmail.com@vger.kernel.org designates 139.178.88.99 as permitted sender) smtp.mailfrom="linux-kernel+bounces-158324-linux.lists.archive=gmail.com@vger.kernel.org" Return-Path: Received: from sv.mirrors.kernel.org (sv.mirrors.kernel.org. [139.178.88.99]) by mx.google.com with ESMTPS id h5-20020a170902f70500b001e039a37289si13519154plo.613.2024.04.25.02.47.00 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 25 Apr 2024 02:47:01 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel+bounces-158324-linux.lists.archive=gmail.com@vger.kernel.org designates 139.178.88.99 as permitted sender) client-ip=139.178.88.99; Authentication-Results: mx.google.com; dkim=pass header.i=@metaspace-dk.20230601.gappssmtp.com header.s=20230601 header.b=k6zgdYNM; arc=pass (i=1 dkim=pass dkdomain=metaspace-dk.20230601.gappssmtp.com); spf=pass (google.com: domain of linux-kernel+bounces-158324-linux.lists.archive=gmail.com@vger.kernel.org designates 139.178.88.99 as permitted sender) smtp.mailfrom="linux-kernel+bounces-158324-linux.lists.archive=gmail.com@vger.kernel.org" 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 sv.mirrors.kernel.org (Postfix) with ESMTPS id B634128250D for ; Thu, 25 Apr 2024 09:47:00 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 7C7E984FA2; Thu, 25 Apr 2024 09:46:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=metaspace-dk.20230601.gappssmtp.com header.i=@metaspace-dk.20230601.gappssmtp.com header.b="k6zgdYNM" Received: from mail-lf1-f51.google.com (mail-lf1-f51.google.com [209.85.167.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D1D9E7FBA3 for ; Thu, 25 Apr 2024 09:46:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.51 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714038412; cv=none; b=EZa8VwczqnGI16Ny5kFwFMSZUpYtUVfN534saflm8o4MQmWdqifDNqmlkCsjvSE/nfC2wFkwOszYwf67V5DSklGF4wgo1D/uWEVAWCtLjfWcjHP/+JqhUulBAnm6b0SjkNEO/4HkaZwpanfUrWTJ8FcKVLC0IaHTXqO2ZV5jnnE= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714038412; c=relaxed/simple; bh=XNiX59yyogpNk83JFHRFXm+QLl0yTAoKYYq8VyPozu4=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=Os/BmwfG1hpwNtAJ58lgFatj4RiBzwU2A7SMS2ezdiuDFXkXQUWzYYaXL67uNd3lEiq2VHG+qQwq1KzzqtGuEIaM+2z+IE98PODgPGUBGOvD0d7sx13kJfQPP09TBEg7I6Ky6Bm9mzqWO2xw2L3gNy87e1qDthnZtpdfTmiXp4c= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=metaspace.dk; spf=none smtp.mailfrom=metaspace.dk; dkim=pass (2048-bit key) header.d=metaspace-dk.20230601.gappssmtp.com header.i=@metaspace-dk.20230601.gappssmtp.com header.b=k6zgdYNM; arc=none smtp.client-ip=209.85.167.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=metaspace.dk Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=metaspace.dk Received: by mail-lf1-f51.google.com with SMTP id 2adb3069b0e04-518a56cdbcfso1086897e87.2 for ; Thu, 25 Apr 2024 02:46:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=metaspace-dk.20230601.gappssmtp.com; s=20230601; t=1714038408; x=1714643208; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=eRa9kc42TXW2jUhWRVQQ1tURo8DR/aWzs48K8hurW1Q=; b=k6zgdYNMhcPQLQ+qXMoUv2eaP9Xuv5DIWjpcG3DEF+tE2n/EUqWU2ZNDTKDPU2hZlr KaELsGhojUkdxoHNo1MAe/f41rQ84fzBUpt6vCVk2hKCVbTX3mArAYL8Etg5T6tefWj6 LYGvGQL4xJ2+L+m7J+1G9MZuQ5elmHT2hTsh8Tr0xNqkKkxvcpOrDHuaMxGxfV2kUiSO 97We8BnGEnjK74L8/4Hxqd3cUhdHjPvT6fxYA4xCzB01CuWOlgSM8BZtF+sAo2xmSB+e 12se8Th3typ86vR7nL5DlD5qej6eZJR84Bwog2n0iYXl4rI3tmg9qMTnlpIVmbBue0qG pXJA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714038408; x=1714643208; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=eRa9kc42TXW2jUhWRVQQ1tURo8DR/aWzs48K8hurW1Q=; b=dDC1eofPUxNfIcEXxc++LpuULvcz3ypuSRlP6f1lJ7MAJYZWZgSrm7es2rY6zAD3Dd GR6Dv3odS3IjVXiJPa+g4oKh5FN4TkCPsRa214vJpoj5hXiVa5e7hdM+VIus6MubAuXz SpME+IgWC2Vu6TEN47EoaFHP4GSe+zuoKLJp3jvW8tOuheJv8OD5oduX2EdYCQhL3LDj +SpVZZTGCA58B3JzUPpX4FLd/UwfnrkusDcbAk1kvyx2WIeX17nunXBXIrzH1T2d1n8T Ipi7Udb0zRt2BOghPwTj4miFNam7IRt2sOpfeyle2mLX60zcXrG+JxX7OCeADYetR3lU 9uxA== X-Forwarded-Encrypted: i=1; AJvYcCVFpMFYf6ZvxkTBHHiFPssg5dh+U7UeRB87CaRJFH+i/misrafGd9oVgOiLf/2wDgxB0e7RaEWRKKVYHl5j4+c7afoINcSp+w3hJ+Ts X-Gm-Message-State: AOJu0YzqEM+qjPsLFDonPPY2tMWs77WkL9+IvJEX8/KxIifMk9uzWxID 89hX+50/29yjAPxc7OQSvbJNyLK/6m3sqIaSRmMR+v+OWqWLYvoCs22itvDe8uw= X-Received: by 2002:a05:6512:793:b0:51a:affe:252e with SMTP id x19-20020a056512079300b0051aaffe252emr4303240lfr.37.1714038407646; Thu, 25 Apr 2024 02:46:47 -0700 (PDT) Received: from localhost ([79.142.230.34]) by smtp.gmail.com with ESMTPSA id dk21-20020a170907941500b00a55aee4bf74sm5432387ejc.79.2024.04.25.02.46.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 25 Apr 2024 02:46:47 -0700 (PDT) From: Andreas Hindborg To: Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Anna-Maria Behnsen , Frederic Weisbecker , Thomas Gleixner Cc: Andreas Hindborg , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Alice Ryhl , 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 Message-ID: <20240425094634.262674-1-nmi@metaspace.dk> X-Mailer: git-send-email 2.44.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Andreas Hindborg 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 --- 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, +//! flag: AtomicBool, +//! } +//! +//! impl IntrusiveTimer { +//! fn new() -> impl PinInit { +//! pin_init!(Self { +//! timer <- Timer::new(), +//! flag: AtomicBool::new(false), +//! }) +//! } +//! } +//! +//! impl TimerCallback for IntrusiveTimer { +//! type Receiver = 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 = 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 { + #[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 operate 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 { + 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 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()); + } + } +} + +/// 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`], [`Arc`], [`ARef`] where `T` +/// has a field of type `Timer`. +/// +/// Target must be [`Sync`] because timer callbacks happen in another thread 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 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 { + /// 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`] pointed + /// to by `ptr`. + /// + /// # Safety + /// + /// `ptr` must point to a [`Timer`] field in a struct of type `Self`. + 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 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 RawTimer for Arc +where + T: Send + Sync, + T: HasTimer, +{ + 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::(); + + // 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 kernel::hrtimer::RawTimerCallback for Arc +where + T: Send + Sync, + T: HasTimer, + T: TimerCallback, +{ + unsafe extern "C" fn run(ptr: *mut bindings::hrtimer) -> bindings::hrtimer_restart { + // `Timer` is `repr(transparent)` + let timer_ptr = 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 = 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`] 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