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