diff mbox series

[net-next,v1,2/2] net: phy: qt2025: wait until PHY becomes ready

Message ID 20241001112512.4861-3-fujita.tomonori@gmail.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series add delay abstraction (sleep functions) | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 9 this patch: 9
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers warning 5 maintainers not CCed: pabeni@redhat.com kuba@kernel.org edumazet@google.com linux@armlinux.org.uk boqun.feng@gmail.com
netdev/build_clang success Errors and warnings before: 9 this patch: 9
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 12 this patch: 12
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 17 lines checked
netdev/build_clang_rust fail Errors and warnings before: 12 this patch: 12
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

FUJITA Tomonori Oct. 1, 2024, 11:25 a.m. UTC
Wait until a PHY becomes ready in the probe callback by using a sleep
function.

Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
 drivers/net/phy/qt2025.rs | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

Comments

Alice Ryhl Oct. 1, 2024, 11:36 a.m. UTC | #1
On Tue, Oct 1, 2024 at 1:27 PM FUJITA Tomonori
<fujita.tomonori@gmail.com> wrote:
>
> Wait until a PHY becomes ready in the probe callback by using a sleep
> function.
>
> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
> ---
>  drivers/net/phy/qt2025.rs | 11 +++++++++--
>  1 file changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/phy/qt2025.rs b/drivers/net/phy/qt2025.rs
> index 28d8981f410b..3a8ef9f73642 100644
> --- a/drivers/net/phy/qt2025.rs
> +++ b/drivers/net/phy/qt2025.rs
> @@ -93,8 +93,15 @@ fn probe(dev: &mut phy::Device) -> Result<()> {
>          // The micro-controller will start running from SRAM.
>          dev.write(C45::new(Mmd::PCS, 0xe854), 0x0040)?;
>
> -        // TODO: sleep here until the hw becomes ready.
> -        Ok(())
> +        // sleep here until the hw becomes ready.
> +        for _ in 0..60 {
> +            kernel::delay::sleep(core::time::Duration::from_millis(50));
> +            let val = dev.read(C45::new(Mmd::PCS, 0xd7fd))?;
> +            if val != 0x00 && val != 0x10 {
> +                return Ok(());
> +            }

Why not place the sleep after this check? That way, we don't need to
sleep if the check succeeds immediately.

> +        }
> +        Err(code::ENODEV)

This sleeps for up to 3 seconds. Is that the right amount to sleep?

Alice
Andrew Lunn Oct. 1, 2024, 12:48 p.m. UTC | #2
On Tue, Oct 01, 2024 at 01:36:41PM +0200, Alice Ryhl wrote:
> On Tue, Oct 1, 2024 at 1:27 PM FUJITA Tomonori
> <fujita.tomonori@gmail.com> wrote:
> >
> > Wait until a PHY becomes ready in the probe callback by using a sleep
> > function.
> >
> > Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
> > ---
> >  drivers/net/phy/qt2025.rs | 11 +++++++++--
> >  1 file changed, 9 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/net/phy/qt2025.rs b/drivers/net/phy/qt2025.rs
> > index 28d8981f410b..3a8ef9f73642 100644
> > --- a/drivers/net/phy/qt2025.rs
> > +++ b/drivers/net/phy/qt2025.rs
> > @@ -93,8 +93,15 @@ fn probe(dev: &mut phy::Device) -> Result<()> {
> >          // The micro-controller will start running from SRAM.
> >          dev.write(C45::new(Mmd::PCS, 0xe854), 0x0040)?;
> >
> > -        // TODO: sleep here until the hw becomes ready.
> > -        Ok(())
> > +        // sleep here until the hw becomes ready.
> > +        for _ in 0..60 {
> > +            kernel::delay::sleep(core::time::Duration::from_millis(50));
> > +            let val = dev.read(C45::new(Mmd::PCS, 0xd7fd))?;
> > +            if val != 0x00 && val != 0x10 {
> > +                return Ok(());
> > +            }
> 
> Why not place the sleep after this check? That way, we don't need to
> sleep if the check succeeds immediately.

Nice, you just made my point :-)

I generally point developers at iopoll.h, because developers nearly
always get this sort of polling for something to happen wrong. 

The kernel sleep functions guarantee the minimum sleep time. They say
nothing about the maximum sleep time. You can ask it to sleep for 1ms,
and in reality, due to something stealing the CPU and not being RT
friendly, it actually sleeps for 10ms. This extra long sleep time
blows straight past your timeout, if you have a time based timeout.
What most developers do is after the sleep() returns they check to see
if the timeout has been reached and then exit with -ETIMEDOUT. They
don't check the state of the hardware, which given its had a long time
to do its thing, probably is now in a good state. But the function
returns -ETIMEDOUT.

There should always be a check of the hardware state after the sleep,
in order to determine ETIMEDOUT vs 0.

As i said, most C developers get this wrong. So i don't really see why
Rust developers also will not get this wrong. So i like to discourage
this sort of code, and have Rust implementations of iopoll.h.

	Andrew
Dirk Behme Oct. 2, 2024, 4:39 a.m. UTC | #3
On 01.10.2024 14:48, Andrew Lunn wrote:
> On Tue, Oct 01, 2024 at 01:36:41PM +0200, Alice Ryhl wrote:
>> On Tue, Oct 1, 2024 at 1:27 PM FUJITA Tomonori
>> <fujita.tomonori@gmail.com> wrote:
>>>
>>> Wait until a PHY becomes ready in the probe callback by using a sleep
>>> function.
>>>
>>> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
>>> ---
>>>   drivers/net/phy/qt2025.rs | 11 +++++++++--
>>>   1 file changed, 9 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/drivers/net/phy/qt2025.rs b/drivers/net/phy/qt2025.rs
>>> index 28d8981f410b..3a8ef9f73642 100644
>>> --- a/drivers/net/phy/qt2025.rs
>>> +++ b/drivers/net/phy/qt2025.rs
>>> @@ -93,8 +93,15 @@ fn probe(dev: &mut phy::Device) -> Result<()> {
>>>           // The micro-controller will start running from SRAM.
>>>           dev.write(C45::new(Mmd::PCS, 0xe854), 0x0040)?;
>>>
>>> -        // TODO: sleep here until the hw becomes ready.
>>> -        Ok(())
>>> +        // sleep here until the hw becomes ready.
>>> +        for _ in 0..60 {
>>> +            kernel::delay::sleep(core::time::Duration::from_millis(50));
>>> +            let val = dev.read(C45::new(Mmd::PCS, 0xd7fd))?;
>>> +            if val != 0x00 && val != 0x10 {
>>> +                return Ok(());
>>> +            }
>>
>> Why not place the sleep after this check? That way, we don't need to
>> sleep if the check succeeds immediately.
> 
> Nice, you just made my point :-)
> 
> I generally point developers at iopoll.h, because developers nearly
> always get this sort of polling for something to happen wrong.
> 
> The kernel sleep functions guarantee the minimum sleep time. They say
> nothing about the maximum sleep time. You can ask it to sleep for 1ms,
> and in reality, due to something stealing the CPU and not being RT
> friendly, it actually sleeps for 10ms. This extra long sleep time
> blows straight past your timeout, if you have a time based timeout.
> What most developers do is after the sleep() returns they check to see
> if the timeout has been reached and then exit with -ETIMEDOUT. They
> don't check the state of the hardware, which given its had a long time
> to do its thing, probably is now in a good state. But the function
> returns -ETIMEDOUT.
> 
> There should always be a check of the hardware state after the sleep,
> in order to determine ETIMEDOUT vs 0.
> 
> As i said, most C developers get this wrong. So i don't really see why
> Rust developers also will not get this wrong. So i like to discourage
> this sort of code, and have Rust implementations of iopoll.h.


Do we talk about some simple Rust wrappers for the macros in iopoll.h? 
E.g. something like [1]?

Or are we talking about some more complex (safety) dependencies which 
need some more complex abstraction handling?

Best regards

Dirk

[1]

int rust_helper_readb_poll_timeout(const volatile void * addr,
                                   u64 val, u64 cond, u64 delay_us,
                                   u64 timeout_us)
{
        return readb_poll_timeout(addr, val, cond, delay_us, timeout_us);
}
FUJITA Tomonori Oct. 2, 2024, 9:56 a.m. UTC | #4
On Wed, 2 Oct 2024 06:39:48 +0200
Dirk Behme <dirk.behme@de.bosch.com> wrote:

>> I generally point developers at iopoll.h, because developers nearly
>> always get this sort of polling for something to happen wrong.
>> The kernel sleep functions guarantee the minimum sleep time. They say
>> nothing about the maximum sleep time. You can ask it to sleep for 1ms,
>> and in reality, due to something stealing the CPU and not being RT
>> friendly, it actually sleeps for 10ms. This extra long sleep time
>> blows straight past your timeout, if you have a time based timeout.
>> What most developers do is after the sleep() returns they check to see
>> if the timeout has been reached and then exit with -ETIMEDOUT. They
>> don't check the state of the hardware, which given its had a long time
>> to do its thing, probably is now in a good state. But the function
>> returns -ETIMEDOUT.
>> There should always be a check of the hardware state after the sleep,
>> in order to determine ETIMEDOUT vs 0.
>> As i said, most C developers get this wrong. So i don't really see why
>> Rust developers also will not get this wrong. So i like to discourage
>> this sort of code, and have Rust implementations of iopoll.h.
> 
> 
> Do we talk about some simple Rust wrappers for the macros in iopoll.h?
> E.g. something like [1]?
> 
> Or are we talking about some more complex (safety) dependencies which
> need some more complex abstraction handling?

(snip)

> int rust_helper_readb_poll_timeout(const volatile void * addr,
>                                   u64 val, u64 cond, u64 delay_us,
>                                   u64 timeout_us)
> {
>        return readb_poll_timeout(addr, val, cond, delay_us, timeout_us);
> }

I'm not sure a simple wrapper for iopoll.h works. We need to pass a
function. I'm testing a macro like the following (not added ktime
timeout yet):

macro_rules! read_poll_timeout {
    ($op:expr, $val:expr, $cond:expr, $sleep:expr, $timeout:expr, $($args:expr),*) => {{
        let _ = $val;
        loop {
            $val = $op($($args),*);
            if $cond {
                break;
            }
            kernel::delay::sleep($sleep);
        }
        if $cond {
            Ok(())
        } else {
            Err(kernel::error::code::ETIMEDOUT)
        }
    }};
}
FUJITA Tomonori Oct. 2, 2024, 10:13 a.m. UTC | #5
On Tue, 1 Oct 2024 14:48:06 +0200
Andrew Lunn <andrew@lunn.ch> wrote:

> I generally point developers at iopoll.h, because developers nearly
> always get this sort of polling for something to happen wrong. 

Ah, I had forgotten about iopoll.h. Make senses. I'll try implement an
equivalent in Rust.
FUJITA Tomonori Oct. 2, 2024, 11:17 a.m. UTC | #6
On Tue, 1 Oct 2024 13:36:41 +0200
Alice Ryhl <aliceryhl@google.com> wrote:

>> diff --git a/drivers/net/phy/qt2025.rs b/drivers/net/phy/qt2025.rs
>> index 28d8981f410b..3a8ef9f73642 100644
>> --- a/drivers/net/phy/qt2025.rs
>> +++ b/drivers/net/phy/qt2025.rs
>> @@ -93,8 +93,15 @@ fn probe(dev: &mut phy::Device) -> Result<()> {
>>          // The micro-controller will start running from SRAM.
>>          dev.write(C45::new(Mmd::PCS, 0xe854), 0x0040)?;
>>
>> -        // TODO: sleep here until the hw becomes ready.
>> -        Ok(())
>> +        // sleep here until the hw becomes ready.
>> +        for _ in 0..60 {
>> +            kernel::delay::sleep(core::time::Duration::from_millis(50));
>> +            let val = dev.read(C45::new(Mmd::PCS, 0xd7fd))?;
>> +            if val != 0x00 && val != 0x10 {
>> +                return Ok(());
>> +            }
> 
> Why not place the sleep after this check? That way, we don't need to
> sleep if the check succeeds immediately.

Yeah, that's better. I copied the logic in the vendor driver without
thinking.

As Andrew pointed out, the code like this in C drivers isn't
recommended; iopoll.h should be used. So I'll try to implement such.


>> +        }
>> +        Err(code::ENODEV)
> 
> This sleeps for up to 3 seconds. Is that the right amount to sleep?

That's what the vendor driver does.
Andrew Lunn Oct. 2, 2024, 12:31 p.m. UTC | #7
On Wed, Oct 02, 2024 at 10:13:39AM +0000, FUJITA Tomonori wrote:
> On Tue, 1 Oct 2024 14:48:06 +0200
> Andrew Lunn <andrew@lunn.ch> wrote:
> 
> > I generally point developers at iopoll.h, because developers nearly
> > always get this sort of polling for something to happen wrong. 
> 
> Ah, I had forgotten about iopoll.h. Make senses. I'll try implement an
> equivalent in Rust.

There are some subtleties involved with PHYs, which is why we have our
own wrapper around the macros in iopoll.h:

https://elixir.bootlin.com/linux/v6.11.1/source/include/linux/phy.h#L1288

Normally an IO operation cannot fail. But PHYs are different, a read
could return -EOPNOTSUPP, -EIO, -ETIMEDOUT etc. That needs to be take
into account and checked before evaluating the condition.

So you might need to think about something similar for Rust. A generic
read_poll_timeout() and a phy_read_poll_timeout() built on top of it.

This is a worthwhile set of helpers to have, since the bugs in this
are nothing to do with memory safety, but plain simple logic bugs,
which Rust itself is unlikely to help with.

	Andrew
FUJITA Tomonori Oct. 3, 2024, 5:07 a.m. UTC | #8
On Wed, 2 Oct 2024 14:31:07 +0200
Andrew Lunn <andrew@lunn.ch> wrote:

> There are some subtleties involved with PHYs, which is why we have our
> own wrapper around the macros in iopoll.h:
> 
> https://elixir.bootlin.com/linux/v6.11.1/source/include/linux/phy.h#L1288
> 
> Normally an IO operation cannot fail. But PHYs are different, a read
> could return -EOPNOTSUPP, -EIO, -ETIMEDOUT etc. That needs to be take
> into account and checked before evaluating the condition.

Thanks, I didn't know this function. I think that a generic
read_poll_timeout in Rust can handle such case.

#define read_poll_timeout(op, val, cond, sleep_us, timeout_us, \
				sleep_before_read, args...) \
({ \
	u64 __timeout_us = (timeout_us); \
	unsigned long __sleep_us = (sleep_us); \
	ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \
	might_sleep_if((__sleep_us) != 0); \
	if (sleep_before_read && __sleep_us) \
		usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
	for (;;) { \
		(val) = op(args); \
		if (cond) \
			break; \

The reason why the C version cannot handle such case is that after
call `op` function, read_poll_timeout macro can't know whether `val`
is an error or not.

In Rust version, 'op' function can return Result type, explicitly
tells success or an error. It can return an error before evaluating
the condition.
Boqun Feng Oct. 3, 2024, 11:52 a.m. UTC | #9
On Wed, Oct 02, 2024 at 09:56:36AM +0000, FUJITA Tomonori wrote:
> On Wed, 2 Oct 2024 06:39:48 +0200
> Dirk Behme <dirk.behme@de.bosch.com> wrote:
> 
> >> I generally point developers at iopoll.h, because developers nearly
> >> always get this sort of polling for something to happen wrong.
> >> The kernel sleep functions guarantee the minimum sleep time. They say
> >> nothing about the maximum sleep time. You can ask it to sleep for 1ms,
> >> and in reality, due to something stealing the CPU and not being RT
> >> friendly, it actually sleeps for 10ms. This extra long sleep time
> >> blows straight past your timeout, if you have a time based timeout.
> >> What most developers do is after the sleep() returns they check to see
> >> if the timeout has been reached and then exit with -ETIMEDOUT. They
> >> don't check the state of the hardware, which given its had a long time
> >> to do its thing, probably is now in a good state. But the function
> >> returns -ETIMEDOUT.
> >> There should always be a check of the hardware state after the sleep,
> >> in order to determine ETIMEDOUT vs 0.
> >> As i said, most C developers get this wrong. So i don't really see why
> >> Rust developers also will not get this wrong. So i like to discourage
> >> this sort of code, and have Rust implementations of iopoll.h.
> > 
> > 
> > Do we talk about some simple Rust wrappers for the macros in iopoll.h?
> > E.g. something like [1]?
> > 
> > Or are we talking about some more complex (safety) dependencies which
> > need some more complex abstraction handling?
> 
> (snip)
> 
> > int rust_helper_readb_poll_timeout(const volatile void * addr,
> >                                   u64 val, u64 cond, u64 delay_us,
> >                                   u64 timeout_us)
> > {
> >        return readb_poll_timeout(addr, val, cond, delay_us, timeout_us);
> > }
> 
> I'm not sure a simple wrapper for iopoll.h works. We need to pass a
> function. I'm testing a macro like the following (not added ktime
> timeout yet):

You could use closure as a parameter to avoid macro interface, something
like:

	fn read_poll_timeout<Op, Cond, T>(
	    op: Op,
	    cond: Cond,
	    sleep: Delta,
	    timeout: Delta,
	) -> Result<T> where
	    Op: Fn() -> T,
	    cond: Fn() -> bool {

	    let __timeout = kernel::Ktime::ktime_get() + timeout;

	    let val = loop {
		let val = op();
		if cond() {
		    break Some(val);
		}
		kernel::delay::sleep(sleep);

		if __timeout.after(kernel::Ktime::ktime_get()) {
		    break None;
		}
	    };

	    if cond() {
		val
	    } else {
		Err(kernel::error::code::ETIMEDOUT)
	    }
	}

note that you don't need the args part, because `op` is a closure that
can capature value, so for example, if in C code you need to call foo(a,
b), with closure, you can do:

	<a and b are defined in the caller>
	read_poll_timeout(|| { foo(a, b) }, ...);

with above API.

Regards,
Boqun

> 
> macro_rules! read_poll_timeout {
>     ($op:expr, $val:expr, $cond:expr, $sleep:expr, $timeout:expr, $($args:expr),*) => {{
>         let _ = $val;
>         loop {
>             $val = $op($($args),*);
>             if $cond {
>                 break;
>             }
>             kernel::delay::sleep($sleep);
>         }
>         if $cond {
>             Ok(())
>         } else {
>             Err(kernel::error::code::ETIMEDOUT)
>         }
>     }};
> }
>
FUJITA Tomonori Oct. 3, 2024, 1:45 p.m. UTC | #10
On Thu, 3 Oct 2024 04:52:48 -0700
Boqun Feng <boqun.feng@gmail.com> wrote:

> You could use closure as a parameter to avoid macro interface, something
> like:
> 
> 	fn read_poll_timeout<Op, Cond, T>(
> 	    op: Op,
> 	    cond: Cond,
> 	    sleep: Delta,
> 	    timeout: Delta,
> 	) -> Result<T> where
> 	    Op: Fn() -> T,
> 	    cond: Fn() -> bool {
> 
> 	    let __timeout = kernel::Ktime::ktime_get() + timeout;
> 
> 	    let val = loop {
> 		let val = op();
> 		if cond() {
> 		    break Some(val);
> 		}
> 		kernel::delay::sleep(sleep);
> 
> 		if __timeout.after(kernel::Ktime::ktime_get()) {
> 		    break None;
> 		}
> 	    };
> 
> 	    if cond() {
> 		val
> 	    } else {
> 		Err(kernel::error::code::ETIMEDOUT)
> 	    }
> 	}

Great! I changed couple of things.

1. Op typically reads a value from a register and could need mut objects. So I use FnMut.
2. reading from hardware could fail so Op had better to return an error [1].
3. Cond needs val; typically check the value from a register.

[1] https://lore.kernel.org/netdev/ec7267b5-ae77-4c4a-94f8-aa933c87a9a2@lunn.ch

Seems that the following works QT2025 driver. How does it look?

fn read_poll_timeout<Op, Cond, T: Copy>(
    mut op: Op,
    cond: Cond,
    sleep: Delta,
    timeout: Delta,
) -> Result<T>
where
    Op: FnMut() -> Result<T>,
    Cond: Fn(T) -> bool,
{
    let timeout = Ktime::ktime_get() + timeout;
    let ret = loop {
        let val = op()?;
        if cond(val) {
            break Ok(val);
        }
        kernel::delay::sleep(sleep);

        if Ktime::ktime_get() > timeout {
            break Err(code::ETIMEDOUT);
        }
    };

    ret
}
Boqun Feng Oct. 3, 2024, 2:25 p.m. UTC | #11
On Thu, Oct 03, 2024 at 01:45:18PM +0000, FUJITA Tomonori wrote:
> On Thu, 3 Oct 2024 04:52:48 -0700
> Boqun Feng <boqun.feng@gmail.com> wrote:
> 
> > You could use closure as a parameter to avoid macro interface, something
> > like:
> > 
> > 	fn read_poll_timeout<Op, Cond, T>(
> > 	    op: Op,
> > 	    cond: Cond,
> > 	    sleep: Delta,
> > 	    timeout: Delta,
> > 	) -> Result<T> where
> > 	    Op: Fn() -> T,
> > 	    cond: Fn() -> bool {
> > 
> > 	    let __timeout = kernel::Ktime::ktime_get() + timeout;
> > 
> > 	    let val = loop {
> > 		let val = op();
> > 		if cond() {
> > 		    break Some(val);
> > 		}
> > 		kernel::delay::sleep(sleep);
> > 
> > 		if __timeout.after(kernel::Ktime::ktime_get()) {
> > 		    break None;
> > 		}
> > 	    };
> > 
> > 	    if cond() {
> > 		val
> > 	    } else {
> > 		Err(kernel::error::code::ETIMEDOUT)
> > 	    }
> > 	}
> 
> Great! I changed couple of things.
> 
> 1. Op typically reads a value from a register and could need mut objects. So I use FnMut.
> 2. reading from hardware could fail so Op had better to return an error [1].
> 3. Cond needs val; typically check the value from a register.
> 
> [1] https://lore.kernel.org/netdev/ec7267b5-ae77-4c4a-94f8-aa933c87a9a2@lunn.ch
> 
> Seems that the following works QT2025 driver. How does it look?
> 

Makes sense to me. Of course, more users will probably tell us whether
we cover all the cases, but this is a good starting point. I would put
the function at kernel::io::poll, but that's my personal preference ;-)

Regards,
Boqun

> fn read_poll_timeout<Op, Cond, T: Copy>(
>     mut op: Op,
>     cond: Cond,
>     sleep: Delta,
>     timeout: Delta,
> ) -> Result<T>
> where
>     Op: FnMut() -> Result<T>,
>     Cond: Fn(T) -> bool,
> {
>     let timeout = Ktime::ktime_get() + timeout;
>     let ret = loop {
>         let val = op()?;
>         if cond(val) {
>             break Ok(val);
>         }
>         kernel::delay::sleep(sleep);
> 
>         if Ktime::ktime_get() > timeout {
>             break Err(code::ETIMEDOUT);
>         }
>     };
> 
>     ret
> }
Andrew Lunn Oct. 3, 2024, 4 p.m. UTC | #12
> > fn read_poll_timeout<Op, Cond, T: Copy>(
> >     mut op: Op,
> >     cond: Cond,
> >     sleep: Delta,
> >     timeout: Delta,
> > ) -> Result<T>
> > where
> >     Op: FnMut() -> Result<T>,
> >     Cond: Fn(T) -> bool,
> > {
> >     let timeout = Ktime::ktime_get() + timeout;
> >     let ret = loop {
> >         let val = op()?;
> >         if cond(val) {
> >             break Ok(val);
> >         }
> >         kernel::delay::sleep(sleep);
> > 
> >         if Ktime::ktime_get() > timeout {
> >             break Err(code::ETIMEDOUT);
> >         }
> >     };
> > 
> >     ret
> > }

This appears to have the usual bug when people implement it themselves
and i then point them at iopoll.h, which so far as been bug free.

kernel::delay::sleep(sleep) can sleep for an arbitrary amount of time
greater than sleep, if the system is busy doing other things. You
might only get around this loop once, and exit with ETIMEOUT, but
while you have been sleeping a long time the hardware has completed
its operation, but you never check.

There must be a call to cond() after the timeout to handle this
condition.

And this is not theoretical. I had a very reproducible case of this
during the boot of a device. It is less likely today, with SMP
systems, and all the RT patches, but if it does happen, it will be
very hard to track down.

	Andrew
Andrew Lunn Oct. 3, 2024, 4:09 p.m. UTC | #13
> we cover all the cases, but this is a good starting point. I would put
> the function at kernel::io::poll, but that's my personal preference ;-)

iopoll.h has a few variants. The variant being implemented here can
only be used in thread context where it can sleep. There is also
read_poll_timeout_atomic() which can be used in atomic context, which
uses udelay() rather than sleeping, since you are not allowed to sleep
in atomic context.

So we should keep the naming open to allow for the atomic variant
sometime in the future.

We probably also want a comment that this helper cannot be used in
atomic context.

Do we have a Rust equivalent of might_sleep()?

https://elixir.bootlin.com/linux/v6.12-rc1/source/include/linux/kernel.h#L93

	Andrew
FUJITA Tomonori Oct. 4, 2024, 11:48 a.m. UTC | #14
On Thu, 3 Oct 2024 18:09:15 +0200
Andrew Lunn <andrew@lunn.ch> wrote:

> We probably also want a comment that this helper cannot be used in
> atomic context.

Yeah, I'll add such.

> Do we have a Rust equivalent of might_sleep()?
> 
> https://elixir.bootlin.com/linux/v6.12-rc1/source/include/linux/kernel.h#L93

No. I'll add bindings for might_sleep() and cpu_relax().
FUJITA Tomonori Oct. 4, 2024, 11:54 a.m. UTC | #15
On Thu, 3 Oct 2024 18:00:36 +0200
Andrew Lunn <andrew@lunn.ch> wrote:

>> > fn read_poll_timeout<Op, Cond, T: Copy>(
>> >     mut op: Op,
>> >     cond: Cond,
>> >     sleep: Delta,
>> >     timeout: Delta,
>> > ) -> Result<T>
>> > where
>> >     Op: FnMut() -> Result<T>,
>> >     Cond: Fn(T) -> bool,
>> > {
>> >     let timeout = Ktime::ktime_get() + timeout;
>> >     let ret = loop {
>> >         let val = op()?;
>> >         if cond(val) {
>> >             break Ok(val);
>> >         }
>> >         kernel::delay::sleep(sleep);
>> > 
>> >         if Ktime::ktime_get() > timeout {
>> >             break Err(code::ETIMEDOUT);
>> >         }
>> >     };
>> > 
>> >     ret
>> > }
> 
> This appears to have the usual bug when people implement it themselves
> and i then point them at iopoll.h, which so far as been bug free.

Ah, in the next submission, I'll try to ensure that the Rust version
works the same way as the C version.
Andrew Lunn Oct. 4, 2024, 1:37 p.m. UTC | #16
On Fri, Oct 04, 2024 at 08:48:03PM +0900, FUJITA Tomonori wrote:
> On Thu, 3 Oct 2024 18:09:15 +0200
> Andrew Lunn <andrew@lunn.ch> wrote:
> 
> > We probably also want a comment that this helper cannot be used in
> > atomic context.
> 
> Yeah, I'll add such.
> 
> > Do we have a Rust equivalent of might_sleep()?
> > 
> > https://elixir.bootlin.com/linux/v6.12-rc1/source/include/linux/kernel.h#L93
> 
> No. I'll add bindings for might_sleep() and cpu_relax().

Please make sure you involve the scheduler people. This is now well
outside of networking, same as the discussion around time has little
to do with networking.

The might_sleep() is not a strong requirement for iopoll, so you might
want to get the basic functionality merged first, and then once
might_sleep() is agreed on, add it to iopoll. It is just a debug
feature.

	Andrew
diff mbox series

Patch

diff --git a/drivers/net/phy/qt2025.rs b/drivers/net/phy/qt2025.rs
index 28d8981f410b..3a8ef9f73642 100644
--- a/drivers/net/phy/qt2025.rs
+++ b/drivers/net/phy/qt2025.rs
@@ -93,8 +93,15 @@  fn probe(dev: &mut phy::Device) -> Result<()> {
         // The micro-controller will start running from SRAM.
         dev.write(C45::new(Mmd::PCS, 0xe854), 0x0040)?;
 
-        // TODO: sleep here until the hw becomes ready.
-        Ok(())
+        // sleep here until the hw becomes ready.
+        for _ in 0..60 {
+            kernel::delay::sleep(core::time::Duration::from_millis(50));
+            let val = dev.read(C45::new(Mmd::PCS, 0xd7fd))?;
+            if val != 0x00 && val != 0x10 {
+                return Ok(());
+            }
+        }
+        Err(code::ENODEV)
     }
 
     fn read_status(dev: &mut phy::Device) -> Result<u16> {