Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

rust: Add read_poll_timeout function

Add read_poll_timeout function which polls periodically until a
condition is met, an error occurs, or the timeout is reached.

The C's read_poll_timeout (include/linux/iopoll.h) is a complicated
macro and a simple wrapper for Rust doesn't work. So this implements
the same functionality in Rust.

The C version uses usleep_range() while the Rust version uses
fsleep(), which uses the best sleep method so it works with spans that
usleep_range() doesn't work nicely with.

The sleep_before_read argument isn't supported since there is no user
for now. It's rarely used in the C version.

Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Reviewed-by: Fiona Behrens <me@kloenk.dev>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Tested-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Tested-by: Daniel Almeida <daniel.almeida@collabora.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
Link: https://lore.kernel.org/r/20250821002055.3654160-3-fujita.tomonori@gmail.com
[ Fix a minor typo and add missing backticks. - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>

authored by

FUJITA Tomonori and committed by
Danilo Krummrich
349a6425 842aedc3

+105
+1
rust/kernel/io.rs
··· 8 8 use crate::{bindings, build_assert, ffi::c_void}; 9 9 10 10 pub mod mem; 11 + pub mod poll; 11 12 pub mod resource; 12 13 13 14 pub use resource::Resource;
+104
rust/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 + 7 + use crate::{ 8 + error::{code::*, Result}, 9 + processor::cpu_relax, 10 + task::might_sleep, 11 + time::{delay::fsleep, Delta, Instant, Monotonic}, 12 + }; 13 + 14 + /// Polls periodically until a condition is met, an error occurs, 15 + /// or the timeout is reached. 16 + /// 17 + /// The function repeatedly executes the given operation `op` closure and 18 + /// checks its result using the condition closure `cond`. 19 + /// 20 + /// If `cond` returns `true`, the function returns successfully with 21 + /// the result of `op`. Otherwise, it waits for a duration specified 22 + /// by `sleep_delta` before executing `op` again. 23 + /// 24 + /// This process continues until either `op` returns an error, `cond` 25 + /// returns `true`, or the timeout specified by `timeout_delta` is 26 + /// reached. 27 + /// 28 + /// This function can only be used in a nonatomic context. 29 + /// 30 + /// # Errors 31 + /// 32 + /// If `op` returns an error, then that error is returned directly. 33 + /// 34 + /// If the timeout specified by `timeout_delta` is reached, then 35 + /// `Err(ETIMEDOUT)` is returned. 36 + /// 37 + /// # Examples 38 + /// 39 + /// ```no_run 40 + /// use kernel::io::{Io, poll::read_poll_timeout}; 41 + /// use kernel::time::Delta; 42 + /// 43 + /// const HW_READY: u16 = 0x01; 44 + /// 45 + /// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result<()> { 46 + /// match read_poll_timeout( 47 + /// // The `op` closure reads the value of a specific status register. 48 + /// || io.try_read16(0x1000), 49 + /// // The `cond` closure takes a reference to the value returned by `op` 50 + /// // and checks whether the hardware is ready. 51 + /// |val: &u16| *val == HW_READY, 52 + /// Delta::from_millis(50), 53 + /// Delta::from_secs(3), 54 + /// ) { 55 + /// Ok(_) => { 56 + /// // The hardware is ready. The returned value of the `op` closure 57 + /// // isn't used. 58 + /// Ok(()) 59 + /// } 60 + /// Err(e) => Err(e), 61 + /// } 62 + /// } 63 + /// ``` 64 + #[track_caller] 65 + pub fn read_poll_timeout<Op, Cond, T>( 66 + mut op: Op, 67 + mut cond: Cond, 68 + sleep_delta: Delta, 69 + timeout_delta: Delta, 70 + ) -> Result<T> 71 + where 72 + Op: FnMut() -> Result<T>, 73 + Cond: FnMut(&T) -> bool, 74 + { 75 + let start: Instant<Monotonic> = Instant::now(); 76 + 77 + // Unlike the C version, we always call `might_sleep()` unconditionally, 78 + // as conditional calls are error-prone. We clearly separate 79 + // `read_poll_timeout()` and `read_poll_timeout_atomic()` to aid 80 + // tools like klint. 81 + might_sleep(); 82 + 83 + loop { 84 + let val = op()?; 85 + if cond(&val) { 86 + // Unlike the C version, we immediately return. 87 + // We know the condition is met so we don't need to check again. 88 + return Ok(val); 89 + } 90 + 91 + if start.elapsed() > timeout_delta { 92 + // Unlike the C version, we immediately return. 93 + // We have just called `op()` so we don't need to call it again. 94 + return Err(ETIMEDOUT); 95 + } 96 + 97 + if !sleep_delta.is_zero() { 98 + fsleep(sleep_delta); 99 + } 100 + 101 + // `fsleep()` could be a busy-wait loop so we always call `cpu_relax()`. 102 + cpu_relax(); 103 + } 104 + }