Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f0c76d61 authored by Jay Vosburgh's avatar Jay Vosburgh Committed by Jeff Garzik
Browse files

bonding: refactor mii monitor



	Refactor mii monitor.  As with the previous ARP monitor refactor,
the motivation for this is to handle locking rationally (in this case,
removing conditional locking) and generally clean up the code.

	This patch breaks up the monolithic mii monitor into two phases:
an inspection phase, followed by an optional commit phase.  The commit phase
is the only portion that requires RTNL or makes changes to state, and is
only called when inspection finds something to change.

Signed-off-by: default avatarJay Vosburgh <fubar@us.ibm.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent c16d1185
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -2107,6 +2107,7 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
			aggregator = __get_first_agg(port);
			aggregator = __get_first_agg(port);
			ad_agg_selection_logic(aggregator);
			ad_agg_selection_logic(aggregator);
		}
		}
		bond_3ad_set_carrier(bond);
	}
	}


	// for each port run the state machines
	// for each port run the state machines
+172 −222
Original line number Original line Diff line number Diff line
@@ -2223,174 +2223,118 @@ static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *in


/*-------------------------------- Monitoring -------------------------------*/
/*-------------------------------- Monitoring -------------------------------*/


/*
 * 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 slave *slave, *oldcurrent;
	int do_failover = 0;
	int i;


	if (bond->slave_cnt == 0)
static int bond_miimon_inspect(struct bonding *bond)
		goto out;
{

	struct slave *slave;
	/* we will try to read the link status of each of our slaves, and
	int i, link_state, commit = 0;
	 * set their IFF_RUNNING flag appropriately. For each slave not
	 * supporting MII status, we won't do anything so that a user-space
	 * program could monitor the link itself if needed.
	 */

	read_lock(&bond->curr_slave_lock);
	oldcurrent = bond->curr_active_slave;
	read_unlock(&bond->curr_slave_lock);


	bond_for_each_slave(bond, slave, i) {
	bond_for_each_slave(bond, slave, i) {
		struct net_device *slave_dev = slave->dev;
		slave->new_link = BOND_LINK_NOCHANGE;
		int link_state;
		u16 old_speed = slave->speed;
		u8 old_duplex = slave->duplex;


		link_state = bond_check_dev_link(bond, slave_dev, 0);
		link_state = bond_check_dev_link(bond, slave->dev, 0);


		switch (slave->link) {
		switch (slave->link) {
		case BOND_LINK_UP:	/* the link was up */
		case BOND_LINK_UP:
			if (link_state == BMSR_LSTATUS) {
			if (link_state)
				if (!oldcurrent) {
				continue;
					if (!have_locks)

						return 1;
					do_failover = 1;
				}
				break;
			} else { /* link going down */
			slave->link = BOND_LINK_FAIL;
			slave->link = BOND_LINK_FAIL;
			slave->delay = bond->params.downdelay;
			slave->delay = bond->params.downdelay;

			if (slave->delay) {
				if (slave->link_failure_count < UINT_MAX) {
					slave->link_failure_count++;
				}

				if (bond->params.downdelay) {
				printk(KERN_INFO DRV_NAME
				printk(KERN_INFO DRV_NAME
				       ": %s: link status down for %s"
				       ": %s: link status down for %s"
					       "interface %s, disabling it in "
				       "interface %s, disabling it in %d ms.\n",
					       "%d ms.\n",
				       bond->dev->name,
				       bond->dev->name,
					       IS_UP(slave_dev)
				       (bond->params.mode ==
					       ? ((bond->params.mode == BOND_MODE_ACTIVEBACKUP)
					BOND_MODE_ACTIVEBACKUP) ?
						  ? ((slave == oldcurrent)
				       ((slave->state == BOND_STATE_ACTIVE) ?
						     ? "active " : "backup ")
					"active " : "backup ") : "",
						  : "")
				       slave->dev->name,
					       : "idle ",
					       slave_dev->name,
				       bond->params.downdelay * bond->params.miimon);
				       bond->params.downdelay * bond->params.miimon);
			}
			}
			}
			/*FALLTHRU*/
			/* no break ! fall through the BOND_LINK_FAIL test to
		case BOND_LINK_FAIL:
			   ensure proper action to be taken
			if (link_state) {
			*/
				/*
		case BOND_LINK_FAIL:	/* the link has just gone down */
				 * recovered before downdelay expired
			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;

					/* in active/backup mode, we must
					 * completely disable this interface
				 */
				 */
					if ((bond->params.mode == BOND_MODE_ACTIVEBACKUP) ||
					    (bond->params.mode == BOND_MODE_8023AD)) {
						bond_set_slave_inactive_flags(slave);
					}

					printk(KERN_INFO DRV_NAME
					       ": %s: link status definitely "
					       "down for interface %s, "
					       "disabling it\n",
					       bond->dev->name,
					       slave_dev->name);

					/* notify ad that the link status has changed */
					if (bond->params.mode == BOND_MODE_8023AD) {
						bond_3ad_handle_link_change(slave, BOND_LINK_DOWN);
					}

					if ((bond->params.mode == BOND_MODE_TLB) ||
					    (bond->params.mode == BOND_MODE_ALB)) {
						bond_alb_handle_link_change(bond, slave, BOND_LINK_DOWN);
					}

					if (slave == oldcurrent) {
						do_failover = 1;
					}
				} else {
					slave->delay--;
				}
			} else {
				/* link up again */
				slave->link = BOND_LINK_UP;
				slave->link = BOND_LINK_UP;
				slave->jiffies = jiffies;
				slave->jiffies = jiffies;
				printk(KERN_INFO DRV_NAME
				printk(KERN_INFO DRV_NAME
				       ": %s: link status up again after %d "
				       ": %s: link status up again after %d "
				       "ms for interface %s.\n",
				       "ms for interface %s.\n",
				       bond->dev->name,
				       bond->dev->name,
				       (bond->params.downdelay - slave->delay) * bond->params.miimon,
				       (bond->params.downdelay - slave->delay) *
				       slave_dev->name);
				       bond->params.miimon,
				       slave->dev->name);
				continue;
			}
			}

			if (slave->delay <= 0) {
				slave->new_link = BOND_LINK_DOWN;
				commit++;
				continue;
			}

			slave->delay--;
			break;
			break;
		case BOND_LINK_DOWN:	/* the link was down */

			if (link_state != BMSR_LSTATUS) {
		case BOND_LINK_DOWN:
				/* the link stays down, nothing more to do */
			if (!link_state)
				break;
				continue;
			} else {	/* link going up */

			slave->link = BOND_LINK_BACK;
			slave->link = BOND_LINK_BACK;
			slave->delay = bond->params.updelay;
			slave->delay = bond->params.updelay;


				if (bond->params.updelay) {
			if (slave->delay) {
					/* if updelay == 0, no need to
					   advertise about a 0 ms delay */
				printk(KERN_INFO DRV_NAME
				printk(KERN_INFO DRV_NAME
				       ": %s: link status up for "
				       ": %s: link status up for "
					       "interface %s, enabling it "
				       "interface %s, enabling it in %d ms.\n",
					       "in %d ms.\n",
				       bond->dev->name, slave->dev->name,
					       bond->dev->name,
				       bond->params.updelay *
					       slave_dev->name,
				       bond->params.miimon);
					       bond->params.updelay * bond->params.miimon);
				}
			}
			}
			/* no break ! fall through the BOND_LINK_BACK state in
			/*FALLTHRU*/
			   case there's something to do.
		case BOND_LINK_BACK:
			*/
			if (!link_state) {
		case BOND_LINK_BACK:	/* the link has just come back */
			if (link_state != BMSR_LSTATUS) {
				/* link down again */
				slave->link = BOND_LINK_DOWN;
				slave->link = BOND_LINK_DOWN;

				printk(KERN_INFO DRV_NAME
				printk(KERN_INFO DRV_NAME
				       ": %s: link status down again after %d "
				       ": %s: link status down again after %d "
				       "ms for interface %s.\n",
				       "ms for interface %s.\n",
				       bond->dev->name,
				       bond->dev->name,
				       (bond->params.updelay - slave->delay) * bond->params.miimon,
				       (bond->params.updelay - slave->delay) *
				       slave_dev->name);
				       bond->params.miimon,
			} else {
				       slave->dev->name);
				/* link stays up */

				if (slave->delay == 0) {
				continue;
					if (!have_locks)
			}
						return 1;

			if (slave->delay <= 0) {
				slave->new_link = BOND_LINK_UP;
				commit++;
				continue;
			}

			slave->delay--;
			break;
		}
	}

	return commit;
}

