]> err.no Git - linux-2.6/blobdiff - drivers/net/bonding/bond_main.c
bonding: refactor ARP active-backup monitor
[linux-2.6] / drivers / net / bonding / bond_main.c
index 2a0039f193770d677a63fef13be00aa744f7a79e..51e0f2de42c62e3c4ee43cc94203986be230b67e 100644 (file)
@@ -88,6 +88,7 @@
 #define BOND_LINK_ARP_INTERV   0
 
 static int max_bonds   = BOND_DEFAULT_MAX_BONDS;
+static int num_grat_arp = 1;
 static int miimon      = BOND_LINK_MON_INTERV;
 static int updelay     = 0;
 static int downdelay   = 0;
@@ -104,6 +105,8 @@ struct bond_params bonding_defaults;
 
 module_param(max_bonds, int, 0);
 MODULE_PARM_DESC(max_bonds, "Max number of bonded devices");
+module_param(num_grat_arp, int, 0644);
+MODULE_PARM_DESC(num_grat_arp, "Number of gratuitous ARP packets to send on failover event");
 module_param(miimon, int, 0);
 MODULE_PARM_DESC(miimon, "Link check interval in milliseconds");
 module_param(updelay, int, 0);
@@ -1048,6 +1051,8 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
        }
 
        if (new_active) {
+               new_active->jiffies = jiffies;
+
                if (new_active->link == BOND_LINK_BACK) {
                        if (USES_PRIMARY(bond->params.mode)) {
                                printk(KERN_INFO DRV_NAME
@@ -1059,7 +1064,6 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
 
                        new_active->delay = 0;
                        new_active->link = BOND_LINK_UP;
-                       new_active->jiffies = jiffies;
 
                        if (bond->params.mode == BOND_MODE_8023AD) {
                                bond_3ad_handle_link_change(new_active, BOND_LINK_UP);
@@ -1109,14 +1113,18 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
                if (new_active && bond->params.fail_over_mac)
                        memcpy(bond->dev->dev_addr,  new_active->dev->dev_addr,
                                new_active->dev->addr_len);
+               bond->send_grat_arp = bond->params.num_grat_arp;
                if (bond->curr_active_slave &&
                        test_bit(__LINK_STATE_LINKWATCH_PENDING,
                                        &bond->curr_active_slave->dev->state)) {
                        dprintk("delaying gratuitous arp on %s\n",
                                bond->curr_active_slave->dev->name);
-                       bond->send_grat_arp = 1;
-               } else
-                       bond_send_gratuitous_arp(bond);
+               } else {
+                       if (bond->send_grat_arp > 0) {
+                               bond_send_gratuitous_arp(bond);
+                               bond->send_grat_arp--;
+                       }
+               }
        }
 }
 
@@ -2144,7 +2152,7 @@ static int __bond_mii_monitor(struct bonding *bond, int have_locks)
                        dprintk("sending delayed gratuitous arp on on %s\n",
                                bond->curr_active_slave->dev->name);
                        bond_send_gratuitous_arp(bond);
-                       bond->send_grat_arp = 0;
+                       bond->send_grat_arp--;
                }
        }
        read_lock(&bond->curr_slave_lock);
@@ -2788,243 +2796,299 @@ out:
 }
 
 /*
- * When using arp monitoring in active-backup mode, this function is
- * called to determine if any backup slaves have went down or a new
- * current slave needs to be found.
- * The backup slaves never generate traffic, they are considered up by merely
- * receiving traffic. If the current slave goes down, each backup slave will
- * be given the opportunity to tx/rx an arp before being taken down - this
- * prevents all slaves from being taken down due to the current slave not
- * sending any traffic for the backups to receive. The arps are not necessarily
- * necessary, any tx and rx traffic will keep the current slave up. While any
- * rx traffic will keep the backup slaves up, the current slave is responsible
- * for generating traffic to keep them up regardless of any other traffic they
- * may have received.
- * see loadbalance_arp_monitor for arp monitoring in load balancing mode
+ * Called to inspect slaves for active-backup mode ARP monitor link state
+ * changes.  Sets new_link in slaves to specify what action should take
+ * place for the slave.  Returns 0 if no changes are found, >0 if changes
+ * to link states must be committed.
+ *
+ * Called with bond->lock held for read.
  */
