rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/2] Add read_poll_timeout_atomic support
@ 2025-10-26 12:54 FUJITA Tomonori
  2025-10-26 12:54 ` [PATCH v3 1/2] rust: add udelay() function FUJITA Tomonori
  2025-10-26 12:54 ` [PATCH v3 2/2] rust: Add read_poll_timeout_atomic function FUJITA Tomonori
  0 siblings, 2 replies; 6+ messages in thread
From: FUJITA Tomonori @ 2025-10-26 12:54 UTC (permalink / raw)
  To: a.hindborg, alex.gaynor, aliceryhl, dakr, daniel.almeida, ojeda
  Cc: anna-maria, bjorn3_gh, boqun.feng, frederic, gary, jstultz,
	linux-kernel, lossin, lyude, rust-for-linux, sboyd, tglx, tmgross

Add read_poll_timeout_atomic function which polls periodically until a
condition is met, an error occurs, or the attempt limit is reached.

This helper is used to wait for a condition in atomic context,
mirroring the C's read_poll_timeout_atomic().

In atomic context, the timekeeping infrastructure is unavailable, so
reliable time-based timeouts cannot be implemented. So instead, the
helper accepts a maximum number of attempts and busy-waits (udelay +
cpu_relax) between tries.


v2: https://lore.kernel.org/lkml/20251021071146.2357069-1-fujita.tomonori@gmail.com/
- revert the function name
- simplify the example code
- add debug_assert to check the range for udelay
v1: https://lore.kernel.org/lkml/20250821035710.3692455-1-fujita.tomonori@gmail.com/
- use the attempt limit instead of timeout
- rename the function to read_poll_count_atomic
- add the comment about C's udelay behavior.


FUJITA Tomonori (2):
  rust: add udelay() function
  rust: Add read_poll_timeout_atomic function

 rust/helpers/time.c       |  5 +++
 rust/kernel/io/poll.rs    | 72 ++++++++++++++++++++++++++++++++++++++-
 rust/kernel/time/delay.rs | 39 +++++++++++++++++++++
 3 files changed, 115 insertions(+), 1 deletion(-)


base-commit: 26c1a20bf7ce76e9afe4030f25bec20e3c63dcf8
-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v3 1/2] rust: add udelay() function
  2025-10-26 12:54 [PATCH v3 0/2] Add read_poll_timeout_atomic support FUJITA Tomonori
@ 2025-10-26 12:54 ` FUJITA Tomonori
  2025-10-27  9:48   ` Andreas Hindborg
  2025-10-28  9:55   ` Alice Ryhl
  2025-10-26 12:54 ` [PATCH v3 2/2] rust: Add read_poll_timeout_atomic function FUJITA Tomonori
  1 sibling, 2 replies; 6+ messages in thread
From: FUJITA Tomonori @ 2025-10-26 12:54 UTC (permalink / raw)
  To: a.hindborg, alex.gaynor, aliceryhl, dakr, daniel.almeida, ojeda
  Cc: anna-maria, bjorn3_gh, boqun.feng, frederic, gary, jstultz,
	linux-kernel, lossin, lyude, rust-for-linux, sboyd, tglx, tmgross

Add udelay() function, inserts a delay based on microseconds with busy
waiting, in preparation for supporting read_poll_timeout_atomic().

Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
 rust/helpers/time.c       |  5 +++++
 rust/kernel/time/delay.rs | 39 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 44 insertions(+)

diff --git a/rust/helpers/time.c b/rust/helpers/time.c
index a318e9fa4408..67a36ccc3ec4 100644
--- a/rust/helpers/time.c
+++ b/rust/helpers/time.c
@@ -33,3 +33,8 @@ s64 rust_helper_ktime_to_ms(const ktime_t kt)
 {
 	return ktime_to_ms(kt);
 }
+
+void rust_helper_udelay(unsigned long usec)
+{
+	udelay(usec);
+}
diff --git a/rust/kernel/time/delay.rs b/rust/kernel/time/delay.rs
index eb8838da62bc..0739b75fb9c3 100644
--- a/rust/kernel/time/delay.rs
+++ b/rust/kernel/time/delay.rs
@@ -47,3 +47,42 @@ pub fn fsleep(delta: Delta) {
         bindings::fsleep(delta.as_micros_ceil() as c_ulong)
     }
 }
