xref: /linux/rust/kernel/io/poll.rs (revision 59e6295fac26b8e85c1ea859cdd89fa1e47519d7)
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     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]
72 pub 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>
78 where
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 /// ```
159 pub 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>
165 where
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 }
184