-void bond_activebackup_arp_mon(struct work_struct *work)
+static int bond_ab_arp_inspect(struct bonding *bond, int delta_in_ticks)
 {
-       struct bonding *bond = container_of(work, struct bonding,
-                                           arp_work.work);
        struct slave *slave;
-       int delta_in_ticks;
-       int i;
+       int i, commit = 0;
 
-       read_lock(&bond->lock);
+       bond_for_each_slave(bond, slave, i) {
+               slave->new_link = BOND_LINK_NOCHANGE;
 
-       delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval);
+               if (slave->link != BOND_LINK_UP) {
+                       if (time_before_eq(jiffies, slave_last_rx(bond, slave) +
+                                          delta_in_ticks)) {
+                               slave->new_link = BOND_LINK_UP;
+                               commit++;
+                       }
 
-       if (bond->kill_timers) {
-               goto out;
-       }
+                       continue;
+               }
 
-       if (bond->slave_cnt == 0) {
-               goto re_arm;
+               /*
+                * Give slaves 2*delta after being enslaved or made
+                * active.  This avoids bouncing, as the last receive
+                * times need a full ARP monitor cycle to be updated.
+                */
+               if (!time_after_eq(jiffies, slave->jiffies +
+                                  2 * delta_in_ticks))
+                       continue;
+
+               /*
+                * Backup slave is down if:
+                * - No current_arp_slave AND
+                * - more than 3*delta since last receive AND
+                * - the bond has an IP address
+                *
+                * Note: a non-null current_arp_slave indicates
+                * the curr_active_slave went down and we are
+                * searching for a new one; under this condition
+                * we only take the curr_active_slave down - this
+                * gives each slave a chance to tx/rx traffic
+                * before being taken out
+                */
+               if (slave->state == BOND_STATE_BACKUP &&
+                   !bond->current_arp_slave &&
+                   time_after(jiffies, slave_last_rx(bond, slave) +
+                              3 * delta_in_ticks)) {
+                       slave->new_link = BOND_LINK_DOWN;
+                       commit++;
+               }
+
+               /*
+                * Active slave is down if:
+                * - more than 2*delta since transmitting OR
+                * - (more than 2*delta since receive AND
+                *    the bond has an IP address)
+                */
+               if ((slave->state == BOND_STATE_ACTIVE) &&
+                   (time_after_eq(jiffies, slave->dev->trans_start +
+                                   2 * delta_in_ticks) ||
+                     (time_after_eq(jiffies, slave_last_rx(bond, slave)
+                                    + 2 * delta_in_ticks)))) {
+                       slave->new_link = BOND_LINK_DOWN;
+                       commit++;
+               }
        }
 
-       /* determine if any slave has come up or any backup slave has
-        * gone down
-        * TODO: what about up/down delay in arp mode? it wasn't here before
-        *       so it can wait
+       read_lock(&bond->curr_slave_lock);
+
+       /*
+        * Trigger a commit if the primary option setting has changed.
         */
-       bond_for_each_slave(bond, slave, i) {
-               if (slave->link != BOND_LINK_UP) {
-                       if (time_before_eq(jiffies,
-                           slave_last_rx(bond, slave) + delta_in_ticks)) {
+       if (bond->primary_slave &&
+           (bond->primary_slave != bond->curr_active_slave) &&
+           (bond->primary_slave->link == BOND_LINK_UP))
+               commit++;
 
-                               slave->link = BOND_LINK_UP;
+       read_unlock(&bond->curr_slave_lock);
 
-                               write_lock_bh(&bond->curr_slave_lock);
+       return commit;
+}
 
-                               if ((!bond->curr_active_slave) &&
-                                   time_before_eq(jiffies, slave->dev->trans_start + delta_in_ticks)) {
-                                       bond_change_active_slave(bond, slave);
-                                       bond->current_arp_slave = NULL;
-                               } else if (bond->curr_active_slave != slave) {
-                                       /* this slave has just come up but we
-                                        * already have a current slave; this
-                                        * can also happen if bond_enslave adds
-                                        * a new slave that is up while we are
-                                        * searching for a new slave
-                                        */
-                                       bond_set_slave_inactive_flags(slave);
-                                       bond->current_arp_slave = NULL;
-                               }
+/*
+ * Called to commit link state changes noted by inspection step of
+ * active-backup mode ARP monitor.
+ *
+ * Called with RTNL and bond->lock for read.
+ */
+static void bond_ab_arp_commit(struct bonding *bond, int delta_in_ticks)
+{
+       struct slave *slave;
+       int i;
 
-                               bond_set_carrier(bond);
+       bond_for_each_slave(bond, slave, i) {
+               switch (slave->new_link) {
+               case BOND_LINK_NOCHANGE:
+                       continue;
 
-                               if (slave == bond->curr_active_slave) {
-                                       printk(KERN_INFO DRV_NAME
-                                              ": %s: %s is up and now the "
-                                              "active interface\n",
-                                              bond->dev->name,
-                                              slave->dev->name);
-                                       netif_carrier_on(bond->dev);
-                               } else {
-                                       printk(KERN_INFO DRV_NAME
-                                              ": %s: backup interface %s is "
-                                              "now up\n",
-                                              bond->dev->name,
-                                              slave->dev->name);
-                               }
+               case BOND_LINK_UP:
+                       write_lock_bh(&bond->curr_slave_lock);
 
-                               write_unlock_bh(&bond->curr_slave_lock);
-                       }
-               } else {
-                       read_lock(&bond->curr_slave_lock);
+                       if (!bond->curr_active_slave &&
+                           time_before_eq(jiffies, slave->dev->trans_start +
+                                          delta_in_ticks)) {
+                               slave->link = BOND_LINK_UP;
+                               bond_change_active_slave(bond, slave);
+                               bond->current_arp_slave = NULL;
 
-                       if ((slave != bond->curr_active_slave) &&
-                           (!bond->current_arp_slave) &&
-                           (time_after_eq(jiffies, slave_last_rx(bond, slave) + 3*delta_in_ticks))) {
-                               /* a backup slave has gone down; three times
-                                * the delta allows the current slave to be
-                                * taken out before the backup slave.
-                                * note: a non-null current_arp_slave indicates
-                                * the curr_active_slave went down and we are
-                                * searching for a new one; under this
-                                * condition we only take the curr_active_slave
-                                * down - this gives each slave a chance to
-                                * tx/rx traffic before being taken out
+                               printk(KERN_INFO DRV_NAME
+                                      ": %s: %s is up and now the "
+                                      "active interface\n",
+                                      bond->dev->name, slave->dev->name);
+
+                       } else if (bond->curr_active_slave != slave) {
+                               /* this slave has just come up but we
+                                * already have a current slave; this can
+                                * also happen if bond_enslave adds a new
+                                * slave that is up while we are searching
+                                * for a new slave
                                 */
+                               slave->link = BOND_LINK_UP;
+                               bond_set_slave_inactive_flags(slave);
+                               bond->current_arp_slave = NULL;
+
+                               printk(KERN_INFO DRV_NAME
+                                      ": %s: backup interface %s is now up\n",
+                                      bond->dev->name, slave->dev->name);
+                       }
 
-                               read_unlock(&bond->curr_slave_lock);
+                       write_unlock_bh(&bond->curr_slave_lock);
 
-                               slave->link  = BOND_LINK_DOWN;
+                       break;
 
-                               if (slave->link_failure_count < UINT_MAX) {
-                                       slave->link_failure_count++;
-                               }
+               case BOND_LINK_DOWN:
+                       if (slave->link_failure_count < UINT_MAX)
+                               slave->link_failure_count++;
+
+                       slave->link = BOND_LINK_DOWN;
+
+                       if (slave == bond->curr_active_slave) {
+                               printk(KERN_INFO DRV_NAME
+                                      ": %s: link status down for active "
+                                      "interface %s, disabling it\n",
+                                      bond->dev->name, slave->dev->name);
 
                                bond_set_slave_inactive_flags(slave);
 
+                               write_lock_bh(&bond->curr_slave_lock);
+
+                               bond_select_active_slave(bond);
+                               if (bond->curr_active_slave)
+                                       bond->curr_active_slave->jiffies =
+                                               jiffies;
+
+                               write_unlock_bh(&bond->curr_slave_lock);
+
+                               bond->current_arp_slave = NULL;
+
+                       } else if (slave->state == BOND_STATE_BACKUP) {
                                printk(KERN_INFO DRV_NAME
                                       ": %s: backup interface %s is now down\n",
-                                      bond->dev->name,
-                                      slave->dev->name);
-                       } else {
-                               read_unlock(&bond->curr_slave_lock);
+                                      bond->dev->name, slave->dev->name);
+
+                               bond_set_slave_inactive_flags(slave);
                        }
+                       break;
+
+               default:
+                       printk(KERN_ERR DRV_NAME
+                              ": %s: impossible: new_link %d on slave %s\n",
+                              bond->dev->name, slave->new_link,
+                              slave->dev->name);
                }
        }
 
-       read_lock(&bond->curr_slave_lock);
-       slave = bond->curr_active_slave;
-       read_unlock(&bond->curr_slave_lock);
-
-       if (slave) {
-               /* if we have sent traffic in the past 2*arp_intervals but
-                * haven't xmit and rx traffic in that time interval, select
-                * a different slave. slave->jiffies is only updated when
-                * a slave first becomes the curr_active_slave - not necessarily
-                * after every arp; this ensures the slave has a full 2*delta
-                * before being taken out. if a primary is being used, check
-                * if it is up and needs to take over as the curr_active_slave
-                */
-               if ((time_after_eq(jiffies, slave->dev->trans_start + 2*delta_in_ticks) ||
-                    (time_after_eq(jiffies, slave_last_rx(bond, slave) + 2*delta_in_ticks))) &&
-                       time_after_eq(jiffies, slave->jiffies + 2*delta_in_ticks)) {
+       /*
+        * No race with changes to primary via sysfs, as we hold rtnl.
+        */
+       if (bond->primary_slave &&
+           (bond->primary_slave != bond->curr_active_slave) &&
+           (bond->primary_slave->link == BOND_LINK_UP)) {
+               write_lock_bh(&bond->curr_slave_lock);
+               bond_change_active_slave(bond, bond->primary_slave);
+               write_unlock_bh(&bond->curr_slave_lock);
+       }
 
-                       slave->link  = BOND_LINK_DOWN;
+       bond_set_carrier(bond);
+}
 
-                       if (slave->link_failure_count < UINT_MAX) {
-                               slave->link_failure_count++;
-                       }
+/*
+ * Send ARP probes for active-backup mode ARP monitor.
+ *
+ * Called with bond->lock held for read.
+ */
+static void bond_ab_arp_probe(struct bonding *bond)
+{
+       struct slave *slave;
+       int i;
 
-                       printk(KERN_INFO DRV_NAME
-                              ": %s: link status down for active interface "
-                              "%s, disabling it\n",
-                              bond->dev->name,
-                              slave->dev->name);
+       read_lock(&bond->curr_slave_lock);
 
-                       write_lock_bh(&bond->curr_slave_lock);
+       if (bond->current_arp_slave && bond->curr_active_slave)
+               printk("PROBE: c_arp %s && cas %s BAD\n",
+                      bond->current_arp_slave->dev->name,
+                      bond->curr_active_slave->dev->name);
 
-                       bond_select_active_slave(bond);
-                       slave = bond->curr_active_slave;
+       if (bond->curr_active_slave) {
+               bond_arp_send_all(bond, bond->curr_active_slave);
+               read_unlock(&bond->curr_slave_lock);
+               return;
+       }
 
-                       write_unlock_bh(&bond->curr_slave_lock);
+       read_unlock(&bond->curr_slave_lock);
 
-                       bond->current_arp_slave = slave;
+       /* if we don't have a curr_active_slave, search for the next available
+        * backup slave from the current_arp_slave and make it the candidate
+        * for becoming the curr_active_slave
+        */
 
-                       if (slave) {
-                               slave->jiffies = jiffies;
-                       }
-               } else if ((bond->primary_slave) &&
-                          (bond->primary_slave != slave) &&
-                          (bond->primary_slave->link == BOND_LINK_UP)) {
-                       /* at this point, slave is the curr_active_slave */
-                       printk(KERN_INFO DRV_NAME
-                              ": %s: changing from interface %s to primary "
-                              "interface %s\n",
-                              bond->dev->name,
-                              slave->dev->name,
-                              bond->primary_slave->dev->name);
+       if (!bond->current_arp_slave) {
+               bond->current_arp_slave = bond->first_slave;
+               if (!bond->current_arp_slave)
+                       return;
+       }
 
-                       /* primary is up so switch to it */
-                       write_lock_bh(&bond->curr_slave_lock);
-                       bond_change_active_slave(bond, bond->primary_slave);
-                       write_unlock_bh(&bond->curr_slave_lock);
+       bond_set_slave_inactive_flags(bond->current_arp_slave);
 
-                       slave = bond->primary_slave;
+       /* search for next candidate */
+       bond_for_each_slave_from(bond, slave, i, bond->current_arp_slave->next) {
+               if (IS_UP(slave->dev)) {
+                       slave->link = BOND_LINK_BACK;
+                       bond_set_slave_active_flags(slave);
+                       bond_arp_send_all(bond, slave);
                        slave->jiffies = jiffies;
-               } else {
-                       bond->current_arp_slave = NULL;
+                       bond->current_arp_slave = slave;
+                       break;
                }
 
-               /* the current slave must tx an arp to ensure backup slaves
-                * rx traffic
+               /* if the link state is up at this point, we
+                * mark it down - this can happen if we have
+                * simultaneous link failures and
+                * reselect_active_interface doesn't make this
+                * one the current slave so it is still marked
+                * up when it is actually down
                 */
-               if (slave && IS_UP(slave->dev))
-                       bond_arp_send_all(bond, slave);
-       }
+               if (slave->link == BOND_LINK_UP) {
+                       slave->link = BOND_LINK_DOWN;
+                       if (slave->link_failure_count < UINT_MAX)
+                               slave->link_failure_count++;
 
-       /* if we don't have a curr_active_slave, search for the next available
-        * backup slave from the current_arp_slave and make it the candidate
-        * for becoming the curr_active_slave
-        */
-       if (!slave) {
-               if (!bond->current_arp_slave) {
-                       bond->current_arp_slave = bond->first_slave;
+                       bond_set_slave_inactive_flags(slave);
+
+                       printk(KERN_INFO DRV_NAME
+                              ": %s: backup interface %s is now down.\n",
+                              bond->dev->name, slave->dev->name);
                }
+       }
+}
 
-               if (bond->current_arp_slave) {
-                       bond_set_slave_inactive_flags(bond->current_arp_slave);
+void bond_activebackup_arp_mon(struct work_struct *work)
+{
+       struct bonding *bond = container_of(work, struct bonding,
+                                           arp_work.work);
+       int delta_in_ticks;
 
-                       /* search for next candidate */
-                       bond_for_each_slave_from(bond, slave, i, bond->current_arp_slave->next) {
-                               if (IS_UP(slave->dev)) {
-                                       slave->link = BOND_LINK_BACK;
-                                       bond_set_slave_active_flags(slave);
-                                       bond_arp_send_all(bond, slave);
-                                       slave->jiffies = jiffies;
-                                       bond->current_arp_slave = slave;
-                                       break;
-                               }
+       read_lock(&bond->lock);
 
-                               /* if the link state is up at this point, we
-                                * mark it down - this can happen if we have
-                                * simultaneous link failures and
-                                * reselect_active_interface doesn't make this
-                                * one the current slave so it is still marked
-                                * up when it is actually down
-                                */
-                               if (slave->link == BOND_LINK_UP) {
-                                       slave->link  = BOND_LINK_DOWN;
-                                       if (slave->link_failure_count < UINT_MAX) {
-                                               slave->link_failure_count++;
-                                       }
+       if (bond->kill_timers)
+               goto out;
 
-                                       bond_set_slave_inactive_flags(slave);
+       delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval);
 
-                                       printk(KERN_INFO DRV_NAME
-                                              ": %s: backup interface %s is "
-                                              "now down.\n",
-                                              bond->dev->name,
-                                              slave->dev->name);
-                               }
-                       }
-               }
+       if (bond->slave_cnt == 0)
+               goto re_arm;
+
+       if (bond_ab_arp_inspect(bond, delta_in_ticks)) {
+               read_unlock(&bond->lock);
+               rtnl_lock();
+               read_lock(&bond->lock);
+
+               bond_ab_arp_commit(bond, delta_in_ticks);
+
+               read_unlock(&bond->lock);
+               rtnl_unlock();
+               read_lock(&bond->lock);
        }
 
+       bond_ab_arp_probe(bond);
+
 re_arm:
        if (bond->params.arp_interval) {
                queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks);
@@ -4626,6 +4690,13 @@ static int bond_check_params(struct bond_params *params)
                use_carrier = 1;
        }
 
+       if (num_grat_arp < 0 || num_grat_arp > 255) {
+               printk(KERN_WARNING DRV_NAME
+                      ": Warning: num_grat_arp (%d) not in range 0-255 so it "
+                      "was reset to 1 \n", num_grat_arp);
+               num_grat_arp = 1;
+       }
+
        /* reset values for 802.3ad */
        if (bond_mode == BOND_MODE_8023AD) {
                if (!miimon) {
@@ -4813,6 +4884,7 @@ static int bond_check_params(struct bond_params *params)
        params->mode = bond_mode;
        params->xmit_policy = xmit_hashtype;
        params->miimon = miimon;
+       params->num_grat_arp = num_grat_arp;
        params->arp_interval = arp_interval;
        params->arp_validate = arp_validate_value;
        params->updelay = updelay;