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