diff options
author | Michael Brown | 2011-12-08 01:06:53 +0100 |
---|---|---|
committer | Michael Brown | 2011-12-08 01:12:28 +0100 |
commit | fa3ca017acbf70c900a101409f006c37e9d0e994 (patch) | |
tree | d2376cb9d8d157994b07ef177ce9dddb47adcd9e /src/arch/i386/drivers/net/undinet.c | |
parent | [dhcp] Add PXE-mandated DHCP options [128,135] to parameter request list (diff) | |
download | ipxe-fa3ca017acbf70c900a101409f006c37e9d0e994.tar.gz ipxe-fa3ca017acbf70c900a101409f006c37e9d0e994.tar.xz ipxe-fa3ca017acbf70c900a101409f006c37e9d0e994.zip |
[undi] Retry PXENV_UNDI_INITIALIZE multiple times
On at least one PXE stack (Realtek r8169), PXENV_UNDI_INITIALIZE has
been observed to fail intermittently due to a media test failure (PXE
error 0x00000061). Retrying the call to PXENV_UNDI_INITIALIZE
succeeds, and the NIC is then usable.
It is worth noting that this particular Realtek PXE stack is already
known to be unreliable: for example, it repeatably fails its own
boot-time media test after every warm reboot.
Fix by attempting PXENV_UNDI_INITIALIZE multiple times, with a short
delay between each attempt to allow the link to settle.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/arch/i386/drivers/net/undinet.c')
-rw-r--r-- | src/arch/i386/drivers/net/undinet.c | 38 |
1 files changed, 30 insertions, 8 deletions
diff --git a/src/arch/i386/drivers/net/undinet.c b/src/arch/i386/drivers/net/undinet.c index c391469f..63b37389 100644 --- a/src/arch/i386/drivers/net/undinet.c +++ b/src/arch/i386/drivers/net/undinet.c @@ -19,6 +19,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <string.h> +#include <unistd.h> #include <pxe.h> #include <realmode.h> #include <pic8259.h> @@ -34,7 +35,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <undinet.h> #include <pxeparent.h> - /** @file * * UNDI network device driver @@ -63,6 +63,12 @@ struct undi_nic { /** @} */ +/** Maximum number of times to retry PXENV_UNDI_INITIALIZE */ +#define UNDI_INITIALIZE_RETRY_MAX 10 + +/** Delay between retries of PXENV_UNDI_INITIALIZE */ +#define UNDI_INITIALIZE_RETRY_DELAY_MS 200 + static void undinet_close ( struct net_device *netdev ); /** Address of UNDI entry point */ @@ -482,12 +488,13 @@ int undinet_probe ( struct undi_device *undi ) { struct undi_nic *undinic; struct s_PXENV_START_UNDI start_undi; struct s_PXENV_UNDI_STARTUP undi_startup; - struct s_PXENV_UNDI_INITIALIZE undi_initialize; + struct s_PXENV_UNDI_INITIALIZE undi_init; struct s_PXENV_UNDI_GET_INFORMATION undi_info; struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface; struct s_PXENV_UNDI_SHUTDOWN undi_shutdown; struct s_PXENV_UNDI_CLEANUP undi_cleanup; struct s_PXENV_STOP_UNDI stop_undi; + unsigned int retry; int rc; /* Allocate net device */ @@ -524,12 +531,27 @@ int undinet_probe ( struct undi_device *undi ) { &undi_startup, sizeof ( undi_startup ) ) ) != 0 ) goto err_undi_startup; - memset ( &undi_initialize, 0, sizeof ( undi_initialize ) ); - if ( ( rc = pxeparent_call ( undinet_entry, - PXENV_UNDI_INITIALIZE, - &undi_initialize, - sizeof ( undi_initialize ))) != 0 ) - goto err_undi_initialize; + /* On some PXE stacks, PXENV_UNDI_INITIALIZE may fail + * due to a transient condition (e.g. media test + * failing because the link has only just come out of + * reset). We may therefore need to retry this call + * several times. + */ + for ( retry = 0 ; ; ) { + memset ( &undi_init, 0, sizeof ( undi_init ) ); + if ( ( rc = pxeparent_call ( undinet_entry, + PXENV_UNDI_INITIALIZE, + &undi_init, + sizeof ( undi_init ))) ==0) + break; + if ( ++retry > UNDI_INITIALIZE_RETRY_MAX ) + goto err_undi_initialize; + DBGC ( undinic, "UNDINIC %p retrying " + "PXENV_UNDI_INITIALIZE (retry %d)\n", + undinic, retry ); + /* Delay to allow link to settle if necessary */ + mdelay ( UNDI_INITIALIZE_RETRY_DELAY_MS ); + } } undi->flags |= UNDI_FL_INITIALIZED; |