+
+/// Inserts a delay based on microseconds with busy waiting.
+///
+/// Equivalent to the C side [`udelay()`], which delays in microseconds.
+///
+/// `delta` must be within `[0, `MAX_UDELAY_MS`]` in milliseconds;
+/// 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 insert a delay for at least the maximum value in the range and
+/// may warn in the future.
+///
+/// The behavior above differs from the C side [`udelay()`] for which out-of-range
+/// values could lead to an overflow and unexpected behavior.
+///
+/// [`udelay()`]: https://docs.kernel.org/timers/delay_sleep_functions.html#c.udelay
+pub fn udelay(delta: Delta) {
+    const MAX_UDELAY_DELTA: Delta = Delta::from_millis(bindings::MAX_UDELAY_MS as i64);
+
+    debug_assert!(delta.as_nanos() >= 0);
+    debug_assert!(delta <= MAX_UDELAY_DELTA);
+
+    let delta = if (Delta::ZERO..=MAX_UDELAY_DELTA).contains(&delta) {
+        delta
+    } else {
+        MAX_UDELAY_DELTA
+    };
+
+    // SAFETY: It is always safe to call `udelay()` with any duration.
+    // Note that the kernel is compiled with `-fno-strict-overflow`
+    // so any out-of-range value could lead to unexpected behavior
+    // but won't lead to undefined behavior.
+    unsafe {
+        // Convert the duration to microseconds and round up to preserve
+        // the guarantee; `udelay()` inserts a delay for at least
+        // the provided duration, but that it may delay for longer
+        // under some circumstances.
+        bindings::udelay(delta.as_micros_ceil() as c_ulong)
+    }
+}
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v3 2/2] rust: Add read_poll_timeout_atomic function
  2025-10-26 12:54 [PATCH v3 0/2] Add read_poll_timeout_atomic support FUJITA Tomonori
  2025-10-26 12:54 ` [PATCH v3 1/2] rust: add udelay() function FUJITA Tomonori
@ 2025-10-26 12:54 ` FUJITA Tomonori
  2025-10-28  9:56   ` Alice Ryhl
  1 sibling, 1 reply; 6+ messages in thread
From: FUJITA Tomonori @ 2025-10-26 12:54 UTC (permalink / raw)
  To: a.hindborg, alex.gaynor, aliceryhl, dakr, daniel.almeida, ojeda
  Cc: anna-maria, bjorn3_gh, boqun.feng, frederic, gary, jstultz,
	linux-kernel, lossin, lyude, rust-for-linux, sboyd, tglx, tmgross

Add read_poll_timeout_atomic function which polls periodically until a
condition is met, an error occurs, or the attempt limit is reached.

The C's read_poll_timeout_atomic() is used for the similar purpose.
In atomic context the timekeeping infrastructure is unavailable, so
reliable time-based timeouts cannot be implemented. So instead, the
helper accepts a maximum number of attempts and busy-waits (udelay +
cpu_relax) between tries.

Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
 rust/kernel/io/poll.rs | 72 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 71 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/io/poll.rs b/rust/kernel/io/poll.rs
index 613eb25047ef..65c8ec3fb683 100644
--- a/rust/kernel/io/poll.rs
+++ b/rust/kernel/io/poll.rs
@@ -8,7 +8,10 @@
     error::{code::*, Result},
     processor::cpu_relax,
     task::might_sleep,
-    time::{delay::fsleep, Delta, Instant, Monotonic},
+    time::{
+        delay::{fsleep, udelay},
+        Delta, Instant, Monotonic,
+    },
 };
 
 /// Polls periodically until a condition is met, an error occurs,
@@ -102,3 +105,70 @@ pub fn read_poll_timeout<Op, Cond, T>(
         cpu_relax();
     }
 }
+
+/// Polls periodically until a condition is met, an error occurs,
+/// or the attempt limit is reached.
+///
+/// The function repeatedly executes the given operation `op` closure and
+/// checks its result using the condition closure `cond`.
+///
+/// If `cond` returns `true`, the function returns successfully with the result of `op`.
+/// Otherwise, it performs a busy wait for a duration specified by `delay_delta`
+/// before executing `op` again.
+///
+/// This process continues until either `op` returns an error, `cond`
+/// returns `true`, or the attempt limit specified by `retry` is reached.
+///
+/// # Errors
+///
+/// If `op` returns an error, then that error is returned directly.
+///
+/// If the attempt limit specified by `retry` is reached, then
+/// `Err(ETIMEDOUT)` is returned.
+///
+/// # Examples
+///
+/// ```no_run
+/// use kernel::io::{poll::read_poll_timeout_atomic, Io};
+/// use kernel::time::Delta;
+///
+/// const HW_READY: u16 = 0x01;
+///
+/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result {
+///     read_poll_timeout_atomic(
+///         // The `op` closure reads the value of a specific status register.
+///         || io.try_read16(0x1000),
+///         // The `cond` closure takes a reference to the value returned by `op`
+///         // and checks whether the hardware is ready.
+///         |val: &u16| *val == HW_READY,
+///         Delta::from_micros(50),
+///         1000,
+///     )?;
+///     Ok(())
+/// }
+/// ```
+pub fn read_poll_timeout_atomic<Op, Cond, T>(
+    mut op: Op,
+    mut cond: Cond,
+    delay_delta: Delta,
+    retry: usize,
+) -> Result<T>
+where
+    Op: FnMut() -> Result<T>,
+    Cond: FnMut(&T) -> bool,
+{
+    for _ in 0..retry {
+        let val = op()?;
+        if cond(&val) {
+            return Ok(val);
+        }
+
+        if !delay_delta.is_zero() {
+            udelay(delay_delta);
+        }
+
+        cpu_relax();
+    }
+
+    Err(ETIMEDOUT)
+}
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 1/2] rust: add udelay() function
  2025-10-26 12:54 ` [PATCH v3 1/2] rust: add udelay() function FUJITA Tomonori
@ 2025-10-27  9:48   ` Andreas Hindborg
  2025-10-28  9:55   ` Alice Ryhl
  1 sibling, 0 replies; 6+ messages in thread
From: Andreas Hindborg @ 2025-10-27  9:48 UTC (permalink / raw)
  To: FUJITA Tomonori, alex.gaynor, aliceryhl, dakr, daniel.almeida,
	ojeda
  Cc: anna-maria, bjorn3_gh, boqun.feng, frederic, gary, jstultz,
	linux-kernel, lossin, lyude, rust-for-linux, sboyd, tglx, tmgross

"FUJITA Tomonori" <fujita.tomonori@gmail.com> writes:

> Add udelay() function, inserts a delay based on microseconds with busy
> waiting, in preparation for supporting read_poll_timeout_atomic().
>
> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>


Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>


Best regards,
Andreas Hindborg




^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 1/2] rust: add udelay() function
  2025-10-26 12:54 ` [PATCH v3 1/2] rust: add udelay() function FUJITA Tomonori
  2025-10-27  9:48   ` Andreas Hindborg
@ 2025-10-28  9:55   ` Alice Ryhl
  1 sibling, 0 replies; 6+ messages in thread
