xref: /linux/rust/kernel/io/poll.rs (revision 416f99c3b16f582a3fc6d64a1f77f39d94b76de5)
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::{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]
read_poll_timeout<Op, Cond, T>( mut op: Op, mut cond: Cond, sleep_delta: Delta, timeout_delta: Delta, ) -> Result<T> where Op: FnMut() -> Result<T>, Cond: FnMut(&T) -> bool,67 pub 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>
73 where
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 /// ```
read_poll_timeout_atomic<Op, Cond, T>( mut op: Op, mut cond: Cond, delay_delta: Delta, retry: usize, ) -> Result<T> where Op: FnMut() -> Result<T>, Cond: FnMut(&T) -> bool,149 pub 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>
155 where
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 }
174