aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Marineau <mike@marineau.org>2009-01-04 20:18:51 -0500
committerDavid S. Miller <davem@davemloft.net>2009-01-04 20:18:51 -0500
commit22604c866889c4b2e12b73cbf1683bda1b72a313 (patch)
tree4ed1c7cb6cc022b599b8eb3e8bc274dc9df92362
parent745417e20684e4951afcabfe74583a3884e54980 (diff)
net: Fix for initial link state in 2.6.28
From: Michael Marineau <mike@marineau.org> Commit b47300168e770b60ab96c8924854c3b0eb4260eb "Do not fire linkwatch events until the device is registered." was made as a workaround for drivers that call netif_carrier_off before registering the device. Unfortunately this causes these drivers to incorrectly report their link status as IF_OPER_UNKNOWN which can falsely set the IFF_RUNNING flag when the interface is first brought up. This issues was previously pointed out[1] but was dismissed saying that IFF_RUNNING is not related to the link status. From my digging IFF_RUNNING, as reported to userspace, is based on the link state. It is set based on __LINK_STATE_START and IF_OPER_UP or IF_OPER_UNKNOWN. See [2], [3], and [4]. (Whether or not the kernel has IFF_RUNNING set in flags is not reported to user space so it may well be independent of the link, I don't know if and when it may get set.) The end result depends slightly depending on the driver. The the two I tested were e1000e and b44. With e1000e if the system is booted without a network cable attached the interface will falsely report RUNNING when it is brought up causing NetworkManager to attempt to start it and eventually time out. With b44 when the system is booted with a network cable attached and brought up with dhcpcd it will time out the first time. The attached patch that will still set the operstate variable correctly to IF_OPER_UP/DOWN/etc when linkwatch_fire_event is called but then return rather than skipping the linkwatch_fire_event call entirely as the previous fix did. (sorry it isn't inline, I don't have a patch friendly email client at the moment) Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/core/link_watch.c7
-rw-r--r--net/sched/sch_generic.c4
2 files changed, 6 insertions, 5 deletions
diff --git a/net/core/link_watch.c b/net/core/link_watch.c
index bf8f7af699d7..1e401e12dc72 100644
--- a/net/core/link_watch.c
+++ b/net/core/link_watch.c
@@ -178,7 +178,6 @@ static void __linkwatch_run_queue(int urgent_only)
178 */ 178 */
179 clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state); 179 clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state);
180 180
181 rfc2863_policy(dev);
182 if (dev->flags & IFF_UP) { 181 if (dev->flags & IFF_UP) {
183 if (netif_carrier_ok(dev)) 182 if (netif_carrier_ok(dev))
184 dev_activate(dev); 183 dev_activate(dev);
@@ -215,6 +214,12 @@ void linkwatch_fire_event(struct net_device *dev)
215{ 214{
216 bool urgent = linkwatch_urgent_event(dev); 215 bool urgent = linkwatch_urgent_event(dev);
217 216
217 rfc2863_policy(dev);
218
219 /* Some drivers call netif_carrier_off early */
220 if (dev->reg_state == NETREG_UNINITIALIZED)
221 return;
222
218 if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) { 223 if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) {
219 dev_hold(dev); 224 dev_hold(dev);
220 225
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 5f5efe4e6072..23a8e6141a00 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -270,8 +270,6 @@ static void dev_watchdog_down(struct net_device *dev)
270void netif_carrier_on(struct net_device *dev) 270void netif_carrier_on(struct net_device *dev)
271{ 271{
272 if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) { 272 if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) {
273 if (dev->reg_state == NETREG_UNINITIALIZED)
274 return;
275 linkwatch_fire_event(dev); 273 linkwatch_fire_event(dev);
276 if (netif_running(dev)) 274 if (netif_running(dev))
277 __netdev_watchdog_up(dev); 275 __netdev_watchdog_up(dev);
@@ -288,8 +286,6 @@ EXPORT_SYMBOL(netif_carrier_on);
288void netif_carrier_off(struct net_device *dev) 286void netif_carrier_off(struct net_device *dev)
289{ 287{
290 if (!test_and_set_bit(__LINK_STATE_NOCARRIER, &dev->state)) { 288 if (!test_and_set_bit(__LINK_STATE_NOCARRIER, &dev->state)) {
291 if (dev->reg_state == NETREG_UNINITIALIZED)
292 return;
293 linkwatch_fire_event(dev); 289 linkwatch_fire_event(dev);
294 } 290 }
295} 291}