From: Alice Ryhl @ 2025-10-28  9:55 UTC (permalink / raw)
  To: FUJITA Tomonori
  Cc: a.hindborg, alex.gaynor, dakr, daniel.almeida, ojeda, anna-maria,
	bjorn3_gh, boqun.feng, frederic, gary, jstultz, linux-kernel,
	lossin, lyude, rust-for-linux, sboyd, tglx, tmgross

On Sun, Oct 26, 2025 at 09:54:57PM +0900, FUJITA Tomonori wrote:
> Add udelay() function, inserts a delay based on microseconds with busy
> waiting, in preparation for supporting read_poll_timeout_atomic().
> 
> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>

With below nits addressed:
Reviewed-by: Alice Ryhl <aliceryhl@google.com>

> ---
>  rust/helpers/time.c       |  5 +++++
>  rust/kernel/time/delay.rs | 39 +++++++++++++++++++++++++++++++++++++++
>  2 files changed, 44 insertions(+)
> 
> diff --git a/rust/helpers/time.c b/rust/helpers/time.c
> index a318e9fa4408..67a36ccc3ec4 100644
> --- a/rust/helpers/time.c
> +++ b/rust/helpers/time.c
> @@ -33,3 +33,8 @@ s64 rust_helper_ktime_to_ms(const ktime_t kt)
>  {
>  	return ktime_to_ms(kt);
>  }
> +
> +void rust_helper_udelay(unsigned long usec)
> +{
> +	udelay(usec);
> +}
> diff --git a/rust/kernel/time/delay.rs b/rust/kernel/time/delay.rs
> index eb8838da62bc..0739b75fb9c3 100644
> --- a/rust/kernel/time/delay.rs
> +++ b/rust/kernel/time/delay.rs
> @@ -47,3 +47,42 @@ pub fn fsleep(delta: Delta) {
>          bindings::fsleep(delta.as_micros_ceil() as c_ulong)
>      }
>  }
> +
> +/// Inserts a delay based on microseconds with busy waiting.
> +///
> +/// Equivalent to the C side [`udelay()`], which delays in microseconds.
> +///
> +/// `delta` must be within `[0, `MAX_UDELAY_MS`]` in milliseconds;

