* Re: [PATCH v1 2/2] rust: Add read_poll_timeout functions
2025-08-11 4:10 ` [PATCH v1 2/2] rust: Add read_poll_timeout functions FUJITA Tomonori
@ 2025-08-11 9:50 ` Alice Ryhl
2025-08-14 6:11 ` FUJITA Tomonori
2025-08-11 9:55 ` Danilo Krummrich
` (2 subsequent siblings)
3 siblings, 1 reply; 16+ messages in thread
From: Alice Ryhl @ 2025-08-11 9:50 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: a.hindborg, alex.gaynor, ojeda, anna-maria, bjorn3_gh, boqun.feng,
dakr, frederic, gary, jstultz, linux-kernel, lossin, lyude,
rust-for-linux, sboyd, tglx, tmgross, acourbot, daniel.almeida,
Fiona Behrens
On Mon, Aug 11, 2025 at 01:10:38PM +0900, FUJITA Tomonori wrote:
> Add read_poll_timeout functions which poll periodically until a
> condition is met or a 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.
>
> read_poll_timeout() can only be used in a nonatomic context. This
> requirement is not checked by these abstractions, but it is intended
> that klint [1] or a similar tool will be used to check it in the
> future.
I would drop this paragraph. You have a call to might_sleep() now.
> +#[track_caller]
> +pub fn read_poll_timeout<Op, Cond, T>(
> + mut op: Op,
> + mut cond: Cond,
> + sleep_delta: Delta,
> + timeout_delta: Option<Delta>,
> +) -> Result<T>
> +where
> + Op: FnMut() -> Result<T>,
> + Cond: FnMut(&T) -> bool,
I would consider just writing this as:
pub fn read_poll_timeout<T>(
mut op: impl FnMut() -> Result<T>,
mut cond: impl FnMut(&T) -> bool,
sleep_delta: Delta,
timeout_delta: Option<Delta>,
) -> Result<T>
And I would also consider adding a new error type called TimeoutError
similar to BadFdError in `rust/kernel/fs/file.rs`. That way, we promise
to the caller that we never return error codes other than a timeout.
Another thing is the `timeout_delta` option. I would just have written
it as two methods, one that takes a timeout and one that doesn't. That
way, callers that don't need a timeout do not need to handle timeout
errors. (Do we have any users without a timeout? If not, maybe just
remove the Option.)
> +{
> + let start: Instant<Monotonic> = Instant::now();
> + let sleep = !sleep_delta.is_zero();
> +
> + // Unlike the C version, we always call `might_sleep()`.
> + might_sleep();
> +
> + loop {
> + let val = op()?;
> + if cond(&val) {
> + // Unlike the C version, we immediately return.
> + // We know the condition is met so we don't need to check again.
> + return Ok(val);
> + }
> + if let Some(timeout_delta) = timeout_delta {
> + if start.elapsed() > timeout_delta {
> + // Unlike the C version, we immediately return.
> + // We have just called `op()` so we don't need to call it again.
> + return Err(ETIMEDOUT);
> + }
> + }
> + if sleep {
> + fsleep(sleep_delta);
> + }
I would just do:
if !sleep_delta.is_zero() {
fsleep(sleep_delta);
}
instead of the extra variable.
> + // fsleep() could be busy-wait loop so we always call cpu_relax().
> + cpu_relax();
> + }
> +}
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v1 2/2] rust: Add read_poll_timeout functions
2025-08-11 9:50 ` Alice Ryhl
@ 2025-08-14 6:11 ` FUJITA Tomonori
2025-08-14 8:26 ` Alice Ryhl
2025-08-17 4:21 ` FUJITA Tomonori
0 siblings, 2 replies; 16+ messages in thread
From: FUJITA Tomonori @ 2025-08-14 6:11 UTC (permalink / raw)
To: aliceryhl
Cc: fujita.tomonori, a.hindborg, alex.gaynor, ojeda, anna-maria,
bjorn3_gh, boqun.feng, dakr, frederic, gary, jstultz,
linux-kernel, lossin, lyude, rust-for-linux, sboyd, tglx, tmgross,
acourbot, daniel.almeida, me
On Mon, 11 Aug 2025 09:50:59 +0000
Alice Ryhl <aliceryhl@google.com> wrote:
> On Mon, Aug 11, 2025 at 01:10:38PM +0900, FUJITA Tomonori wrote:
>> Add read_poll_timeout functions which poll periodically until a
>> condition is met or a 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.
>>
>> read_poll_timeout() can only be used in a nonatomic context. This
>> requirement is not checked by these abstractions, but it is intended
>> that klint [1] or a similar tool will be used to check it in the
>> future.
>
> I would drop this paragraph. You have a call to might_sleep() now.
Do you mean that, since it’s obvious might_sleep() can only be used in
a non-atomic context, the above statement is redundant and can be
dropped?
>> +#[track_caller]
>> +pub fn read_poll_timeout<Op, Cond, T>(
>> + mut op: Op,
>> + mut cond: Cond,
>> + sleep_delta: Delta,
>> + timeout_delta: Option<Delta>,
>> +) -> Result<T>
>> +where
>> + Op: FnMut() -> Result<T>,
>> + Cond: FnMut(&T) -> bool,
>
> I would consider just writing this as:
>
> pub fn read_poll_timeout<T>(
> mut op: impl FnMut() -> Result<T>,
> mut cond: impl FnMut(&T) -> bool,
> sleep_delta: Delta,
> timeout_delta: Option<Delta>,
> ) -> Result<T>
Surely, I'll do.
> And I would also consider adding a new error type called TimeoutError
> similar to BadFdError in `rust/kernel/fs/file.rs`. That way, we promise
> to the caller that we never return error codes other than a timeout.
Understood, I'll.
> Another thing is the `timeout_delta` option. I would just have written
> it as two methods, one that takes a timeout and one that doesn't. That
> way, callers that don't need a timeout do not need to handle timeout
> errors. (Do we have any users without a timeout? If not, maybe just
> remove the Option.)
I'll remove the Option and let's see if we’ll need a version without a
timeout.
>> +{
>> + let start: Instant<Monotonic> = Instant::now();
>> + let sleep = !sleep_delta.is_zero();
>> +
>> + // Unlike the C version, we always call `might_sleep()`.
>> + might_sleep();
>> +
>> + loop {
>> + let val = op()?;
>> + if cond(&val) {
>> + // Unlike the C version, we immediately return.
>> + // We know the condition is met so we don't need to check again.
>> + return Ok(val);
>> + }
>> + if let Some(timeout_delta) = timeout_delta {
>> + if start.elapsed() > timeout_delta {
>> + // Unlike the C version, we immediately return.
>> + // We have just called `op()` so we don't need to call it again.
>> + return Err(ETIMEDOUT);
>> + }
>> + }
>> + if sleep {
>> + fsleep(sleep_delta);
>> + }
>
> I would just do:
>
> if !sleep_delta.is_zero() {
> fsleep(sleep_delta);
> }
>
> instead of the extra variable.
I'll in the next version.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v1 2/2] rust: Add read_poll_timeout functions
2025-08-14 6:11 ` FUJITA Tomonori
@ 2025-08-14 8:26 ` Alice Ryhl
2025-08-17 4:21 ` FUJITA Tomonori
1 sibling, 0 replies; 16+ messages in thread
From: Alice Ryhl @ 2025-08-14 8:26 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: a.hindborg, alex.gaynor, ojeda, anna-maria, bjorn3_gh, boqun.feng,
dakr, frederic, gary, jstultz, linux-kernel, lossin, lyude,
rust-for-linux, sboyd, tglx, tmgross, acourbot, daniel.almeida,
me
On Thu, Aug 14, 2025 at 03:11:47PM +0900, FUJITA Tomonori wrote:
> On Mon, 11 Aug 2025 09:50:59 +0000
> Alice Ryhl <aliceryhl@google.com> wrote:
>
> > On Mon, Aug 11, 2025 at 01:10:38PM +0900, FUJITA Tomonori wrote:
> >> Add read_poll_timeout functions which poll periodically until a
> >> condition is met or a 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.
> >>
> >> read_poll_timeout() can only be used in a nonatomic context. This
> >> requirement is not checked by these abstractions, but it is intended
> >> that klint [1] or a similar tool will be used to check it in the
> >> future.
> >
> > I would drop this paragraph. You have a call to might_sleep() now.
>
> Do you mean that, since it’s obvious might_sleep() can only be used in
> a non-atomic context, the above statement is redundant and can be
> dropped?
I mean, klint is nice as it's a compile-time check rather than a
runtime check. But might_sleep() still counts as having the
abstractions check it in my book. So you shouldn't say that you are not
checking it, when you are checking it.
Alice
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v1 2/2] rust: Add read_poll_timeout functions
2025-08-14 6:11 ` FUJITA Tomonori
2025-08-14 8:26 ` Alice Ryhl
@ 2025-08-17 4:21 ` FUJITA Tomonori
1 sibling, 0 replies; 16+ messages in thread
From: FUJITA Tomonori @ 2025-08-17 4:21 UTC (permalink / raw)
To: aliceryhl
Cc: a.hindborg, alex.gaynor, ojeda, anna-maria, bjorn3_gh, boqun.feng,
dakr, frederic, gary, jstultz, linux-kernel, lossin, lyude,
rust-for-linux, sboyd, tglx, tmgross, acourbot, daniel.almeida,
me
On Thu, 14 Aug 2025 15:11:47 +0900 (JST)
FUJITA Tomonori <fujita.tomonori@gmail.com> wrote:
>>> +#[track_caller]
>>> +pub fn read_poll_timeout<Op, Cond, T>(
>>> + mut op: Op,
>>> + mut cond: Cond,
>>> + sleep_delta: Delta,
>>> + timeout_delta: Option<Delta>,
>>> +) -> Result<T>
>>> +where
>>> + Op: FnMut() -> Result<T>,
>>> + Cond: FnMut(&T) -> bool,
>>
>> I would consider just writing this as:
>>
>> pub fn read_poll_timeout<T>(
>> mut op: impl FnMut() -> Result<T>,
>> mut cond: impl FnMut(&T) -> bool,
>> sleep_delta: Delta,
>> timeout_delta: Option<Delta>,
>> ) -> Result<T>
>
> Surely, I'll do.
The above change caused the example code to fail to compile with a
"type annotations needed" error, so I kept the original code.
>> And I would also consider adding a new error type called TimeoutError
>> similar to BadFdError in `rust/kernel/fs/file.rs`. That way, we promise
>> to the caller that we never return error codes other than a timeout.
>
> Understood, I'll.
I was reminded that this function can return errors other than
timeout; Op closure returns an any error like register read failure.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v1 2/2] rust: Add read_poll_timeout functions
2025-08-11 4:10 ` [PATCH v1 2/2] rust: Add read_poll_timeout functions FUJITA Tomonori
2025-08-11 9:50 ` Alice Ryhl
@ 2025-08-11 9:55 ` Danilo Krummrich
2025-08-11 10:32 ` Andreas Hindborg
2025-08-14 6:29 ` FUJITA Tomonori
2025-08-11 10:47 ` Andreas Hindborg
2025-08-13 2:56 ` Alexandre Courbot
3 siblings, 2 replies; 16+ messages in thread
From: Danilo Krummrich @ 2025-08-11 9:55 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: a.hindborg, alex.gaynor, ojeda, aliceryhl, anna-maria, bjorn3_gh,
boqun.feng, frederic, gary, jstultz, linux-kernel, lossin, lyude,
rust-for-linux, sboyd, tglx, tmgross, acourbot, daniel.almeida,
Fiona Behrens
On Mon Aug 11, 2025 at 6:10 AM CEST, FUJITA Tomonori wrote:
> Add read_poll_timeout functions which poll periodically until a
> condition is met or a 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.
>
> read_poll_timeout() can only be used in a nonatomic context. This
> requirement is not checked by these abstractions, but it is intended
> that klint [1] or a similar tool will be used to check it in the
> future.
>
> Link: https://rust-for-linux.com/klint [1]
> Reviewed-by: Fiona Behrens <me@kloenk.dev>
> Tested-by: Daniel Almeida <daniel.almeida@collabora.com>
> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
> ---
> rust/kernel/time.rs | 1 +
> rust/kernel/time/poll.rs | 104 +++++++++++++++++++++++++++++++++++++++
Hm, are we should this should go in the time module? I does use timekeeping
stuff, but not every user of timekeeping stuff should go under the time module.
This is rather I/O stuff and I'd expect it in rust/kernel/io/poll.rs instead.
> +/// Polls periodically until a condition is met or a timeout is reached.
> +///
> +/// The function repeatedly executes the given operation `op` closure and
> +/// checks its result using the condition closure `cond`.
> +///
> +/// If `cond` returns `true`, the function returns successfully with the result of `op`.
> +/// Otherwise, it waits for a duration specified by `sleep_delta`
> +/// before executing `op` again.
> +///
> +/// This process continues until either `cond` returns `true` or the timeout,
> +/// specified by `timeout_delta`, is reached. If `timeout_delta` is `None`,
> +/// polling continues indefinitely until `cond` evaluates to `true` or an error occurs.
> +///
> +/// This function can only be used in a nonatomic context.
> +///
> +/// # Examples
> +///
> +/// ```no_run
> +/// use kernel::io::Io;
> +/// use kernel::time::{poll::read_poll_timeout, Delta};
> +///
> +/// const HW_READY: u16 = 0x01;
> +///
> +/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result<()> {
> +/// // The `op` closure reads the value of a specific status register.
> +/// let op = || -> Result<u16> { io.try_read16(0x1000) };
> +///
> +/// // The `cond` closure takes a reference to the value returned by `op`
> +/// // and checks whether the hardware is ready.
> +/// let cond = |val: &u16| *val == HW_READY;
> +///
> +/// match read_poll_timeout(op, cond, Delta::from_millis(50), Some(Delta::from_secs(3))) {
> +/// Ok(_) => {
> +/// // The hardware is ready. The returned value of the `op`` closure isn't used.
> +/// Ok(())
> +/// }
> +/// Err(e) => Err(e),
> +/// }
> +/// }
> +/// ```
This is exactly what I had in mind, thanks!
> +/// ```rust
> +/// use kernel::sync::{SpinLock, new_spinlock};
> +/// use kernel::time::Delta;
> +/// use kernel::time::poll::read_poll_timeout;
> +///
> +/// let lock = KBox::pin_init(new_spinlock!(()), kernel::alloc::flags::GFP_KERNEL)?;
> +/// let g = lock.lock();
> +/// read_poll_timeout(|| Ok(()), |()| true, Delta::from_micros(42), Some(Delta::from_micros(42)));
I assume you want to demonstrate misuse from atomic contex here? I'd rather not
do so. But if we really want that, there should be a *very* obvious comment
about this being wrong somewhere.
> +/// drop(g);
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +#[track_caller]
> +pub fn read_poll_timeout<Op, Cond, T>(
> + mut op: Op,
> + mut cond: Cond,
> + sleep_delta: Delta,
> + timeout_delta: Option<Delta>,
> +) -> Result<T>
> +where
> + Op: FnMut() -> Result<T>,
> + Cond: FnMut(&T) -> bool,
> +{
> + let start: Instant<Monotonic> = Instant::now();
> + let sleep = !sleep_delta.is_zero();
> +
> + // Unlike the C version, we always call `might_sleep()`.
I think we should explain why, i.e. the argument about being error prone, clear
separation of read_poll_timeout() and read_poll_timeout_atomic() for klint, etc.
(I also think the C version should not have done this conditionally to begin
with.)
> + might_sleep();
> +
> + loop {
> + let val = op()?;
> + if cond(&val) {
> + // Unlike the C version, we immediately return.
> + // We know the condition is met so we don't need to check again.
> + return Ok(val);
> + }
> + if let Some(timeout_delta) = timeout_delta {
> + if start.elapsed() > timeout_delta {
> + // Unlike the C version, we immediately return.
> + // We have just called `op()` so we don't need to call it again.
> + return Err(ETIMEDOUT);
> + }
> + }
> + if sleep {
> + fsleep(sleep_delta);
> + }
> + // fsleep() could be busy-wait loop so we always call cpu_relax().
> + cpu_relax();
> + }
> +}
> --
> 2.43.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v1 2/2] rust: Add read_poll_timeout functions
2025-08-11 9:55 ` Danilo Krummrich
@ 2025-08-11 10:32 ` Andreas Hindborg
2025-08-14 6:29 ` FUJITA Tomonori
1 sibling, 0 replies; 16+ messages in thread
From: Andreas Hindborg @ 2025-08-11 10:32 UTC (permalink / raw)
To: Danilo Krummrich, FUJITA Tomonori
Cc: alex.gaynor, ojeda, aliceryhl, anna-maria, bjorn3_gh, boqun.feng,
frederic, gary, jstultz, linux-kernel, lossin, lyude,
rust-for-linux, sboyd, tglx, tmgross, acourbot, daniel.almeida,
Fiona Behrens
"Danilo Krummrich" <dakr@kernel.org> writes:
> On Mon Aug 11, 2025 at 6:10 AM CEST, FUJITA Tomonori wrote:
>> Add read_poll_timeout functions which poll periodically until a
>> condition is met or a 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.
>>
>> read_poll_timeout() can only be used in a nonatomic context. This
>> requirement is not checked by these abstractions, but it is intended
>> that klint [1] or a similar tool will be used to check it in the
>> future.
>>
>> Link: https://rust-for-linux.com/klint [1]
>> Reviewed-by: Fiona Behrens <me@kloenk.dev>
>> Tested-by: Daniel Almeida <daniel.almeida@collabora.com>
>> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
>> ---
>> rust/kernel/time.rs | 1 +
>> rust/kernel/time/poll.rs | 104 +++++++++++++++++++++++++++++++++++++++
>
> Hm, are we should this should go in the time module? I does use timekeeping
> stuff, but not every user of timekeeping stuff should go under the time module.
>
> This is rather I/O stuff and I'd expect it in rust/kernel/io/poll.rs instead.
>
>> +/// Polls periodically until a condition is met or a timeout is reached.
>> +///
>> +/// The function repeatedly executes the given operation `op` closure and
>> +/// checks its result using the condition closure `cond`.
>> +///
>> +/// If `cond` returns `true`, the function returns successfully with the result of `op`.
>> +/// Otherwise, it waits for a duration specified by `sleep_delta`
>> +/// before executing `op` again.
>> +///
>> +/// This process continues until either `cond` returns `true` or the timeout,
>> +/// specified by `timeout_delta`, is reached. If `timeout_delta` is `None`,
>> +/// polling continues indefinitely until `cond` evaluates to `true` or an error occurs.
>> +///
>> +/// This function can only be used in a nonatomic context.
>> +///
>> +/// # Examples
>> +///
>> +/// ```no_run
>> +/// use kernel::io::Io;
>> +/// use kernel::time::{poll::read_poll_timeout, Delta};
>> +///
>> +/// const HW_READY: u16 = 0x01;
>> +///
>> +/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result<()> {
>> +/// // The `op` closure reads the value of a specific status register.
>> +/// let op = || -> Result<u16> { io.try_read16(0x1000) };
>> +///
>> +/// // The `cond` closure takes a reference to the value returned by `op`
>> +/// // and checks whether the hardware is ready.
>> +/// let cond = |val: &u16| *val == HW_READY;
>> +///
>> +/// match read_poll_timeout(op, cond, Delta::from_millis(50), Some(Delta::from_secs(3))) {
>> +/// Ok(_) => {
>> +/// // The hardware is ready. The returned value of the `op`` closure isn't used.
>> +/// Ok(())
>> +/// }
>> +/// Err(e) => Err(e),
>> +/// }
>> +/// }
>> +/// ```
>
> This is exactly what I had in mind, thanks!
>
>> +/// ```rust
>> +/// use kernel::sync::{SpinLock, new_spinlock};
>> +/// use kernel::time::Delta;
>> +/// use kernel::time::poll::read_poll_timeout;
>> +///
>> +/// let lock = KBox::pin_init(new_spinlock!(()), kernel::alloc::flags::GFP_KERNEL)?;
>> +/// let g = lock.lock();
>> +/// read_poll_timeout(|| Ok(()), |()| true, Delta::from_micros(42), Some(Delta::from_micros(42)));
>
> I assume you want to demonstrate misuse from atomic contex here? I'd rather not
> do so. But if we really want that, there should be a *very* obvious comment
> about this being wrong somewhere.
I think we should just drop this example.
Best regards,
Andreas Hindborg
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v1 2/2] rust: Add read_poll_timeout functions
2025-08-11 9:55 ` Danilo Krummrich
2025-08-11 10:32 ` Andreas Hindborg
@ 2025-08-14 6:29 ` FUJITA Tomonori
1 sibling, 0 replies; 16+ messages in thread
From: FUJITA Tomonori @ 2025-08-14 6:29 UTC (permalink / raw)
To: dakr
Cc: fujita.tomonori, a.hindborg, alex.gaynor, ojeda, aliceryhl,
anna-maria, bjorn3_gh, boqun.feng, frederic, gary, jstultz,
linux-kernel, lossin, lyude, rust-for-linux, sboyd, tglx, tmgross,
acourbot, daniel.almeida, me
On Mon, 11 Aug 2025 11:55:56 +0200
"Danilo Krummrich" <dakr@kernel.org> wrote:
> On Mon Aug 11, 2025 at 6:10 AM CEST, FUJITA Tomonori wrote:
>> Add read_poll_timeout functions which poll periodically until a
>> condition is met or a 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.
>>
>> read_poll_timeout() can only be used in a nonatomic context. This
>> requirement is not checked by these abstractions, but it is intended
>> that klint [1] or a similar tool will be used to check it in the
>> future.
>>
>> Link: https://rust-for-linux.com/klint [1]
>> Reviewed-by: Fiona Behrens <me@kloenk.dev>
>> Tested-by: Daniel Almeida <daniel.almeida@collabora.com>
>> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
>> ---
>> rust/kernel/time.rs | 1 +
>> rust/kernel/time/poll.rs | 104 +++++++++++++++++++++++++++++++++++++++
>
> Hm, are we should this should go in the time module? I does use timekeeping
> stuff, but not every user of timekeeping stuff should go under the time module.
>
> This is rather I/O stuff and I'd expect it in rust/kernel/io/poll.rs instead.
Either is fine by me.
If there are no other opinions, I’ll go with io/poll.rs in the next
version.
>> +/// ```rust
>> +/// use kernel::sync::{SpinLock, new_spinlock};
>> +/// use kernel::time::Delta;
>> +/// use kernel::time::poll::read_poll_timeout;
>> +///
>> +/// let lock = KBox::pin_init(new_spinlock!(()), kernel::alloc::flags::GFP_KERNEL)?;
>> +/// let g = lock.lock();
>> +/// read_poll_timeout(|| Ok(()), |()| true, Delta::from_micros(42), Some(Delta::from_micros(42)));
>
> I assume you want to demonstrate misuse from atomic contex here? I'd rather not
> do so. But if we really want that, there should be a *very* obvious comment
> about this being wrong somewhere.
I was discussing with Andreas, and I’ll remove this example.
>> +/// drop(g);
>> +///
>> +/// # Ok::<(), Error>(())
>> +/// ```
>> +#[track_caller]
>> +pub fn read_poll_timeout<Op, Cond, T>(
>> + mut op: Op,
>> + mut cond: Cond,
>> + sleep_delta: Delta,
>> + timeout_delta: Option<Delta>,
>> +) -> Result<T>
>> +where
>> + Op: FnMut() -> Result<T>,
>> + Cond: FnMut(&T) -> bool,
>> +{
>> + let start: Instant<Monotonic> = Instant::now();
>> + let sleep = !sleep_delta.is_zero();
>> +
>> + // Unlike the C version, we always call `might_sleep()`.
>
> I think we should explain why, i.e. the argument about being error prone, clear
> separation of read_poll_timeout() and read_poll_timeout_atomic() for klint, etc.
> (I also think the C version should not have done this conditionally to begin
> with.)
// Unlike the C version, we always call `might_sleep()`, because
// conditional calls are error-prone. We clearly separate the
// `read_poll_timeout()` and `read_poll_timeout_atomic()` functions for
// tools like klint.
Looks reasonable?
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v1 2/2] rust: Add read_poll_timeout functions
2025-08-11 4:10 ` [PATCH v1 2/2] rust: Add read_poll_timeout functions FUJITA Tomonori
2025-08-11 9:50 ` Alice Ryhl
2025-08-11 9:55 ` Danilo Krummrich
@ 2025-08-11 10:47 ` Andreas Hindborg
2025-08-13 2:56 ` Alexandre Courbot
3 siblings, 0 replies; 16+ messages in thread
From: Andreas Hindborg @ 2025-08-11 10:47 UTC (permalink / raw)
To: FUJITA Tomonori, alex.gaynor, ojeda
Cc: aliceryhl, anna-maria, bjorn3_gh, boqun.feng, dakr, frederic,
gary, jstultz, linux-kernel, lossin, lyude, rust-for-linux, sboyd,
tglx, tmgross, acourbot, daniel.almeida, Fiona Behrens
"FUJITA Tomonori" <fujita.tomonori@gmail.com> writes:
> Add read_poll_timeout functions which poll periodically until a
> condition is met or a 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.
>
> read_poll_timeout() can only be used in a nonatomic context. This
> requirement is not checked by these abstractions, but it is intended
> that klint [1] or a similar tool will be used to check it in the
> future.
>
> Link: https://rust-for-linux.com/klint [1]
> Reviewed-by: Fiona Behrens <me@kloenk.dev>
> Tested-by: Daniel Almeida <daniel.almeida@collabora.com>
> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
With the example removed:
-///
-/// ```rust
-/// use kernel::sync::{SpinLock, new_spinlock};
-/// use kernel::time::Delta;
-/// use kernel::time::poll::read_poll_timeout;
-///
-/// let lock = KBox::pin_init(new_spinlock!(()), kernel::alloc::flags::GFP_KERNEL)?;
-/// let g = lock.lock();
-/// read_poll_timeout(|| Ok(()), |()| true, Delta::from_micros(42), Some(Delta::from_micros(42)));
-/// drop(g);
-///
-/// # Ok::<(), Error>(())
-/// ```
Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Best regards,
Andreas Hindborg
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v1 2/2] rust: Add read_poll_timeout functions
2025-08-11 4:10 ` [PATCH v1 2/2] rust: Add read_poll_timeout functions FUJITA Tomonori
` (2 preceding siblings ...)
2025-08-11 10:47 ` Andreas Hindborg
@ 2025-08-13 2:56 ` Alexandre Courbot
2025-08-14 6:39 ` FUJITA Tomonori
3 siblings, 1 reply; 16+ messages in thread
From: Alexandre Courbot @ 2025-08-13 2:56 UTC (permalink / raw)
To: FUJITA Tomonori, a.hindborg, alex.gaynor, ojeda
Cc: aliceryhl, anna-maria, bjorn3_gh, boqun.feng, dakr, frederic,
gary, jstultz, linux-kernel, lossin, lyude, rust-for-linux, sboyd,
tglx, tmgross, daniel.almeida, Fiona Behrens
On Mon Aug 11, 2025 at 1:10 PM JST, FUJITA Tomonori wrote:
> Add read_poll_timeout functions which poll periodically until a
"functions" should be the singular "function" as this patch only adds
one function.
<snip>
> +/// # Examples
> +///
> +/// ```no_run
> +/// use kernel::io::Io;
> +/// use kernel::time::{poll::read_poll_timeout, Delta};
> +///
> +/// const HW_READY: u16 = 0x01;
> +///
> +/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result<()> {
> +/// // The `op` closure reads the value of a specific status register.
> +/// let op = || -> Result<u16> { io.try_read16(0x1000) };
> +///
> +/// // The `cond` closure takes a reference to the value returned by `op`
> +/// // and checks whether the hardware is ready.
> +/// let cond = |val: &u16| *val == HW_READY;
> +///
> +/// match read_poll_timeout(op, cond, Delta::from_millis(50), Some(Delta::from_secs(3))) {
Is there a reason for not writing the closures directly inline? I.e.
match read_poll_timeout(
// The `op` closure reads the value of a specific status register.
|| io.try_read16(0x1000),
// The `cond` closure takes a reference to the value returned by `op`
// and checks whether the hardware is ready.
|val| *val == HW_READY,
Delta::from_millis(50),
Some(Delta::from_secs(3))
)
I think it is closer to how people will actually use this function, and
the expected types for the closures are available right in the function
definition if they need more details.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v1 2/2] rust: Add read_poll_timeout functions
2025-08-13 2:56 ` Alexandre Courbot
@ 2025-08-14 6:39 ` FUJITA Tomonori
0 siblings, 0 replies; 16+ messages in thread
From: FUJITA Tomonori @ 2025-08-14 6:39 UTC (permalink / raw)
To: acourbot
Cc: fujita.tomonori, a.hindborg, alex.gaynor, ojeda, aliceryhl,
anna-maria, bjorn3_gh, boqun.feng, dakr, frederic, gary, jstultz,
linux-kernel, lossin, lyude, rust-for-linux, sboyd, tglx, tmgross,
daniel.almeida, me
On Wed, 13 Aug 2025 11:56:26 +0900
"Alexandre Courbot" <acourbot@nvidia.com> wrote:
> On Mon Aug 11, 2025 at 1:10 PM JST, FUJITA Tomonori wrote:
>> Add read_poll_timeout functions which poll periodically until a
>
> "functions" should be the singular "function" as this patch only adds
> one function.
Oops, thanks. I'll fix.
> <snip>
>> +/// # Examples
>> +///
>> +/// ```no_run
>> +/// use kernel::io::Io;
>> +/// use kernel::time::{poll::read_poll_timeout, Delta};
>> +///
>> +/// const HW_READY: u16 = 0x01;
>> +///
>> +/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result<()> {
>> +/// // The `op` closure reads the value of a specific status register.
>> +/// let op = || -> Result<u16> { io.try_read16(0x1000) };
>> +///
>> +/// // The `cond` closure takes a reference to the value returned by `op`
>> +/// // and checks whether the hardware is ready.
>> +/// let cond = |val: &u16| *val == HW_READY;
>> +///
>> +/// match read_poll_timeout(op, cond, Delta::from_millis(50), Some(Delta::from_secs(3))) {
>
> Is there a reason for not writing the closures directly inline? I.e.
>
> match read_poll_timeout(
> // The `op` closure reads the value of a specific status register.
> || io.try_read16(0x1000),
> // The `cond` closure takes a reference to the value returned by `op`
> // and checks whether the hardware is ready.
> |val| *val == HW_READY,
> Delta::from_millis(50),
> Some(Delta::from_secs(3))
> )
>
> I think it is closer to how people will actually use this function, and
> the expected types for the closures are available right in the function
> definition if they need more details.
Either is fine by me. I thought that not writing directly is more
readable.
Anyone else have an opinion?
^ permalink raw reply [flat|nested] 16+ messages in thread