xref: /linux/rust/kernel/io/poll.rs (revision eb3dad518e4da48ab6c6df16aa8895b8b0bd6ecf)
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 ///     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]
71 pub 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>
77 where
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 /// ```
157 pub 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>
163 where
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 }
182