Add read_poll_count_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 | 80 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 79 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/io/poll.rs b/rust/kernel/io/poll.rs
index 613eb25047ef..c7dd0816205f 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,78 @@ 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 `count` is reached.
+///
+/// # Errors
+///
+/// If `op` returns an error, then that error is returned directly.
+///
+/// If the attempt limit specified by `count` is reached, then
+/// `Err(ETIMEDOUT)` is returned.
+///
+/// # Examples
+///
+/// ```no_run
+/// use kernel::io::{Io, poll::read_poll_count_atomic};
+/// use kernel::time::Delta;
+///
+/// const HW_READY: u16 = 0x01;
+///
+/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result {
+/// match read_poll_count_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(_) => {
+/// // The hardware is ready. The returned value of the `op` closure
+/// // isn't used.
+/// Ok(())
+/// }
+/// Err(e) => Err(e),
+/// }
+/// }
+/// ```
+pub fn read_poll_count_atomic<Op, Cond, T>(
+ mut op: Op,
+ mut cond: Cond,
+ delay_delta: Delta,
+ count: usize,
+) -> Result<T>
+where
+ Op: FnMut() -> Result<T>,
+ Cond: FnMut(&T) -> bool,
+{
+ for _ in 0..count {
+ let val = op()?;
+ if cond(&val) {
+ // Unlike the C version, we immediately return.
+ // We know the condition is met so we don't need to check again.
+ return Ok(val);
+ }
+
+ if !delay_delta.is_zero() {
+ udelay(delay_delta);
+ }
+
+ cpu_relax();
+ }
+
+ Err(ETIMEDOUT)
+}
--
2.43.0
On Tue Oct 21, 2025 at 9:11 AM CEST, FUJITA Tomonori wrote:
> +/// 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 `count` is reached.
> +///
> +/// # Errors
> +///
> +/// If `op` returns an error, then that error is returned directly.
> +///
> +/// If the attempt limit specified by `count` is reached, then
> +/// `Err(ETIMEDOUT)` is returned.
> +///
> +/// # Examples
> +///
> +/// ```no_run
> +/// use kernel::io::{Io, poll::read_poll_count_atomic};
> +/// use kernel::time::Delta;
> +///
> +/// const HW_READY: u16 = 0x01;
> +///
> +/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result {
> +/// match read_poll_count_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(_) => {
> +/// // The hardware is ready. The returned value of the `op` closure
> +/// // isn't used.
> +/// Ok(())
> +/// }
> +/// Err(e) => Err(e),
> +/// }
Please replace the match statement with map().
read_poll_count_atomic(
...
)
.map(|_| ())
> +/// }
> +/// ```
> +pub fn read_poll_count_atomic<Op, Cond, T>(
I understand why you renamed the function, but read_poll_timeout_atomic() would
still be accurate -- it does perform a timeout in every iteration. Let's keep
the original name please.
> + mut op: Op,
> + mut cond: Cond,
> + delay_delta: Delta,
> + count: usize,
Maybe retry would be a slightly better fit compared to count. If we want to be a
bit more verbose, I suggest retry_count. :)
> +) -> Result<T>
> +where
> + Op: FnMut() -> Result<T>,
> + Cond: FnMut(&T) -> bool,
> +{
> + for _ in 0..count {
> + let val = op()?;
> + if cond(&val) {
> + // Unlike the C version, we immediately return.
> + // We know the condition is met so we don't need to check again.
NIT: Just like in read_poll_timeout() I think this comment does not carry much
value, but I'm fine if you want to keep it.
> + return Ok(val);
> + }
> +
> + if !delay_delta.is_zero() {
> + udelay(delay_delta);
> + }
> +
> + cpu_relax();
> + }
> +
> + Err(ETIMEDOUT)
> +}
> --
> 2.43.0
On Tue, 21 Oct 2025 14:35:34 +0200 "Danilo Krummrich" <dakr@kernel.org> wrote: >> +pub fn read_poll_count_atomic<Op, Cond, T>( > > I understand why you renamed the function, but read_poll_timeout_atomic() would > still be accurate -- it does perform a timeout in every iteration. Let's keep > the original name please. Ok, I'll revert the name. >> + mut op: Op, >> + mut cond: Cond, >> + delay_delta: Delta, >> + count: usize, > > Maybe retry would be a slightly better fit compared to count. If we want to be a > bit more verbose, I suggest retry_count. :) Sure, I'll go with 'retry'.
On Tue, Oct 21, 2025 at 02:35:34PM +0200, Danilo Krummrich wrote:
> On Tue Oct 21, 2025 at 9:11 AM CEST, FUJITA Tomonori wrote:
> > +/// 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 `count` is reached.
> > +///
> > +/// # Errors
> > +///
> > +/// If `op` returns an error, then that error is returned directly.
> > +///
> > +/// If the attempt limit specified by `count` is reached, then
> > +/// `Err(ETIMEDOUT)` is returned.
> > +///
> > +/// # Examples
> > +///
> > +/// ```no_run
> > +/// use kernel::io::{Io, poll::read_poll_count_atomic};
> > +/// use kernel::time::Delta;
> > +///
> > +/// const HW_READY: u16 = 0x01;
> > +///
> > +/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result {
> > +/// match read_poll_count_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(_) => {
> > +/// // The hardware is ready. The returned value of the `op` closure
> > +/// // isn't used.
> > +/// Ok(())
> > +/// }
> > +/// Err(e) => Err(e),
> > +/// }
>
> Please replace the match statement with map().
>
> read_poll_count_atomic(
> ...
> )
> .map(|_| ())
>
IMO, this should instead be:
read_poll_count_atomic(
...
)?
Ok(())
Alice
"Alice Ryhl" <aliceryhl@google.com> writes:
> On Tue, Oct 21, 2025 at 02:35:34PM +0200, Danilo Krummrich wrote:
>> On Tue Oct 21, 2025 at 9:11 AM CEST, FUJITA Tomonori wrote:
>> > +/// 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 `count` is reached.
>> > +///
>> > +/// # Errors
>> > +///
>> > +/// If `op` returns an error, then that error is returned directly.
>> > +///
>> > +/// If the attempt limit specified by `count` is reached, then
>> > +/// `Err(ETIMEDOUT)` is returned.
>> > +///
>> > +/// # Examples
>> > +///
>> > +/// ```no_run
>> > +/// use kernel::io::{Io, poll::read_poll_count_atomic};
>> > +/// use kernel::time::Delta;
>> > +///
>> > +/// const HW_READY: u16 = 0x01;
>> > +///
>> > +/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result {
>> > +/// match read_poll_count_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(_) => {
>> > +/// // The hardware is ready. The returned value of the `op` closure
>> > +/// // isn't used.
>> > +/// Ok(())
>> > +/// }
>> > +/// Err(e) => Err(e),
>> > +/// }
>>
>> Please replace the match statement with map().
>>
>> read_poll_count_atomic(
>> ...
>> )
>> .map(|_| ())
>>
>
> IMO, this should instead be:
>
> read_poll_count_atomic(
> ...
> )?
> Ok(())
It does not really matter to me. Why do you prefer one to the other?
Best regards,
Andreas Hindborg
On Fri, Oct 24, 2025 at 10:25:05AM +0200, Andreas Hindborg wrote:
> "Alice Ryhl" <aliceryhl@google.com> writes:
>
> > On Tue, Oct 21, 2025 at 02:35:34PM +0200, Danilo Krummrich wrote:
> >> On Tue Oct 21, 2025 at 9:11 AM CEST, FUJITA Tomonori wrote:
> >> > +/// 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 `count` is reached.
> >> > +///
> >> > +/// # Errors
> >> > +///
> >> > +/// If `op` returns an error, then that error is returned directly.
> >> > +///
> >> > +/// If the attempt limit specified by `count` is reached, then
> >> > +/// `Err(ETIMEDOUT)` is returned.
> >> > +///
> >> > +/// # Examples
> >> > +///
> >> > +/// ```no_run
> >> > +/// use kernel::io::{Io, poll::read_poll_count_atomic};
> >> > +/// use kernel::time::Delta;
> >> > +///
> >> > +/// const HW_READY: u16 = 0x01;
> >> > +///
> >> > +/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result {
> >> > +/// match read_poll_count_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(_) => {
> >> > +/// // The hardware is ready. The returned value of the `op` closure
> >> > +/// // isn't used.
> >> > +/// Ok(())
> >> > +/// }
> >> > +/// Err(e) => Err(e),
> >> > +/// }
> >>
> >> Please replace the match statement with map().
> >>
> >> read_poll_count_atomic(
> >> ...
> >> )
> >> .map(|_| ())
> >>
> >
> > IMO, this should instead be:
> >
> > read_poll_count_atomic(
> > ...
> > )?
> > Ok(())
>
> It does not really matter to me. Why do you prefer one to the other?
I think it's simpler.
Alice
On 10/21/25 4:05 PM, Alice Ryhl wrote: > On Tue, Oct 21, 2025 at 02:35:34PM +0200, Danilo Krummrich wrote: >> Please replace the match statement with map(). >> >> read_poll_count_atomic( >> ... >> ) >> .map(|_| ()) >> > > IMO, this should instead be: > > read_poll_count_atomic( > ... > )? > Ok(()) I think map() has the advantage that it is a bit more explicit about the fact that the return value is discarded intentionally. But I'm fine with either version.
On Tue, 21 Oct 2025 18:02:39 +0200 Danilo Krummrich <dakr@kernel.org> wrote: > On 10/21/25 4:05 PM, Alice Ryhl wrote: >> On Tue, Oct 21, 2025 at 02:35:34PM +0200, Danilo Krummrich wrote: >>> Please replace the match statement with map(). >>> >>> read_poll_count_atomic( >>> ... >>> ) >>> .map(|_| ()) >>> >> >> IMO, this should instead be: >> >> read_poll_count_atomic( >> ... >> )? >> Ok(()) > > I think map() has the advantage that it is a bit more explicit about the fact > that the return value is discarded intentionally. > > But I'm fine with either version. Then I'll go with 'Ok'. I'll send to a different patch to update read_poll_wait's example for the same change.
© 2016 - 2026 Red Hat, Inc.