Merge git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
[pandora-kernel.git] / drivers / net / bonding / bond_main.c
index 862ed8e..423298c 100644 (file)
@@ -188,6 +188,7 @@ struct bond_parm_tbl arp_validate_tbl[] = {
 /*-------------------------- Forward declarations ---------------------------*/
 
 static void bond_send_gratuitous_arp(struct bonding *bond);
+static void bond_deinit(struct net_device *bond_dev);
 
 /*---------------------------- General routines -----------------------------*/
 
@@ -1590,15 +1591,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
        case BOND_MODE_TLB:
        case BOND_MODE_ALB:
                new_slave->state = BOND_STATE_ACTIVE;
-               if ((!bond->curr_active_slave) &&
-                   (new_slave->link != BOND_LINK_DOWN)) {
-                       /* first slave or no active slave yet, and this link
-                        * is OK, so make this interface the active one
-                        */
-                       bond_change_active_slave(bond, new_slave);
-               } else {
-                       bond_set_slave_inactive_flags(new_slave);
-               }
+               bond_set_slave_inactive_flags(new_slave);
                break;
        default:
                dprintk("This slave is always active in trunk mode\n");
@@ -1754,9 +1747,23 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
                bond_alb_deinit_slave(bond, slave);
        }
 
-       if (oldcurrent == slave)
+       if (oldcurrent == slave) {
+               /*
+                * Note that we hold RTNL over this sequence, so there
+                * is no concern that another slave add/remove event
+                * will interfere.
+                */
+               write_unlock_bh(&bond->lock);
+               read_lock(&bond->lock);
+               write_lock_bh(&bond->curr_slave_lock);
+
                bond_select_active_slave(bond);
 
+               write_unlock_bh(&bond->curr_slave_lock);
+               read_unlock(&bond->lock);
+               write_lock_bh(&bond->lock);
+       }
+
        if (bond->slave_cnt == 0) {
                bond_set_carrier(bond);
 
@@ -2012,16 +2019,19 @@ static int bond_ioctl_change_active(struct net_device *bond_dev, struct net_devi
                return -EINVAL;
        }
 
-       write_lock_bh(&bond->lock);
+       read_lock(&bond->lock);
 
+       read_lock(&bond->curr_slave_lock);
        old_active = bond->curr_active_slave;
+       read_unlock(&bond->curr_slave_lock);
+
        new_active = bond_get_slave_by_dev(bond, slave_dev);
 
        /*
         * Changing to the current active: do nothing; return success.
         */
        if (new_active && (new_active == old_active)) {
-               write_unlock_bh(&bond->lock);
+               read_unlock(&bond->lock);
                return 0;
        }
 
@@ -2029,12 +2039,14 @@ static int bond_ioctl_change_active(struct net_device *bond_dev, struct net_devi
            (old_active) &&
            (new_active->link == BOND_LINK_UP) &&
            IS_UP(new_active->dev)) {
+               write_lock_bh(&bond->curr_slave_lock);
                bond_change_active_slave(bond, new_active);
+               write_unlock_bh(&bond->curr_slave_lock);
        } else {
                res = -EINVAL;
        }
 
-       write_unlock_bh(&bond->lock);
+       read_unlock(&bond->lock);
 
        return res;
 }
@@ -2046,9 +2058,9 @@ static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
        info->bond_mode = bond->params.mode;
        info->miimon = bond->params.miimon;
 
-       read_lock_bh(&bond->lock);
+       read_lock(&bond->lock);
        info->num_slaves = bond->slave_cnt;
-       read_unlock_bh(&bond->lock);
+       read_unlock(&bond->lock);
 
        return 0;
 }
@@ -2063,7 +2075,7 @@ static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *in
                return -ENODEV;
        }
 
-       read_lock_bh(&bond->lock);
+       read_lock(&bond->lock);
 
        bond_for_each_slave(bond, slave, i) {
                if (i == (int)info->slave_id) {
@@ -2072,7 +2084,7 @@ static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *in
                }
        }
 
