[NET] link_watch: Always schedule urgent events

Urgent events may be delayed if we already have a non-urgent event
queued for that device.  This patch changes this by making sure that
an urgent event is always looked at immediately.

I've replaced the LW_RUNNING flag by LW_URGENT since whether work
is scheduled is already kept track by the work queue system.

The only complication is that we have to provide some exclusion for
the setting linkwatch_nextevent which is available in the actual
work function.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Herbert Xu 2007-05-09 00:17:30 -07:00 committed by David S. Miller
parent db0ccffed9
commit d9568ba91b

View File

@ -26,7 +26,7 @@
enum lw_bits { enum lw_bits {
LW_RUNNING = 0, LW_URGENT = 0,
}; };
static unsigned long linkwatch_flags; static unsigned long linkwatch_flags;
@ -95,18 +95,41 @@ static void linkwatch_add_event(struct net_device *dev)
} }
static void linkwatch_schedule_work(unsigned long delay) static void linkwatch_schedule_work(int urgent)
{ {
if (test_and_set_bit(LW_RUNNING, &linkwatch_flags)) unsigned long delay = linkwatch_nextevent - jiffies;
if (test_bit(LW_URGENT, &linkwatch_flags))
return; return;
/* If we wrap around we'll delay it by at most HZ. */ /* Minimise down-time: drop delay for up event. */
if (delay > HZ) { if (urgent) {
linkwatch_nextevent = jiffies; if (test_and_set_bit(LW_URGENT, &linkwatch_flags))
return;
delay = 0; delay = 0;
} }
schedule_delayed_work(&linkwatch_work, delay); /* If we wrap around we'll delay it by at most HZ. */
if (delay > HZ)
delay = 0;
/*
* This is true if we've scheduled it immeditately or if we don't
* need an immediate execution and it's already pending.
*/
if (schedule_delayed_work(&linkwatch_work, delay) == !delay)
return;
/* Don't bother if there is nothing urgent. */
if (!test_bit(LW_URGENT, &linkwatch_flags))
return;
/* It's already running which is good enough. */
if (!cancel_delayed_work(&linkwatch_work))
return;
/* Otherwise we reschedule it again for immediate exection. */
schedule_delayed_work(&linkwatch_work, 0);
} }
@ -123,7 +146,11 @@ static void __linkwatch_run_queue(int urgent_only)
*/ */
if (!urgent_only) if (!urgent_only)
linkwatch_nextevent = jiffies + HZ; linkwatch_nextevent = jiffies + HZ;
clear_bit(LW_RUNNING, &linkwatch_flags); /* Limit wrap-around effect on delay. */
else if (time_after(linkwatch_nextevent, jiffies + HZ))
linkwatch_nextevent = jiffies;
clear_bit(LW_URGENT, &linkwatch_flags);
spin_lock_irq(&lweventlist_lock); spin_lock_irq(&lweventlist_lock);
next = lweventlist; next = lweventlist;
@ -166,7 +193,7 @@ static void __linkwatch_run_queue(int urgent_only)
} }
if (lweventlist) if (lweventlist)
linkwatch_schedule_work(linkwatch_nextevent - jiffies); linkwatch_schedule_work(0);
} }
@ -187,21 +214,16 @@ static void linkwatch_event(struct work_struct *dummy)
void linkwatch_fire_event(struct net_device *dev) void linkwatch_fire_event(struct net_device *dev)
{ {
if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) { int urgent = linkwatch_urgent_event(dev);
unsigned long delay;
if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) {
dev_hold(dev); dev_hold(dev);
linkwatch_add_event(dev); linkwatch_add_event(dev);
} else if (!urgent)
return;
delay = linkwatch_nextevent - jiffies; linkwatch_schedule_work(urgent);
/* Minimise down-time: drop delay for up event. */
if (linkwatch_urgent_event(dev))
delay = 0;
linkwatch_schedule_work(delay);
}
} }
EXPORT_SYMBOL(linkwatch_fire_event); EXPORT_SYMBOL(linkwatch_fire_event);