We can nest backticks. This should be:

	`[0, MAX_UDELAY_MS]`

> +/// 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 insert a delay for at least the maximum value in the range and
> +/// may warn in the future.

There's a debug assertion now so I would remove the "maxmimu value"
part.

> +/// The behavior above differs from the C side [`udelay()`] for which out-of-range
> +/// values could lead to an overflow and unexpected behavior.
> +///
> +/// [`udelay()`]: https://docs.kernel.org/timers/delay_sleep_functions.html#c.udelay
> +pub fn udelay(delta: Delta) {
> +    const MAX_UDELAY_DELTA: Delta = Delta::from_millis(bindings::MAX_UDELAY_MS as i64);
> +
> +    debug_assert!(delta.as_nanos() >= 0);
> +    debug_assert!(delta <= MAX_UDELAY_DELTA);
> +
> +    let delta = if (Delta::ZERO..=MAX_UDELAY_DELTA).contains(&delta) {
> +        delta
> +    } else {
> +        MAX_UDELAY_DELTA
> +    };
> +
> +    // SAFETY: It is always safe to call `udelay()` with any duration.
> +    // Note that the kernel is compiled with `-fno-strict-overflow`
> +    // so any out-of-range value could lead to unexpected behavior
> +    // but won't lead to undefined behavior.
> +    unsafe {
> +        // Convert the duration to microseconds and round up to preserve
> +        // the guarantee; `udelay()` inserts a delay for at least
> +        // the provided duration, but that it may delay for longer
> +        // under some circumstances.
> +        bindings::udelay(delta.as_micros_ceil() as c_ulong)
> +    }
> +}
> -- 
> 2.43.0
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 2/2] rust: Add read_poll_timeout_atomic function
  2025-10-26 12:54 ` [PATCH v3 2/2] rust: Add read_poll_timeout_atomic function FUJITA Tomonori
@ 2025-10-28  9:56   ` Alice Ryhl
  0 siblings, 0 replies; 6+ messages in thread
From: Alice Ryhl @ 2025-10-28  9:56 UTC (permalink / raw)
  To: FUJITA Tomonori
  Cc: a.hindborg, alex.gaynor, dakr, daniel.almeida, ojeda, anna-maria,
	bjorn3_gh, boqun.feng, frederic, gary, jstultz, linux-kernel,
	lossin, lyude, rust-for-linux, sboyd, tglx, tmgross

On Sun, Oct 26, 2025 at 09:54:58PM +0900, FUJITA Tomonori wrote:
> Add read_poll_timeout_atomic function which polls periodically until a
> condition is met, an error occurs, or the attempt limit is reached.
> 
> The C's read_poll_timeout_atomic() is used for the similar purpose.
> In atomic context the timekeeping infrastructure is unavailable, so
> reliable time-based timeouts cannot be implemented. So instead, the
> helper accepts a maximum number of attempts and busy-waits (udelay +
> cpu_relax) between tries.
> 
> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>

Reviewed-by: Alice Ryhl <aliceryhl@google.com>

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2025-10-28  9:56 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-26 12:54 [PATCH v3 0/2] Add read_poll_timeout_atomic support FUJITA Tomonori
2025-10-26 12:54 ` [PATCH v3 1/2] rust: add udelay() function FUJITA Tomonori
2025-10-27  9:48   ` Andreas Hindborg
2025-10-28  9:55   ` Alice Ryhl
2025-10-26 12:54 ` [PATCH v3 2/2] rust: Add read_poll_timeout_atomic function FUJITA Tomonori
2025-10-28  9:56   ` Alice Ryhl

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).