]> Pileus Git - ~andy/linux/commitdiff
net: Set device operstate at registration time
authorBen Hutchings <bhutchings@solarflare.com>
Mon, 20 Aug 2012 21:16:51 +0000 (22:16 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 24 Aug 2012 16:46:13 +0000 (12:46 -0400)
The operstate of a device is initially IF_OPER_UNKNOWN and is updated
asynchronously by linkwatch after each change of carrier state
reported by the driver.  The default carrier state of a net device is
on, and this will never be changed on drivers that do not support
carrier detection, thus the operstate remains IF_OPER_UNKNOWN.

For devices that do support carrier detection, the driver must set the
carrier state to off initially, then poll the hardware state when the
device is opened.  However, we must not activate linkwatch for a
unregistered device, and commit b473001 ('net: Do not fire linkwatch
events until the device is registered.') ensured that we don't.  But
this means that the operstate for many devices that support carrier
detection remains IF_OPER_UNKNOWN when it should be IF_OPER_DOWN.

The same issue exists with the dormant state.

The proper initialisation sequence, avoiding a race with opening of
the device, is:

        rtnl_lock();
        rc = register_netdevice(dev);
        if (rc)
                goto out_unlock;
        netif_carrier_off(dev); /* or netif_dormant_on(dev) */
        rtnl_unlock();

but it seems silly that this should have to be repeated in so many
drivers.  Further, the operstate seen immediately after opening the
device may still be IF_OPER_UNKNOWN due to the asynchronous nature of
linkwatch.

Commit 22604c8 ('net: Fix for initial link state in 2.6.28') attempted
to fix this by setting the operstate synchronously, but it was
reverted as it could lead to deadlock.

This initialises the operstate synchronously at registration time
only.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netdevice.h
net/core/dev.c
net/core/link_watch.c

index 9ad7fa8c10e0672b6c67580d6a0459d636e9d3f9..ccac82e61604873c7b3b2ec26251e1ae45b46c1e 100644 (file)
@@ -2227,6 +2227,7 @@ static inline void dev_hold(struct net_device *dev)
  * kind of lower layer not just hardware media.
  */
 
+extern void linkwatch_init_dev(struct net_device *dev);
 extern void linkwatch_fire_event(struct net_device *dev);
 extern void linkwatch_forget_dev(struct net_device *dev);
 
index bc857fead8c82831b2caa202baf0cff735bd83d2..2f25d0cac51cb9667be8828f40a547c20cf3d52c 100644 (file)
@@ -5648,6 +5648,8 @@ int register_netdevice(struct net_device *dev)
 
        set_bit(__LINK_STATE_PRESENT, &dev->state);
 
+       linkwatch_init_dev(dev);
+
        dev_init_scheduler(dev);
        dev_hold(dev);
        list_netdevice(dev);
index c3519c6d1b169a5c895efd781c7958218d7f8dc6..a01922219a23da6cbd23f5506b2b5ce329b2f617 100644 (file)
@@ -76,6 +76,14 @@ static void rfc2863_policy(struct net_device *dev)
 }
 
 
+void linkwatch_init_dev(struct net_device *dev)
+{
+       /* Handle pre-registration link state changes */
+       if (!netif_carrier_ok(dev) || netif_dormant(dev))
+               rfc2863_policy(dev);
+}
+
+
 static bool linkwatch_urgent_event(struct net_device *dev)
 {
        if (!netif_running(dev))