Message ID | 20230714-rust-time-v2-1-f5aed84218c4@asahilina.net |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:a6b2:0:b0:3e4:2afc:c1 with SMTP id c18csp2367235vqm; Fri, 14 Jul 2023 02:01:39 -0700 (PDT) X-Google-Smtp-Source: APBJJlHXoJ98AVlzJszIWdkS/Ftk3mCZM1eTOjZX1OeFkPbA0g9tDnFUnibYwyj22lSeXoUdPV0C X-Received: by 2002:a17:902:e54a:b0:1b8:8dab:64e8 with SMTP id n10-20020a170902e54a00b001b88dab64e8mr3468332plf.36.1689325298881; Fri, 14 Jul 2023 02:01:38 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689325298; cv=none; d=google.com; s=arc-20160816; b=RbcihYvBH6BBZxuYeX9o2InRqOmjpYiV5Tf1uJqoVDLDqufUdEkgCdDOl01izlg4HA waUHYShTOmfBCZuH1C85pWAX1OMf6B+WqFsf9ijGoT6tEJzT6NcRB3o5IHDiclUsICp8 yBv1ooT0GaTuq1LBRZIdRmlPcUqN95N95daQPn5acgkiNkRNXrxHE/Ao1n6/95vhzxog 8nt60o+bP8TxvFLBkjuNbYCg23wRHqybiyeV+e8OemWO+AuEfAwtdCZiqNTqwBdat+80 idbD4m5unVDDm8i6cHHJldN3uDfp2PJHwGKFefXK0xhsSP7BkKmKCgedIefvxgJMG/T/ KvMg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:message-id:content-transfer-encoding :mime-version:subject:date:from:dkim-signature; bh=xQY/nKKCCdMSr1dT05yeUeTeHK4wzjKBBVNPVrsUV50=; fh=Xv89svHPXPtAd5coVKODwNrGrkIgcV8LZK3820039sw=; b=hG9cf+jFlhfIpD7i/jlbSX0c/5hexq/wOGX/z5BkLHzqphscT+RIsu6Px+s/BlWkWV OjA7eqY1nANK5Ctee+PbCLmEjW9xZr1BCgfRLZ0BNfgXPonNfwCMf/sGhFRT4nZsEMYz mIBN9LJ9dgVaMiZqETqS2cih9qXou8mTAtGWNvR05UDq63//30TT7a2nFeudlzalHw6x 7yVX4raie3i+L+KpE1ow6AlpA3WqLybcyS2bI0eEUP9vSyRMQ3V5lGXyImtnJnqgIjUF hgXPeytb99Oc3gX5ccgTVZjcZhrLaNGzGAmCr2uZYO6axuTgt5fzko+pBYzXLxju4AK8 xxsw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@asahilina.net header.s=default header.b=0XXctXa8; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=asahilina.net Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id l3-20020a170902f68300b001b9e3be3504si7113344plg.579.2023.07.14.02.01.23; Fri, 14 Jul 2023 02:01:38 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@asahilina.net header.s=default header.b=0XXctXa8; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=asahilina.net Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234995AbjGNIVM (ORCPT <rfc822;tebrre53rla2o@gmail.com> + 99 others); Fri, 14 Jul 2023 04:21:12 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41018 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234619AbjGNIVL (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Fri, 14 Jul 2023 04:21:11 -0400 X-Greylist: delayed 900 seconds by postgrey-1.37 at lindbergh.monkeyblade.net; Fri, 14 Jul 2023 01:21:08 PDT Received: from mail.marcansoft.com (marcansoft.com [IPv6:2a01:298:fe:f::2]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 903539B; Fri, 14 Jul 2023 01:21:08 -0700 (PDT) Received: from [127.0.0.1] (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: linasend@asahilina.net) by mail.marcansoft.com (Postfix) with ESMTPSA id 269E95BAE8; Fri, 14 Jul 2023 07:55:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=asahilina.net; s=default; t=1689321319; bh=7J6No4Uq+18pRNDNsxGUwdq6k7ZetFTxJmVCqu0gelc=; h=From:Date:Subject:To:Cc; b=0XXctXa8kAUwqUR/n14lJzAp1QSwB4QVYeTBiOsv07KBbD6NvF6XF73BDiRjxaldA L2A7a62Y5JE8n4pAqEf4GGzbkejs/EuMTKpOIlxO6oMAxZ7d4mpT2x7jOZJXCxFb2S iIHDs92fj0sV4K48baDpfQdTTWaTKcLuQU7JwUN4uo34nWeLtCMMVxxOQeh0aouLg6 zlaXXK8eVEREW+a1ZIF7LE93+fo/vVZa0iR367TjUSSOpeisKx5Y+gq7AD0m9d5Irj WExg0duPuLNEP3ehs0DOUCo890NEr5Ql1OsX7Q0Sk5uM1btsArfvz9I/Zafk2K5uNq c7tnSZxM5TxXA== From: Asahi Lina <lina@asahilina.net> Date: Fri, 14 Jul 2023 16:55:01 +0900 Subject: [PATCH v2] rust: time: New module for timekeeping functions MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20230714-rust-time-v2-1-f5aed84218c4@asahilina.net> X-B4-Tracking: v=1; b=H4sIAFT/sGQC/6tWKk4tykwtVrJSqFYqSi3LLM7MzwNyjHQUlJIzE vPSU3UzU4B8JSMDI2MDc0MT3aLS4hLdkszcVF1Dg5QUy9QUI1NjY2MloPqCotS0zAqwWdGxtbU AYS2TUFsAAAA= To: Miguel Ojeda <ojeda@kernel.org>, Alex Gaynor <alex.gaynor@gmail.com>, Wedson Almeida Filho <wedsonaf@gmail.com>, Boqun Feng <boqun.feng@gmail.com>, Gary Guo <gary@garyguo.net>, =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= <bjorn3_gh@protonmail.com>, John Stultz <jstultz@google.com>, Thomas Gleixner <tglx@linutronix.de>, Stephen Boyd <sboyd@kernel.org>, Josh Stone <jistone@redhat.com>, Gaelan Steele <gbs@canishe.com>, Heghedus Razvan <heghedus.razvan@protonmail.com> Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, asahi@lists.linux.dev, Asahi Lina <lina@asahilina.net> X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1689321314; l=8468; i=lina@asahilina.net; s=20230221; h=from:subject:message-id; bh=7J6No4Uq+18pRNDNsxGUwdq6k7ZetFTxJmVCqu0gelc=; b=HpiQKFG0rAdsT7ooEpKBQ1fhz3+0ARMyc8DgriWfRofBJbVXrK3y3rBG0HRIM0Ja4hnOFqG8e O6Bd+SjWBAZB2M8WGX8ZawAVdoKqD+hx6KCJhJjK/5HojC5Vm5BNgDD X-Developer-Key: i=lina@asahilina.net; a=ed25519; pk=Qn8jZuOtR1m5GaiDfTrAoQ4NE1XoYVZ/wmt5YtXWFC4= X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,SPF_HELO_NONE,SPF_PASS, T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1771385964452526128 X-GMAIL-MSGID: 1771385964452526128 |
Series |
[v2] rust: time: New module for timekeeping functions
|
|
Commit Message
Asahi Lina
July 14, 2023, 7:55 a.m. UTC
This module is intended to contain functions related to kernel
timekeeping and time.
Initially, this implements an abstraction for a time Instant (analogous
to the Rust std::time::Instant) that represents an opaque instant in
time. Unlike the std Instant, this is a generic type that is bound to a
specific clock source, so that only Instants from the same clock source
can be subtracted/compared.
Then we implement the relevant clocks available to the kernel:
KernelTime (CLOCK_MONOTONIC), BootTime (CLOCK_BOOTTIME),
RealTime (CLOCK_REALTIME), and TaiTime.
Co-developed-by: Heghedus Razvan <heghedus.razvan@protonmail.com>
Signed-off-by: Asahi Lina <lina@asahilina.net>
---
Based on the feedback to v1, now we have proper type checking for kernel
time. I decided to implement marker traits for monotonic vs. wallclock
time sources, since it's useful to be able to safely implement different
semantics conditional on that, but that left me with a name conflict of
the Monotonic trait with the CLOCK_MONOTONIC / "default ktime" clock
source. I ended up calling it KernelTime since it's the most fundamental
kernel timesource, but suggestions welcome!
Heghedus: I think I need a signoff on this since this is based on the
playground demo you wrote in the feedback to v1. Can you provide that? I
can fold it into v3 (if there is one, otherwise Miguel can probably just
add it when he applies it). Thanks!
---
rust/bindings/bindings_helper.h | 2 +
rust/helpers.c | 16 +++++
rust/kernel/lib.rs | 1 +
rust/kernel/time.rs | 150 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 169 insertions(+)
---
base-commit: 06c2afb862f9da8dc5efa4b6076a0e48c3fbaaa5
change-id: 20230714-rust-time-10dd9ed25333
Thank you,
~~ Lina
Comments
Asahi Lina <lina@asahilina.net> writes: > +/// Marker trait for clock sources that represent a calendar (wall clock) > +/// relative to the UNIX epoch. > +pub trait WallTime {} What's the purpose of this trait? Perhaps it should have a method to get the UNIX epoch as an Instant? > + /// Returns the time elapsed since an earlier Instant<t>, or > + /// None if the argument is a later Instant. > + pub fn since(&self, earlier: Instant<T>) -> Option<Duration> { > + if earlier.nanoseconds > self.nanoseconds { > + None > + } else { > + // Casting to u64 and subtracting is guaranteed to give the right > + // result for all inputs, as long as the condition we checked above > + // holds. > + Some(Duration::from_nanos( > + self.nanoseconds as u64 - earlier.nanoseconds as u64, > + )) It looks like you intend to use wrapping semantics for this subtraction so that self=1,earlier=-1 results in a difference of two. In that case, you should explicitly use `.wrapping_sub` instead to convey your intent. I guess you could also use `abs_diff`, which takes two i64s and returns an u64. > +/// Contains the various clock source types available to the kernel. > +pub mod clock { > + use super::*; > + > + /// A clock representing the default kernel time source. > + /// > + /// This is `CLOCK_MONOTONIC` (though it is not the only > + /// monotonic clock) and also the default clock used by > + /// `ktime_get()` in the C API. > + /// > + /// This is like `BootTime`, but does not include time > + /// spent sleeping. > + > + pub struct KernelTime; > + > + impl Clock for KernelTime {} > + impl Monotonic for KernelTime {} > + impl Now for KernelTime { > + fn now() -> Instant<Self> { > + Instant::<Self>::new(unsafe { bindings::ktime_get() }) > + } > + } We usually add a SAFETY comment even if it is trivial. // SAFETY: Just an FFI call without any safety requirements. Alice
On 7/14/23 04:55, Asahi Lina wrote: > This module is intended to contain functions related to kernel > timekeeping and time. > > Initially, this implements an abstraction for a time Instant (analogous > to the Rust std::time::Instant) that represents an opaque instant in > time. Unlike the std Instant, this is a generic type that is bound to a > specific clock source, so that only Instants from the same clock source > can be subtracted/compared. > > Then we implement the relevant clocks available to the kernel: > KernelTime (CLOCK_MONOTONIC), BootTime (CLOCK_BOOTTIME), > RealTime (CLOCK_REALTIME), and TaiTime. > > Co-developed-by: Heghedus Razvan <heghedus.razvan@protonmail.com> > Signed-off-by: Asahi Lina <lina@asahilina.net> > --- > [...] Reviewed-by: Martin Rodriguez Reboredo <yakoyoku@gmail.com>
On Fri, Jul 14, 2023 at 04:55:01PM +0900, Asahi Lina wrote: > This module is intended to contain functions related to kernel > timekeeping and time. > > Initially, this implements an abstraction for a time Instant (analogous > to the Rust std::time::Instant) that represents an opaque instant in > time. Unlike the std Instant, this is a generic type that is bound to a > specific clock source, so that only Instants from the same clock source > can be subtracted/compared. > > Then we implement the relevant clocks available to the kernel: > KernelTime (CLOCK_MONOTONIC), BootTime (CLOCK_BOOTTIME), > RealTime (CLOCK_REALTIME), and TaiTime. > > Co-developed-by: Heghedus Razvan <heghedus.razvan@protonmail.com> > Signed-off-by: Asahi Lina <lina@asahilina.net> > --- > > Based on the feedback to v1, now we have proper type checking for kernel > time. I decided to implement marker traits for monotonic vs. wallclock > time sources, since it's useful to be able to safely implement different > semantics conditional on that, but that left me with a name conflict of > the Monotonic trait with the CLOCK_MONOTONIC / "default ktime" clock > source. I ended up calling it KernelTime since it's the most fundamental > kernel timesource, but suggestions welcome! > > Heghedus: I think I need a signoff on this since this is based on the > playground demo you wrote in the feedback to v1. Can you provide that? I > can fold it into v3 (if there is one, otherwise Miguel can probably just > add it when he applies it). Thanks! > --- > rust/bindings/bindings_helper.h | 2 + > rust/helpers.c | 16 +++++ > rust/kernel/lib.rs | 1 + > rust/kernel/time.rs | 150 ++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 169 insertions(+) > > diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h > index 3e601ce2548d..eddfdf887364 100644 > --- a/rust/bindings/bindings_helper.h > +++ b/rust/bindings/bindings_helper.h > @@ -8,9 +8,11 @@ > > #include <linux/errname.h> > #include <linux/slab.h> > +#include <linux/ktime.h> > #include <linux/refcount.h> > #include <linux/wait.h> > #include <linux/sched.h> > +#include <linux/timekeeping.h> > > /* `bindgen` gets confused at certain things. */ > const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL; > diff --git a/rust/helpers.c b/rust/helpers.c > index bb594da56137..eff092302e23 100644 > --- a/rust/helpers.c > +++ b/rust/helpers.c > @@ -26,6 +26,7 @@ > #include <linux/mutex.h> > #include <linux/spinlock.h> > #include <linux/sched/signal.h> > +#include <linux/timekeeping.h> > #include <linux/wait.h> > > __noreturn void rust_helper_BUG(void) > @@ -135,6 +136,21 @@ void rust_helper_put_task_struct(struct task_struct *t) > } > EXPORT_SYMBOL_GPL(rust_helper_put_task_struct); > > +ktime_t rust_helper_ktime_get_real(void) { > + return ktime_get_real(); > +} > +EXPORT_SYMBOL_GPL(rust_helper_ktime_get_real); > + > +ktime_t rust_helper_ktime_get_boottime(void) { > + return ktime_get_boottime(); > +} > +EXPORT_SYMBOL_GPL(rust_helper_ktime_get_boottime); > + > +ktime_t rust_helper_ktime_get_clocktai(void) { > + return ktime_get_clocktai(); > +} > +EXPORT_SYMBOL_GPL(rust_helper_ktime_get_clocktai); > + > /* > * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type > * as the Rust `usize` type, so we can use it in contexts where Rust > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs > index 85b261209977..52c91484c5d8 100644 > --- a/rust/kernel/lib.rs > +++ b/rust/kernel/lib.rs > @@ -42,6 +42,7 @@ > pub mod str; > pub mod sync; > pub mod task; > +pub mod time; > pub mod types; > > #[doc(hidden)] > diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs > new file mode 100644 > index 000000000000..f3bfeed20145 > --- /dev/null > +++ b/rust/kernel/time.rs > @@ -0,0 +1,150 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +//! Timekeeping functions. > +//! > +//! C header: [`include/linux/ktime.h`](../../../../include/linux/ktime.h) > +//! C header: [`include/linux/timekeeping.h`](../../../../include/linux/timekeeping.h) > + > +use crate::{bindings, pr_err}; > +use core::marker::PhantomData; > +use core::time::Duration; > + > +/// Represents a clock, that is, a unique time source. > +pub trait Clock: Sized {} > + > +/// A time source that can be queried for the current time. > +pub trait Now: Clock { Is there a `Clock` that we cannot get the current time? ;-) > + /// Returns the current time for this clock. > + fn now() -> Instant<Self>; > +} > + > +/// Marker trait for clock sources that are guaranteed to be monotonic. > +pub trait Monotonic {} > + > +/// Marker trait for clock sources that represent a calendar (wall clock) > +/// relative to the UNIX epoch. > +pub trait WallTime {} > + > +/// An instant in time associated with a given clock source. > +#[derive(Debug)] > +pub struct Instant<T: Clock> { > + nanoseconds: i64, > + _type: PhantomData<T>, > +} > + > +impl<T: Clock> Clone for Instant<T> { > + fn clone(&self) -> Self { > + *self > + } > +} > + > +impl<T: Clock> Copy for Instant<T> {} > + > +impl<T: Clock> Instant<T> { > + fn new(nanoseconds: i64) -> Self { > + Instant { > + nanoseconds, > + _type: PhantomData, > + } > + } > + > + /// Returns the time elapsed since an earlier Instant<t>, or > + /// None if the argument is a later Instant. > + pub fn since(&self, earlier: Instant<T>) -> Option<Duration> { > + if earlier.nanoseconds > self.nanoseconds { > + None > + } else { > + // Casting to u64 and subtracting is guaranteed to give the right > + // result for all inputs, as long as the condition we checked above > + // holds. > + Some(Duration::from_nanos( > + self.nanoseconds as u64 - earlier.nanoseconds as u64, > + )) > + } > + } > +} > + > +impl<T: Clock + Now + Monotonic> Instant<T> { > + /// Returns the time elapsed since this Instant<T>. > + /// > + /// This is guaranteed to return a positive result, since > + /// it is only implemented for monotonic clocks. > + pub fn elapsed(&self) -> Duration { > + T::now().since(*self).unwrap_or_else(|| { > + pr_err!( > + "Monotonic clock {} went backwards!", > + core::any::type_name::<T>() > + ); > + Duration::ZERO > + }) > + } > +} > + > +/// Contains the various clock source types available to the kernel. > +pub mod clock { > + use super::*; > + > + /// A clock representing the default kernel time source. > + /// > + /// This is `CLOCK_MONOTONIC` (though it is not the only > + /// monotonic clock) and also the default clock used by > + /// `ktime_get()` in the C API. > + /// > + /// This is like `BootTime`, but does not include time > + /// spent sleeping. > + Could you add one example for each `*Time` type? Not only they can show people how to use the API, but also we can use them to generate unit tests: https://lore.kernel.org/rust-for-linux/20230614180837.630180-1-ojeda@kernel.orge If you want to test your examples, you can either apply that patchset or use the rust-dev branch: https://github.com/Rust-for-Linux/linux.git rust-dev I use the following command to run tests: ./tools/testing/kunit/kunit.py run --make_options LLVM=1 --arch x86_64 --kconfig_add CONFIG_RUST=y Thanks! Regards, Boqun > + pub struct KernelTime; > + > + impl Clock for KernelTime {} > + impl Monotonic for KernelTime {} > + impl Now for KernelTime { > + fn now() -> Instant<Self> { > + Instant::<Self>::new(unsafe { bindings::ktime_get() }) > + } > + } > + > + /// A clock representing the time elapsed since boot. > + /// > + /// This is `CLOCK_MONOTONIC` (though it is not the only > + /// monotonic clock) and also the default clock used by > + /// `ktime_get()` in the C API. > + /// > + /// This is like `KernelTime`, but does include time > + /// spent sleeping. > + pub struct BootTime; > + > + impl Clock for BootTime {} > + impl Monotonic for BootTime {} > + impl Now for BootTime { > + fn now() -> Instant<Self> { > + Instant::<Self>::new(unsafe { bindings::ktime_get_boottime() }) > + } > + } > + > + /// A clock representing TAI time. > + /// > + /// This clock is not monotonic and can be changed from userspace. > + /// However, it is not affected by leap seconds. > + pub struct TaiTime; > + > + impl Clock for TaiTime {} > + impl WallTime for TaiTime {} > + impl Now for TaiTime { > + fn now() -> Instant<Self> { > + Instant::<Self>::new(unsafe { bindings::ktime_get_clocktai() }) > + } > + } > + > + /// A clock representing wall clock time. > + /// > + /// This clock is not monotonic and can be changed from userspace. > + pub struct RealTime; > + > + impl Clock for RealTime {} > + impl WallTime for RealTime {} > + impl Now for RealTime { > + fn now() -> Instant<Self> { > + Instant::<Self>::new(unsafe { bindings::ktime_get_real() }) > + } > + } > +} > > --- > base-commit: 06c2afb862f9da8dc5efa4b6076a0e48c3fbaaa5 > change-id: 20230714-rust-time-10dd9ed25333 > > Thank you, > ~~ Lina >
Lina! On Fri, Jul 14 2023 at 16:55, Asahi Lina wrote: > +ktime_t rust_helper_ktime_get_real(void) { > + return ktime_get_real(); > +} > +EXPORT_SYMBOL_GPL(rust_helper_ktime_get_real); Colour me confused. But why does this need another export? This just creates yet another layer of duct tape. If it's unsafe from the rust perspective then wrapping it into rust_"unsafe_function" does not make it any better. Why on earth can't you use the actual C interfaces diretly which are already exported? > +use crate::{bindings, pr_err}; > +use core::marker::PhantomData; > +use core::time::Duration; > + > +/// Represents a clock, that is, a unique time source. > +pub trait Clock: Sized {} > + > +/// A time source that can be queried for the current time. I doubt you can read anything else than current time from a time source. At least the C side does not provide time traveling interfaces unless the underlying hardware goes south. > +pub trait Now: Clock { > +impl<T: Clock> Instant<T> { > + fn new(nanoseconds: i64) -> Self { > + Instant { > + nanoseconds, > + _type: PhantomData, > + } > + } > + > + /// Returns the time elapsed since an earlier Instant<t>, or > + /// None if the argument is a later Instant. > + pub fn since(&self, earlier: Instant<T>) -> Option<Duration> { > + if earlier.nanoseconds > self.nanoseconds { > + None > + } else { > + // Casting to u64 and subtracting is guaranteed to give the right > + // result for all inputs, as long as the condition we checked above > + // holds. > + Some(Duration::from_nanos( > + self.nanoseconds as u64 - earlier.nanoseconds as u64, Clever, but any timestamp greater than KTIME_MAX or less than 0 for such a comparison is invalid. I'm too lazy to do the math for you.. > + )) > + } > + } > +} > + > +impl<T: Clock + Now + Monotonic> Instant<T> { > + /// Returns the time elapsed since this Instant<T>. > + /// > + /// This is guaranteed to return a positive result, since > + /// it is only implemented for monotonic clocks. > + pub fn elapsed(&self) -> Duration { > + T::now().since(*self).unwrap_or_else(|| { > + pr_err!( > + "Monotonic clock {} went backwards!", > + core::any::type_name::<T>() Can you please write this in one line? + pr_err!("Monotonic clock {} went backwards!", core::any::type_name::<T>() The above is unreadable gunk for no reason. > +/// Contains the various clock source types available to the kernel. > +pub mod clock { > + use super::*; > + > + /// A clock representing the default kernel time source. > + /// > + /// This is `CLOCK_MONOTONIC` (though it is not the only > + /// monotonic clock) and also the default clock used by > + /// `ktime_get()` in the C API. This "(though it is not the only monotonic clock)" phrase is irritating at best. CLOCK_MONOTONIC is well defined as the other CLOCK_* variants. > + /// > + /// This is like `BootTime`, but does not include time > + /// spent sleeping. > + > + pub struct KernelTime; > + > + impl Clock for KernelTime {} > + impl Monotonic for KernelTime {} > + impl Now for KernelTime { > + fn now() -> Instant<Self> { > + Instant::<Self>::new(unsafe { bindings::ktime_get() }) > + } > + } > + > + /// A clock representing the time elapsed since boot. > + /// > + /// This is `CLOCK_MONOTONIC` (though it is not the only > + /// monotonic clock) and also the default clock used by > + /// `ktime_get()` in the C API. The wonders of copy and pasta... > + /// > + /// This is like `KernelTime`, but does include time > + /// spent sleeping. Can you please expand your editors line wrap limit to the year 2023 standards? This looks like a IBM 2260 terminal. > + /// A clock representing TAI time. > + /// > + /// This clock is not monotonic and can be changed from userspace. > + /// However, it is not affected by leap seconds. I'm not impressed by this at all. Lots of copy and pasta with zero content. I don't see how this is an improvement over the admittedly lousy or non-existant kernel interface documentations. I thought Rust is set out to be better, but obviously it's just another variant of copy & pasta and sloppy wrappers with useless documentation around some admittedly not well documented, but well understood C interfaces. So the right approach to this is: 1) Extend the kernel C-API documentations first if required 2) Build your wrapper so that it can refer to the documentation which was either there already or got refined/added in #1 3) Add one clock per patch with a proper changelog and not some wholesale drop it all. It's well documented in Documentation/process/*, no? Thanks, tglx
On 7/15/23 03:17, Thomas Gleixner wrote: > On Fri, Jul 14 2023 at 16:55, Asahi Lina wrote: >> +ktime_t rust_helper_ktime_get_real(void) { >> + return ktime_get_real(); >> +} >> +EXPORT_SYMBOL_GPL(rust_helper_ktime_get_real); > > Colour me confused. But why does this need another export? > > This just creates yet another layer of duct tape. If it's unsafe from > the rust perspective then wrapping it into rust_"unsafe_function" does not > make it any better. > > Why on earth can't you use the actual C interfaces diretly which are > already exported? Currently, you can't call inline C functions directly from Rust. So we wrap them in non-inline functions for now. It's something we definitely want to fix, but it hasn't been fixed yet. Alice
On Fri, 14 Jul 2023 16:55:01 +0900 Asahi Lina <lina@asahilina.net> wrote: > This module is intended to contain functions related to kernel > timekeeping and time. > > Initially, this implements an abstraction for a time Instant (analogous > to the Rust std::time::Instant) that represents an opaque instant in > time. Unlike the std Instant, this is a generic type that is bound to a > specific clock source, so that only Instants from the same clock source > can be subtracted/compared. > > Then we implement the relevant clocks available to the kernel: > KernelTime (CLOCK_MONOTONIC), BootTime (CLOCK_BOOTTIME), > RealTime (CLOCK_REALTIME), and TaiTime. > > Co-developed-by: Heghedus Razvan <heghedus.razvan@protonmail.com> > Signed-off-by: Asahi Lina <lina@asahilina.net> The API seems much nicer. Comments inline below. > --- > > Based on the feedback to v1, now we have proper type checking for kernel > time. I decided to implement marker traits for monotonic vs. wallclock > time sources, since it's useful to be able to safely implement different > semantics conditional on that, but that left me with a name conflict of > the Monotonic trait with the CLOCK_MONOTONIC / "default ktime" clock > source. I ended up calling it KernelTime since it's the most fundamental > kernel timesource, but suggestions welcome! > > Heghedus: I think I need a signoff on this since this is based on the > playground demo you wrote in the feedback to v1. Can you provide that? I > can fold it into v3 (if there is one, otherwise Miguel can probably just > add it when he applies it). Thanks! > --- > rust/bindings/bindings_helper.h | 2 + > rust/helpers.c | 16 +++++ > rust/kernel/lib.rs | 1 + > rust/kernel/time.rs | 150 ++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 169 insertions(+) > > diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h > index 3e601ce2548d..eddfdf887364 100644 > --- a/rust/bindings/bindings_helper.h > +++ b/rust/bindings/bindings_helper.h > @@ -8,9 +8,11 @@ > > #include <linux/errname.h> > #include <linux/slab.h> > +#include <linux/ktime.h> > #include <linux/refcount.h> > #include <linux/wait.h> > #include <linux/sched.h> > +#include <linux/timekeeping.h> > > /* `bindgen` gets confused at certain things. */ > const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL; > diff --git a/rust/helpers.c b/rust/helpers.c > index bb594da56137..eff092302e23 100644 > --- a/rust/helpers.c > +++ b/rust/helpers.c > @@ -26,6 +26,7 @@ > #include <linux/mutex.h> > #include <linux/spinlock.h> > #include <linux/sched/signal.h> > +#include <linux/timekeeping.h> > #include <linux/wait.h> > > __noreturn void rust_helper_BUG(void) > @@ -135,6 +136,21 @@ void rust_helper_put_task_struct(struct task_struct *t) > } > EXPORT_SYMBOL_GPL(rust_helper_put_task_struct); > > +ktime_t rust_helper_ktime_get_real(void) { > + return ktime_get_real(); > +} > +EXPORT_SYMBOL_GPL(rust_helper_ktime_get_real); > + > +ktime_t rust_helper_ktime_get_boottime(void) { > + return ktime_get_boottime(); > +} > +EXPORT_SYMBOL_GPL(rust_helper_ktime_get_boottime); > + > +ktime_t rust_helper_ktime_get_clocktai(void) { > + return ktime_get_clocktai(); > +} > +EXPORT_SYMBOL_GPL(rust_helper_ktime_get_clocktai); > + > /* > * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type > * as the Rust `usize` type, so we can use it in contexts where Rust > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs > index 85b261209977..52c91484c5d8 100644 > --- a/rust/kernel/lib.rs > +++ b/rust/kernel/lib.rs > @@ -42,6 +42,7 @@ > pub mod str; > pub mod sync; > pub mod task; > +pub mod time; > pub mod types; > > #[doc(hidden)] > diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs > new file mode 100644 > index 000000000000..f3bfeed20145 > --- /dev/null > +++ b/rust/kernel/time.rs > @@ -0,0 +1,150 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +//! Timekeeping functions. > +//! > +//! C header: [`include/linux/ktime.h`](../../../../include/linux/ktime.h) > +//! C header: [`include/linux/timekeeping.h`](../../../../include/linux/timekeeping.h) > + > +use crate::{bindings, pr_err}; > +use core::marker::PhantomData; > +use core::time::Duration; > + > +/// Represents a clock, that is, a unique time source. > +pub trait Clock: Sized {} > + > +/// A time source that can be queried for the current time. > +pub trait Now: Clock { > + /// Returns the current time for this clock. > + fn now() -> Instant<Self>; > +} > + > +/// Marker trait for clock sources that are guaranteed to be monotonic. > +pub trait Monotonic {} > + > +/// Marker trait for clock sources that represent a calendar (wall clock) > +/// relative to the UNIX epoch. > +pub trait WallTime {} > + > +/// An instant in time associated with a given clock source. > +#[derive(Debug)] We probably want a manual Debug impl, but that could be left to a future patch. > +pub struct Instant<T: Clock> { > + nanoseconds: i64, > + _type: PhantomData<T>, > +} Do we want to pick a default type parameter? The `KernelTime` could be a good default, given that it's `ktime_get`. > + > +impl<T: Clock> Clone for Instant<T> { > + fn clone(&self) -> Self { > + *self > + } > +} > + > +impl<T: Clock> Copy for Instant<T> {} > + > +impl<T: Clock> Instant<T> { > + fn new(nanoseconds: i64) -> Self { > + Instant { > + nanoseconds, > + _type: PhantomData, > + } > + } > + > + /// Returns the time elapsed since an earlier Instant<t>, or > + /// None if the argument is a later Instant. > + pub fn since(&self, earlier: Instant<T>) -> Option<Duration> { > + if earlier.nanoseconds > self.nanoseconds { > + None > + } else { > + // Casting to u64 and subtracting is guaranteed to give the right > + // result for all inputs, as long as the condition we checked above > + // holds. > + Some(Duration::from_nanos( > + self.nanoseconds as u64 - earlier.nanoseconds as u64, `wrapping_sub` as other people have already point out. > + )) > + } > + } > +} > + > +impl<T: Clock + Now + Monotonic> Instant<T> { > + /// Returns the time elapsed since this Instant<T>. > + /// > + /// This is guaranteed to return a positive result, since > + /// it is only implemented for monotonic clocks. > + pub fn elapsed(&self) -> Duration { > + T::now().since(*self).unwrap_or_else(|| { > + pr_err!( > + "Monotonic clock {} went backwards!", > + core::any::type_name::<T>() > + ); > + Duration::ZERO > + }) > + } > +} > + > +/// Contains the various clock source types available to the kernel. > +pub mod clock { > + use super::*; > + > + /// A clock representing the default kernel time source. > + /// > + /// This is `CLOCK_MONOTONIC` (though it is not the only > + /// monotonic clock) and also the default clock used by > + /// `ktime_get()` in the C API. > + /// > + /// This is like `BootTime`, but does not include time > + /// spent sleeping. > + Extra line > + pub struct KernelTime; I feel that the struct names are a bit awkward, but couldn't think of any better suggestions... > + > + impl Clock for KernelTime {} > + impl Monotonic for KernelTime {} > + impl Now for KernelTime { > + fn now() -> Instant<Self> { > + Instant::<Self>::new(unsafe { bindings::ktime_get() }) > + } > + } > + > + /// A clock representing the time elapsed since boot. > + /// > + /// This is `CLOCK_MONOTONIC` (though it is not the only > + /// monotonic clock) and also the default clock used by > + /// `ktime_get()` in the C API. > + /// > + /// This is like `KernelTime`, but does include time > + /// spent sleeping. > + pub struct BootTime; > + > + impl Clock for BootTime {} > + impl Monotonic for BootTime {} > + impl Now for BootTime { > + fn now() -> Instant<Self> { > + Instant::<Self>::new(unsafe { bindings::ktime_get_boottime() }) > + } > + } > + > + /// A clock representing TAI time. > + /// > + /// This clock is not monotonic and can be changed from userspace. > + /// However, it is not affected by leap seconds. > + pub struct TaiTime; > + > + impl Clock for TaiTime {} > + impl WallTime for TaiTime {} > + impl Now for TaiTime { > + fn now() -> Instant<Self> { > + Instant::<Self>::new(unsafe { bindings::ktime_get_clocktai() }) > + } > + } > + > + /// A clock representing wall clock time. > + /// > + /// This clock is not monotonic and can be changed from userspace. > + pub struct RealTime; > + > + impl Clock for RealTime {} > + impl WallTime for RealTime {} > + impl Now for RealTime { > + fn now() -> Instant<Self> { > + Instant::<Self>::new(unsafe { bindings::ktime_get_real() }) > + } > + } > +} > > --- > base-commit: 06c2afb862f9da8dc5efa4b6076a0e48c3fbaaa5 > change-id: 20230714-rust-time-10dd9ed25333 > > Thank you, > ~~ Lina >
On Fri, Jul 14, 2023 at 12:05 PM Alice Ryhl <aliceryhl@google.com> wrote: > > In that case, you should explicitly use `.wrapping_sub` instead to > convey your intent. Also to prevent extra code and panics with `CONFIG_RUST_OVERFLOW_CHECKS=y`. > I guess you could also use `abs_diff`, which takes two i64s and returns > an u64. I think `abs_diff` sounds best. It introduces an extra branch in its implementation from what I see, though... However, since it is the same branch that Lina does for returning `None`, it should be getting removed. At least in a quick test, LLVM generates the same code: https://godbolt.org/z/61rafzx71 Cheers, Miguel
On 15/07/2023 10.17, Thomas Gleixner wrote: > Lina! > > On Fri, Jul 14 2023 at 16:55, Asahi Lina wrote: >> +ktime_t rust_helper_ktime_get_real(void) { >> + return ktime_get_real(); >> +} >> +EXPORT_SYMBOL_GPL(rust_helper_ktime_get_real); > > Colour me confused. But why does this need another export? Static inline functions aren't exported, so Rust can't call them. > This just creates yet another layer of duct tape. If it's unsafe from > the rust perspective then wrapping it into rust_"unsafe_function" does not > make it any better. > > Why on earth can't you use the actual C interfaces diretly which are > already exported? Because Rust isn't C and can't compile static inline C code... Bindgen very recently gained a feature to autogenerate these wrappers, but we're not on that version yet. But there's no reasonable way around the wrappers, whether they are automatically generated or not, unless someone wants to write a whole C->Rust transpiler... >> +use crate::{bindings, pr_err}; >> +use core::marker::PhantomData; >> +use core::time::Duration; >> + >> +/// Represents a clock, that is, a unique time source. >> +pub trait Clock: Sized {} >> + >> +/// A time source that can be queried for the current time. > > I doubt you can read anything else than current time from a time > source. At least the C side does not provide time traveling interfaces > unless the underlying hardware goes south. My thought was that we might have time sources (think some kind of hardware clock) which might want to use these types but cannot be queried for the current time globally (e.g. they are bound to a specific device and need state to query the time, so you can't just have a global read function with no arguments). Those would probably be declared within other subsystems or drivers, so they don't belong here, but the idea that a clock source and the ability to query it statically at any time are distinct concepts does, to enable that use case with this common API. >> +pub trait Now: Clock { >> +impl<T: Clock> Instant<T> { >> + fn new(nanoseconds: i64) -> Self { >> + Instant { >> + nanoseconds, >> + _type: PhantomData, >> + } >> + } >> + >> + /// Returns the time elapsed since an earlier Instant<t>, or >> + /// None if the argument is a later Instant. >> + pub fn since(&self, earlier: Instant<T>) -> Option<Duration> { >> + if earlier.nanoseconds > self.nanoseconds { >> + None >> + } else { >> + // Casting to u64 and subtracting is guaranteed to give the right >> + // result for all inputs, as long as the condition we checked above >> + // holds. >> + Some(Duration::from_nanos( >> + self.nanoseconds as u64 - earlier.nanoseconds as u64, > > Clever, but any timestamp greater than KTIME_MAX or less than 0 for such > a comparison is invalid. I'm too lazy to do the math for you.. This is computing a Rust Duration (which is unsigned and always positive) from the difference between two ktimes. As long as the two ktimes have the right >= relationship, the difference will always lie within the representable range of an unsigned 64-bit integer, which is itself a subset of the representable range of a Rust Duration (which is u64 seconds + u32 nanoseconds). This is trivially true, since the types have the same size and the absolute difference between two values can never exceed the number of values representable in that number of bits, regardless of whether the types are signed or unsigned. Feel free to do the math ^^ Or are you saying ktime timestamps can never be negative anyway, or that those conditions should be special-cased somehow? > >> + )) >> + } >> + } >> +} >> + >> +impl<T: Clock + Now + Monotonic> Instant<T> { >> + /// Returns the time elapsed since this Instant<T>. >> + /// >> + /// This is guaranteed to return a positive result, since >> + /// it is only implemented for monotonic clocks. >> + pub fn elapsed(&self) -> Duration { >> + T::now().since(*self).unwrap_or_else(|| { >> + pr_err!( >> + "Monotonic clock {} went backwards!", >> + core::any::type_name::<T>() > > Can you please write this in one line? > > + pr_err!("Monotonic clock {} went backwards!", core::any::type_name::<T>() > > The above is unreadable gunk for no reason. We use rustfmt style for all Rust code in the kernel, and this code follows that. See Documentation/rust/coding-guidelines.rst. >> +/// Contains the various clock source types available to the kernel. >> +pub mod clock { >> + use super::*; >> + >> + /// A clock representing the default kernel time source. >> + /// >> + /// This is `CLOCK_MONOTONIC` (though it is not the only >> + /// monotonic clock) and also the default clock used by >> + /// `ktime_get()` in the C API. > > This "(though it is not the only monotonic clock)" phrase is irritating > at best. CLOCK_MONOTONIC is well defined as the other CLOCK_* variants. The issue I ran into here is what to call this clock when "Monotonic" is both a trait that a clock can have and the name of the canonical default clock. CLOCK_BOOTTIME is also monotonic and therefore implements the Monotonic trait. That's why I called it KernelTime and why I made a point that, while this one is called CLOCK_MONOTONIC in C, it's not the *only* monotonic clock. I'm open to suggestions for the naming, I just think we might as well try to make things clear since clock selection can be a confusing thing. >> + /// >> + /// This is like `BootTime`, but does not include time >> + /// spent sleeping. >> + >> + pub struct KernelTime; >> + >> + impl Clock for KernelTime {} >> + impl Monotonic for KernelTime {} >> + impl Now for KernelTime { >> + fn now() -> Instant<Self> { >> + Instant::<Self>::new(unsafe { bindings::ktime_get() }) >> + } >> + } >> + >> + /// A clock representing the time elapsed since boot. >> + /// >> + /// This is `CLOCK_MONOTONIC` (though it is not the only >> + /// monotonic clock) and also the default clock used by >> + /// `ktime_get()` in the C API. > > The wonders of copy and pasta... Oops, sorry... I'll fix it for v2. > >> + /// >> + /// This is like `KernelTime`, but does include time >> + /// spent sleeping. > > Can you please expand your editors line wrap limit to the year 2023 > standards? This looks like a IBM 2260 terminal. I think I manually wrapped this, I can rewrap it up to the rustfmt limit for v2. >> + /// A clock representing TAI time. >> + /// >> + /// This clock is not monotonic and can be changed from userspace. >> + /// However, it is not affected by leap seconds. > > I'm not impressed by this at all. > > Lots of copy and pasta with zero content. I don't see how this is an > improvement over the admittedly lousy or non-existant kernel interface > documentations. > > I thought Rust is set out to be better, but obviously it's just another > variant of copy & pasta and sloppy wrappers with useless documentation > around some admittedly not well documented, but well understood C > interfaces. At least the API doesn't conflate all clock sources as well as intervals derived from them into a single type, like the C API does... I thought that was what we were aiming to fix here, based on the previous discussion. I can definitely improve the docs, but I don't think it's fair to call this "copy & pasta sloppy wrappers"... > So the right approach to this is: > > 1) Extend the kernel C-API documentations first if required I am not comfortable further documenting the Linux timekeeping subsystem, since I am not an expert in that area... if you think the docs should be improved across the board, I'm afraid you'll have to do that yourself first. It doesn't make any sense for me to become a timekeeping expert just to write some missing C docs. I'm trying to upstream abstractions for a whole bunch of Linux subsystems, I can't become an expert in all of them to improve the C docs just because the people writing the C code didn't document it properly themselves... it's hard enough wrapping my head around the lifetime constraints and all that stuff already (which is almost never documented), just to make sure the Rust abstraction is safe. Thankfully that one isn't an issue for timekeeping, since there are no objects with lifetimes... > 2) Build your wrapper so that it can refer to the documentation which > was either there already or got refined/added in #1 The Rust documentation needs to at least cover the Rust API. I can link to Documentation/core-api/timekeeping.rst for reference, but we still need module/function docs in the Rust code. What I can certainly do is expand on the current docs with more details from that file, to try to get things more consistent. I'll look it over for v2. > 3) Add one clock per patch with a proper changelog and not some > wholesale drop it all. The whole file is 151 lines of code... do you really want me to break it up into 5 patches where the last 4 add 10 lines each? What does that accomplish? I don't get it... it's just a bunch of clock options, it's not like there's any logic to review or actual clock implementations here. ~~ Lina
On Fri Jul 14, 2023 at 10:55 AM EEST, Asahi Lina wrote: > This module is intended to contain functions related to kernel > timekeeping and time. > > Initially, this implements an abstraction for a time Instant (analogous > to the Rust std::time::Instant) that represents an opaque instant in > time. Unlike the std Instant, this is a generic type that is bound to a > specific clock source, so that only Instants from the same clock source > can be subtracted/compared. > > Then we implement the relevant clocks available to the kernel: > KernelTime (CLOCK_MONOTONIC), BootTime (CLOCK_BOOTTIME), > RealTime (CLOCK_REALTIME), and TaiTime. > > Co-developed-by: Heghedus Razvan <heghedus.razvan@protonmail.com> > Signed-off-by: Asahi Lina <lina@asahilina.net> Signed-off-by: Heghedus Razvan <heghedus.razvan@protonmail.com> > --- > > Based on the feedback to v1, now we have proper type checking for kernel > time. I decided to implement marker traits for monotonic vs. wallclock > time sources, since it's useful to be able to safely implement different > semantics conditional on that, but that left me with a name conflict of > the Monotonic trait with the CLOCK_MONOTONIC / "default ktime" clock > source. I ended up calling it KernelTime since it's the most fundamental > kernel timesource, but suggestions welcome! > > Heghedus: I think I need a signoff on this since this is based on the > playground demo you wrote in the feedback to v1. Can you provide that? I > can fold it into v3 (if there is one, otherwise Miguel can probably just > add it when he applies it). Thanks! > --- > rust/bindings/bindings_helper.h | 2 + > rust/helpers.c | 16 +++++ > rust/kernel/lib.rs | 1 + > rust/kernel/time.rs | 150 ++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 169 insertions(+) > > diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h > index 3e601ce2548d..eddfdf887364 100644 > --- a/rust/bindings/bindings_helper.h > +++ b/rust/bindings/bindings_helper.h > @@ -8,9 +8,11 @@ > > #include <linux/errname.h> > #include <linux/slab.h> > +#include <linux/ktime.h> > #include <linux/refcount.h> > #include <linux/wait.h> > #include <linux/sched.h> > +#include <linux/timekeeping.h> > > /* `bindgen` gets confused at certain things. */ > const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL; > diff --git a/rust/helpers.c b/rust/helpers.c > index bb594da56137..eff092302e23 100644 > --- a/rust/helpers.c > +++ b/rust/helpers.c > @@ -26,6 +26,7 @@ > #include <linux/mutex.h> > #include <linux/spinlock.h> > #include <linux/sched/signal.h> > +#include <linux/timekeeping.h> > #include <linux/wait.h> > > __noreturn void rust_helper_BUG(void) > @@ -135,6 +136,21 @@ void rust_helper_put_task_struct(struct task_struct *t) > } > EXPORT_SYMBOL_GPL(rust_helper_put_task_struct); > > +ktime_t rust_helper_ktime_get_real(void) { > + return ktime_get_real(); > +} > +EXPORT_SYMBOL_GPL(rust_helper_ktime_get_real); > + > +ktime_t rust_helper_ktime_get_boottime(void) { > + return ktime_get_boottime(); > +} > +EXPORT_SYMBOL_GPL(rust_helper_ktime_get_boottime); > + > +ktime_t rust_helper_ktime_get_clocktai(void) { > + return ktime_get_clocktai(); > +} > +EXPORT_SYMBOL_GPL(rust_helper_ktime_get_clocktai); > + > /* > * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type > * as the Rust `usize` type, so we can use it in contexts where Rust > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs > index 85b261209977..52c91484c5d8 100644 > --- a/rust/kernel/lib.rs > +++ b/rust/kernel/lib.rs > @@ -42,6 +42,7 @@ > pub mod str; > pub mod sync; > pub mod task; > +pub mod time; > pub mod types; > > #[doc(hidden)] > diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs > new file mode 100644 > index 000000000000..f3bfeed20145 > --- /dev/null > +++ b/rust/kernel/time.rs > @@ -0,0 +1,150 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +//! Timekeeping functions. > +//! > +//! C header: [`include/linux/ktime.h`](../../../../include/linux/ktime.h) > +//! C header: [`include/linux/timekeeping.h`](../../../../include/linux/timekeeping.h) > + > +use crate::{bindings, pr_err}; > +use core::marker::PhantomData; > +use core::time::Duration; > + > +/// Represents a clock, that is, a unique time source. > +pub trait Clock: Sized {} > + > +/// A time source that can be queried for the current time. > +pub trait Now: Clock { > + /// Returns the current time for this clock. > + fn now() -> Instant<Self>; > +} > + > +/// Marker trait for clock sources that are guaranteed to be monotonic. > +pub trait Monotonic {} > + > +/// Marker trait for clock sources that represent a calendar (wall clock) > +/// relative to the UNIX epoch. > +pub trait WallTime {} > + > +/// An instant in time associated with a given clock source. > +#[derive(Debug)] > +pub struct Instant<T: Clock> { > + nanoseconds: i64, > + _type: PhantomData<T>, > +} > + > +impl<T: Clock> Clone for Instant<T> { > + fn clone(&self) -> Self { > + *self > + } > +} > + > +impl<T: Clock> Copy for Instant<T> {} > + > +impl<T: Clock> Instant<T> { > + fn new(nanoseconds: i64) -> Self { > + Instant { > + nanoseconds, > + _type: PhantomData, > + } > + } > + > + /// Returns the time elapsed since an earlier Instant<t>, or > + /// None if the argument is a later Instant. > + pub fn since(&self, earlier: Instant<T>) -> Option<Duration> { > + if earlier.nanoseconds > self.nanoseconds { > + None > + } else { > + // Casting to u64 and subtracting is guaranteed to give the right > + // result for all inputs, as long as the condition we checked above > + // holds. > + Some(Duration::from_nanos( > + self.nanoseconds as u64 - earlier.nanoseconds as u64, > + )) > + } > + } > +} > + > +impl<T: Clock + Now + Monotonic> Instant<T> { > + /// Returns the time elapsed since this Instant<T>. > + /// > + /// This is guaranteed to return a positive result, since > + /// it is only implemented for monotonic clocks. > + pub fn elapsed(&self) -> Duration { > + T::now().since(*self).unwrap_or_else(|| { > + pr_err!( > + "Monotonic clock {} went backwards!", > + core::any::type_name::<T>() > + ); > + Duration::ZERO > + }) > + } > +} > + > +/// Contains the various clock source types available to the kernel. > +pub mod clock { > + use super::*; > + > + /// A clock representing the default kernel time source. > + /// > + /// This is `CLOCK_MONOTONIC` (though it is not the only > + /// monotonic clock) and also the default clock used by > + /// `ktime_get()` in the C API. > + /// > + /// This is like `BootTime`, but does not include time > + /// spent sleeping. > + > + pub struct KernelTime; > + > + impl Clock for KernelTime {} > + impl Monotonic for KernelTime {} > + impl Now for KernelTime { > + fn now() -> Instant<Self> { > + Instant::<Self>::new(unsafe { bindings::ktime_get() }) > + } > + } > + > + /// A clock representing the time elapsed since boot. > + /// > + /// This is `CLOCK_MONOTONIC` (though it is not the only > + /// monotonic clock) and also the default clock used by > + /// `ktime_get()` in the C API. > + /// > + /// This is like `KernelTime`, but does include time > + /// spent sleeping. > + pub struct BootTime; > + > + impl Clock for BootTime {} > + impl Monotonic for BootTime {} > + impl Now for BootTime { > + fn now() -> Instant<Self> { > + Instant::<Self>::new(unsafe { bindings::ktime_get_boottime() }) > + } > + } > + > + /// A clock representing TAI time. > + /// > + /// This clock is not monotonic and can be changed from userspace. > + /// However, it is not affected by leap seconds. > + pub struct TaiTime; > + > + impl Clock for TaiTime {} > + impl WallTime for TaiTime {} > + impl Now for TaiTime { > + fn now() -> Instant<Self> { > + Instant::<Self>::new(unsafe { bindings::ktime_get_clocktai() }) > + } > + } > + > + /// A clock representing wall clock time. > + /// > + /// This clock is not monotonic and can be changed from userspace. > + pub struct RealTime; > + > + impl Clock for RealTime {} > + impl WallTime for RealTime {} > + impl Now for RealTime { > + fn now() -> Instant<Self> { > + Instant::<Self>::new(unsafe { bindings::ktime_get_real() }) > + } > + } > +} > > --- > base-commit: 06c2afb862f9da8dc5efa4b6076a0e48c3fbaaa5 > change-id: 20230714-rust-time-10dd9ed25333 > > Thank you, > ~~ Lina
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 3e601ce2548d..eddfdf887364 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -8,9 +8,11 @@ #include <linux/errname.h> #include <linux/slab.h> +#include <linux/ktime.h> #include <linux/refcount.h> #include <linux/wait.h> #include <linux/sched.h> +#include <linux/timekeeping.h> /* `bindgen` gets confused at certain things. */ const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL; diff --git a/rust/helpers.c b/rust/helpers.c index bb594da56137..eff092302e23 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -26,6 +26,7 @@ #include <linux/mutex.h> #include <linux/spinlock.h> #include <linux/sched/signal.h> +#include <linux/timekeeping.h> #include <linux/wait.h> __noreturn void rust_helper_BUG(void) @@ -135,6 +136,21 @@ void rust_helper_put_task_struct(struct task_struct *t) } EXPORT_SYMBOL_GPL(rust_helper_put_task_struct); +ktime_t rust_helper_ktime_get_real(void) { + return ktime_get_real(); +} +EXPORT_SYMBOL_GPL(rust_helper_ktime_get_real); + +ktime_t rust_helper_ktime_get_boottime(void) { + return ktime_get_boottime(); +} +EXPORT_SYMBOL_GPL(rust_helper_ktime_get_boottime); + +ktime_t rust_helper_ktime_get_clocktai(void) { + return ktime_get_clocktai(); +} +EXPORT_SYMBOL_GPL(rust_helper_ktime_get_clocktai); + /* * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type * as the Rust `usize` type, so we can use it in contexts where Rust diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 85b261209977..52c91484c5d8 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -42,6 +42,7 @@ pub mod str; pub mod sync; pub mod task; +pub mod time; pub mod types; #[doc(hidden)] diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs new file mode 100644 index 000000000000..f3bfeed20145 --- /dev/null +++ b/rust/kernel/time.rs @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Timekeeping functions. +//! +//! C header: [`include/linux/ktime.h`](../../../../include/linux/ktime.h) +//! C header: [`include/linux/timekeeping.h`](../../../../include/linux/timekeeping.h) + +use crate::{bindings, pr_err}; +use core::marker::PhantomData; +use core::time::Duration; + +/// Represents a clock, that is, a unique time source. +pub trait Clock: Sized {} + +/// A time source that can be queried for the current time. +pub trait Now: Clock { + /// Returns the current time for this clock. + fn now() -> Instant<Self>; +} + +/// Marker trait for clock sources that are guaranteed to be monotonic. +pub trait Monotonic {} + +/// Marker trait for clock sources that represent a calendar (wall clock) +/// relative to the UNIX epoch. +pub trait WallTime {} + +/// An instant in time associated with a given clock source. +#[derive(Debug)] +pub struct Instant<T: Clock> { + nanoseconds: i64, + _type: PhantomData<T>, +} + +impl<T: Clock> Clone for Instant<T> { + fn clone(&self) -> Self { + *self + } +} + +impl<T: Clock> Copy for Instant<T> {} + +impl<T: Clock> Instant<T> { + fn new(nanoseconds: i64) -> Self { + Instant { + nanoseconds, + _type: PhantomData, + } + } + + /// Returns the time elapsed since an earlier Instant<t>, or + /// None if the argument is a later Instant. + pub fn since(&self, earlier: Instant<T>) -> Option<Duration> { + if earlier.nanoseconds > self.nanoseconds { + None + } else { + // Casting to u64 and subtracting is guaranteed to give the right + // result for all inputs, as long as the condition we checked above + // holds. + Some(Duration::from_nanos( + self.nanoseconds as u64 - earlier.nanoseconds as u64, + )) + } + } +} + +impl<T: Clock + Now + Monotonic> Instant<T> { + /// Returns the time elapsed since this Instant<T>. + /// + /// This is guaranteed to return a positive result, since + /// it is only implemented for monotonic clocks. + pub fn elapsed(&self) -> Duration { + T::now().since(*self).unwrap_or_else(|| { + pr_err!( + "Monotonic clock {} went backwards!", + core::any::type_name::<T>() + ); + Duration::ZERO + }) + } +} + +/// Contains the various clock source types available to the kernel. +pub mod clock { + use super::*; + + /// A clock representing the default kernel time source. + /// + /// This is `CLOCK_MONOTONIC` (though it is not the only + /// monotonic clock) and also the default clock used by + /// `ktime_get()` in the C API. + /// + /// This is like `BootTime`, but does not include time + /// spent sleeping. + + pub struct KernelTime; + + impl Clock for KernelTime {} + impl Monotonic for KernelTime {} + impl Now for KernelTime { + fn now() -> Instant<Self> { + Instant::<Self>::new(unsafe { bindings::ktime_get() }) + } + } + + /// A clock representing the time elapsed since boot. + /// + /// This is `CLOCK_MONOTONIC` (though it is not the only + /// monotonic clock) and also the default clock used by + /// `ktime_get()` in the C API. + /// + /// This is like `KernelTime`, but does include time + /// spent sleeping. + pub struct BootTime; + + impl Clock for BootTime {} + impl Monotonic for BootTime {} + impl Now for BootTime { + fn now() -> Instant<Self> { + Instant::<Self>::new(unsafe { bindings::ktime_get_boottime() }) + } + } + + /// A clock representing TAI time. + /// + /// This clock is not monotonic and can be changed from userspace. + /// However, it is not affected by leap seconds. + pub struct TaiTime; + + impl Clock for TaiTime {} + impl WallTime for TaiTime {} + impl Now for TaiTime { + fn now() -> Instant<Self> { + Instant::<Self>::new(unsafe { bindings::ktime_get_clocktai() }) + } + } + + /// A clock representing wall clock time. + /// + /// This clock is not monotonic and can be changed from userspace. + pub struct RealTime; + + impl Clock for RealTime {} + impl WallTime for RealTime {} + impl Now for RealTime { + fn now() -> Instant<Self> { + Instant::<Self>::new(unsafe { bindings::ktime_get_real() }) + } + } +}