]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[undi] Retry PXENV_UNDI_INITIALIZE multiple times
authorMichael Brown <mcb30@ipxe.org>
Thu, 8 Dec 2011 00:06:53 +0000 (00:06 +0000)
committerMichael Brown <mcb30@ipxe.org>
Thu, 8 Dec 2011 00:12:28 +0000 (00:12 +0000)
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>
src/arch/i386/drivers/net/undinet.c

index c391469f8ed58d8b588ba023e297294215e4527c..63b373898e7745c2f8e20f16a8ce498e5d1aea35 100644 (file)
@@ -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;