static void bond_miimon_commit(struct bonding *bond)
{
	struct slave *slave;
	int i;


					/* now the link has been up for long time enough */
	bond_for_each_slave(bond, slave, i) {
		switch (slave->new_link) {
		case BOND_LINK_NOCHANGE:
			continue;

		case BOND_LINK_UP:
			slave->link = BOND_LINK_UP;
			slave->link = BOND_LINK_UP;
			slave->jiffies = jiffies;
			slave->jiffies = jiffies;


@@ -2408,87 +2352,88 @@ static int __bond_mii_monitor(struct bonding *bond, int have_locks)
			printk(KERN_INFO DRV_NAME
			printk(KERN_INFO DRV_NAME
			       ": %s: link status definitely "
			       ": %s: link status definitely "
			       "up for interface %s.\n",
			       "up for interface %s.\n",
					       bond->dev->name,
			       bond->dev->name, slave->dev->name);
					       slave_dev->name);


			/* notify ad that the link status has changed */
			/* notify ad that the link status has changed */
					if (bond->params.mode == BOND_MODE_8023AD) {
			if (bond->params.mode == BOND_MODE_8023AD)
				bond_3ad_handle_link_change(slave, BOND_LINK_UP);
				bond_3ad_handle_link_change(slave, BOND_LINK_UP);
					}


			if ((bond->params.mode == BOND_MODE_TLB) ||
			if ((bond->params.mode == BOND_MODE_TLB) ||
					    (bond->params.mode == BOND_MODE_ALB)) {
			    (bond->params.mode == BOND_MODE_ALB))
						bond_alb_handle_link_change(bond, slave, BOND_LINK_UP);
				bond_alb_handle_link_change(bond, slave,
					}
							    BOND_LINK_UP);


					if ((!oldcurrent) ||
			if (!bond->curr_active_slave ||
					    (slave == bond->primary_slave)) {
			    (slave == bond->primary_slave))
						do_failover = 1;
				goto do_failover;
					}

				} else {
			continue;
					slave->delay--;
				}
			}
			break;
		default:
			/* Should not happen */
			printk(KERN_ERR DRV_NAME
			       ": %s: Error: %s Illegal value (link=%d)\n",
			       bond->dev->name,
			       slave->dev->name,
			       slave->link);
			goto out;
		} /* end of switch (slave->link) */


		bond_update_speed_duplex(slave);
		case BOND_LINK_DOWN:
			slave->link = BOND_LINK_DOWN;


		if (bond->params.mode == BOND_MODE_8023AD) {
			if (bond->params.mode == BOND_MODE_ACTIVEBACKUP ||
			if (old_speed != slave->speed) {
			    bond->params.mode == BOND_MODE_8023AD)
				bond_3ad_adapter_speed_changed(slave);
				bond_set_slave_inactive_flags(slave);
			}


			if (old_duplex != slave->duplex) {
			printk(KERN_INFO DRV_NAME
				bond_3ad_adapter_duplex_changed(slave);
			       ": %s: link status definitely down for "
			}
			       "interface %s, disabling it\n",
		}
			       bond->dev->name, slave->dev->name);

			if (bond->params.mode == BOND_MODE_8023AD)
				bond_3ad_handle_link_change(slave,
							    BOND_LINK_DOWN);


	} /* end of for */
			if (bond->params.mode == BOND_MODE_TLB ||
			    bond->params.mode == BOND_MODE_ALB)
				bond_alb_handle_link_change(bond, slave,
							    BOND_LINK_DOWN);


	if (do_failover) {
			if (slave == bond->curr_active_slave)
		ASSERT_RTNL();
				goto do_failover;


		write_lock_bh(&bond->curr_slave_lock);
			continue;


		bond_select_active_slave(bond);
		default:
			printk(KERN_ERR DRV_NAME
			       ": %s: invalid new link %d on slave %s\n",
			       bond->dev->name, slave->new_link,
			       slave->dev->name);
			slave->new_link = BOND_LINK_NOCHANGE;


			continue;
		}

