ipconfig: Handle devices which take some time to come up.
David S. Miller [Fri, 12 Mar 2010 08:00:17 +0000 (00:00 -0800)]
Some network devices, particularly USB ones, take several seconds to
fully init and appear in the device list.

If the user turned ipconfig on, they are using it for NFS root or some
other early booting purpose.  So it makes no sense to just flat out
fail immediately if the device isn't found.

It also doesn't make sense to just jack up the initial wait to
something crazy like 10 seconds.

Instead, poll immediately, and then periodically once a second,
waiting for a usable device to appear.  Fail after 12 seconds.

Signed-off-by: David S. Miller <davem@davemloft.net>
Tested-by: Christian Pellegrin <chripell@fsfe.org>

net/ipv4/ipconfig.c

index 10a6a60..6789092 100644 (file)
@@ -187,6 +187,16 @@ struct ic_device {
 static struct ic_device *ic_first_dev __initdata = NULL;/* List of open device */
 static struct net_device *ic_dev __initdata = NULL;    /* Selected device */
 
+static bool __init ic_device_match(struct net_device *dev)
+{
+       if (user_dev_name[0] ? !strcmp(dev->name, user_dev_name) :
+           (!(dev->flags & IFF_LOOPBACK) &&
+            (dev->flags & (IFF_POINTOPOINT|IFF_BROADCAST)) &&
+            strncmp(dev->name, "dummy", 5)))
+               return true;
+       return false;
+}
+
 static int __init ic_open_devs(void)
 {
        struct ic_device *d, **last;
@@ -207,10 +217,7 @@ static int __init ic_open_devs(void)
        for_each_netdev(&init_net, dev) {
                if (dev->flags & IFF_LOOPBACK)
                        continue;
-               if (user_dev_name[0] ? !strcmp(dev->name, user_dev_name) :
-                   (!(dev->flags & IFF_LOOPBACK) &&
-                    (dev->flags & (IFF_POINTOPOINT|IFF_BROADCAST)) &&
-                    strncmp(dev->name, "dummy", 5))) {
+               if (ic_device_match(dev)) {
                        int able = 0;
                        if (dev->mtu >= 364)
                                able |= IC_BOOTP;
@@ -228,7 +235,7 @@ static int __init ic_open_devs(void)
                        }
                        if (!(d = kmalloc(sizeof(struct ic_device), GFP_KERNEL))) {
                                rtnl_unlock();
-                               return -1;
+                               return -ENOMEM;
                        }
                        d->dev = dev;
                        *last = d;
@@ -253,7 +260,7 @@ static int __init ic_open_devs(void)
                        printk(KERN_ERR "IP-Config: Device `%s' not found.\n", user_dev_name);
                else
                        printk(KERN_ERR "IP-Config: No network devices available.\n");
-               return -1;
+               return -ENODEV;
        }
        return 0;
 }
@@ -1303,6 +1310,32 @@ __be32 __init root_nfs_parse_addr(char *name)
        return addr;
 }
 
+#define DEVICE_WAIT_MAX                12 /* 12 seconds */
+
+static int __init wait_for_devices(void)
+{
+       int i;
+
+       msleep(CONF_PRE_OPEN);
+       for (i = 0; i < DEVICE_WAIT_MAX; i++) {
+               struct net_device *dev;
+               int found = 0;
+
+               rtnl_lock();
+               for_each_netdev(&init_net, dev) {
+                       if (ic_device_match(dev)) {
+                               found = 1;
+                               break;
+                       }
+               }
+               rtnl_unlock();
+               if (found)
+                       return 0;
+               ssleep(1);
+       }
+       return -ENODEV;
+}
+
 /*
  *     IP Autoconfig dispatcher.
  */
@@ -1313,6 +1346,7 @@ static int __init ip_auto_config(void)
 #ifdef IPCONFIG_DYNAMIC
        int retries = CONF_OPEN_RETRIES;
 #endif
+       int err;
 
 #ifdef CONFIG_PROC_FS
        proc_net_fops_create(&init_net, "pnp", S_IRUGO, &pnp_seq_fops);
@@ -1325,12 +1359,15 @@ static int __init ip_auto_config(void)
 #ifdef IPCONFIG_DYNAMIC
  try_try_again:
 #endif
-       /* Give hardware a chance to settle */
-       msleep(CONF_PRE_OPEN);
+       /* Wait for devices to appear */
+       err = wait_for_devices();
+       if (err)
+               return err;
 
        /* Setup all network devices */
-       if (ic_open_devs() < 0)
-               return -1;
+       err = ic_open_devs();
+       if (err)
+               return err;
 
        /* Give drivers a chance to settle */
        ssleep(CONF_POST_OPEN);