-       read_unlock_bh(&bond->lock);
+       read_unlock(&bond->lock);
 
        if (found) {
                strcpy(info->slave_name, slave->dev->name);
@@ -2088,27 +2100,25 @@ static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *in
 
 /*-------------------------------- Monitoring -------------------------------*/
 
-/* this function is called regularly to monitor each slave's link. */
-void bond_mii_monitor(struct work_struct *work)
+/*
+ * if !have_locks, return nonzero if a failover is necessary.  if
+ * have_locks, do whatever failover activities are needed.
+ *
+ * This is to separate the inspection and failover steps for locking
+ * purposes; failover requires rtnl, but acquiring it for every
+ * inspection is undesirable, so a wrapper first does inspection, and
+ * the acquires the necessary locks and calls again to perform
+ * failover if needed.  Since all locks are dropped, a complete
+ * restart is needed between calls.
+ */
+static int __bond_mii_monitor(struct bonding *bond, int have_locks)
 {
-       struct bonding *bond = container_of(work, struct bonding,
-                                           mii_work.work);
        struct slave *slave, *oldcurrent;
        int do_failover = 0;
-       int delta_in_ticks;
        int i;
 
-       read_lock(&bond->lock);
-
-       delta_in_ticks = (bond->params.miimon * HZ) / 1000;
-
-       if (bond->kill_timers) {
+       if (bond->slave_cnt == 0)
                goto out;
-       }
-
-       if (bond->slave_cnt == 0) {
-               goto re_arm;
-       }
 
        /* we will try to read the link status of each of our slaves, and
         * set their IFF_RUNNING flag appropriately. For each slave not
@@ -2142,7 +2152,11 @@ void bond_mii_monitor(struct work_struct *work)
                switch (slave->link) {
                case BOND_LINK_UP:      /* the link was up */
                        if (link_state == BMSR_LSTATUS) {
-                               /* link stays up, nothing more to do */
+                               if (!oldcurrent) {
+                                       if (!have_locks)
+                                               return 1;
+                                       do_failover = 1;
+                               }
                                break;
                        } else { /* link going down */
                                slave->link  = BOND_LINK_FAIL;
@@ -2175,6 +2189,9 @@ void bond_mii_monitor(struct work_struct *work)
                        if (link_state != BMSR_LSTATUS) {
                                /* link stays down */
                                if (slave->delay <= 0) {
+                                       if (!have_locks)
+                                               return 1;
+
                                        /* link down for too long time */
                                        slave->link = BOND_LINK_DOWN;
 
@@ -2258,6 +2275,9 @@ void bond_mii_monitor(struct work_struct *work)
                        } else {
                                /* link stays up */
                                if (slave->delay == 0) {
+                                       if (!have_locks)
+                                               return 1;
+
                                        /* now the link has been up for long time enough */
                                        slave->link = BOND_LINK_UP;
                                        slave->jiffies = jiffies;
@@ -2323,21 +2343,52 @@ void bond_mii_monitor(struct work_struct *work)
        } /* end of for */
 
        if (do_failover) {
-               write_lock(&bond->curr_slave_lock);
+               ASSERT_RTNL();
+
+               write_lock_bh(&bond->curr_slave_lock);
 
                bond_select_active_slave(bond);
 
-               write_unlock(&bond->curr_slave_lock);
+               write_unlock_bh(&bond->curr_slave_lock);
+
        } else
                bond_set_carrier(bond);
 
-re_arm:
-       if (bond->params.miimon)
-               queue_delayed_work(bond->wq, &bond->mii_work, delta_in_ticks);
 out:
-       read_unlock(&bond->lock);
+       return 0;
 }
 
+/*
+ * bond_mii_monitor
+ *
+ * Really a wrapper that splits the mii monitor into two phases: an
+ * inspection, then (if inspection indicates something needs to be
+ * done) an acquisition of appropriate locks followed by another pass
+ * to implement whatever link state changes are indicated.
+ */
+void bond_mii_monitor(struct work_struct *work)
+{
+       struct bonding *bond = container_of(work, struct bonding,
+                                           mii_work.work);
+       unsigned long delay;
+
+       read_lock(&bond->lock);
+       if (bond->kill_timers) {
+               read_unlock(&bond->lock);
+               return;
+       }
+       if (__bond_mii_monitor(bond, 0)) {
+               read_unlock(&bond->lock);
+               rtnl_lock();
+               read_lock(&bond->lock);
+               __bond_mii_monitor(bond, 1);
+               rtnl_unlock();
+       }
+
+       delay = ((bond->params.miimon * HZ) / 1000) ? : 1;
+       read_unlock(&bond->lock);
+       queue_delayed_work(bond->wq, &bond->mii_work, delay);
+}
 
 static __be32 bond_glean_dev_ip(struct net_device *dev)
 {
@@ -2738,11 +2789,14 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
        }
 
        if (do_failover) {
-               write_lock(&bond->curr_slave_lock);
+               rtnl_lock();
+               write_lock_bh(&bond->curr_slave_lock);
 
                bond_select_active_slave(bond);
 
-               write_unlock(&bond->curr_slave_lock);
+               write_unlock_bh(&bond->curr_slave_lock);
+               rtnl_unlock();
+
        }
 
 re_arm:
@@ -2799,7 +2853,9 @@ void bond_activebackup_arp_mon(struct work_struct *work)
 
                                slave->link = BOND_LINK_UP;
 
-                               write_lock(&bond->curr_slave_lock);
+                               rtnl_lock();
+
+                               write_lock_bh(&bond->curr_slave_lock);
 
                                if ((!bond->curr_active_slave) &&
                                    ((jiffies - slave->dev->trans_start) <= delta_in_ticks)) {
@@ -2833,7 +2889,8 @@ void bond_activebackup_arp_mon(struct work_struct *work)
                                               slave->dev->name);
                                }
 
-                               write_unlock(&bond->curr_slave_lock);
+                               write_unlock_bh(&bond->curr_slave_lock);
+                               rtnl_unlock();
                        }
                } else {
                        read_lock(&bond->curr_slave_lock);
@@ -2903,12 +2960,15 @@ void bond_activebackup_arp_mon(struct work_struct *work)
                               bond->dev->name,
                               slave->dev->name);
 
-                       write_lock(&bond->curr_slave_lock);
+                       rtnl_lock();
+                       write_lock_bh(&bond->curr_slave_lock);
 
                        bond_select_active_slave(bond);
                        slave = bond->curr_active_slave;
 
-                       write_unlock(&bond->curr_slave_lock);
+                       write_unlock_bh(&bond->curr_slave_lock);
+
+                       rtnl_unlock();
 
                        bond->current_arp_slave = slave;
 
@@ -2927,9 +2987,12 @@ void bond_activebackup_arp_mon(struct work_struct *work)
                               bond->primary_slave->dev->name);
 
                        /* primary is up so switch to it */
