Commit 8f4cccbb authored by Ben Hutchings's avatar Ben Hutchings Committed by David S. Miller

net: Set device operstate at registration time

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 b4730016 ('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 22604c86 ('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: default avatarBen Hutchings <bhutchings@solarflare.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9f35a734
...@@ -2227,6 +2227,7 @@ static inline void dev_hold(struct net_device *dev) ...@@ -2227,6 +2227,7 @@ static inline void dev_hold(struct net_device *dev)
* kind of lower layer not just hardware media. * 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_fire_event(struct net_device *dev);
extern void linkwatch_forget_dev(struct net_device *dev); extern void linkwatch_forget_dev(struct net_device *dev);
......
...@@ -5648,6 +5648,8 @@ int register_netdevice(struct net_device *dev) ...@@ -5648,6 +5648,8 @@ int register_netdevice(struct net_device *dev)
set_bit(__LINK_STATE_PRESENT, &dev->state); set_bit(__LINK_STATE_PRESENT, &dev->state);
linkwatch_init_dev(dev);
dev_init_scheduler(dev); dev_init_scheduler(dev);
dev_hold(dev); dev_hold(dev);
list_netdevice(dev); list_netdevice(dev);
......
...@@ -76,6 +76,14 @@ static void rfc2863_policy(struct net_device *dev) ...@@ -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) static bool linkwatch_urgent_event(struct net_device *dev)
{ {
if (!netif_running(dev)) if (!netif_running(dev))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment