From 1b7bbd5975279a1cf8d907fbc719f350031194c2 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Fri, 2 May 2025 09:45:24 +0900 Subject: rust: time: Avoid 64-bit integer division on 32-bit architectures Avoid 64-bit integer division that 32-bit architectures don't implement generally. This uses ktime_to_us() and ktime_to_ms() instead. The time abstraction needs i64 / u32 division so C's div_s64() can be used but ktime_to_us() and ktime_to_ms() provide a simpler solution for this time abstraction problem on 32-bit architectures. 32-bit ARM is the only 32-bit architecture currently supported by Rust. Using the cfg attribute, only 32-bit architectures will call ktime_to_us() and ktime_to_ms(), while the other 64-bit architectures will continue to use the current code as-is to avoid the overhead. One downside of calling the C's functions is that the as_micros/millis methods can no longer be const fn. We stick with the simpler approach unless there's a compelling need for a const fn. Suggested-by: Arnd Bergmann Suggested-by: Boqun Feng Signed-off-by: FUJITA Tomonori Reviewed-by: Andreas Hindborg Link: https://lore.kernel.org/r/20250502004524.230553-1-fujita.tomonori@gmail.com Signed-off-by: Andreas Hindborg --- rust/kernel/time.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'rust/kernel/time.rs') diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index a8089a98da9e..b0a8f3c0ba49 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -228,13 +228,31 @@ impl Delta { /// Return the smallest number of microseconds greater than or equal /// to the value in the [`Delta`]. #[inline] - pub const fn as_micros_ceil(self) -> i64 { - self.as_nanos().saturating_add(NSEC_PER_USEC - 1) / NSEC_PER_USEC + pub fn as_micros_ceil(self) -> i64 { + #[cfg(CONFIG_64BIT)] + { + self.as_nanos().saturating_add(NSEC_PER_USEC - 1) / NSEC_PER_USEC + } + + #[cfg(not(CONFIG_64BIT))] + // SAFETY: It is always safe to call `ktime_to_us()` with any value. + unsafe { + bindings::ktime_to_us(self.as_nanos().saturating_add(NSEC_PER_USEC - 1)) + } } /// Return the number of milliseconds in the [`Delta`]. #[inline] - pub const fn as_millis(self) -> i64 { - self.as_nanos() / NSEC_PER_MSEC + pub fn as_millis(self) -> i64 { + #[cfg(CONFIG_64BIT)] + { + self.as_nanos() / NSEC_PER_MSEC + } + + #[cfg(not(CONFIG_64BIT))] + // SAFETY: It is always safe to call `ktime_to_ms()` with any value. + unsafe { + bindings::ktime_to_ms(self.as_nanos()) + } } } -- cgit v1.2.3 From 1664a671be46a0b0daf5250eb124d94a5501a64c Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 10 Jun 2025 18:32:54 +0900 Subject: rust: time: Replace ClockId enum with ClockSource trait Replace the ClockId enum with a trait-based abstraction called ClockSource. This change enables expressing clock sources as types and leveraging the Rust type system to enforce clock correctness at compile time. This also sets the stage for future generic abstractions over Instant types such as Instant. Reviewed-by: Andreas Hindborg Reviewed-by: Boqun Feng Signed-off-by: FUJITA Tomonori Link: https://lore.kernel.org/r/20250610093258.3435874-2-fujita.tomonori@gmail.com Signed-off-by: Andreas Hindborg --- rust/kernel/time.rs | 147 ++++++++++++++++++++++++-------------------- rust/kernel/time/hrtimer.rs | 6 +- 2 files changed, 84 insertions(+), 69 deletions(-) (limited to 'rust/kernel/time.rs') diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index b0a8f3c0ba49..1d2600288ed1 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -49,6 +49,87 @@ pub fn msecs_to_jiffies(msecs: Msecs) -> Jiffies { unsafe { bindings::__msecs_to_jiffies(msecs) } } +/// Trait for clock sources. +/// +/// Selection of the clock source depends on the use case. In some cases the usage of a +/// particular clock is mandatory, e.g. in network protocols, filesystems. In other +/// cases the user of the clock has to decide which clock is best suited for the +/// purpose. In most scenarios clock [`Monotonic`] is the best choice as it +/// provides a accurate monotonic notion of time (leap second smearing ignored). +pub trait ClockSource { + /// The kernel clock ID associated with this clock source. + /// + /// This constant corresponds to the C side `clockid_t` value. + const ID: bindings::clockid_t; +} + +/// A monotonically increasing clock. +/// +/// A nonsettable system-wide clock that represents monotonic time since as +/// described by POSIX, "some unspecified point in the past". On Linux, that +/// point corresponds to the number of seconds that the system has been +/// running since it was booted. +/// +/// The CLOCK_MONOTONIC clock is not affected by discontinuous jumps in the +/// CLOCK_REAL (e.g., if the system administrator manually changes the +/// clock), but is affected by frequency adjustments. This clock does not +/// count time that the system is suspended. +pub struct Monotonic; + +impl ClockSource for Monotonic { + const ID: bindings::clockid_t = bindings::CLOCK_MONOTONIC as bindings::clockid_t; +} + +/// A settable system-wide clock that measures real (i.e., wall-clock) time. +/// +/// Setting this clock requires appropriate privileges. This clock is +/// affected by discontinuous jumps in the system time (e.g., if the system +/// administrator manually changes the clock), and by frequency adjustments +/// performed by NTP and similar applications via adjtime(3), adjtimex(2), +/// clock_adjtime(2), and ntp_adjtime(3). This clock normally counts the +/// number of seconds since 1970-01-01 00:00:00 Coordinated Universal Time +/// (UTC) except that it ignores leap seconds; near a leap second it may be +/// adjusted by leap second smearing to stay roughly in sync with UTC. Leap +/// second smearing applies frequency adjustments to the clock to speed up +/// or slow down the clock to account for the leap second without +/// discontinuities in the clock. If leap second smearing is not applied, +/// the clock will experience discontinuity around leap second adjustment. +pub struct RealTime; + +impl ClockSource for RealTime { + const ID: bindings::clockid_t = bindings::CLOCK_REALTIME as bindings::clockid_t; +} + +/// A monotonic that ticks while system is suspended. +/// +/// A nonsettable system-wide clock that is identical to CLOCK_MONOTONIC, +/// except that it also includes any time that the system is suspended. This +/// allows applications to get a suspend-aware monotonic clock without +/// having to deal with the complications of CLOCK_REALTIME, which may have +/// discontinuities if the time is changed using settimeofday(2) or similar. +pub struct BootTime; + +impl ClockSource for BootTime { + const ID: bindings::clockid_t = bindings::CLOCK_BOOTTIME as bindings::clockid_t; +} + +/// International Atomic Time. +/// +/// A system-wide clock derived from wall-clock time but counting leap seconds. +/// +/// This clock is coupled to CLOCK_REALTIME and will be set when CLOCK_REALTIME is +/// set, or when the offset to CLOCK_REALTIME is changed via adjtimex(2). This +/// usually happens during boot and **should** not happen during normal operations. +/// However, if NTP or another application adjusts CLOCK_REALTIME by leap second +/// smearing, this clock will not be precise during leap second smearing. +/// +/// The acronym TAI refers to International Atomic Time. +pub struct Tai; + +impl ClockSource for Tai { + const ID: bindings::clockid_t = bindings::CLOCK_TAI as bindings::clockid_t; +} + /// A specific point in time. /// /// # Invariants @@ -91,72 +172,6 @@ impl core::ops::Sub for Instant { } } -/// An identifier for a clock. Used when specifying clock sources. -/// -/// -/// Selection of the clock depends on the use case. In some cases the usage of a -/// particular clock is mandatory, e.g. in network protocols, filesystems.In other -/// cases the user of the clock has to decide which clock is best suited for the -/// purpose. In most scenarios clock [`ClockId::Monotonic`] is the best choice as it -/// provides a accurate monotonic notion of time (leap second smearing ignored). -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[repr(u32)] -pub enum ClockId { - /// A settable system-wide clock that measures real (i.e., wall-clock) time. - /// - /// Setting this clock requires appropriate privileges. This clock is - /// affected by discontinuous jumps in the system time (e.g., if the system - /// administrator manually changes the clock), and by frequency adjustments - /// performed by NTP and similar applications via adjtime(3), adjtimex(2), - /// clock_adjtime(2), and ntp_adjtime(3). This clock normally counts the - /// number of seconds since 1970-01-01 00:00:00 Coordinated Universal Time - /// (UTC) except that it ignores leap seconds; near a leap second it may be - /// adjusted by leap second smearing to stay roughly in sync with UTC. Leap - /// second smearing applies frequency adjustments to the clock to speed up - /// or slow down the clock to account for the leap second without - /// discontinuities in the clock. If leap second smearing is not applied, - /// the clock will experience discontinuity around leap second adjustment. - RealTime = bindings::CLOCK_REALTIME, - /// A monotonically increasing clock. - /// - /// A nonsettable system-wide clock that represents monotonic time since—as - /// described by POSIX—"some unspecified point in the past". On Linux, that - /// point corresponds to the number of seconds that the system has been - /// running since it was booted. - /// - /// The CLOCK_MONOTONIC clock is not affected by discontinuous jumps in the - /// CLOCK_REAL (e.g., if the system administrator manually changes the - /// clock), but is affected by frequency adjustments. This clock does not - /// count time that the system is suspended. - Monotonic = bindings::CLOCK_MONOTONIC, - /// A monotonic that ticks while system is suspended. - /// - /// A nonsettable system-wide clock that is identical to CLOCK_MONOTONIC, - /// except that it also includes any time that the system is suspended. This - /// allows applications to get a suspend-aware monotonic clock without - /// having to deal with the complications of CLOCK_REALTIME, which may have - /// discontinuities if the time is changed using settimeofday(2) or similar. - BootTime = bindings::CLOCK_BOOTTIME, - /// International Atomic Time. - /// - /// A system-wide clock derived from wall-clock time but counting leap seconds. - /// - /// This clock is coupled to CLOCK_REALTIME and will be set when CLOCK_REALTIME is - /// set, or when the offset to CLOCK_REALTIME is changed via adjtimex(2). This - /// usually happens during boot and **should** not happen during normal operations. - /// However, if NTP or another application adjusts CLOCK_REALTIME by leap second - /// smearing, this clock will not be precise during leap second smearing. - /// - /// The acronym TAI refers to International Atomic Time. - TAI = bindings::CLOCK_TAI, -} - -impl ClockId { - fn into_c(self) -> bindings::clockid_t { - self as bindings::clockid_t - } -} - /// A span of time. /// /// This struct represents a span of time, with its value stored as nanoseconds. diff --git a/rust/kernel/time/hrtimer.rs b/rust/kernel/time/hrtimer.rs index 36e1290cd079..20b87a4d65ae 100644 --- a/rust/kernel/time/hrtimer.rs +++ b/rust/kernel/time/hrtimer.rs @@ -67,7 +67,7 @@ //! A `restart` operation on a timer in the **stopped** state is equivalent to a //! `start` operation. -use super::ClockId; +use super::ClockSource; use crate::{prelude::*, types::Opaque}; use core::marker::PhantomData; use pin_init::PinInit; @@ -112,7 +112,7 @@ unsafe impl Sync for HrTimer {} impl HrTimer { /// Return an initializer for a new timer instance. - pub fn new(mode: HrTimerMode, clock: ClockId) -> impl PinInit + pub fn new(mode: HrTimerMode) -> impl PinInit where T: HrTimerCallback, { @@ -126,7 +126,7 @@ impl HrTimer { bindings::hrtimer_setup( place, Some(T::Pointer::run), - clock.into_c(), + U::ID, mode.into_c(), ); } -- cgit v1.2.3 From 768dfbfc98e26cfad45f7165a1801d188f3cbd81 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 10 Jun 2025 18:32:55 +0900 Subject: rust: time: Make Instant generic over ClockSource Refactor the Instant type to be generic over a ClockSource type parameter, enabling static enforcement of clock correctness across APIs that deal with time. Previously, the clock source was implicitly fixed (typically CLOCK_MONOTONIC), and developers had to ensure compatibility manually. This design eliminates runtime mismatches between clock sources, and enables stronger type-level guarantees throughout the timer subsystem. Reviewed-by: Andreas Hindborg Reviewed-by: Boqun Feng Signed-off-by: FUJITA Tomonori Link: https://lore.kernel.org/r/20250610093258.3435874-3-fujita.tomonori@gmail.com Signed-off-by: Andreas Hindborg --- rust/kernel/time.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'rust/kernel/time.rs') diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index 1d2600288ed1..3bc76f75bfd0 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -24,6 +24,8 @@ //! C header: [`include/linux/jiffies.h`](srctree/include/linux/jiffies.h). //! C header: [`include/linux/ktime.h`](srctree/include/linux/ktime.h). +use core::marker::PhantomData; + pub mod hrtimer; /// The number of nanoseconds per microsecond. @@ -136,12 +138,21 @@ impl ClockSource for Tai { /// /// The `inner` value is in the range from 0 to `KTIME_MAX`. #[repr(transparent)] -#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord)] -pub struct Instant { +#[derive(PartialEq, PartialOrd, Eq, Ord)] +pub struct Instant { inner: bindings::ktime_t, + _c: PhantomData, } -impl Instant { +impl Clone for Instant { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for Instant {} + +impl Instant { /// Get the current time using `CLOCK_MONOTONIC`. #[inline] pub fn now() -> Self { @@ -150,6 +161,7 @@ impl Instant { Self { // SAFETY: It is always safe to call `ktime_get()` outside of NMI context. inner: unsafe { bindings::ktime_get() }, + _c: PhantomData, } } @@ -160,12 +172,12 @@ impl Instant { } } -impl core::ops::Sub for Instant { +impl core::ops::Sub for Instant { type Output = Delta; // By the type invariant, it never overflows. #[inline] - fn sub(self, other: Instant) -> Delta { + fn sub(self, other: Instant) -> Delta { Delta { nanos: self.inner - other.inner, } -- cgit v1.2.3 From cc6d1098b4cca6ec8e659de8361457c59a90b583 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 10 Jun 2025 18:32:56 +0900 Subject: rust: time: Add ktime_get() to ClockSource trait Introduce the ktime_get() associated function to the ClockSource trait, allowing each clock source to specify how it retrieves the current time. This enables Instant::now() to be implemented generically using the type-level ClockSource abstraction. This change enhances the type safety and extensibility of timekeeping by statically associating time retrieval mechanisms with their respective clock types. It also reduces the reliance on hardcoded clock logic within Instant. Signed-off-by: FUJITA Tomonori Link: https://lore.kernel.org/r/20250610093258.3435874-4-fujita.tomonori@gmail.com Signed-off-by: Andreas Hindborg --- rust/helpers/time.c | 16 ++++++++++++++++ rust/kernel/time.rs | 32 ++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 4 deletions(-) (limited to 'rust/kernel/time.rs') diff --git a/rust/helpers/time.c b/rust/helpers/time.c index 3d31473bce08..08755db43fc2 100644 --- a/rust/helpers/time.c +++ b/rust/helpers/time.c @@ -1,6 +1,22 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include + +ktime_t rust_helper_ktime_get_real(void) +{ + return ktime_get_real(); +} + +ktime_t rust_helper_ktime_get_boottime(void) +{ + return ktime_get_boottime(); +} + +ktime_t rust_helper_ktime_get_clocktai(void) +{ + return ktime_get_clocktai(); +} s64 rust_helper_ktime_to_us(const ktime_t kt) { diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index 3bc76f75bfd0..1be5ecd814d3 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -63,6 +63,11 @@ pub trait ClockSource { /// /// This constant corresponds to the C side `clockid_t` value. const ID: bindings::clockid_t; + + /// Get the current time from the clock source. + /// + /// The function must return a value in the range from 0 to `KTIME_MAX`. + fn ktime_get() -> bindings::ktime_t; } /// A monotonically increasing clock. @@ -80,6 +85,11 @@ pub struct Monotonic; impl ClockSource for Monotonic { const ID: bindings::clockid_t = bindings::CLOCK_MONOTONIC as bindings::clockid_t; + + fn ktime_get() -> bindings::ktime_t { + // SAFETY: It is always safe to call `ktime_get()` outside of NMI context. + unsafe { bindings::ktime_get() } + } } /// A settable system-wide clock that measures real (i.e., wall-clock) time. @@ -100,6 +110,11 @@ pub struct RealTime; impl ClockSource for RealTime { const ID: bindings::clockid_t = bindings::CLOCK_REALTIME as bindings::clockid_t; + + fn ktime_get() -> bindings::ktime_t { + // SAFETY: It is always safe to call `ktime_get_real()` outside of NMI context. + unsafe { bindings::ktime_get_real() } + } } /// A monotonic that ticks while system is suspended. @@ -113,6 +128,11 @@ pub struct BootTime; impl ClockSource for BootTime { const ID: bindings::clockid_t = bindings::CLOCK_BOOTTIME as bindings::clockid_t; + + fn ktime_get() -> bindings::ktime_t { + // SAFETY: It is always safe to call `ktime_get_boottime()` outside of NMI context. + unsafe { bindings::ktime_get_boottime() } + } } /// International Atomic Time. @@ -130,6 +150,11 @@ pub struct Tai; impl ClockSource for Tai { const ID: bindings::clockid_t = bindings::CLOCK_TAI as bindings::clockid_t; + + fn ktime_get() -> bindings::ktime_t { + // SAFETY: It is always safe to call `ktime_get_tai()` outside of NMI context. + unsafe { bindings::ktime_get_clocktai() } + } } /// A specific point in time. @@ -153,14 +178,13 @@ impl Clone for Instant { impl Copy for Instant {} impl Instant { - /// Get the current time using `CLOCK_MONOTONIC`. + /// Get the current time from the clock source. #[inline] pub fn now() -> Self { - // INVARIANT: The `ktime_get()` function returns a value in the range + // INVARIANT: The `ClockSource::ktime_get()` function returns a value in the range // from 0 to `KTIME_MAX`. Self { - // SAFETY: It is always safe to call `ktime_get()` outside of NMI context. - inner: unsafe { bindings::ktime_get() }, + inner: C::ktime_get(), _c: PhantomData, } } -- cgit v1.2.3 From d9fc00dc73542eef98db74085447c57174ca290d Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 10 Jun 2025 22:28:21 +0900 Subject: rust: time: Add HrTimerExpires trait Introduce the `HrTimerExpires` trait to represent types that can be used as expiration values for high-resolution timers. Define a required method, `into_nanos()`, which returns the expiration time as a raw nanosecond value suitable for use with C's hrtimer APIs. Also extend the `HrTimerMode` to use the `HrTimerExpires` trait. This refactoring is a preparation for enabling hrtimer code to work uniformly with both absolute and relative expiration modes. Reviewed-by: Andreas Hindborg Signed-off-by: FUJITA Tomonori Link: https://lore.kernel.org/r/20250610132823.3457263-4-fujita.tomonori@gmail.com [ changed conversion method names to `as_*` - Andreas ] Signed-off-by: Andreas Hindborg --- rust/kernel/time.rs | 5 ++ rust/kernel/time/hrtimer.rs | 128 +++++++++++++++++++++++++++++++------------- 2 files changed, 97 insertions(+), 36 deletions(-) (limited to 'rust/kernel/time.rs') diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index 1be5ecd814d3..5a9ca0d3b7d4 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -194,6 +194,11 @@ impl Instant { pub fn elapsed(&self) -> Delta { Self::now() - *self } + + #[inline] + pub(crate) fn as_nanos(&self) -> i64 { + self.inner + } } impl core::ops::Sub for Instant { diff --git a/rust/kernel/time/hrtimer.rs b/rust/kernel/time/hrtimer.rs index b6322f4b860f..cae7aad6e46d 100644 --- a/rust/kernel/time/hrtimer.rs +++ b/rust/kernel/time/hrtimer.rs @@ -67,7 +67,7 @@ //! A `restart` operation on a timer in the **stopped** state is equivalent to a //! `start` operation. -use super::ClockSource; +use super::{ClockSource, Delta, Instant}; use crate::{prelude::*, types::Opaque}; use core::marker::PhantomData; use pin_init::PinInit; @@ -411,94 +411,150 @@ impl HrTimerRestart { } } +/// Time representations that can be used as expiration values in [`HrTimer`]. +pub trait HrTimerExpires { + /// Converts the expiration time into a nanosecond representation. + /// + /// This value corresponds to a raw ktime_t value, suitable for passing to kernel + /// timer functions. The interpretation (absolute vs relative) depends on the + /// associated [HrTimerMode] in use. + fn as_nanos(&self) -> i64; +} + +impl HrTimerExpires for Instant { + #[inline] + fn as_nanos(&self) -> i64 { + Instant::::as_nanos(self) + } +} + +impl HrTimerExpires for Delta { + #[inline] + fn as_nanos(&self) -> i64 { + Delta::as_nanos(*self) + } +} + /// Operational mode of [`HrTimer`]. pub trait HrTimerMode { /// The C representation of hrtimer mode. const C_MODE: bindings::hrtimer_mode; + + /// Type representing the clock source. + type Clock: ClockSource; + + /// Type representing the expiration specification (absolute or relative time). + type Expires: HrTimerExpires; } /// Timer that expires at a fixed point in time. -pub struct AbsoluteMode; +pub struct AbsoluteMode(PhantomData); -impl HrTimerMode for AbsoluteMode { +impl HrTimerMode for AbsoluteMode { const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_ABS; + + type Clock = C; + type Expires = Instant; } /// Timer that expires after a delay from now. -pub struct RelativeMode; +pub struct RelativeMode(PhantomData); -impl HrTimerMode for RelativeMode { +impl HrTimerMode for RelativeMode { const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_REL; + + type Clock = C; + type Expires = Delta; } /// Timer with absolute expiration time, pinned to its current CPU. -pub struct AbsolutePinnedMode; - -impl HrTimerMode for AbsolutePinnedMode { +pub struct AbsolutePinnedMode(PhantomData); +impl HrTimerMode for AbsolutePinnedMode { const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_ABS_PINNED; + + type Clock = C; + type Expires = Instant; } /// Timer with relative expiration time, pinned to its current CPU. -pub struct RelativePinnedMode; - -impl HrTimerMode for RelativePinnedMode { +pub struct RelativePinnedMode(PhantomData); +impl HrTimerMode for RelativePinnedMode { const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_REL_PINNED; + + type Clock = C; + type Expires = Delta; } /// Timer with absolute expiration, handled in soft irq context. -pub struct AbsoluteSoftMode; - -impl HrTimerMode for AbsoluteSoftMode { +pub struct AbsoluteSoftMode(PhantomData); +impl HrTimerMode for AbsoluteSoftMode { const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_ABS_SOFT; + + type Clock = C; + type Expires = Instant; } /// Timer with relative expiration, handled in soft irq context. -pub struct RelativeSoftMode; - -impl HrTimerMode for RelativeSoftMode { +pub struct RelativeSoftMode(PhantomData); +impl HrTimerMode for RelativeSoftMode { const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_REL_SOFT; + + type Clock = C; + type Expires = Delta; } /// Timer with absolute expiration, pinned to CPU and handled in soft irq context. -pub struct AbsolutePinnedSoftMode; - -impl HrTimerMode for AbsolutePinnedSoftMode { +pub struct AbsolutePinnedSoftMode(PhantomData); +impl HrTimerMode for AbsolutePinnedSoftMode { const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_ABS_PINNED_SOFT; -} -/// Timer with relative expiration, pinned to CPU and handled in soft irq context. -pub struct RelativePinnedSoftMode; + type Clock = C; + type Expires = Instant; +} -impl HrTimerMode for RelativePinnedSoftMode { +/// Timer with absolute expiration, pinned to CPU and handled in soft irq context. +pub struct RelativePinnedSoftMode(PhantomData); +impl HrTimerMode for RelativePinnedSoftMode { const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_REL_PINNED_SOFT; + + type Clock = C; + type Expires = Delta; } /// Timer with absolute expiration, handled in hard irq context. -pub struct AbsoluteHardMode; - -impl HrTimerMode for AbsoluteHardMode { +pub struct AbsoluteHardMode(PhantomData); +impl HrTimerMode for AbsoluteHardMode { const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_ABS_HARD; + + type Clock = C; + type Expires = Instant; } /// Timer with relative expiration, handled in hard irq context. -pub struct RelativeHardMode; - -impl HrTimerMode for RelativeHardMode { +pub struct RelativeHardMode(PhantomData); +impl HrTimerMode for RelativeHardMode { const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_REL_HARD; + + type Clock = C; + type Expires = Delta; } /// Timer with absolute expiration, pinned to CPU and handled in hard irq context. -pub struct AbsolutePinnedHardMode; - -impl HrTimerMode for AbsolutePinnedHardMode { +pub struct AbsolutePinnedHardMode(PhantomData); +impl HrTimerMode for AbsolutePinnedHardMode { const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_ABS_PINNED_HARD; + + type Clock = C; + type Expires = Instant; } /// Timer with relative expiration, pinned to CPU and handled in hard irq context. -pub struct RelativePinnedHardMode; - -impl HrTimerMode for RelativePinnedHardMode { +pub struct RelativePinnedHardMode(PhantomData); +impl HrTimerMode for RelativePinnedHardMode { const C_MODE: bindings::hrtimer_mode = bindings::hrtimer_mode_HRTIMER_MODE_REL_PINNED_HARD; + + type Clock = C; + type Expires = Delta; } /// Use to implement the [`HasHrTimer`] trait. -- cgit v1.2.3 From d4b29ddf82a458935f1bd4909b8a7a13df9d3bdc Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 17 Jun 2025 23:41:55 +0900 Subject: rust: time: Add wrapper for fsleep() function Add a wrapper for fsleep(), flexible sleep functions in include/linux/delay.h which typically deals with hardware delays. The kernel supports several sleep functions to handle various lengths of delay. This adds fsleep(), automatically chooses the best sleep method based on a duration. fsleep() can only be used in a nonatomic context. This requirement is not checked by these abstractions, but it is intended that klint [1] or a similar tool will be used to check it in the future. Link: https://rust-for-linux.com/klint [1] Reviewed-by: Gary Guo Reviewed-by: Alice Ryhl Reviewed-by: Fiona Behrens Tested-by: Daniel Almeida Reviewed-by: Andreas Hindborg Signed-off-by: FUJITA Tomonori Link: https://lore.kernel.org/r/20250617144155.3903431-3-fujita.tomonori@gmail.com Signed-off-by: Andreas Hindborg --- rust/helpers/time.c | 6 ++++++ rust/kernel/time.rs | 1 + rust/kernel/time/delay.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 rust/kernel/time/delay.rs (limited to 'rust/kernel/time.rs') diff --git a/rust/helpers/time.c b/rust/helpers/time.c index 08755db43fc2..a318e9fa4408 100644 --- a/rust/helpers/time.c +++ b/rust/helpers/time.c @@ -1,8 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 +#include #include #include +void rust_helper_fsleep(unsigned long usecs) +{ + fsleep(usecs); +} + ktime_t rust_helper_ktime_get_real(void) { return ktime_get_real(); diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index 5a9ca0d3b7d4..64c8dcf548d6 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -26,6 +26,7 @@ use core::marker::PhantomData; +pub mod delay; pub mod hrtimer; /// The number of nanoseconds per microsecond. diff --git a/rust/kernel/time/delay.rs b/rust/kernel/time/delay.rs new file mode 100644 index 000000000000..eb8838da62bc --- /dev/null +++ b/rust/kernel/time/delay.rs @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Delay and sleep primitives. +//! +//! This module contains the kernel APIs related to delay and sleep that +//! have been ported or wrapped for usage by Rust code in the kernel. +//! +//! C header: [`include/linux/delay.h`](srctree/include/linux/delay.h). + +use super::Delta; +use crate::prelude::*; + +/// Sleeps for a given duration at least. +/// +/// Equivalent to the C side [`fsleep()`], flexible sleep function, +/// which automatically chooses the best sleep method based on a duration. +/// +/// `delta` must be within `[0, i32::MAX]` microseconds; +/// otherwise, it is erroneous behavior. That is, it is considered a bug +/// to call this function with an out-of-range value, in which case the function +/// will sleep for at least the maximum value in the range and may warn +/// in the future. +/// +/// The behavior above differs from the C side [`fsleep()`] for which out-of-range +/// values mean "infinite timeout" instead. +/// +/// This function can only be used in a nonatomic context. +/// +/// [`fsleep()`]: https://docs.kernel.org/timers/delay_sleep_functions.html#c.fsleep +pub fn fsleep(delta: Delta) { + // The maximum value is set to `i32::MAX` microseconds to prevent integer + // overflow inside fsleep, which could lead to unintentional infinite sleep. + const MAX_DELTA: Delta = Delta::from_micros(i32::MAX as i64); + + let delta = if (Delta::ZERO..=MAX_DELTA).contains(&delta) { + delta + } else { + // TODO: Add WARN_ONCE() when it's supported. + MAX_DELTA + }; + + // SAFETY: It is always safe to call `fsleep()` with any duration. + unsafe { + // Convert the duration to microseconds and round up to preserve + // the guarantee; `fsleep()` sleeps for at least the provided duration, + // but that it may sleep for longer under some circumstances. + bindings::fsleep(delta.as_micros_ceil() as c_ulong) + } +} -- cgit v1.2.3