do_failover:
		ASSERT_RTNL();
		write_lock_bh(&bond->curr_slave_lock);
		bond_select_active_slave(bond);
		write_unlock_bh(&bond->curr_slave_lock);
		write_unlock_bh(&bond->curr_slave_lock);
	}


	} else
	bond_set_carrier(bond);
	bond_set_carrier(bond);

out:
	return 0;
}
}


/*
/*
 * bond_mii_monitor
 * bond_mii_monitor
 *
 *
 * Really a wrapper that splits the mii monitor into two phases: an
 * Really a wrapper that splits the mii monitor into two phases: an
 * inspection, then (if inspection indicates something needs to be
 * inspection, then (if inspection indicates something needs to be done)
 * done) an acquisition of appropriate locks followed by another pass
 * an acquisition of appropriate locks followed by a commit phase to
 * to implement whatever link state changes are indicated.
 * implement whatever link state changes are indicated.
 */
 */
void bond_mii_monitor(struct work_struct *work)
void bond_mii_monitor(struct work_struct *work)
{
{
	struct bonding *bond = container_of(work, struct bonding,
	struct bonding *bond = container_of(work, struct bonding,
					    mii_work.work);
					    mii_work.work);
	unsigned long delay;


	read_lock(&bond->lock);
	read_lock(&bond->lock);
	if (bond->kill_timers) {
	if (bond->kill_timers)
		read_unlock(&bond->lock);
		goto out;
		return;

	}
	if (bond->slave_cnt == 0)
		goto re_arm;


	if (bond->send_grat_arp) {
	if (bond->send_grat_arp) {
		read_lock(&bond->curr_slave_lock);
		read_lock(&bond->curr_slave_lock);
@@ -2496,19 +2441,24 @@ void bond_mii_monitor(struct work_struct *work)
		read_unlock(&bond->curr_slave_lock);
		read_unlock(&bond->curr_slave_lock);
	}
	}


	if (__bond_mii_monitor(bond, 0)) {
	if (bond_miimon_inspect(bond)) {
		read_unlock(&bond->lock);
		read_unlock(&bond->lock);
		rtnl_lock();
		rtnl_lock();
		read_lock(&bond->lock);
		read_lock(&bond->lock);
		__bond_mii_monitor(bond, 1);

		bond_miimon_commit(bond);

		read_unlock(&bond->lock);
		read_unlock(&bond->lock);
		rtnl_unlock();	/* might sleep, hold no other locks */
		rtnl_unlock();	/* might sleep, hold no other locks */
		read_lock(&bond->lock);
		read_lock(&bond->lock);
	}
	}


	delay = msecs_to_jiffies(bond->params.miimon);
re_arm:
	if (bond->params.miimon)
		queue_delayed_work(bond->wq, &bond->mii_work,
				   msecs_to_jiffies(bond->params.miimon));
out:
	read_unlock(&bond->lock);
	read_unlock(&bond->lock);
	queue_delayed_work(bond->wq, &bond->mii_work, delay);
}
}


static __be32 bond_glean_dev_ip(struct net_device *dev)
static __be32 bond_glean_dev_ip(struct net_device *dev)