|
@@ -77,6 +77,7 @@
|
|
|
#include <net/net_namespace.h>
|
|
|
#include <net/netns/generic.h>
|
|
|
#include <net/pkt_sched.h>
|
|
|
+#include <linux/rculist.h>
|
|
|
#include "bonding.h"
|
|
|
#include "bond_3ad.h"
|
|
|
#include "bond_alb.h"
|
|
@@ -106,7 +107,7 @@ static char *arp_ip_target[BOND_MAX_ARP_TARGETS];
|
|
|
static char *arp_validate;
|
|
|
static char *arp_all_targets;
|
|
|
static char *fail_over_mac;
|
|
|
-static int all_slaves_active = 0;
|
|
|
+static int all_slaves_active;
|
|
|
static struct bond_params bonding_defaults;
|
|
|
static int resend_igmp = BOND_DEFAULT_RESEND_IGMP;
|
|
|
|
|
@@ -273,7 +274,7 @@ const char *bond_mode_name(int mode)
|
|
|
[BOND_MODE_ALB] = "adaptive load balancing",
|
|
|
};
|
|
|
|
|
|
- if (mode < 0 || mode > BOND_MODE_ALB)
|
|
|
+ if (mode < BOND_MODE_ROUNDROBIN || mode > BOND_MODE_ALB)
|
|
|
return "unknown";
|
|
|
|
|
|
return names[mode];
|
|
@@ -281,116 +282,6 @@ const char *bond_mode_name(int mode)
|
|
|
|
|
|
/*---------------------------------- VLAN -----------------------------------*/
|
|
|
|
|
|
-/**
|
|
|
- * bond_add_vlan - add a new vlan id on bond
|
|
|
- * @bond: bond that got the notification
|
|
|
- * @vlan_id: the vlan id to add
|
|
|
- *
|
|
|
- * Returns -ENOMEM if allocation failed.
|
|
|
- */
|
|
|
-static int bond_add_vlan(struct bonding *bond, unsigned short vlan_id)
|
|
|
-{
|
|
|
- struct vlan_entry *vlan;
|
|
|
-
|
|
|
- pr_debug("bond: %s, vlan id %d\n",
|
|
|
- (bond ? bond->dev->name : "None"), vlan_id);
|
|
|
-
|
|
|
- vlan = kzalloc(sizeof(struct vlan_entry), GFP_KERNEL);
|
|
|
- if (!vlan)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- INIT_LIST_HEAD(&vlan->vlan_list);
|
|
|
- vlan->vlan_id = vlan_id;
|
|
|
-
|
|
|
- write_lock_bh(&bond->lock);
|
|
|
-
|
|
|
- list_add_tail(&vlan->vlan_list, &bond->vlan_list);
|
|
|
-
|
|
|
- write_unlock_bh(&bond->lock);
|
|
|
-
|
|
|
- pr_debug("added VLAN ID %d on bond %s\n", vlan_id, bond->dev->name);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * bond_del_vlan - delete a vlan id from bond
|
|
|
- * @bond: bond that got the notification
|
|
|
- * @vlan_id: the vlan id to delete
|
|
|
- *
|
|
|
- * returns -ENODEV if @vlan_id was not found in @bond.
|
|
|
- */
|
|
|
-static int bond_del_vlan(struct bonding *bond, unsigned short vlan_id)
|
|
|
-{
|
|
|
- struct vlan_entry *vlan;
|
|
|
- int res = -ENODEV;
|
|
|
-
|
|
|
- pr_debug("bond: %s, vlan id %d\n", bond->dev->name, vlan_id);
|
|
|
-
|
|
|
- block_netpoll_tx();
|
|
|
- write_lock_bh(&bond->lock);
|
|
|
-
|
|
|
- list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
|
|
- if (vlan->vlan_id == vlan_id) {
|
|
|
- list_del(&vlan->vlan_list);
|
|
|
-
|
|
|
- if (bond_is_lb(bond))
|
|
|
- bond_alb_clear_vlan(bond, vlan_id);
|
|
|
-
|
|
|
- pr_debug("removed VLAN ID %d from bond %s\n",
|
|
|
- vlan_id, bond->dev->name);
|
|
|
-
|
|
|
- kfree(vlan);
|
|
|
-
|
|
|
- res = 0;
|
|
|
- goto out;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- pr_debug("couldn't find VLAN ID %d in bond %s\n",
|
|
|
- vlan_id, bond->dev->name);
|
|
|
-
|
|
|
-out:
|
|
|
- write_unlock_bh(&bond->lock);
|
|
|
- unblock_netpoll_tx();
|
|
|
- return res;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * bond_next_vlan - safely skip to the next item in the vlans list.
|
|
|
- * @bond: the bond we're working on
|
|
|
- * @curr: item we're advancing from
|
|
|
- *
|
|
|
- * Returns %NULL if list is empty, bond->next_vlan if @curr is %NULL,
|
|
|
- * or @curr->next otherwise (even if it is @curr itself again).
|
|
|
- *
|
|
|
- * Caller must hold bond->lock
|
|
|
- */
|
|
|
-struct vlan_entry *bond_next_vlan(struct bonding *bond, struct vlan_entry *curr)
|
|
|
-{
|
|
|
- struct vlan_entry *next, *last;
|
|
|
-
|
|
|
- if (list_empty(&bond->vlan_list))
|
|
|
- return NULL;
|
|
|
-
|
|
|
- if (!curr) {
|
|
|
- next = list_entry(bond->vlan_list.next,
|
|
|
- struct vlan_entry, vlan_list);
|
|
|
- } else {
|
|
|
- last = list_entry(bond->vlan_list.prev,
|
|
|
- struct vlan_entry, vlan_list);
|
|
|
- if (last == curr) {
|
|
|
- next = list_entry(bond->vlan_list.next,
|
|
|
- struct vlan_entry, vlan_list);
|
|
|
- } else {
|
|
|
- next = list_entry(curr->vlan_list.next,
|
|
|
- struct vlan_entry, vlan_list);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return next;
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* bond_dev_queue_xmit - Prepare skb for xmit.
|
|
|
*
|
|
@@ -441,28 +332,20 @@ static int bond_vlan_rx_add_vid(struct net_device *bond_dev,
|
|
|
__be16 proto, u16 vid)
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
|
- struct slave *slave, *stop_at;
|
|
|
- int i, res;
|
|
|
+ struct slave *slave;
|
|
|
+ int res;
|
|
|
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
res = vlan_vid_add(slave->dev, proto, vid);
|
|
|
if (res)
|
|
|
goto unwind;
|
|
|
}
|
|
|
|
|
|
- res = bond_add_vlan(bond, vid);
|
|
|
- if (res) {
|
|
|
- pr_err("%s: Error: Failed to add vlan id %d\n",
|
|
|
- bond_dev->name, vid);
|
|
|
- return res;
|
|
|
- }
|
|
|
-
|
|
|
return 0;
|
|
|
|
|
|
unwind:
|
|
|
- /* unwind from head to the slave that failed */
|
|
|
- stop_at = slave;
|
|
|
- bond_for_each_slave_from_to(bond, slave, i, bond->first_slave, stop_at)
|
|
|
+ /* unwind from the slave that failed */
|
|
|
+ bond_for_each_slave_continue_reverse(bond, slave)
|
|
|
vlan_vid_del(slave->dev, proto, vid);
|
|
|
|
|
|
return res;
|
|
@@ -478,48 +361,16 @@ static int bond_vlan_rx_kill_vid(struct net_device *bond_dev,
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
|
struct slave *slave;
|
|
|
- int i, res;
|
|
|
|
|
|
- bond_for_each_slave(bond, slave, i)
|
|
|
+ bond_for_each_slave(bond, slave)
|
|
|
vlan_vid_del(slave->dev, proto, vid);
|
|
|
|
|
|
- res = bond_del_vlan(bond, vid);
|
|
|
- if (res) {
|
|
|
- pr_err("%s: Error: Failed to remove vlan id %d\n",
|
|
|
- bond_dev->name, vid);
|
|
|
- return res;
|
|
|
- }
|
|
|
+ if (bond_is_lb(bond))
|
|
|
+ bond_alb_clear_vlan(bond, vid);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void bond_add_vlans_on_slave(struct bonding *bond, struct net_device *slave_dev)
|
|
|
-{
|
|
|
- struct vlan_entry *vlan;
|
|
|
- int res;
|
|
|
-
|
|
|
- list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
|
|
- res = vlan_vid_add(slave_dev, htons(ETH_P_8021Q),
|
|
|
- vlan->vlan_id);
|
|
|
- if (res)
|
|
|
- pr_warning("%s: Failed to add vlan id %d to device %s\n",
|
|
|
- bond->dev->name, vlan->vlan_id,
|
|
|
- slave_dev->name);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static void bond_del_vlans_from_slave(struct bonding *bond,
|
|
|
- struct net_device *slave_dev)
|
|
|
-{
|
|
|
- struct vlan_entry *vlan;
|
|
|
-
|
|
|
- list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
|
|
- if (!vlan->vlan_id)
|
|
|
- continue;
|
|
|
- vlan_vid_del(slave_dev, htons(ETH_P_8021Q), vlan->vlan_id);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
/*------------------------------- Link status -------------------------------*/
|
|
|
|
|
|
/*
|
|
@@ -532,15 +383,14 @@ static void bond_del_vlans_from_slave(struct bonding *bond,
|
|
|
static int bond_set_carrier(struct bonding *bond)
|
|
|
{
|
|
|
struct slave *slave;
|
|
|
- int i;
|
|
|
|
|
|
- if (bond->slave_cnt == 0)
|
|
|
+ if (list_empty(&bond->slave_list))
|
|
|
goto down;
|
|
|
|
|
|
if (bond->params.mode == BOND_MODE_8023AD)
|
|
|
return bond_3ad_set_carrier(bond);
|
|
|
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
if (slave->link == BOND_LINK_UP) {
|
|
|
if (!netif_carrier_ok(bond->dev)) {
|
|
|
netif_carrier_on(bond->dev);
|
|
@@ -681,8 +531,8 @@ static int bond_set_promiscuity(struct bonding *bond, int inc)
|
|
|
}
|
|
|
} else {
|
|
|
struct slave *slave;
|
|
|
- int i;
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
err = dev_set_promiscuity(slave->dev, inc);
|
|
|
if (err)
|
|
|
return err;
|
|
@@ -705,8 +555,8 @@ static int bond_set_allmulti(struct bonding *bond, int inc)
|
|
|
}
|
|
|
} else {
|
|
|
struct slave *slave;
|
|
|
- int i;
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
err = dev_set_allmulti(slave->dev, inc);
|
|
|
if (err)
|
|
|
return err;
|
|
@@ -715,15 +565,6 @@ static int bond_set_allmulti(struct bonding *bond, int inc)
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-static void __bond_resend_igmp_join_requests(struct net_device *dev)
|
|
|
-{
|
|
|
- struct in_device *in_dev;
|
|
|
-
|
|
|
- in_dev = __in_dev_get_rcu(dev);
|
|
|
- if (in_dev)
|
|
|
- ip_mc_rejoin_groups(in_dev);
|
|
|
-}
|
|
|
-
|
|
|
/*
|
|
|
* Retrieve the list of registered multicast addresses for the bonding
|
|
|
* device and retransmit an IGMP JOIN request to the current active
|
|
@@ -731,33 +572,12 @@ static void __bond_resend_igmp_join_requests(struct net_device *dev)
|
|
|
*/
|
|
|
static void bond_resend_igmp_join_requests(struct bonding *bond)
|
|
|
{
|
|
|
- struct net_device *bond_dev, *vlan_dev, *upper_dev;
|
|
|
- struct vlan_entry *vlan;
|
|
|
-
|
|
|
- read_lock(&bond->lock);
|
|
|
- rcu_read_lock();
|
|
|
-
|
|
|
- bond_dev = bond->dev;
|
|
|
-
|
|
|
- /* rejoin all groups on bond device */
|
|
|
- __bond_resend_igmp_join_requests(bond_dev);
|
|
|
-
|
|
|
- /*
|
|
|
- * if bond is enslaved to a bridge,
|
|
|
- * then rejoin all groups on its master
|
|
|
- */
|
|
|
- upper_dev = netdev_master_upper_dev_get_rcu(bond_dev);
|
|
|
- if (upper_dev && upper_dev->priv_flags & IFF_EBRIDGE)
|
|
|
- __bond_resend_igmp_join_requests(upper_dev);
|
|
|
-
|
|
|
- /* rejoin all groups on vlan devices */
|
|
|
- list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
|
|
- vlan_dev = __vlan_find_dev_deep(bond_dev, htons(ETH_P_8021Q),
|
|
|
- vlan->vlan_id);
|
|
|
- if (vlan_dev)
|
|
|
- __bond_resend_igmp_join_requests(vlan_dev);
|
|
|
+ if (!rtnl_trylock()) {
|
|
|
+ queue_delayed_work(bond->wq, &bond->mcast_work, 1);
|
|
|
+ return;
|
|
|
}
|
|
|
- rcu_read_unlock();
|
|
|
+ call_netdevice_notifiers(NETDEV_RESEND_IGMP, bond->dev);
|
|
|
+ rtnl_unlock();
|
|
|
|
|
|
/* We use curr_slave_lock to protect against concurrent access to
|
|
|
* igmp_retrans from multiple running instances of this function and
|
|
@@ -769,7 +589,6 @@ static void bond_resend_igmp_join_requests(struct bonding *bond)
|
|
|
queue_delayed_work(bond->wq, &bond->mcast_work, HZ/5);
|
|
|
}
|
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
|
|
- read_unlock(&bond->lock);
|
|
|
}
|
|
|
|
|
|
static void bond_resend_igmp_join_requests_delayed(struct work_struct *work)
|
|
@@ -808,6 +627,8 @@ static void bond_hw_addr_flush(struct net_device *bond_dev,
|
|
|
static void bond_hw_addr_swap(struct bonding *bond, struct slave *new_active,
|
|
|
struct slave *old_active)
|
|
|
{
|
|
|
+ ASSERT_RTNL();
|
|
|
+
|
|
|
if (old_active) {
|
|
|
if (bond->dev->flags & IFF_PROMISC)
|
|
|
dev_set_promiscuity(old_active->dev, -1);
|
|
@@ -966,9 +787,8 @@ static struct slave *bond_find_best_slave(struct bonding *bond)
|
|
|
new_active = bond->curr_active_slave;
|
|
|
|
|
|
if (!new_active) { /* there were no active slaves left */
|
|
|
- if (bond->slave_cnt > 0) /* found one slave */
|
|
|
- new_active = bond->first_slave;
|
|
|
- else
|
|
|
+ new_active = bond_first_slave(bond);
|
|
|
+ if (!new_active)
|
|
|
return NULL; /* still no slave, return NULL */
|
|
|
}
|
|
|
|
|
@@ -1008,7 +828,6 @@ static bool bond_should_notify_peers(struct bonding *bond)
|
|
|
test_bit(__LINK_STATE_LINKWATCH_PENDING, &slave->dev->state))
|
|
|
return false;
|
|
|
|
|
|
- bond->send_peer_notif--;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
@@ -1071,7 +890,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
|
|
|
if (new_active)
|
|
|
bond_set_slave_active_flags(new_active);
|
|
|
} else {
|
|
|
- bond->curr_active_slave = new_active;
|
|
|
+ rcu_assign_pointer(bond->curr_active_slave, new_active);
|
|
|
}
|
|
|
|
|
|
if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) {
|
|
@@ -1115,7 +934,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
|
|
|
((USES_PRIMARY(bond->params.mode) && new_active) ||
|
|
|
bond->params.mode == BOND_MODE_ROUNDROBIN)) {
|
|
|
bond->igmp_retrans = bond->params.resend_igmp;
|
|
|
- queue_delayed_work(bond->wq, &bond->mcast_work, 0);
|
|
|
+ queue_delayed_work(bond->wq, &bond->mcast_work, 1);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1161,17 +980,7 @@ void bond_select_active_slave(struct bonding *bond)
|
|
|
*/
|
|
|
static void bond_attach_slave(struct bonding *bond, struct slave *new_slave)
|
|
|
{
|
|
|
- if (bond->first_slave == NULL) { /* attaching the first slave */
|
|
|
- new_slave->next = new_slave;
|
|
|
- new_slave->prev = new_slave;
|
|
|
- bond->first_slave = new_slave;
|
|
|
- } else {
|
|
|
- new_slave->next = bond->first_slave;
|
|
|
- new_slave->prev = bond->first_slave->prev;
|
|
|
- new_slave->next->prev = new_slave;
|
|
|
- new_slave->prev->next = new_slave;
|
|
|
- }
|
|
|
-
|
|
|
+ list_add_tail_rcu(&new_slave->list, &bond->slave_list);
|
|
|
bond->slave_cnt++;
|
|
|
}
|
|
|
|
|
@@ -1187,22 +996,7 @@ static void bond_attach_slave(struct bonding *bond, struct slave *new_slave)
|
|
|
*/
|
|
|
static void bond_detach_slave(struct bonding *bond, struct slave *slave)
|
|
|
{
|
|
|
- if (slave->next)
|
|
|
- slave->next->prev = slave->prev;
|
|
|
-
|
|
|
- if (slave->prev)
|
|
|
- slave->prev->next = slave->next;
|
|
|
-
|
|
|
- if (bond->first_slave == slave) { /* slave is the first slave */
|
|
|
- if (bond->slave_cnt > 1) { /* there are more slave */
|
|
|
- bond->first_slave = slave->next;
|
|
|
- } else {
|
|
|
- bond->first_slave = NULL; /* slave was the last one */
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- slave->next = NULL;
|
|
|
- slave->prev = NULL;
|
|
|
+ list_del_rcu(&slave->list);
|
|
|
bond->slave_cnt--;
|
|
|
}
|
|
|
|
|
@@ -1249,47 +1043,31 @@ static void bond_poll_controller(struct net_device *bond_dev)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
-static void __bond_netpoll_cleanup(struct bonding *bond)
|
|
|
+static void bond_netpoll_cleanup(struct net_device *bond_dev)
|
|
|
{
|
|
|
+ struct bonding *bond = netdev_priv(bond_dev);
|
|
|
struct slave *slave;
|
|
|
- int i;
|
|
|
|
|
|
- bond_for_each_slave(bond, slave, i)
|
|
|
+ bond_for_each_slave(bond, slave)
|
|
|
if (IS_UP(slave->dev))
|
|
|
slave_disable_netpoll(slave);
|
|
|
}
|
|
|
-static void bond_netpoll_cleanup(struct net_device *bond_dev)
|
|
|
-{
|
|
|
- struct bonding *bond = netdev_priv(bond_dev);
|
|
|
-
|
|
|
- read_lock(&bond->lock);
|
|
|
- __bond_netpoll_cleanup(bond);
|
|
|
- read_unlock(&bond->lock);
|
|
|
-}
|
|
|
|
|
|
static int bond_netpoll_setup(struct net_device *dev, struct netpoll_info *ni, gfp_t gfp)
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(dev);
|
|
|
struct slave *slave;
|
|
|
- int i, err = 0;
|
|
|
+ int err = 0;
|
|
|
|
|
|
- read_lock(&bond->lock);
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
err = slave_enable_netpoll(slave);
|
|
|
if (err) {
|
|
|
- __bond_netpoll_cleanup(bond);
|
|
|
+ bond_netpoll_cleanup(dev);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
- read_unlock(&bond->lock);
|
|
|
return err;
|
|
|
}
|
|
|
-
|
|
|
-static struct netpoll_info *bond_netpoll_info(struct bonding *bond)
|
|
|
-{
|
|
|
- return bond->dev->npinfo;
|
|
|
-}
|
|
|
-
|
|
|
#else
|
|
|
static inline int slave_enable_netpoll(struct slave *slave)
|
|
|
{
|
|
@@ -1306,34 +1084,29 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev)
|
|
|
/*---------------------------------- IOCTL ----------------------------------*/
|
|
|
|
|
|
static netdev_features_t bond_fix_features(struct net_device *dev,
|
|
|
- netdev_features_t features)
|
|
|
+ netdev_features_t features)
|
|
|
{
|
|
|
- struct slave *slave;
|
|
|
struct bonding *bond = netdev_priv(dev);
|
|
|
netdev_features_t mask;
|
|
|
- int i;
|
|
|
-
|
|
|
- read_lock(&bond->lock);
|
|
|
+ struct slave *slave;
|
|
|
|
|
|
- if (!bond->first_slave) {
|
|
|
+ if (list_empty(&bond->slave_list)) {
|
|
|
/* Disable adding VLANs to empty bond. But why? --mq */
|
|
|
features |= NETIF_F_VLAN_CHALLENGED;
|
|
|
- goto out;
|
|
|
+ return features;
|
|
|
}
|
|
|
|
|
|
mask = features;
|
|
|
features &= ~NETIF_F_ONE_FOR_ALL;
|
|
|
features |= NETIF_F_ALL_FOR_ALL;
|
|
|
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
features = netdev_increment_features(features,
|
|
|
slave->dev->features,
|
|
|
mask);
|
|
|
}
|
|
|
features = netdev_add_tso_features(features, mask);
|
|
|
|
|
|
-out:
|
|
|
- read_unlock(&bond->lock);
|
|
|
return features;
|
|
|
}
|
|
|
|
|
@@ -1343,21 +1116,18 @@ out:
|
|
|
|
|
|
static void bond_compute_features(struct bonding *bond)
|
|
|
{
|
|
|
- struct slave *slave;
|
|
|
- struct net_device *bond_dev = bond->dev;
|
|
|
+ unsigned int flags, dst_release_flag = IFF_XMIT_DST_RELEASE;
|
|
|
netdev_features_t vlan_features = BOND_VLAN_FEATURES;
|
|
|
unsigned short max_hard_header_len = ETH_HLEN;
|
|
|
unsigned int gso_max_size = GSO_MAX_SIZE;
|
|
|
+ struct net_device *bond_dev = bond->dev;
|
|
|
u16 gso_max_segs = GSO_MAX_SEGS;
|
|
|
- int i;
|
|
|
- unsigned int flags, dst_release_flag = IFF_XMIT_DST_RELEASE;
|
|
|
-
|
|
|
- read_lock(&bond->lock);
|
|
|
+ struct slave *slave;
|
|
|
|
|
|
- if (!bond->first_slave)
|
|
|
+ if (list_empty(&bond->slave_list))
|
|
|
goto done;
|
|
|
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
vlan_features = netdev_increment_features(vlan_features,
|
|
|
slave->dev->vlan_features, BOND_VLAN_FEATURES);
|
|
|
|
|
@@ -1378,8 +1148,6 @@ done:
|
|
|
flags = bond_dev->priv_flags & ~IFF_XMIT_DST_RELEASE;
|
|
|
bond_dev->priv_flags = flags | dst_release_flag;
|
|
|
|
|
|
- read_unlock(&bond->lock);
|
|
|
-
|
|
|
netdev_change_features(bond_dev);
|
|
|
}
|
|
|
|
|
@@ -1545,7 +1313,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|
|
* bond ether type mutual exclusion - don't allow slaves of dissimilar
|
|
|
* ether type (eg ARPHRD_ETHER and ARPHRD_INFINIBAND) share the same bond
|
|
|
*/
|
|
|
- if (bond->slave_cnt == 0) {
|
|
|
+ if (list_empty(&bond->slave_list)) {
|
|
|
if (bond_dev->type != slave_dev->type) {
|
|
|
pr_debug("%s: change device type from %d to %d\n",
|
|
|
bond_dev->name,
|
|
@@ -1584,7 +1352,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|
|
}
|
|
|
|
|
|
if (slave_ops->ndo_set_mac_address == NULL) {
|
|
|
- if (bond->slave_cnt == 0) {
|
|
|
+ if (list_empty(&bond->slave_list)) {
|
|
|
pr_warning("%s: Warning: The first slave device specified does not support setting the MAC address. Setting fail_over_mac to active.",
|
|
|
bond_dev->name);
|
|
|
bond->params.fail_over_mac = BOND_FOM_ACTIVE;
|
|
@@ -1600,7 +1368,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|
|
|
|
|
/* If this is the first slave, then we need to set the master's hardware
|
|
|
* address to be the same as the slave's. */
|
|
|
- if (!bond->slave_cnt && bond->dev->addr_assign_type == NET_ADDR_RANDOM)
|
|
|
+ if (list_empty(&bond->slave_list) &&
|
|
|
+ bond->dev->addr_assign_type == NET_ADDR_RANDOM)
|
|
|
bond_set_dev_addr(bond->dev, slave_dev);
|
|
|
|
|
|
new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
|
|
@@ -1608,7 +1377,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|
|
res = -ENOMEM;
|
|
|
goto err_undo_flags;
|
|
|
}
|
|
|
-
|
|
|
+ INIT_LIST_HEAD(&new_slave->list);
|
|
|
/*
|
|
|
* Set the new_slave's queue_id to be zero. Queue ID mapping
|
|
|
* is set via sysfs or module option if desired.
|
|
@@ -1703,7 +1472,12 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|
|
dev_mc_add(slave_dev, lacpdu_multicast);
|
|
|
}
|
|
|
|
|
|
- bond_add_vlans_on_slave(bond, slave_dev);
|
|
|
+ res = vlan_vids_add_by_dev(slave_dev, bond_dev);
|
|
|
+ if (res) {
|
|
|
+ pr_err("%s: Error: Couldn't add bond vlan ids to %s\n",
|
|
|
+ bond_dev->name, slave_dev->name);
|
|
|
+ goto err_close;
|
|
|
+ }
|
|
|
|
|
|
write_lock_bh(&bond->lock);
|
|
|
|
|
@@ -1794,15 +1568,18 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|
|
*/
|
|
|
bond_set_slave_inactive_flags(new_slave);
|
|
|
/* if this is the first slave */
|
|
|
- if (bond->slave_cnt == 1) {
|
|
|
+ if (bond_first_slave(bond) == new_slave) {
|
|
|
SLAVE_AD_INFO(new_slave).id = 1;
|
|
|
/* Initialize AD with the number of times that the AD timer is called in 1 second
|
|
|
* can be called only after the mac address of the bond is set
|
|
|
*/
|
|
|
bond_3ad_initialize(bond, 1000/AD_TIMER_INTERVAL);
|
|
|
} else {
|
|
|
+ struct slave *prev_slave;
|
|
|
+
|
|
|
+ prev_slave = bond_prev_slave(bond, new_slave);
|
|
|
SLAVE_AD_INFO(new_slave).id =
|
|
|
- SLAVE_AD_INFO(new_slave->prev).id + 1;
|
|
|
+ SLAVE_AD_INFO(prev_slave).id + 1;
|
|
|
}
|
|
|
|
|
|
bond_3ad_bind_slave(new_slave);
|
|
@@ -1824,7 +1601,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|
|
* so we can change it without calling change_active_interface()
|
|
|
*/
|
|
|
if (!bond->curr_active_slave && new_slave->link == BOND_LINK_UP)
|
|
|
- bond->curr_active_slave = new_slave;
|
|
|
+ rcu_assign_pointer(bond->curr_active_slave, new_slave);
|
|
|
|
|
|
break;
|
|
|
} /* switch(bond_mode) */
|
|
@@ -1834,7 +1611,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|
|
bond_set_carrier(bond);
|
|
|
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
- slave_dev->npinfo = bond_netpoll_info(bond);
|
|
|
+ slave_dev->npinfo = bond->dev->npinfo;
|
|
|
if (slave_dev->npinfo) {
|
|
|
if (slave_enable_netpoll(new_slave)) {
|
|
|
read_unlock(&bond->lock);
|
|
@@ -1876,7 +1653,7 @@ err_detach:
|
|
|
if (!USES_PRIMARY(bond->params.mode))
|
|
|
bond_hw_addr_flush(bond_dev, slave_dev);
|
|
|
|
|
|
- bond_del_vlans_from_slave(bond, slave_dev);
|
|
|
+ vlan_vids_del_by_dev(slave_dev, bond_dev);
|
|
|
write_lock_bh(&bond->lock);
|
|
|
bond_detach_slave(bond, new_slave);
|
|
|
if (bond->primary_slave == new_slave)
|
|
@@ -1921,7 +1698,7 @@ err_free:
|
|
|
err_undo_flags:
|
|
|
bond_compute_features(bond);
|
|
|
/* Enslave of first slave has failed and we need to fix master's mac */
|
|
|
- if (bond->slave_cnt == 0 &&
|
|
|
+ if (list_empty(&bond->slave_list) &&
|
|
|
ether_addr_equal(bond_dev->dev_addr, slave_dev->dev_addr))
|
|
|
eth_hw_addr_random(bond_dev);
|
|
|
|
|
@@ -1977,15 +1754,6 @@ static int __bond_release_one(struct net_device *bond_dev,
|
|
|
netdev_rx_handler_unregister(slave_dev);
|
|
|
write_lock_bh(&bond->lock);
|
|
|
|
|
|
- if (!all && !bond->params.fail_over_mac) {
|
|
|
- if (ether_addr_equal(bond_dev->dev_addr, slave->perm_hwaddr) &&
|
|
|
- bond->slave_cnt > 1)
|
|
|
- pr_warning("%s: Warning: the permanent HWaddr of %s - %pM - is still in use by %s. Set the HWaddr of %s to a different address to avoid conflicts.\n",
|
|
|
- bond_dev->name, slave_dev->name,
|
|
|
- slave->perm_hwaddr,
|
|
|
- bond_dev->name, slave_dev->name);
|
|
|
- }
|
|
|
-
|
|
|
/* Inform AD package of unbinding of slave. */
|
|
|
if (bond->params.mode == BOND_MODE_8023AD) {
|
|
|
/* must be called before the slave is
|
|
@@ -2006,6 +1774,15 @@ static int __bond_release_one(struct net_device *bond_dev,
|
|
|
/* release the slave from its bond */
|
|
|
bond_detach_slave(bond, slave);
|
|
|
|
|
|
+ if (!all && !bond->params.fail_over_mac) {
|
|
|
+ if (ether_addr_equal(bond_dev->dev_addr, slave->perm_hwaddr) &&
|
|
|
+ !list_empty(&bond->slave_list))
|
|
|
+ pr_warn("%s: Warning: the permanent HWaddr of %s - %pM - is still in use by %s. Set the HWaddr of %s to a different address to avoid conflicts.\n",
|
|
|
+ bond_dev->name, slave_dev->name,
|
|
|
+ slave->perm_hwaddr,
|
|
|
+ bond_dev->name, slave_dev->name);
|
|
|
+ }
|
|
|
+
|
|
|
if (bond->primary_slave == slave)
|
|
|
bond->primary_slave = NULL;
|
|
|
|
|
@@ -2024,7 +1801,7 @@ static int __bond_release_one(struct net_device *bond_dev,
|
|
|
}
|
|
|
|
|
|
if (all) {
|
|
|
- bond->curr_active_slave = NULL;
|
|
|
+ rcu_assign_pointer(bond->curr_active_slave, NULL);
|
|
|
} else if (oldcurrent == slave) {
|
|
|
/*
|
|
|
* Note that we hold RTNL over this sequence, so there
|
|
@@ -2042,11 +1819,11 @@ static int __bond_release_one(struct net_device *bond_dev,
|
|
|
write_lock_bh(&bond->lock);
|
|
|
}
|
|
|
|
|
|
- if (bond->slave_cnt == 0) {
|
|
|
+ if (list_empty(&bond->slave_list)) {
|
|
|
bond_set_carrier(bond);
|
|
|
eth_hw_addr_random(bond_dev);
|
|
|
|
|
|
- if (bond_vlan_used(bond)) {
|
|
|
+ if (vlan_uses_dev(bond_dev)) {
|
|
|
pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n",
|
|
|
bond_dev->name, bond_dev->name);
|
|
|
pr_warning("%s: When re-adding slaves, make sure the bond's HW address matches its VLANs'.\n",
|
|
@@ -2056,8 +1833,9 @@ static int __bond_release_one(struct net_device *bond_dev,
|
|
|
|
|
|
write_unlock_bh(&bond->lock);
|
|
|
unblock_netpoll_tx();
|
|
|
+ synchronize_rcu();
|
|
|
|
|
|
- if (bond->slave_cnt == 0) {
|
|
|
+ if (list_empty(&bond->slave_list)) {
|
|
|
call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev);
|
|
|
call_netdevice_notifiers(NETDEV_RELEASE, bond->dev);
|
|
|
}
|
|
@@ -2071,7 +1849,7 @@ static int __bond_release_one(struct net_device *bond_dev,
|
|
|
/* must do this from outside any spinlocks */
|
|
|
bond_destroy_slave_symlinks(bond_dev, slave_dev);
|
|
|
|
|
|
- bond_del_vlans_from_slave(bond, slave_dev);
|
|
|
+ vlan_vids_del_by_dev(slave_dev, bond_dev);
|
|
|
|
|
|
/* If the mode USES_PRIMARY, then this cases was handled above by
|
|
|
* bond_change_active_slave(..., NULL)
|
|
@@ -2128,7 +1906,7 @@ static int bond_release_and_destroy(struct net_device *bond_dev,
|
|
|
int ret;
|
|
|
|
|
|
ret = bond_release(bond_dev, slave_dev);
|
|
|
- if ((ret == 0) && (bond->slave_cnt == 0)) {
|
|
|
+ if (ret == 0 && list_empty(&bond->slave_list)) {
|
|
|
bond_dev->priv_flags |= IFF_DISABLE_NETPOLL;
|
|
|
pr_info("%s: destroying bond %s.\n",
|
|
|
bond_dev->name, bond_dev->name);
|
|
@@ -2165,23 +1943,19 @@ static int bond_ioctl_change_active(struct net_device *bond_dev, struct net_devi
|
|
|
|
|
|
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)) {
|
|
|
+ if (new_active && new_active == old_active) {
|
|
|
read_unlock(&bond->lock);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- if ((new_active) &&
|
|
|
- (old_active) &&
|
|
|
- (new_active->link == BOND_LINK_UP) &&
|
|
|
+ if (new_active &&
|
|
|
+ old_active &&
|
|
|
+ new_active->link == BOND_LINK_UP &&
|
|
|
IS_UP(new_active->dev)) {
|
|
|
block_netpoll_tx();
|
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
@@ -2213,13 +1987,12 @@ static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
|
|
|
static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *info)
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
|
+ int i = 0, res = -ENODEV;
|
|
|
struct slave *slave;
|
|
|
- int i, res = -ENODEV;
|
|
|
|
|
|
read_lock(&bond->lock);
|
|
|
-
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
- if (i == (int)info->slave_id) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
+ if (i++ == (int)info->slave_id) {
|
|
|
res = 0;
|
|
|
strcpy(info->slave_name, slave->dev->name);
|
|
|
info->link = slave->link;
|
|
@@ -2228,7 +2001,6 @@ static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *in
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
read_unlock(&bond->lock);
|
|
|
|
|
|
return res;
|
|
@@ -2239,13 +2011,13 @@ static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *in
|
|
|
|
|
|
static int bond_miimon_inspect(struct bonding *bond)
|
|
|
{
|
|
|
+ int link_state, commit = 0;
|
|
|
struct slave *slave;
|
|
|
- int i, link_state, commit = 0;
|
|
|
bool ignore_updelay;
|
|
|
|
|
|
ignore_updelay = !bond->curr_active_slave ? true : false;
|
|
|
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
slave->new_link = BOND_LINK_NOCHANGE;
|
|
|
|
|
|
link_state = bond_check_dev_link(bond, slave->dev, 0);
|
|
@@ -2340,9 +2112,8 @@ static int bond_miimon_inspect(struct bonding *bond)
|
|
|
static void bond_miimon_commit(struct bonding *bond)
|
|
|
{
|
|
|
struct slave *slave;
|
|
|
- int i;
|
|
|
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
switch (slave->new_link) {
|
|
|
case BOND_LINK_NOCHANGE:
|
|
|
continue;
|
|
@@ -2447,7 +2218,7 @@ void bond_mii_monitor(struct work_struct *work)
|
|
|
|
|
|
delay = msecs_to_jiffies(bond->params.miimon);
|
|
|
|
|
|
- if (bond->slave_cnt == 0)
|
|
|
+ if (list_empty(&bond->slave_list))
|
|
|
goto re_arm;
|
|
|
|
|
|
should_notify_peers = bond_should_notify_peers(bond);
|
|
@@ -2479,35 +2250,32 @@ re_arm:
|
|
|
read_unlock(&bond->lock);
|
|
|
|
|
|
if (should_notify_peers) {
|
|
|
- if (!rtnl_trylock()) {
|
|
|
- read_lock(&bond->lock);
|
|
|
- bond->send_peer_notif++;
|
|
|
- read_unlock(&bond->lock);
|
|
|
+ if (!rtnl_trylock())
|
|
|
return;
|
|
|
- }
|
|
|
call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, bond->dev);
|
|
|
rtnl_unlock();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static int bond_has_this_ip(struct bonding *bond, __be32 ip)
|
|
|
+static bool bond_has_this_ip(struct bonding *bond, __be32 ip)
|
|
|
{
|
|
|
- struct vlan_entry *vlan;
|
|
|
- struct net_device *vlan_dev;
|
|
|
+ struct net_device *upper;
|
|
|
+ struct list_head *iter;
|
|
|
+ bool ret = false;
|
|
|
|
|
|
if (ip == bond_confirm_addr(bond->dev, 0, ip))
|
|
|
- return 1;
|
|
|
+ return true;
|
|
|
|
|
|
- list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
|
|
- rcu_read_lock();
|
|
|
- vlan_dev = __vlan_find_dev_deep(bond->dev, htons(ETH_P_8021Q),
|
|
|
- vlan->vlan_id);
|
|
|
- rcu_read_unlock();
|
|
|
- if (vlan_dev && ip == bond_confirm_addr(vlan_dev, 0, ip))
|
|
|
- return 1;
|
|
|
+ rcu_read_lock();
|
|
|
+ netdev_for_each_upper_dev_rcu(bond->dev, upper, iter) {
|
|
|
+ if (ip == bond_confirm_addr(upper, 0, ip)) {
|
|
|
+ ret = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
+ rcu_read_unlock();
|
|
|
|
|
|
- return 0;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -2542,81 +2310,79 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op, __be32 dest_
|
|
|
|
|
|
static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
|
|
|
{
|
|
|
- int i, vlan_id;
|
|
|
- __be32 *targets = bond->params.arp_targets;
|
|
|
- struct vlan_entry *vlan;
|
|
|
- struct net_device *vlan_dev = NULL;
|
|
|
+ struct net_device *upper, *vlan_upper;
|
|
|
+ struct list_head *iter, *vlan_iter;
|
|
|
struct rtable *rt;
|
|
|
+ __be32 *targets = bond->params.arp_targets, addr;
|
|
|
+ int i, vlan_id;
|
|
|
|
|
|
- for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) {
|
|
|
- __be32 addr;
|
|
|
- if (!targets[i])
|
|
|
- break;
|
|
|
+ for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i]; i++) {
|
|
|
pr_debug("basa: target %pI4\n", &targets[i]);
|
|
|
- if (!bond_vlan_used(bond)) {
|
|
|
- pr_debug("basa: empty vlan: arp_send\n");
|
|
|
- addr = bond_confirm_addr(bond->dev, targets[i], 0);
|
|
|
- bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
|
|
|
- addr, 0);
|
|
|
- continue;
|
|
|
- }
|
|
|
|
|
|
- /*
|
|
|
- * If VLANs are configured, we do a route lookup to
|
|
|
- * determine which VLAN interface would be used, so we
|
|
|
- * can tag the ARP with the proper VLAN tag.
|
|
|
- */
|
|
|
+ /* Find out through which dev should the packet go */
|
|
|
rt = ip_route_output(dev_net(bond->dev), targets[i], 0,
|
|
|
RTO_ONLINK, 0);
|
|
|
if (IS_ERR(rt)) {
|
|
|
- if (net_ratelimit()) {
|
|
|
- pr_warning("%s: no route to arp_ip_target %pI4\n",
|
|
|
- bond->dev->name, &targets[i]);
|
|
|
- }
|
|
|
+ pr_debug("%s: no route to arp_ip_target %pI4\n",
|
|
|
+ bond->dev->name, &targets[i]);
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * This target is not on a VLAN
|
|
|
+ vlan_id = 0;
|
|
|
+
|
|
|
+ /* bond device itself */
|
|
|
+ if (rt->dst.dev == bond->dev)
|
|
|
+ goto found;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ /* first we search only for vlan devices. for every vlan
|
|
|
+ * found we verify its upper dev list, searching for the
|
|
|
+ * rt->dst.dev. If found we save the tag of the vlan and
|
|
|
+ * proceed to send the packet.
|
|
|
+ *
|
|
|
+ * TODO: QinQ?
|
|
|
*/
|
|
|
- if (rt->dst.dev == bond->dev) {
|
|
|
- ip_rt_put(rt);
|
|
|
- pr_debug("basa: rtdev == bond->dev: arp_send\n");
|
|
|
- addr = bond_confirm_addr(bond->dev, targets[i], 0);
|
|
|
- bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
|
|
|
- addr, 0);
|
|
|
- continue;
|
|
|
+ netdev_for_each_upper_dev_rcu(bond->dev, vlan_upper, vlan_iter) {
|
|
|
+ if (!is_vlan_dev(vlan_upper))
|
|
|
+ continue;
|
|
|
+ netdev_for_each_upper_dev_rcu(vlan_upper, upper, iter) {
|
|
|
+ if (upper == rt->dst.dev) {
|
|
|
+ vlan_id = vlan_dev_vlan_id(vlan_upper);
|
|
|
+ rcu_read_unlock();
|
|
|
+ goto found;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- vlan_id = 0;
|
|
|
- list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
|
|
- rcu_read_lock();
|
|
|
- vlan_dev = __vlan_find_dev_deep(bond->dev,
|
|
|
- htons(ETH_P_8021Q),
|
|
|
- vlan->vlan_id);
|
|
|
- rcu_read_unlock();
|
|
|
- if (vlan_dev == rt->dst.dev) {
|
|
|
- vlan_id = vlan->vlan_id;
|
|
|
- pr_debug("basa: vlan match on %s %d\n",
|
|
|
- vlan_dev->name, vlan_id);
|
|
|
- break;
|
|
|
+ /* if the device we're looking for is not on top of any of
|
|
|
+ * our upper vlans, then just search for any dev that
|
|
|
+ * matches, and in case it's a vlan - save the id
|
|
|
+ */
|
|
|
+ netdev_for_each_upper_dev_rcu(bond->dev, upper, iter) {
|
|
|
+ if (upper == rt->dst.dev) {
|
|
|
+ /* if it's a vlan - get its VID */
|
|
|
+ if (is_vlan_dev(upper))
|
|
|
+ vlan_id = vlan_dev_vlan_id(upper);
|
|
|
+
|
|
|
+ rcu_read_unlock();
|
|
|
+ goto found;
|
|
|
}
|
|
|
}
|
|
|
+ rcu_read_unlock();
|
|
|
|
|
|
- if (vlan_id && vlan_dev) {
|
|
|
- ip_rt_put(rt);
|
|
|
- addr = bond_confirm_addr(vlan_dev, targets[i], 0);
|
|
|
- bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
|
|
|
- addr, vlan_id);
|
|
|
- continue;
|
|
|
- }
|
|
|
+ /* Not our device - skip */
|
|
|
+ pr_debug("%s: no path to arp_ip_target %pI4 via rt.dev %s\n",
|
|
|
+ bond->dev->name, &targets[i],
|
|
|
+ rt->dst.dev ? rt->dst.dev->name : "NULL");
|
|
|
|
|
|
- if (net_ratelimit()) {
|
|
|
- pr_warning("%s: no path to arp_ip_target %pI4 via rt.dev %s\n",
|
|
|
- bond->dev->name, &targets[i],
|
|
|
- rt->dst.dev ? rt->dst.dev->name : "NULL");
|
|
|
- }
|
|
|
ip_rt_put(rt);
|
|
|
+ continue;
|
|
|
+
|
|
|
+found:
|
|
|
+ addr = bond_confirm_addr(rt->dst.dev, targets[i], 0);
|
|
|
+ ip_rt_put(rt);
|
|
|
+ bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
|
|
|
+ addr, vlan_id);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2713,6 +2479,20 @@ out_unlock:
|
|
|
return RX_HANDLER_ANOTHER;
|
|
|
}
|
|
|
|
|
|
+/* function to verify if we're in the arp_interval timeslice, returns true if
|
|
|
+ * (last_act - arp_interval) <= jiffies <= (last_act + mod * arp_interval +
|
|
|
+ * arp_interval/2) . the arp_interval/2 is needed for really fast networks.
|
|
|
+ */
|
|
|
+static bool bond_time_in_interval(struct bonding *bond, unsigned long last_act,
|
|
|
+ int mod)
|
|
|
+{
|
|
|
+ int delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval);
|
|
|
+
|
|
|
+ return time_in_range(jiffies,
|
|
|
+ last_act - delta_in_ticks,
|
|
|
+ last_act + mod * delta_in_ticks + delta_in_ticks/2);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* this function is called regularly to monitor each slave's link
|
|
|
* ensuring that traffic is being sent and received when arp monitoring
|
|
@@ -2726,21 +2506,13 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
|
|
|
arp_work.work);
|
|
|
struct slave *slave, *oldcurrent;
|
|
|
int do_failover = 0;
|
|
|
- int delta_in_ticks, extra_ticks;
|
|
|
- int i;
|
|
|
|
|
|
read_lock(&bond->lock);
|
|
|
|
|
|
- delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval);
|
|
|
- extra_ticks = delta_in_ticks / 2;
|
|
|
-
|
|
|
- if (bond->slave_cnt == 0)
|
|
|
+ if (list_empty(&bond->slave_list))
|
|
|
goto re_arm;
|
|
|
|
|
|
- read_lock(&bond->curr_slave_lock);
|
|
|
oldcurrent = bond->curr_active_slave;
|
|
|
- read_unlock(&bond->curr_slave_lock);
|
|
|
-
|
|
|
/* see if any of the previous devices are up now (i.e. they have
|
|
|
* xmt and rcv traffic). the curr_active_slave does not come into
|
|
|
* the picture unless it is null. also, slave->jiffies is not needed
|
|
@@ -2749,16 +2521,12 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
|
|
|
* TODO: what about up/down delay in arp mode? it wasn't here before
|
|
|
* so it can wait
|
|
|
*/
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
unsigned long trans_start = dev_trans_start(slave->dev);
|
|
|
|
|
|
if (slave->link != BOND_LINK_UP) {
|
|
|
- if (time_in_range(jiffies,
|
|
|
- trans_start - delta_in_ticks,
|
|
|
- trans_start + delta_in_ticks + extra_ticks) &&
|
|
|
- time_in_range(jiffies,
|
|
|
- slave->dev->last_rx - delta_in_ticks,
|
|
|
- slave->dev->last_rx + delta_in_ticks + extra_ticks)) {
|
|
|
+ if (bond_time_in_interval(bond, trans_start, 1) &&
|
|
|
+ bond_time_in_interval(bond, slave->dev->last_rx, 1)) {
|
|
|
|
|
|
slave->link = BOND_LINK_UP;
|
|
|
bond_set_active_slave(slave);
|
|
@@ -2786,12 +2554,8 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
|
|
|
* when the source ip is 0, so don't take the link down
|
|
|
* if we don't know our ip yet
|
|
|
*/
|
|
|
- if (!time_in_range(jiffies,
|
|
|
- trans_start - delta_in_ticks,
|
|
|
- trans_start + 2 * delta_in_ticks + extra_ticks) ||
|
|
|
- !time_in_range(jiffies,
|
|
|
- slave->dev->last_rx - delta_in_ticks,
|
|
|
- slave->dev->last_rx + 2 * delta_in_ticks + extra_ticks)) {
|
|
|
+ if (!bond_time_in_interval(bond, trans_start, 2) ||
|
|
|
+ !bond_time_in_interval(bond, slave->dev->last_rx, 2)) {
|
|
|
|
|
|
slave->link = BOND_LINK_DOWN;
|
|
|
bond_set_backup_slave(slave);
|
|
@@ -2831,7 +2595,8 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
|
|
|
|
|
|
re_arm:
|
|
|
if (bond->params.arp_interval)
|
|
|
- queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks);
|
|
|
+ queue_delayed_work(bond->wq, &bond->arp_work,
|
|
|
+ msecs_to_jiffies(bond->params.arp_interval));
|
|
|
|
|
|
read_unlock(&bond->lock);
|
|
|
}
|
|
@@ -2844,32 +2609,21 @@ re_arm:
|
|
|
*
|
|
|
* Called with bond->lock held for read.
|
|
|
*/
|
|
|
-static int bond_ab_arp_inspect(struct bonding *bond, int delta_in_ticks)
|
|
|
+static int bond_ab_arp_inspect(struct bonding *bond)
|
|
|
{
|
|
|
+ unsigned long trans_start, last_rx;
|
|
|
struct slave *slave;
|
|
|
- int i, commit = 0;
|
|
|
- unsigned long trans_start;
|
|
|
- int extra_ticks;
|
|
|
-
|
|
|
- /* All the time comparisons below need some extra time. Otherwise, on
|
|
|
- * fast networks the ARP probe/reply may arrive within the same jiffy
|
|
|
- * as it was sent. Then, the next time the ARP monitor is run, one
|
|
|
- * arp_interval will already have passed in the comparisons.
|
|
|
- */
|
|
|
- extra_ticks = delta_in_ticks / 2;
|
|
|
+ int commit = 0;
|
|
|
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
slave->new_link = BOND_LINK_NOCHANGE;
|
|
|
+ last_rx = slave_last_rx(bond, slave);
|
|
|
|
|
|
if (slave->link != BOND_LINK_UP) {
|
|
|
- if (time_in_range(jiffies,
|
|
|
- slave_last_rx(bond, slave) - delta_in_ticks,
|
|
|
- slave_last_rx(bond, slave) + delta_in_ticks + extra_ticks)) {
|
|
|
-
|
|
|
+ if (bond_time_in_interval(bond, last_rx, 1)) {
|
|
|
slave->new_link = BOND_LINK_UP;
|
|
|
commit++;
|
|
|
}
|
|
|
-
|
|
|
continue;
|
|
|
}
|
|
|
|
|
@@ -2878,9 +2632,7 @@ static int bond_ab_arp_inspect(struct bonding *bond, int delta_in_ticks)
|
|
|
* active. This avoids bouncing, as the last receive
|
|
|
* times need a full ARP monitor cycle to be updated.
|
|
|
*/
|
|
|
- if (time_in_range(jiffies,
|
|
|
- slave->jiffies - delta_in_ticks,
|
|
|
- slave->jiffies + 2 * delta_in_ticks + extra_ticks))
|
|
|
+ if (bond_time_in_interval(bond, slave->jiffies, 2))
|
|
|
continue;
|
|
|
|
|
|
/*
|
|
@@ -2898,10 +2650,7 @@ static int bond_ab_arp_inspect(struct bonding *bond, int delta_in_ticks)
|
|
|
*/
|
|
|
if (!bond_is_active_slave(slave) &&
|
|
|
!bond->current_arp_slave &&
|
|
|
- !time_in_range(jiffies,
|
|
|
- slave_last_rx(bond, slave) - delta_in_ticks,
|
|
|
- slave_last_rx(bond, slave) + 3 * delta_in_ticks + extra_ticks)) {
|
|
|
-
|
|
|
+ !bond_time_in_interval(bond, last_rx, 3)) {
|
|
|
slave->new_link = BOND_LINK_DOWN;
|
|
|
commit++;
|
|
|
}
|
|
@@ -2914,13 +2663,8 @@ static int bond_ab_arp_inspect(struct bonding *bond, int delta_in_ticks)
|
|
|
*/
|
|
|
trans_start = dev_trans_start(slave->dev);
|
|
|
if (bond_is_active_slave(slave) &&
|
|
|
- (!time_in_range(jiffies,
|
|
|
- trans_start - delta_in_ticks,
|
|
|
- trans_start + 2 * delta_in_ticks + extra_ticks) ||
|
|
|
- !time_in_range(jiffies,
|
|
|
- slave_last_rx(bond, slave) - delta_in_ticks,
|
|
|
- slave_last_rx(bond, slave) + 2 * delta_in_ticks + extra_ticks))) {
|
|
|
-
|
|
|
+ (!bond_time_in_interval(bond, trans_start, 2) ||
|
|
|
+ !bond_time_in_interval(bond, last_rx, 2))) {
|
|
|
slave->new_link = BOND_LINK_DOWN;
|
|
|
commit++;
|
|
|
}
|
|
@@ -2935,24 +2679,21 @@ static int bond_ab_arp_inspect(struct bonding *bond, int delta_in_ticks)
|
|
|
*
|
|
|
* Called with RTNL and bond->lock for read.
|
|
|
*/
|
|
|
-static void bond_ab_arp_commit(struct bonding *bond, int delta_in_ticks)
|
|
|
+static void bond_ab_arp_commit(struct bonding *bond)
|
|
|
{
|
|
|
- struct slave *slave;
|
|
|
- int i;
|
|
|
unsigned long trans_start;
|
|
|
+ struct slave *slave;
|
|
|
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
switch (slave->new_link) {
|
|
|
case BOND_LINK_NOCHANGE:
|
|
|
continue;
|
|
|
|
|
|
case BOND_LINK_UP:
|
|
|
trans_start = dev_trans_start(slave->dev);
|
|
|
- if ((!bond->curr_active_slave &&
|
|
|
- time_in_range(jiffies,
|
|
|
- trans_start - delta_in_ticks,
|
|
|
- trans_start + delta_in_ticks + delta_in_ticks / 2)) ||
|
|
|
- bond->curr_active_slave != slave) {
|
|
|
+ if (bond->curr_active_slave != slave ||
|
|
|
+ (!bond->curr_active_slave &&
|
|
|
+ bond_time_in_interval(bond, trans_start, 1))) {
|
|
|
slave->link = BOND_LINK_UP;
|
|
|
if (bond->current_arp_slave) {
|
|
|
bond_set_slave_inactive_flags(
|
|
@@ -3014,7 +2755,7 @@ do_failover:
|
|
|
*/
|
|
|
static void bond_ab_arp_probe(struct bonding *bond)
|
|
|
{
|
|
|
- struct slave *slave;
|
|
|
+ struct slave *slave, *next_slave;
|
|
|
int i;
|
|
|
|
|
|
read_lock(&bond->curr_slave_lock);
|
|
@@ -3038,7 +2779,7 @@ static void bond_ab_arp_probe(struct bonding *bond)
|
|
|
*/
|
|
|
|
|
|
if (!bond->current_arp_slave) {
|
|
|
- bond->current_arp_slave = bond->first_slave;
|
|
|
+ bond->current_arp_slave = bond_first_slave(bond);
|
|
|
if (!bond->current_arp_slave)
|
|
|
return;
|
|
|
}
|
|
@@ -3046,7 +2787,8 @@ static void bond_ab_arp_probe(struct bonding *bond)
|
|
|
bond_set_slave_inactive_flags(bond->current_arp_slave);
|
|
|
|
|
|
/* search for next candidate */
|
|
|
- bond_for_each_slave_from(bond, slave, i, bond->current_arp_slave->next) {
|
|
|
+ next_slave = bond_next_slave(bond, bond->current_arp_slave);
|
|
|
+ bond_for_each_slave_from(bond, slave, i, next_slave) {
|
|
|
if (IS_UP(slave->dev)) {
|
|
|
slave->link = BOND_LINK_BACK;
|
|
|
bond_set_slave_active_flags(slave);
|
|
@@ -3087,12 +2829,12 @@ void bond_activebackup_arp_mon(struct work_struct *work)
|
|
|
|
|
|
delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval);
|
|
|
|
|
|
- if (bond->slave_cnt == 0)
|
|
|
+ if (list_empty(&bond->slave_list))
|
|
|
goto re_arm;
|
|
|
|
|
|
should_notify_peers = bond_should_notify_peers(bond);
|
|
|
|
|
|
- if (bond_ab_arp_inspect(bond, delta_in_ticks)) {
|
|
|
+ if (bond_ab_arp_inspect(bond)) {
|
|
|
read_unlock(&bond->lock);
|
|
|
|
|
|
/* Race avoidance with bond_close flush of workqueue */
|
|
@@ -3105,7 +2847,7 @@ void bond_activebackup_arp_mon(struct work_struct *work)
|
|
|
|
|
|
read_lock(&bond->lock);
|
|
|
|
|
|
- bond_ab_arp_commit(bond, delta_in_ticks);
|
|
|
+ bond_ab_arp_commit(bond);
|
|
|
|
|
|
read_unlock(&bond->lock);
|
|
|
rtnl_unlock();
|
|
@@ -3121,12 +2863,8 @@ re_arm:
|
|
|
read_unlock(&bond->lock);
|
|
|
|
|
|
if (should_notify_peers) {
|
|
|
- if (!rtnl_trylock()) {
|
|
|
- read_lock(&bond->lock);
|
|
|
- bond->send_peer_notif++;
|
|
|
- read_unlock(&bond->lock);
|
|
|
+ if (!rtnl_trylock())
|
|
|
return;
|
|
|
- }
|
|
|
call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, bond->dev);
|
|
|
rtnl_unlock();
|
|
|
}
|
|
@@ -3161,6 +2899,10 @@ static int bond_master_netdev_event(unsigned long event,
|
|
|
case NETDEV_REGISTER:
|
|
|
bond_create_proc_entry(event_bond);
|
|
|
break;
|
|
|
+ case NETDEV_NOTIFY_PEERS:
|
|
|
+ if (event_bond->send_peer_notif)
|
|
|
+ event_bond->send_peer_notif--;
|
|
|
+ break;
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
@@ -3234,6 +2976,10 @@ static int bond_slave_netdev_event(unsigned long event,
|
|
|
case NETDEV_FEAT_CHANGE:
|
|
|
bond_compute_features(bond);
|
|
|
break;
|
|
|
+ case NETDEV_RESEND_IGMP:
|
|
|
+ /* Propagate to master device */
|
|
|
+ call_netdevice_notifiers(event, slave->bond->dev);
|
|
|
+ break;
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
@@ -3403,13 +3149,12 @@ static int bond_open(struct net_device *bond_dev)
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
|
struct slave *slave;
|
|
|
- int i;
|
|
|
|
|
|
/* reset slave->backup and slave->inactive */
|
|
|
read_lock(&bond->lock);
|
|
|
- if (bond->slave_cnt > 0) {
|
|
|
+ if (!list_empty(&bond->slave_list)) {
|
|
|
read_lock(&bond->curr_slave_lock);
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
if ((bond->params.mode == BOND_MODE_ACTIVEBACKUP)
|
|
|
&& (slave != bond->curr_active_slave)) {
|
|
|
bond_set_slave_inactive_flags(slave);
|
|
@@ -3455,17 +3200,10 @@ static int bond_close(struct net_device *bond_dev)
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
|
|
|
|
- write_lock_bh(&bond->lock);
|
|
|
- bond->send_peer_notif = 0;
|
|
|
- write_unlock_bh(&bond->lock);
|
|
|
-
|
|
|
bond_work_cancel_all(bond);
|
|
|
- if (bond_is_lb(bond)) {
|
|
|
- /* Must be called only after all
|
|
|
- * slaves have been released
|
|
|
- */
|
|
|
+ bond->send_peer_notif = 0;
|
|
|
+ if (bond_is_lb(bond))
|
|
|
bond_alb_deinitialize(bond);
|
|
|
- }
|
|
|
bond->recv_probe = NULL;
|
|
|
|
|
|
return 0;
|
|
@@ -3477,13 +3215,11 @@ static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev,
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
|
struct rtnl_link_stats64 temp;
|
|
|
struct slave *slave;
|
|
|
- int i;
|
|
|
|
|
|
memset(stats, 0, sizeof(*stats));
|
|
|
|
|
|
read_lock_bh(&bond->lock);
|
|
|
-
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
const struct rtnl_link_stats64 *sstats =
|
|
|
dev_get_stats(slave->dev, &temp);
|
|
|
|
|
@@ -3513,7 +3249,6 @@ static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev,
|
|
|
stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors;
|
|
|
stats->tx_window_errors += sstats->tx_window_errors;
|
|
|
}
|
|
|
-
|
|
|
read_unlock_bh(&bond->lock);
|
|
|
|
|
|
return stats;
|
|
@@ -3652,41 +3387,35 @@ static void bond_set_rx_mode(struct net_device *bond_dev)
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
|
struct slave *slave;
|
|
|
- int i;
|
|
|
|
|
|
- read_lock(&bond->lock);
|
|
|
+ ASSERT_RTNL();
|
|
|
|
|
|
if (USES_PRIMARY(bond->params.mode)) {
|
|
|
- read_lock(&bond->curr_slave_lock);
|
|
|
- slave = bond->curr_active_slave;
|
|
|
+ slave = rtnl_dereference(bond->curr_active_slave);
|
|
|
if (slave) {
|
|
|
dev_uc_sync(slave->dev, bond_dev);
|
|
|
dev_mc_sync(slave->dev, bond_dev);
|
|
|
}
|
|
|
- read_unlock(&bond->curr_slave_lock);
|
|
|
} else {
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
dev_uc_sync_multiple(slave->dev, bond_dev);
|
|
|
dev_mc_sync_multiple(slave->dev, bond_dev);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- read_unlock(&bond->lock);
|
|
|
}
|
|
|
|
|
|
static int bond_neigh_init(struct neighbour *n)
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(n->dev);
|
|
|
- struct slave *slave = bond->first_slave;
|
|
|
const struct net_device_ops *slave_ops;
|
|
|
struct neigh_parms parms;
|
|
|
+ struct slave *slave;
|
|
|
int ret;
|
|
|
|
|
|
+ slave = bond_first_slave(bond);
|
|
|
if (!slave)
|
|
|
return 0;
|
|
|
-
|
|
|
slave_ops = slave->dev->netdev_ops;
|
|
|
-
|
|
|
if (!slave_ops->ndo_neigh_setup)
|
|
|
return 0;
|
|
|
|
|
@@ -3735,9 +3464,8 @@ static int bond_neigh_setup(struct net_device *dev,
|
|
|
static int bond_change_mtu(struct net_device *bond_dev, int new_mtu)
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
|
- struct slave *slave, *stop_at;
|
|
|
+ struct slave *slave;
|
|
|
int res = 0;
|
|
|
- int i;
|
|
|
|
|
|
pr_debug("bond=%p, name=%s, new_mtu=%d\n", bond,
|
|
|
(bond_dev ? bond_dev->name : "None"), new_mtu);
|
|
@@ -3757,10 +3485,10 @@ static int bond_change_mtu(struct net_device *bond_dev, int new_mtu)
|
|
|
* call to the base driver.
|
|
|
*/
|
|
|
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
pr_debug("s %p s->p %p c_m %p\n",
|
|
|
slave,
|
|
|
- slave->prev,
|
|
|
+ bond_prev_slave(bond, slave),
|
|
|
slave->dev->netdev_ops->ndo_change_mtu);
|
|
|
|
|
|
res = dev_set_mtu(slave->dev, new_mtu);
|
|
@@ -3785,8 +3513,7 @@ static int bond_change_mtu(struct net_device *bond_dev, int new_mtu)
|
|
|
|
|
|
unwind:
|
|
|
/* unwind from head to the slave that failed */
|
|
|
- stop_at = slave;
|
|
|
- bond_for_each_slave_from_to(bond, slave, i, bond->first_slave, stop_at) {
|
|
|
+ bond_for_each_slave_continue_reverse(bond, slave) {
|
|
|
int tmp_res;
|
|
|
|
|
|
tmp_res = dev_set_mtu(slave->dev, bond_dev->mtu);
|
|
@@ -3810,9 +3537,8 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
|
struct sockaddr *sa = addr, tmp_sa;
|
|
|
- struct slave *slave, *stop_at;
|
|
|
+ struct slave *slave;
|
|
|
int res = 0;
|
|
|
- int i;
|
|
|
|
|
|
if (bond->params.mode == BOND_MODE_ALB)
|
|
|
return bond_alb_set_mac_address(bond_dev, addr);
|
|
@@ -3845,7 +3571,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
|
|
|
* call to the base driver.
|
|
|
*/
|
|
|
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
const struct net_device_ops *slave_ops = slave->dev->netdev_ops;
|
|
|
pr_debug("slave %p %s\n", slave, slave->dev->name);
|
|
|
|
|
@@ -3877,8 +3603,7 @@ unwind:
|
|
|
tmp_sa.sa_family = bond_dev->type;
|
|
|
|
|
|
/* unwind from head to the slave that failed */
|
|
|
- stop_at = slave;
|
|
|
- bond_for_each_slave_from_to(bond, slave, i, bond->first_slave, stop_at) {
|
|
|
+ bond_for_each_slave_continue_reverse(bond, slave) {
|
|
|
int tmp_res;
|
|
|
|
|
|
tmp_res = dev_set_mac_address(slave->dev, &tmp_sa);
|
|
@@ -3891,12 +3616,50 @@ unwind:
|
|
|
return res;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * bond_xmit_slave_id - transmit skb through slave with slave_id
|
|
|
+ * @bond: bonding device that is transmitting
|
|
|
+ * @skb: buffer to transmit
|
|
|
+ * @slave_id: slave id up to slave_cnt-1 through which to transmit
|
|
|
+ *
|
|
|
+ * This function tries to transmit through slave with slave_id but in case
|
|
|
+ * it fails, it tries to find the first available slave for transmission.
|
|
|
+ * The skb is consumed in all cases, thus the function is void.
|
|
|
+ */
|
|
|
+void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int slave_id)
|
|
|
+{
|
|
|
+ struct slave *slave;
|
|
|
+ int i = slave_id;
|
|
|
+
|
|
|
+ /* Here we start from the slave with slave_id */
|
|
|
+ bond_for_each_slave_rcu(bond, slave) {
|
|
|
+ if (--i < 0) {
|
|
|
+ if (slave_can_tx(slave)) {
|
|
|
+ bond_dev_queue_xmit(bond, skb, slave->dev);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Here we start from the first slave up to slave_id */
|
|
|
+ i = slave_id;
|
|
|
+ bond_for_each_slave_rcu(bond, slave) {
|
|
|
+ if (--i < 0)
|
|
|
+ break;
|
|
|
+ if (slave_can_tx(slave)) {
|
|
|
+ bond_dev_queue_xmit(bond, skb, slave->dev);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* no slave that can tx has been found */
|
|
|
+ kfree_skb(skb);
|
|
|
+}
|
|
|
+
|
|
|
static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev)
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
|
- struct slave *slave, *start_at;
|
|
|
- int i, slave_no, res = 1;
|
|
|
struct iphdr *iph = ip_hdr(skb);
|
|
|
+ struct slave *slave;
|
|
|
|
|
|
/*
|
|
|
* Start with the curr_active_slave that joined the bond as the
|
|
@@ -3905,50 +3668,20 @@ static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev
|
|
|
* send the join/membership reports. The curr_active_slave found
|
|
|
* will send all of this type of traffic.
|
|
|
*/
|
|
|
- if ((iph->protocol == IPPROTO_IGMP) &&
|
|
|
- (skb->protocol == htons(ETH_P_IP))) {
|
|
|
-
|
|
|
- read_lock(&bond->curr_slave_lock);
|
|
|
- slave = bond->curr_active_slave;
|
|
|
- read_unlock(&bond->curr_slave_lock);
|
|
|
-
|
|
|
- if (!slave)
|
|
|
- goto out;
|
|
|
+ if (iph->protocol == IPPROTO_IGMP && skb->protocol == htons(ETH_P_IP)) {
|
|
|
+ slave = rcu_dereference(bond->curr_active_slave);
|
|
|
+ if (slave && slave_can_tx(slave))
|
|
|
+ bond_dev_queue_xmit(bond, skb, slave->dev);
|
|
|
+ else
|
|
|
+ bond_xmit_slave_id(bond, skb, 0);
|
|
|
} else {
|
|
|
- /*
|
|
|
- * Concurrent TX may collide on rr_tx_counter; we accept
|
|
|
- * that as being rare enough not to justify using an
|
|
|
- * atomic op here.
|
|
|
- */
|
|
|
- slave_no = bond->rr_tx_counter++ % bond->slave_cnt;
|
|
|
-
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
- slave_no--;
|
|
|
- if (slave_no < 0)
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- start_at = slave;
|
|
|
- bond_for_each_slave_from(bond, slave, i, start_at) {
|
|
|
- if (IS_UP(slave->dev) &&
|
|
|
- (slave->link == BOND_LINK_UP) &&
|
|
|
- bond_is_active_slave(slave)) {
|
|
|
- res = bond_dev_queue_xmit(bond, skb, slave->dev);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-out:
|
|
|
- if (res) {
|
|
|
- /* no suitable interface, frame not sent */
|
|
|
- kfree_skb(skb);
|
|
|
+ bond_xmit_slave_id(bond, skb,
|
|
|
+ bond->rr_tx_counter++ % bond->slave_cnt);
|
|
|
}
|
|
|
|
|
|
return NETDEV_TX_OK;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/*
|
|
|
* in active-backup mode, we know that bond->curr_active_slave is always valid if
|
|
|
* the bond has a usable interface.
|
|
@@ -3956,18 +3689,12 @@ out:
|
|
|
static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_dev)
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
|
- int res = 1;
|
|
|
-
|
|
|
- read_lock(&bond->curr_slave_lock);
|
|
|
-
|
|
|
- if (bond->curr_active_slave)
|
|
|
- res = bond_dev_queue_xmit(bond, skb,
|
|
|
- bond->curr_active_slave->dev);
|
|
|
-
|
|
|
- read_unlock(&bond->curr_slave_lock);
|
|
|
+ struct slave *slave;
|
|
|
|
|
|
- if (res)
|
|
|
- /* no suitable interface, frame not sent */
|
|
|
+ slave = rcu_dereference(bond->curr_active_slave);
|
|
|
+ if (slave)
|
|
|
+ bond_dev_queue_xmit(bond, skb, slave->dev);
|
|
|
+ else
|
|
|
kfree_skb(skb);
|
|
|
|
|
|
return NETDEV_TX_OK;
|
|
@@ -3981,87 +3708,39 @@ static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_d
|
|
|
static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev)
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
|
- struct slave *slave, *start_at;
|
|
|
- int slave_no;
|
|
|
- int i;
|
|
|
- int res = 1;
|
|
|
|
|
|
- slave_no = bond->xmit_hash_policy(skb, bond->slave_cnt);
|
|
|
-
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
- slave_no--;
|
|
|
- if (slave_no < 0)
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- start_at = slave;
|
|
|
-
|
|
|
- bond_for_each_slave_from(bond, slave, i, start_at) {
|
|
|
- if (IS_UP(slave->dev) &&
|
|
|
- (slave->link == BOND_LINK_UP) &&
|
|
|
- bond_is_active_slave(slave)) {
|
|
|
- res = bond_dev_queue_xmit(bond, skb, slave->dev);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (res) {
|
|
|
- /* no suitable interface, frame not sent */
|
|
|
- kfree_skb(skb);
|
|
|
- }
|
|
|
+ bond_xmit_slave_id(bond, skb,
|
|
|
+ bond->xmit_hash_policy(skb, bond->slave_cnt));
|
|
|
|
|
|
return NETDEV_TX_OK;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * in broadcast mode, we send everything to all usable interfaces.
|
|
|
- */
|
|
|
+/* in broadcast mode, we send everything to all usable interfaces. */
|
|
|
static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
|
- struct slave *slave, *start_at;
|
|
|
- struct net_device *tx_dev = NULL;
|
|
|
- int i;
|
|
|
- int res = 1;
|
|
|
-
|
|
|
- read_lock(&bond->curr_slave_lock);
|
|
|
- start_at = bond->curr_active_slave;
|
|
|
- read_unlock(&bond->curr_slave_lock);
|
|
|
-
|
|
|
- if (!start_at)
|
|
|
- goto out;
|
|
|
+ struct slave *slave = NULL;
|
|
|
|
|
|
- bond_for_each_slave_from(bond, slave, i, start_at) {
|
|
|
- if (IS_UP(slave->dev) &&
|
|
|
- (slave->link == BOND_LINK_UP) &&
|
|
|
- bond_is_active_slave(slave)) {
|
|
|
- if (tx_dev) {
|
|
|
- struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
|
|
|
- if (!skb2) {
|
|
|
- pr_err("%s: Error: bond_xmit_broadcast(): skb_clone() failed\n",
|
|
|
- bond_dev->name);
|
|
|
- continue;
|
|
|
- }
|
|
|
+ bond_for_each_slave_rcu(bond, slave) {
|
|
|
+ if (bond_is_last_slave(bond, slave))
|
|
|
+ break;
|
|
|
+ if (IS_UP(slave->dev) && slave->link == BOND_LINK_UP) {
|
|
|
+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
|
|
|
|
|
|
- res = bond_dev_queue_xmit(bond, skb2, tx_dev);
|
|
|
- if (res) {
|
|
|
- kfree_skb(skb2);
|
|
|
- continue;
|
|
|
- }
|
|
|
+ if (!skb2) {
|
|
|
+ pr_err("%s: Error: bond_xmit_broadcast(): skb_clone() failed\n",
|
|
|
+ bond_dev->name);
|
|
|
+ continue;
|
|
|
}
|
|
|
- tx_dev = slave->dev;
|
|
|
+ /* bond_dev_queue_xmit always returns 0 */
|
|
|
+ bond_dev_queue_xmit(bond, skb2, slave->dev);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- if (tx_dev)
|
|
|
- res = bond_dev_queue_xmit(bond, skb, tx_dev);
|
|
|
-
|
|
|
-out:
|
|
|
- if (res)
|
|
|
- /* no suitable interface, frame not sent */
|
|
|
+ if (slave && IS_UP(slave->dev) && slave->link == BOND_LINK_UP)
|
|
|
+ bond_dev_queue_xmit(bond, skb, slave->dev);
|
|
|
+ else
|
|
|
kfree_skb(skb);
|
|
|
|
|
|
- /* frame sent to all suitable interfaces */
|
|
|
return NETDEV_TX_OK;
|
|
|
}
|
|
|
|
|
@@ -4089,15 +3768,15 @@ static void bond_set_xmit_hash_policy(struct bonding *bond)
|
|
|
static inline int bond_slave_override(struct bonding *bond,
|
|
|
struct sk_buff *skb)
|
|
|
{
|
|
|
- int i, res = 1;
|
|
|
struct slave *slave = NULL;
|
|
|
struct slave *check_slave;
|
|
|
+ int res = 1;
|
|
|
|
|
|
if (!skb->queue_mapping)
|
|
|
return 1;
|
|
|
|
|
|
/* Find out if any slaves have the same mapping as this skb. */
|
|
|
- bond_for_each_slave(bond, check_slave, i) {
|
|
|
+ bond_for_each_slave_rcu(bond, check_slave) {
|
|
|
if (check_slave->queue_id == skb->queue_mapping) {
|
|
|
slave = check_slave;
|
|
|
break;
|
|
@@ -4182,14 +3861,12 @@ static netdev_tx_t bond_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
if (is_netpoll_tx_blocked(dev))
|
|
|
return NETDEV_TX_BUSY;
|
|
|
|
|
|
- read_lock(&bond->lock);
|
|
|
-
|
|
|
- if (bond->slave_cnt)
|
|
|
+ rcu_read_lock();
|
|
|
+ if (!list_empty(&bond->slave_list))
|
|
|
ret = __bond_start_xmit(skb, dev);
|
|
|
else
|
|
|
kfree_skb(skb);
|
|
|
-
|
|
|
- read_unlock(&bond->lock);
|
|
|
+ rcu_read_unlock();
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
@@ -4230,9 +3907,8 @@ static int bond_ethtool_get_settings(struct net_device *bond_dev,
|
|
|
struct ethtool_cmd *ecmd)
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
|
- struct slave *slave;
|
|
|
- int i;
|
|
|
unsigned long speed = 0;
|
|
|
+ struct slave *slave;
|
|
|
|
|
|
ecmd->duplex = DUPLEX_UNKNOWN;
|
|
|
ecmd->port = PORT_OTHER;
|
|
@@ -4243,7 +3919,7 @@ static int bond_ethtool_get_settings(struct net_device *bond_dev,
|
|
|
* this is an accurate maximum.
|
|
|
*/
|
|
|
read_lock(&bond->lock);
|
|
|
- bond_for_each_slave(bond, slave, i) {
|
|
|
+ bond_for_each_slave(bond, slave) {
|
|
|
if (SLAVE_IS_OK(slave)) {
|
|
|
if (slave->speed != SPEED_UNKNOWN)
|
|
|
speed += slave->speed;
|
|
@@ -4254,6 +3930,7 @@ static int bond_ethtool_get_settings(struct net_device *bond_dev,
|
|
|
}
|
|
|
ethtool_cmd_speed_set(ecmd, speed ? : SPEED_UNKNOWN);
|
|
|
read_unlock(&bond->lock);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -4317,12 +3994,11 @@ static void bond_setup(struct net_device *bond_dev)
|
|
|
/* initialize rwlocks */
|
|
|
rwlock_init(&bond->lock);
|
|
|
rwlock_init(&bond->curr_slave_lock);
|
|
|
-
|
|
|
+ INIT_LIST_HEAD(&bond->slave_list);
|
|
|
bond->params = bonding_defaults;
|
|
|
|
|
|
/* Initialize pointers */
|
|
|
bond->dev = bond_dev;
|
|
|
- INIT_LIST_HEAD(&bond->vlan_list);
|
|
|
|
|
|
/* Initialize the device entry points */
|
|
|
ether_setup(bond_dev);
|
|
@@ -4374,23 +4050,18 @@ static void bond_setup(struct net_device *bond_dev)
|
|
|
static void bond_uninit(struct net_device *bond_dev)
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
|
- struct vlan_entry *vlan, *tmp;
|
|
|
+ struct slave *slave, *tmp_slave;
|
|
|
|
|
|
bond_netpoll_cleanup(bond_dev);
|
|
|
|
|
|
/* Release the bonded slaves */
|
|
|
- while (bond->first_slave != NULL)
|
|
|
- __bond_release_one(bond_dev, bond->first_slave->dev, true);
|
|
|
+ list_for_each_entry_safe(slave, tmp_slave, &bond->slave_list, list)
|
|
|
+ __bond_release_one(bond_dev, slave->dev, true);
|
|
|
pr_info("%s: released all slaves\n", bond_dev->name);
|
|
|
|
|
|
list_del(&bond->bond_list);
|
|
|
|
|
|
bond_debug_unregister(bond);
|
|
|
-
|
|
|
- list_for_each_entry_safe(vlan, tmp, &bond->vlan_list, vlan_list) {
|
|
|
- list_del(&vlan->vlan_list);
|
|
|
- kfree(vlan);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
/*------------------------- Module initialization ---------------------------*/
|