Commit 46f045db authored by FUJITA Tomonori's avatar FUJITA Tomonori Committed by Danilo Krummrich
Browse files

rust: Add read_poll_timeout_atomic function



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.

Reviewed-by: default avatarAlice Ryhl <aliceryhl@google.com>
Signed-off-by: default avatarFUJITA Tomonori <fujita.tomonori@gmail.com>
Link: https://patch.msgid.link/20251103112958.2961517-3-fujita.tomonori@gmail.com


[ Adjust imports to use "kernel vertical" style. - Danilo ]
Signed-off-by: default avatarDanilo Krummrich <dakr@kernel.org>
parent ad68b55a
Loading
Loading
Loading
Loading
+76 −1
Original line number Diff line number Diff line
@@ -8,7 +8,15 @@
    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,
@@ -96,3 +104,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)
}