1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
// SPDX-License-Identifier: GPL-2.0
//! IO polling.
//!
//! C header: [`include/linux/iopoll.h`](srctree/include/linux/iopoll.h).
use crate::{
prelude::*,
processor::cpu_relax,
task::might_sleep,
time::{
delay::{
fsleep,
udelay, //
},
Delta,
Instant,
Monotonic, //
},
};
/// Polls periodically until a condition is met, an error occurs,
/// or the timeout 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 waits for a duration specified
/// by `sleep_delta` before executing `op` again.
///
/// This process continues until either `op` returns an error, `cond`
/// returns `true`, or the timeout specified by `timeout_delta` is
/// reached.
///
/// This function can only be used in a nonatomic context.
///
/// # Errors
///
/// If `op` returns an error, then that error is returned directly.
///
/// If the timeout specified by `timeout_delta` is reached, then
/// `Err(ETIMEDOUT)` is returned.
///
/// # Examples
///
/// ```no_run
/// use kernel::io::{Io, poll::read_poll_timeout};
/// use kernel::time::Delta;
///
/// const HW_READY: u16 = 0x01;
///
/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result {
/// read_poll_timeout(
/// // 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_millis(50),
/// Delta::from_secs(3),
/// )?;
/// Ok(())
/// }
/// ```
#[track_caller]
pub fn read_poll_timeout<Op, Cond, T>(
mut op: Op,
mut cond: Cond,
sleep_delta: Delta,
timeout_delta: Delta,
) -> Result<T>
where
Op: FnMut() -> Result<T>,
Cond: FnMut(&T) -> bool,
{
let start: Instant<Monotonic> = Instant::now();
// Unlike the C version, we always call `might_sleep()` unconditionally,
// as conditional calls are error-prone. We clearly separate
// `read_poll_timeout()` and `read_poll_timeout_atomic()` to aid
// tools like klint.
might_sleep();
loop {
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 start.elapsed() > timeout_delta {
// Unlike the C version, we immediately return.
// We have just called `op()` so we don't need to call it again.
return Err(ETIMEDOUT);
}
if !sleep_delta.is_zero() {
fsleep(sleep_delta);
}
// `fsleep()` could be a busy-wait loop so we always call `cpu_relax()`.
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)
}
|