xref: /linux/rust/kernel/io/poll.rs (revision 349a64256534aa2c73787b22f7bc0517a211cdab)
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 
7 use 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]
65 pub 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>
71 where
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 }
105