summaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
Diffstat (limited to 'net/core')
-rw-r--r--net/core/link_watch.c90
1 files changed, 67 insertions, 23 deletions
diff --git a/net/core/link_watch.c b/net/core/link_watch.c
index 71a35da275d4..b5f45799c2f3 100644
--- a/net/core/link_watch.c
+++ b/net/core/link_watch.c
@@ -77,11 +77,52 @@ static void rfc2863_policy(struct net_device *dev)
}
-/* Must be called with the rtnl semaphore held */
-void linkwatch_run_queue(void)
+static int linkwatch_urgent_event(struct net_device *dev)
+{
+ return netif_running(dev) && netif_carrier_ok(dev) &&
+ dev->qdisc != dev->qdisc_sleeping;
+}
+
+
+static void linkwatch_add_event(struct net_device *dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&lweventlist_lock, flags);
+ dev->link_watch_next = lweventlist;
+ lweventlist = dev;
+ spin_unlock_irqrestore(&lweventlist_lock, flags);
+}
+
+
+static void linkwatch_schedule_work(unsigned long delay)
+{
+ if (test_and_set_bit(LW_RUNNING, &linkwatch_flags))
+ return;
+
+ /* If we wrap around we'll delay it by at most HZ. */
+ if (delay > HZ)
+ delay = 0;
+
+ schedule_delayed_work(&linkwatch_work, delay);
+}
+
+
+static void __linkwatch_run_queue(int urgent_only)
{
struct net_device *next;
+ /*
+ * Limit the number of linkwatch events to one
+ * per second so that a runaway driver does not
+ * cause a storm of messages on the netlink
+ * socket. This limit does not apply to up events
+ * while the device qdisc is down.
+ */
+ if (!urgent_only)
+ linkwatch_nextevent = jiffies + HZ;
+ clear_bit(LW_RUNNING, &linkwatch_flags);
+
spin_lock_irq(&lweventlist_lock);
next = lweventlist;
lweventlist = NULL;
@@ -92,6 +133,11 @@ void linkwatch_run_queue(void)
next = dev->link_watch_next;
+ if (urgent_only && !linkwatch_urgent_event(dev)) {
+ linkwatch_add_event(dev);
+ continue;
+ }
+
/*
* Make sure the above read is complete since it can be
* rewritten as soon as we clear the bit below.
@@ -116,21 +162,23 @@ void linkwatch_run_queue(void)
dev_put(dev);
}
+
+ if (lweventlist)
+ linkwatch_schedule_work(linkwatch_nextevent - jiffies);
}
-static void linkwatch_event(struct work_struct *dummy)
+/* Must be called with the rtnl semaphore held */
+void linkwatch_run_queue(void)
{
- /* Limit the number of linkwatch events to one
- * per second so that a runaway driver does not
- * cause a storm of messages on the netlink
- * socket
- */
- linkwatch_nextevent = jiffies + HZ;
- clear_bit(LW_RUNNING, &linkwatch_flags);
+ __linkwatch_run_queue(0);
+}
+
+static void linkwatch_event(struct work_struct *dummy)
+{
rtnl_lock();
- linkwatch_run_queue();
+ __linkwatch_run_queue(time_after(linkwatch_nextevent, jiffies));
rtnl_unlock();
}
@@ -138,23 +186,19 @@ static void linkwatch_event(struct work_struct *dummy)
void linkwatch_fire_event(struct net_device *dev)
{
if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) {
- unsigned long flags;
+ unsigned long delay;
dev_hold(dev);
- spin_lock_irqsave(&lweventlist_lock, flags);
- dev->link_watch_next = lweventlist;
- lweventlist = dev;
- spin_unlock_irqrestore(&lweventlist_lock, flags);
+ linkwatch_add_event(dev);
- if (!test_and_set_bit(LW_RUNNING, &linkwatch_flags)) {
- unsigned long delay = linkwatch_nextevent - jiffies;
+ delay = linkwatch_nextevent - jiffies;
- /* If we wrap around we'll delay it by at most HZ. */
- if (delay > HZ)
- delay = 0;
- schedule_delayed_work(&linkwatch_work, delay);
- }
+ /* Minimise down-time: drop delay for up event. */
+ if (linkwatch_urgent_event(dev))
+ delay = 0;
+
+ linkwatch_schedule_work(delay);
}
}