kernel/io/
poll.rs

1// SPDX-License-Identifier: GPL-2.0
2
3//! IO polling.
4//!
5//! C header: [`include/linux/iopoll.h`](srctree/include/linux/iopoll.h).
6
7use crate::{
8    error::{code::*, Result},
9    processor::cpu_relax,
10    task::might_sleep,
11    time::{delay::fsleep, Delta, Instant, Monotonic},
12};
13
14/// Polls periodically until a condition is met, an error occurs,
15/// or the timeout is reached.
16///
17/// The function repeatedly executes the given operation `op` closure and
18/// checks its result using the condition closure `cond`.
19///
20/// If `cond` returns `true`, the function returns successfully with
21/// the result of `op`. Otherwise, it waits for a duration specified
22/// by `sleep_delta` before executing `op` again.
23///
24/// This process continues until either `op` returns an error, `cond`
25/// returns `true`, or the timeout specified by `timeout_delta` is
26/// reached.
27///
28/// This function can only be used in a nonatomic context.
29///
30/// # Errors
31///
32/// If `op` returns an error, then that error is returned directly.
33///
34/// If the timeout specified by `timeout_delta` is reached, then
35/// `Err(ETIMEDOUT)` is returned.
36///
37/// # Examples
38///
39/// ```no_run
40/// use kernel::io::{Io, poll::read_poll_timeout};
41/// use kernel::time::Delta;
42///
43/// const HW_READY: u16 = 0x01;
44///
45/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result<()> {
46///     match read_poll_timeout(
47///         // The `op` closure reads the value of a specific status register.
48///         || io.try_read16(0x1000),
49///         // The `cond` closure takes a reference to the value returned by `op`
50///         // and checks whether the hardware is ready.
51///         |val: &u16| *val == HW_READY,
52///         Delta::from_millis(50),
53///         Delta::from_secs(3),
54///     ) {
55///         Ok(_) => {
56///             // The hardware is ready. The returned value of the `op` closure
57///             // isn't used.
58///             Ok(())
59///         }
60///         Err(e) => Err(e),
61///     }
62/// }
63/// ```
64#[track_caller]
65pub fn read_poll_timeout<Op, Cond, T>(
66    mut op: Op,
67    mut cond: Cond,
68    sleep_delta: Delta,
69    timeout_delta: Delta,
70) -> Result<T>
71where
72    Op: FnMut() -> Result<T>,
73    Cond: FnMut(&T) -> bool,
74{
75    let start: Instant<Monotonic> = Instant::now();
76
77    // Unlike the C version, we always call `might_sleep()` unconditionally,
78    // as conditional calls are error-prone. We clearly separate
79    // `read_poll_timeout()` and `read_poll_timeout_atomic()` to aid
80    // tools like klint.
81    might_sleep();
82
83    loop {
84        let val = op()?;
85        if cond(&val) {
86            // Unlike the C version, we immediately return.
87            // We know the condition is met so we don't need to check again.
88            return Ok(val);
89        }
90
91        if start.elapsed() > timeout_delta {
92            // Unlike the C version, we immediately return.
93            // We have just called `op()` so we don't need to call it again.
94            return Err(ETIMEDOUT);
95        }
96
97        if !sleep_delta.is_zero() {
98            fsleep(sleep_delta);
99        }
100
101        // `fsleep()` could be a busy-wait loop so we always call `cpu_relax()`.
102        cpu_relax();
103    }
104}