-                       write_lock(&bond->curr_slave_lock);
+                       rtnl_lock();
+                       write_lock_bh(&bond->curr_slave_lock);
                        bond_change_active_slave(bond, bond->primary_slave);
-                       write_unlock(&bond->curr_slave_lock);
+                       write_unlock_bh(&bond->curr_slave_lock);
+
+                       rtnl_unlock();
 
                        slave = bond->primary_slave;
                        slave->jiffies = jiffies;
@@ -3016,7 +3079,7 @@ static void *bond_info_seq_start(struct seq_file *seq, loff_t *pos)
 
        /* make sure the bond won't be taken away */
        read_lock(&dev_base_lock);
-       read_lock_bh(&bond->lock);
+       read_lock(&bond->lock);
 
        if (*pos == 0) {
                return SEQ_START_TOKEN;
@@ -3050,7 +3113,7 @@ static void bond_info_seq_stop(struct seq_file *seq, void *v)
 {
        struct bonding *bond = seq->private;
 
-       read_unlock_bh(&bond->lock);
+       read_unlock(&bond->lock);
        read_unlock(&dev_base_lock);
 }
 
@@ -3619,7 +3682,7 @@ static int bond_open(struct net_device *bond_dev)
        }
 
        if (bond->params.mode == BOND_MODE_8023AD) {
-               INIT_DELAYED_WORK(&bond->ad_work, bond_alb_monitor);
+               INIT_DELAYED_WORK(&bond->ad_work, bond_3ad_state_machine_handler);
                queue_delayed_work(bond->wq, &bond->ad_work, 0);
                /* register to receive LACPDUs */
                bond_register_lacpdu(bond);
@@ -3759,13 +3822,13 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
                if (mii->reg_num == 1) {
                        struct bonding *bond = bond_dev->priv;
                        mii->val_out = 0;
-                       read_lock_bh(&bond->lock);
+                       read_lock(&bond->lock);
                        read_lock(&bond->curr_slave_lock);
                        if (netif_carrier_ok(bond->dev)) {
                                mii->val_out = BMSR_LSTATUS;
                        }
                        read_unlock(&bond->curr_slave_lock);
-                       read_unlock_bh(&bond->lock);
+                       read_unlock(&bond->lock);
                }
 
                return 0;
@@ -4342,6 +4405,7 @@ static int bond_init(struct net_device *bond_dev, struct bond_params *params)
        bond_dev->set_multicast_list = bond_set_multicast_list;
        bond_dev->change_mtu = bond_change_mtu;
        bond_dev->set_mac_address = bond_set_mac_address;
+       bond_dev->validate_addr = NULL;
 
        bond_set_mode_ops(bond, bond->params.mode);
 
@@ -4387,7 +4451,7 @@ static int bond_init(struct net_device *bond_dev, struct bond_params *params)
 /* De-initialize device specific data.
  * Caller must hold rtnl_lock.
  */
-void bond_deinit(struct net_device *bond_dev)
+static void bond_deinit(struct net_device *bond_dev)
 {
        struct bonding *bond = bond_dev->priv;