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