fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / net / core / link_watch.c
index d43d120..549a2ce 100644 (file)
@@ -11,7 +11,6 @@
  *
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
 #include <linux/if.h>
@@ -35,8 +34,8 @@ enum lw_bits {
 static unsigned long linkwatch_flags;
 static unsigned long linkwatch_nextevent;
 
-static void linkwatch_event(void *dummy);
-static DECLARE_WORK(linkwatch_work, linkwatch_event, NULL);
+static void linkwatch_event(struct work_struct *dummy);
+static DECLARE_DELAYED_WORK(linkwatch_work, linkwatch_event);
 
 static LIST_HEAD(lweventlist);
 static DEFINE_SPINLOCK(lweventlist_lock);
@@ -49,14 +48,52 @@ struct lw_event {
 /* Avoid kmalloc() for most systems */
 static struct lw_event singleevent;
 
+static unsigned char default_operstate(const struct net_device *dev)
+{
+       if (!netif_carrier_ok(dev))
+               return (dev->ifindex != dev->iflink ?
+                       IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN);
+
+       if (netif_dormant(dev))
+               return IF_OPER_DORMANT;
+
+       return IF_OPER_UP;
+}
+
+
+static void rfc2863_policy(struct net_device *dev)
+{
+       unsigned char operstate = default_operstate(dev);
+
+       if (operstate == dev->operstate)
+               return;
+
+       write_lock_bh(&dev_base_lock);
+
+       switch(dev->link_mode) {
+       case IF_LINK_MODE_DORMANT:
+               if (operstate == IF_OPER_UP)
+                       operstate = IF_OPER_DORMANT;
+               break;
+
+       case IF_LINK_MODE_DEFAULT:
+       default:
+               break;
+       };
+
+       dev->operstate = operstate;
+
+       write_unlock_bh(&dev_base_lock);
+}
+
+
 /* Must be called with the rtnl semaphore held */
 void linkwatch_run_queue(void)
 {
-       LIST_HEAD(head);
-       struct list_head *n, *next;
+       struct list_head head, *n, *next;
 
        spin_lock_irq(&lweventlist_lock);
-       list_splice_init(&lweventlist, &head);
+       list_replace_init(&lweventlist, &head);
        spin_unlock_irq(&lweventlist_lock);
 
        list_for_each_safe(n, next, &head) {
@@ -74,6 +111,7 @@ void linkwatch_run_queue(void)
                 */
                clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state);
 
+               rfc2863_policy(dev);
                if (dev->flags & IFF_UP) {
                        if (netif_carrier_ok(dev)) {
                                WARN_ON(dev->qdisc_sleeping == &noop_qdisc);
@@ -89,7 +127,7 @@ void linkwatch_run_queue(void)
 }       
 
 
-static void linkwatch_event(void *dummy)
+static void linkwatch_event(struct work_struct *dummy)
 {
        /* Limit the number of linkwatch events to one
         * per second so that a runaway driver does not
@@ -99,9 +137,9 @@ static void linkwatch_event(void *dummy)
        linkwatch_nextevent = jiffies + HZ;
        clear_bit(LW_RUNNING, &linkwatch_flags);
 
-       rtnl_shlock();
+       rtnl_lock();
        linkwatch_run_queue();
-       rtnl_shunlock();
+       rtnl_unlock();
 }
 
 
@@ -130,13 +168,12 @@ void linkwatch_fire_event(struct net_device *dev)
                spin_unlock_irqrestore(&lweventlist_lock, flags);
 
                if (!test_and_set_bit(LW_RUNNING, &linkwatch_flags)) {
-                       unsigned long thisevent = jiffies;
+                       unsigned long delay = linkwatch_nextevent - jiffies;
 
-                       if (thisevent >= linkwatch_nextevent) {
-                               schedule_work(&linkwatch_work);
-                       } else {
-                               schedule_delayed_work(&linkwatch_work, linkwatch_nextevent - thisevent);
-                       }
+                       /* If we wrap around we'll delay it by at most HZ. */
+                       if (delay > HZ)
+                               delay = 0;
+                       schedule_delayed_work(&linkwatch_work, delay);
                }
        }
 }