aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/bonding
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/bonding')
-rw-r--r--drivers/net/bonding/bond_3ad.c71
-rw-r--r--drivers/net/bonding/bond_3ad.h9
-rw-r--r--drivers/net/bonding/bond_alb.c17
-rw-r--r--drivers/net/bonding/bond_debugfs.c2
-rw-r--r--drivers/net/bonding/bond_ipv6.c8
-rw-r--r--drivers/net/bonding/bond_main.c485
-rw-r--r--drivers/net/bonding/bond_procfs.c14
-rw-r--r--drivers/net/bonding/bond_sysfs.c219
-rw-r--r--drivers/net/bonding/bonding.h17
9 files changed, 424 insertions, 418 deletions
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index c7537abc..aaa7999 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -262,7 +262,7 @@ static inline u32 __get_agg_selection_mode(struct port *port)
if (bond == NULL)
return BOND_AD_STABLE;
- return BOND_AD_INFO(bond).agg_select_mode;
+ return bond->params.ad_select;
}
/**
@@ -1135,13 +1135,6 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port)
__record_pdu(lacpdu, port);
port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(port->actor_oper_port_state & AD_STATE_LACP_TIMEOUT));
port->actor_oper_port_state &= ~AD_STATE_EXPIRED;
- // verify that if the aggregator is enabled, the port is enabled too.
- //(because if the link goes down for a short time, the 802.3ad will not
- // catch it, and the port will continue to be disabled)
- if (port->aggregator
- && port->aggregator->is_active
- && !__port_is_enabled(port))
- __enable_port(port);
break;
default: //to silence the compiler
break;
@@ -1859,28 +1852,23 @@ static void ad_marker_response_received(struct bond_marker *marker,
void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout)
{
BOND_AD_INFO(bond).agg_select_timer = timeout;
- BOND_AD_INFO(bond).agg_select_mode = bond->params.ad_select;
}
-static u16 aggregator_identifier;
-
/**
* bond_3ad_initialize - initialize a bond's 802.3ad parameters and structures
* @bond: bonding struct to work on
* @tick_resolution: tick duration (millisecond resolution)
- * @lacp_fast: boolean. whether fast periodic should be used
*
* Can be called only after the mac address of the bond is set.
*/
-void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution, int lacp_fast)
+void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution)
{
// check that the bond is not initialized yet
if (MAC_ADDRESS_COMPARE(&(BOND_AD_INFO(bond).system.sys_mac_addr),
bond->dev->dev_addr)) {
- aggregator_identifier = 0;
+ BOND_AD_INFO(bond).aggregator_identifier = 0;
- BOND_AD_INFO(bond).lacp_fast = lacp_fast;
BOND_AD_INFO(bond).system.sys_priority = 0xFFFF;
BOND_AD_INFO(bond).system.sys_mac_addr = *((struct mac_addr *)bond->dev->dev_addr);
@@ -1918,7 +1906,7 @@ int bond_3ad_bind_slave(struct slave *slave)
// port initialization
port = &(SLAVE_AD_INFO(slave).port);
- ad_initialize_port(port, BOND_AD_INFO(bond).lacp_fast);
+ ad_initialize_port(port, bond->params.lacp_fast);
port->slave = slave;
port->actor_port_number = SLAVE_AD_INFO(slave).id;
@@ -1951,7 +1939,7 @@ int bond_3ad_bind_slave(struct slave *slave)
ad_initialize_agg(aggregator);
aggregator->aggregator_mac_address = *((struct mac_addr *)bond->dev->dev_addr);
- aggregator->aggregator_identifier = (++aggregator_identifier);
+ aggregator->aggregator_identifier = ++BOND_AD_INFO(bond).aggregator_identifier;
aggregator->slave = slave;
aggregator->is_active = 0;
aggregator->num_of_ports = 0;
@@ -2120,9 +2108,6 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
read_lock(&bond->lock);
- if (bond->kill_timers)
- goto out;
-
//check if there are any slaves
if (bond->slave_cnt == 0)
goto re_arm;
@@ -2172,7 +2157,7 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
re_arm:
queue_delayed_work(bond->wq, &bond->ad_work, ad_delta_in_ticks);
-out:
+
read_unlock(&bond->lock);
}
@@ -2345,8 +2330,17 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
*/
int bond_3ad_set_carrier(struct bonding *bond)
{
- if (__get_active_agg(&(SLAVE_AD_INFO(bond->first_slave).aggregator))) {
- if (!netif_carrier_ok(bond->dev)) {
+ struct aggregator *active;
+
+ active = __get_active_agg(&(SLAVE_AD_INFO(bond->first_slave).aggregator));
+ if (active) {
+ /* are enough slaves available to consider link up? */
+ if (active->num_of_ports < bond->params.min_links) {
+ if (netif_carrier_ok(bond->dev)) {
+ netif_carrier_off(bond->dev);
+ return 1;
+ }
+ } else if (!netif_carrier_ok(bond->dev)) {
netif_carrier_on(bond->dev);
return 1;
}
@@ -2473,3 +2467,34 @@ void bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond,
bond_3ad_rx_indication((struct lacpdu *) skb->data, slave, skb->len);
read_unlock(&bond->lock);
}
+
+/*
+ * When modify lacp_rate parameter via sysfs,
+ * update actor_oper_port_state of each port.
+ *
+ * Hold slave->state_machine_lock,
+ * so we can modify port->actor_oper_port_state,
+ * no matter bond is up or down.
+ */
+void bond_3ad_update_lacp_rate(struct bonding *bond)
+{
+ int i;
+ struct slave *slave;
+ struct port *port = NULL;
+ int lacp_fast;
+
+ read_lock(&bond->lock);
+ lacp_fast = bond->params.lacp_fast;
+
+ bond_for_each_slave(bond, slave, i) {
+ port = &(SLAVE_AD_INFO(slave).port);
+ __get_state_machine_lock(port);
+ if (lacp_fast)
+ port->actor_oper_port_state |= AD_STATE_LACP_TIMEOUT;
+ else
+ port->actor_oper_port_state &= ~AD_STATE_LACP_TIMEOUT;
+ __release_state_machine_lock(port);
+ }
+
+ read_unlock(&bond->lock);
+}
diff --git a/drivers/net/bonding/bond_3ad.h b/drivers/net/bonding/bond_3ad.h
index 0ee3f16..20d9c78 100644
--- a/drivers/net/bonding/bond_3ad.h
+++ b/drivers/net/bonding/bond_3ad.h
@@ -253,11 +253,7 @@ struct ad_system {
struct ad_bond_info {
struct ad_system system; /* 802.3ad system structure */
u32 agg_select_timer; // Timer to select aggregator after all adapter's hand shakes
- u32 agg_select_mode; // Mode of selection of active aggregator(bandwidth/count)
- int lacp_fast; /* whether fast periodic tx should be
- * requested
- */
- struct timer_list ad_timer;
+ u16 aggregator_identifier;
};
struct ad_slave_info {
@@ -269,7 +265,7 @@ struct ad_slave_info {
};
// ================= AD Exported functions to the main bonding code ==================
-void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution, int lacp_fast);
+void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution);
int bond_3ad_bind_slave(struct slave *slave);
void bond_3ad_unbind_slave(struct slave *slave);
void bond_3ad_state_machine_handler(struct work_struct *);
@@ -282,5 +278,6 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev);
void bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond,
struct slave *slave);
int bond_3ad_set_carrier(struct bonding *bond);
+void bond_3ad_update_lacp_rate(struct bonding *bond);
#endif //__BOND_3AD_H__
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 5e725e0..30431d8 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -635,7 +635,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon
client_info->ntt = 0;
}
- if (bond->vlgrp) {
+ if (bond_vlan_used(bond)) {
if (!vlan_get_tag(skb, &client_info->vlan_id))
client_info->tag = 1;
}
@@ -847,7 +847,7 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[])
skb->priority = TC_PRIO_CONTROL;
skb->dev = slave->dev;
- if (bond->vlgrp) {
+ if (bond_vlan_used(bond)) {
struct vlan_entry *vlan;
vlan = bond_next_vlan(bond,
@@ -1337,10 +1337,6 @@ void bond_alb_monitor(struct work_struct *work)
read_lock(&bond->lock);
- if (bond->kill_timers) {
- goto out;
- }
-
if (bond->slave_cnt == 0) {
bond_info->tx_rebalance_counter = 0;
bond_info->lp_counter = 0;
@@ -1395,10 +1391,13 @@ void bond_alb_monitor(struct work_struct *work)
/*
* dev_set_promiscuity requires rtnl and
- * nothing else.
+ * nothing else. Avoid race with bond_close.
*/
read_unlock(&bond->lock);
- rtnl_lock();
+ if (!rtnl_trylock()) {
+ read_lock(&bond->lock);
+ goto re_arm;
+ }
bond_info->rlb_promisc_timeout_counter = 0;
@@ -1435,7 +1434,7 @@ void bond_alb_monitor(struct work_struct *work)
re_arm:
queue_delayed_work(bond->wq, &bond->alb_work, alb_delta_in_ticks);
-out:
+
read_unlock(&bond->lock);
}
diff --git a/drivers/net/bonding/bond_debugfs.c b/drivers/net/bonding/bond_debugfs.c
index 3680aa2..2cf084e 100644
--- a/drivers/net/bonding/bond_debugfs.c
+++ b/drivers/net/bonding/bond_debugfs.c
@@ -6,7 +6,7 @@
#include "bonding.h"
#include "bond_alb.h"
-#ifdef CONFIG_DEBUG_FS
+#if defined(CONFIG_DEBUG_FS) && !defined(CONFIG_NET_NS)
#include <linux/debugfs.h>
#include <linux/seq_file.h>
diff --git a/drivers/net/bonding/bond_ipv6.c b/drivers/net/bonding/bond_ipv6.c
index 84fbd4e..027a0ee 100644
--- a/drivers/net/bonding/bond_ipv6.c
+++ b/drivers/net/bonding/bond_ipv6.c
@@ -183,10 +183,10 @@ static int bond_inet6addr_event(struct notifier_block *this,
}
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
- if (!bond->vlgrp)
- continue;
- vlan_dev = vlan_group_get_device(bond->vlgrp,
- vlan->vlan_id);
+ rcu_read_lock();
+ vlan_dev = __vlan_find_dev_deep(bond->dev,
+ vlan->vlan_id);
+ rcu_read_unlock();
if (vlan_dev == event_dev) {
switch (event) {
case NETDEV_UP:
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index f1b566f..9145834c 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -99,6 +99,7 @@ static char *mode;
static char *primary;
static char *primary_reselect;
static char *lacp_rate;
+static int min_links;
static char *ad_select;
static char *xmit_hash_policy;
static int arp_interval = BOND_LINK_ARP_INTERV;
@@ -151,6 +152,9 @@ module_param(ad_select, charp, 0);
MODULE_PARM_DESC(ad_select, "803.ad aggregation selection logic; "
"0 for stable (default), 1 for bandwidth, "
"2 for count");
+module_param(min_links, int, 0);
+MODULE_PARM_DESC(min_links, "Minimum number of available links before turning on carrier");
+
module_param(xmit_hash_policy, charp, 0);
MODULE_PARM_DESC(xmit_hash_policy, "balance-xor and 802.3ad hashing method; "
"0 for layer 2 (default), 1 for layer 3+4, "
@@ -330,16 +334,6 @@ static int bond_del_vlan(struct bonding *bond, unsigned short vlan_id)
kfree(vlan);
- if (list_empty(&bond->vlan_list) &&
- (bond->slave_cnt == 0)) {
- /* Last VLAN removed and no slaves, so
- * restore block on adding VLANs. This will
- * be removed once new slaves that are not
- * VLAN challenged will be added.
- */
- bond->dev->features |= NETIF_F_VLAN_CHALLENGED;
- }
-
res = 0;
goto out;
}
@@ -400,7 +394,6 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb,
struct net_device *slave_dev)
{
skb->dev = slave_dev;
- skb->priority = 1;
BUILD_BUG_ON(sizeof(skb->queue_mapping) !=
sizeof(qdisc_skb_cb(skb)->bond_queue_mapping));
@@ -415,9 +408,8 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb,
}
/*
- * In the following 3 functions, bond_vlan_rx_register(), bond_vlan_rx_add_vid
- * and bond_vlan_rx_kill_vid, We don't protect the slave list iteration with a
- * lock because:
+ * In the following 2 functions, bond_vlan_rx_add_vid and bond_vlan_rx_kill_vid,
+ * We don't protect the slave list iteration with a lock because:
* a. This operation is performed in IOCTL context,
* b. The operation is protected by the RTNL semaphore in the 8021q code,
* c. Holding a lock with BH disabled while directly calling a base driver
@@ -433,33 +425,6 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb,
*/
/**
- * bond_vlan_rx_register - Propagates registration to slaves
- * @bond_dev: bonding net device that got called
- * @grp: vlan group being registered
- */
-static void bond_vlan_rx_register(struct net_device *bond_dev,
- struct vlan_group *grp)
-{
- struct bonding *bond = netdev_priv(bond_dev);
- struct slave *slave;
- int i;
-
- write_lock_bh(&bond->lock);
- bond->vlgrp = grp;
- write_unlock_bh(&bond->lock);
-
- bond_for_each_slave(bond, slave, i) {
- struct net_device *slave_dev = slave->dev;
- const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
-
- if ((slave_dev->features & NETIF_F_HW_VLAN_RX) &&
- slave_ops->ndo_vlan_rx_register) {
- slave_ops->ndo_vlan_rx_register(slave_dev, grp);
- }
- }
-}
-
-/**
* bond_vlan_rx_add_vid - Propagates adding an id to slaves
* @bond_dev: bonding net device that got called
* @vid: vlan id being added
@@ -496,7 +461,6 @@ static void bond_vlan_rx_kill_vid(struct net_device *bond_dev, uint16_t vid)
{
struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave;
- struct net_device *vlan_dev;
int i, res;
bond_for_each_slave(bond, slave, i) {
@@ -505,12 +469,7 @@ static void bond_vlan_rx_kill_vid(struct net_device *bond_dev, uint16_t vid)
if ((slave_dev->features & NETIF_F_HW_VLAN_FILTER) &&
slave_ops->ndo_vlan_rx_kill_vid) {
- /* Save and then restore vlan_dev in the grp array,
- * since the slave's driver might clear it.
- */
- vlan_dev = vlan_group_get_device(bond->vlgrp, vid);
slave_ops->ndo_vlan_rx_kill_vid(slave_dev, vid);
- vlan_group_set_device(bond->vlgrp, vid, vlan_dev);
}
}
@@ -526,13 +485,6 @@ static void bond_add_vlans_on_slave(struct bonding *bond, struct net_device *sla
struct vlan_entry *vlan;
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
- if (!bond->vlgrp)
- return;
-
- if ((slave_dev->features & NETIF_F_HW_VLAN_RX) &&
- slave_ops->ndo_vlan_rx_register)
- slave_ops->ndo_vlan_rx_register(slave_dev, bond->vlgrp);
-
if (!(slave_dev->features & NETIF_F_HW_VLAN_FILTER) ||
!(slave_ops->ndo_vlan_rx_add_vid))
return;
@@ -546,30 +498,16 @@ static void bond_del_vlans_from_slave(struct bonding *bond,
{
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
struct vlan_entry *vlan;
- struct net_device *vlan_dev;
-
- if (!bond->vlgrp)
- return;
if (!(slave_dev->features & NETIF_F_HW_VLAN_FILTER) ||
!(slave_ops->ndo_vlan_rx_kill_vid))
- goto unreg;
+ return;
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
if (!vlan->vlan_id)
continue;
- /* Save and then restore vlan_dev in the grp array,
- * since the slave's driver might clear it.
- */
- vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
slave_ops->ndo_vlan_rx_kill_vid(slave_dev, vlan->vlan_id);
- vlan_group_set_device(bond->vlgrp, vlan->vlan_id, vlan_dev);
}
-
-unreg:
- if ((slave_dev->features & NETIF_F_HW_VLAN_RX) &&
- slave_ops->ndo_vlan_rx_register)
- slave_ops->ndo_vlan_rx_register(slave_dev, NULL);
}
/*------------------------------- Link status -------------------------------*/
@@ -613,39 +551,28 @@ down:
/*
* Get link speed and duplex from the slave's base driver
* using ethtool. If for some reason the call fails or the
- * values are invalid, fake speed and duplex to 100/Full
+ * values are invalid, set speed and duplex to -1,
* and return error.
*/
static int bond_update_speed_duplex(struct slave *slave)
{
struct net_device *slave_dev = slave->dev;
- struct ethtool_cmd etool = { .cmd = ETHTOOL_GSET };
+ struct ethtool_cmd ecmd;
u32 slave_speed;
int res;
- /* Fake speed and duplex */
- slave->speed = SPEED_100;
- slave->duplex = DUPLEX_FULL;
-
- if (!slave_dev->ethtool_ops || !slave_dev->ethtool_ops->get_settings)
- return -1;
+ slave->speed = SPEED_UNKNOWN;
+ slave->duplex = DUPLEX_UNKNOWN;
- res = slave_dev->ethtool_ops->get_settings(slave_dev, &etool);
+ res = __ethtool_get_settings(slave_dev, &ecmd);
if (res < 0)
return -1;
- slave_speed = ethtool_cmd_speed(&etool);
- switch (slave_speed) {
- case SPEED_10:
- case SPEED_100:
- case SPEED_1000:
- case SPEED_10000:
- break;
- default:
+ slave_speed = ethtool_cmd_speed(&ecmd);
+ if (slave_speed == 0 || slave_speed == ((__u32) -1))
return -1;
- }
- switch (etool.duplex) {
+ switch (ecmd.duplex) {
case DUPLEX_FULL:
case DUPLEX_HALF:
break;
@@ -654,7 +581,7 @@ static int bond_update_speed_duplex(struct slave *slave)
}
slave->speed = slave_speed;
- slave->duplex = etool.duplex;
+ slave->duplex = ecmd.duplex;
return 0;
}
@@ -850,13 +777,13 @@ static void bond_resend_igmp_join_requests(struct bonding *bond)
__bond_resend_igmp_join_requests(bond->dev);
/* rejoin all groups on vlan devices */
- if (bond->vlgrp) {
- list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
- vlan_dev = vlan_group_get_device(bond->vlgrp,
- vlan->vlan_id);
- if (vlan_dev)
- __bond_resend_igmp_join_requests(vlan_dev);
- }
+ list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
+ rcu_read_lock();
+ vlan_dev = __vlan_find_dev_deep(bond->dev,
+ vlan->vlan_id);
+ rcu_read_unlock();
+ if (vlan_dev)
+ __bond_resend_igmp_join_requests(vlan_dev);
}
if (--bond->igmp_retrans > 0)
@@ -934,6 +861,23 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active,
}
}
+static struct slave *bond_get_old_active(struct bonding *bond,
+ struct slave *new_active)
+{
+ struct slave *slave;
+ int i;
+
+ bond_for_each_slave(bond, slave, i) {
+ if (slave == new_active)
+ continue;
+
+ if (!compare_ether_addr(bond->dev->dev_addr, slave->dev->dev_addr))
+ return slave;
+ }
+
+ return NULL;
+}
+
/*
* bond_do_fail_over_mac
*
@@ -971,6 +915,9 @@ static void bond_do_fail_over_mac(struct bonding *bond,
write_unlock_bh(&bond->curr_slave_lock);
read_unlock(&bond->lock);
+ if (!old_active)
+ old_active = bond_get_old_active(bond, new_active);
+
if (old_active) {
memcpy(tmp_mac, new_active->dev->dev_addr, ETH_ALEN);
memcpy(saddr.sa_data, old_active->dev->dev_addr,
@@ -1581,7 +1528,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
/* no need to lock since we're protected by rtnl_lock */
if (slave_dev->features & NETIF_F_VLAN_CHALLENGED) {
pr_debug("%s: NETIF_F_VLAN_CHALLENGED\n", slave_dev->name);
- if (bond->vlgrp) {
+ if (bond_vlan_used(bond)) {
pr_err("%s: Error: cannot enslave VLAN challenged slave %s on VLAN enabled bond %s\n",
bond_dev->name, slave_dev->name, bond_dev->name);
return -EPERM;
@@ -1788,6 +1735,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
bond_compute_features(bond);
+ bond_update_speed_duplex(new_slave);
+
read_lock(&bond->lock);
new_slave->last_arp_rx = jiffies;
@@ -1831,17 +1780,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
new_slave->link = BOND_LINK_DOWN;
}
- if (bond_update_speed_duplex(new_slave) &&
- (new_slave->link != BOND_LINK_DOWN)) {
- pr_warning("%s: Warning: failed to get speed and duplex from %s, assumed to be 100Mb/sec and Full.\n",
- bond_dev->name, new_slave->dev->name);
-
- if (bond->params.mode == BOND_MODE_8023AD) {
- pr_warning("%s: Warning: Operation of 802.3ad mode requires ETHTOOL support in base driver for proper aggregator selection.\n",
- bond_dev->name);
- }
- }
-
if (USES_PRIMARY(bond->params.mode) && bond->params.primary[0]) {
/* if there is a primary slave, remember it */
if (strcmp(bond->params.primary, new_slave->dev->name) == 0) {
@@ -1869,8 +1807,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
/* 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,
- bond->params.lacp_fast);
+ bond_3ad_initialize(bond, 1000/AD_TIMER_INTERVAL);
} else {
SLAVE_AD_INFO(new_slave).id =
SLAVE_AD_INFO(new_slave->prev).id + 1;
@@ -2098,7 +2035,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
*/
memset(bond_dev->dev_addr, 0, bond_dev->addr_len);
- if (bond->vlgrp) {
+ if (bond_vlan_used(bond)) {
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",
@@ -2286,7 +2223,7 @@ static int bond_release_all(struct net_device *bond_dev)
*/
memset(bond_dev->dev_addr, 0, bond_dev->addr_len);
- if (bond->vlgrp) {
+ if (bond_vlan_used(bond)) {
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",
@@ -2527,8 +2464,6 @@ static void bond_miimon_commit(struct bonding *bond)
bond_set_backup_slave(slave);
}
- bond_update_speed_duplex(slave);
-
pr_info("%s: link status definitely up for interface %s, %u Mbps %s duplex.\n",
bond->dev->name, slave->dev->name,
slave->speed, slave->duplex ? "full" : "half");
@@ -2607,10 +2542,11 @@ void bond_mii_monitor(struct work_struct *work)
struct bonding *bond = container_of(work, struct bonding,
mii_work.work);
bool should_notify_peers = false;
+ unsigned long delay;
read_lock(&bond->lock);
- if (bond->kill_timers)
- goto out;
+
+ delay = msecs_to_jiffies(bond->params.miimon);
if (bond->slave_cnt == 0)
goto re_arm;
@@ -2619,7 +2555,15 @@ void bond_mii_monitor(struct work_struct *work)
if (bond_miimon_inspect(bond)) {
read_unlock(&bond->lock);
- rtnl_lock();
+
+ /* Race avoidance with bond_close cancel of workqueue */
+ if (!rtnl_trylock()) {
+ read_lock(&bond->lock);
+ delay = 1;
+ should_notify_peers = false;
+ goto re_arm;
+ }
+
read_lock(&bond->lock);
bond_miimon_commit(bond);
@@ -2631,42 +2575,22 @@ void bond_mii_monitor(struct work_struct *work)
re_arm:
if (bond->params.miimon)
- queue_delayed_work(bond->wq, &bond->mii_work,
- msecs_to_jiffies(bond->params.miimon));
-out:
+ queue_delayed_work(bond->wq, &bond->mii_work, delay);
+
read_unlock(&bond->lock);
if (should_notify_peers) {
- rtnl_lock();
+ if (!rtnl_trylock()) {
+ read_lock(&bond->lock);
+ bond->send_peer_notif++;
+ read_unlock(&bond->lock);
+ return;
+ }
netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS);
rtnl_unlock();
}
}
-static __be32 bond_glean_dev_ip(struct net_device *dev)
-{
- struct in_device *idev;
- struct in_ifaddr *ifa;
- __be32 addr = 0;
-
- if (!dev)
- return 0;
-
- rcu_read_lock();
- idev = __in_dev_get_rcu(dev);
- if (!idev)
- goto out;
-
- ifa = idev->ifa_list;
- if (!ifa)
- goto out;
-
- addr = ifa->ifa_local;
-out:
- rcu_read_unlock();
- return addr;
-}
-
static int bond_has_this_ip(struct bonding *bond, __be32 ip)
{
struct vlan_entry *vlan;
@@ -2724,7 +2648,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
if (!targets[i])
break;
pr_debug("basa: target %x\n", targets[i]);
- if (!bond->vlgrp) {
+ if (!bond_vlan_used(bond)) {
pr_debug("basa: empty vlan: arp_send\n");
bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
bond->master_ip, 0);
@@ -2759,7 +2683,10 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
vlan_id = 0;
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
- vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
+ rcu_read_lock();
+ vlan_dev = __vlan_find_dev_deep(bond->dev,
+ 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",
@@ -2876,9 +2803,6 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval);
- if (bond->kill_timers)
- goto out;
-
if (bond->slave_cnt == 0)
goto re_arm;
@@ -2977,7 +2901,7 @@ 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);
-out:
+
read_unlock(&bond->lock);
}
@@ -3222,9 +3146,6 @@ void bond_activebackup_arp_mon(struct work_struct *work)
read_lock(&bond->lock);
- if (bond->kill_timers)
- goto out;
-
delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval);
if (bond->slave_cnt == 0)
@@ -3234,7 +3155,15 @@ void bond_activebackup_arp_mon(struct work_struct *work)
if (bond_ab_arp_inspect(bond, delta_in_ticks)) {
read_unlock(&bond->lock);
- rtnl_lock();
+
+ /* Race avoidance with bond_close flush of workqueue */
+ if (!rtnl_trylock()) {
+ read_lock(&bond->lock);
+ delta_in_ticks = 1;
+ should_notify_peers = false;
+ goto re_arm;
+ }
+
read_lock(&bond->lock);
bond_ab_arp_commit(bond, delta_in_ticks);
@@ -3249,11 +3178,16 @@ void bond_activebackup_arp_mon(struct work_struct *work)
re_arm:
if (bond->params.arp_interval)
queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks);
-out:
+
read_unlock(&bond->lock);
if (should_notify_peers) {
- rtnl_lock();
+ if (!rtnl_trylock()) {
+ read_lock(&bond->lock);
+ bond->send_peer_notif++;
+ read_unlock(&bond->lock);
+ return;
+ }
netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS);
rtnl_unlock();
}
@@ -3282,6 +3216,12 @@ static int bond_master_netdev_event(unsigned long event,
switch (event) {
case NETDEV_CHANGENAME:
return bond_event_changename(event_bond);
+ case NETDEV_UNREGISTER:
+ bond_remove_proc_entry(event_bond);
+ break;
+ case NETDEV_REGISTER:
+ bond_create_proc_entry(event_bond);
+ break;
default:
break;
}
@@ -3294,6 +3234,7 @@ static int bond_slave_netdev_event(unsigned long event,
{
struct net_device *bond_dev = slave_dev->master;
struct bonding *bond = netdev_priv(bond_dev);
+ struct slave *slave = NULL;
switch (event) {
case NETDEV_UNREGISTER:
@@ -3304,20 +3245,16 @@ static int bond_slave_netdev_event(unsigned long event,
bond_release(bond_dev, slave_dev);
}
break;
+ case NETDEV_UP:
case NETDEV_CHANGE:
- if (bond->params.mode == BOND_MODE_8023AD || bond_is_lb(bond)) {
- struct slave *slave;
-
- slave = bond_get_slave_by_dev(bond, slave_dev);
- if (slave) {
- u32 old_speed = slave->speed;
- u8 old_duplex = slave->duplex;
-
- bond_update_speed_duplex(slave);
+ slave = bond_get_slave_by_dev(bond, slave_dev);
+ if (slave) {
+ u32 old_speed = slave->speed;
+ u8 old_duplex = slave->duplex;
- if (bond_is_lb(bond))
- break;
+ bond_update_speed_duplex(slave);
+ if (bond->params.mode == BOND_MODE_8023AD) {
if (old_speed != slave->speed)
bond_3ad_adapter_speed_changed(slave);
if (old_duplex != slave->duplex)
@@ -3409,6 +3346,10 @@ static int bond_inetaddr_event(struct notifier_block *this, unsigned long event,
struct bonding *bond;
struct vlan_entry *vlan;
+ /* we only care about primary address */
+ if(ifa->ifa_flags & IFA_F_SECONDARY)
+ return NOTIFY_DONE;
+
list_for_each_entry(bond, &bn->dev_list, bond_list) {
if (bond->dev == event_dev) {
switch (event) {
@@ -3416,7 +3357,7 @@ static int bond_inetaddr_event(struct notifier_block *this, unsigned long event,
bond->master_ip = ifa->ifa_local;
return NOTIFY_OK;
case NETDEV_DOWN:
- bond->master_ip = bond_glean_dev_ip(bond->dev);
+ bond->master_ip = 0;
return NOTIFY_OK;
default:
return NOTIFY_DONE;
@@ -3424,17 +3365,15 @@ static int bond_inetaddr_event(struct notifier_block *this, unsigned long event,
}
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
- if (!bond->vlgrp)
- continue;
- vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
+ vlan_dev = __vlan_find_dev_deep(bond->dev,
+ vlan->vlan_id);
if (vlan_dev == event_dev) {
switch (event) {
case NETDEV_UP:
vlan->vlan_ip = ifa->ifa_local;
return NOTIFY_OK;
case NETDEV_DOWN:
- vlan->vlan_ip =
- bond_glean_dev_ip(vlan_dev);
+ vlan->vlan_ip = 0;
return NOTIFY_OK;
default:
return NOTIFY_DONE;
@@ -3485,7 +3424,7 @@ static int bond_xmit_hash_policy_l34(struct sk_buff *skb, int count)
int layer4_xor = 0;
if (skb->protocol == htons(ETH_P_IP)) {
- if (!(iph->frag_off & htons(IP_MF|IP_OFFSET)) &&
+ if (!ip_is_fragment(iph) &&
(iph->protocol == IPPROTO_TCP ||
iph->protocol == IPPROTO_UDP)) {
layer4_xor = ntohs((*layer4hdr ^ *(layer4hdr + 1)));
@@ -3510,47 +3449,71 @@ static int bond_xmit_hash_policy_l2(struct sk_buff *skb, int count)
/*-------------------------- Device entry points ----------------------------*/
+static void bond_work_init_all(struct bonding *bond)
+{
+ INIT_DELAYED_WORK(&bond->mcast_work,
+ bond_resend_igmp_join_requests_delayed);
+ INIT_DELAYED_WORK(&bond->alb_work, bond_alb_monitor);
+ INIT_DELAYED_WORK(&bond->mii_work, bond_mii_monitor);
+ if (bond->params.mode == BOND_MODE_ACTIVEBACKUP)
+ INIT_DELAYED_WORK(&bond->arp_work, bond_activebackup_arp_mon);
+ else
+ INIT_DELAYED_WORK(&bond->arp_work, bond_loadbalance_arp_mon);
+ INIT_DELAYED_WORK(&bond->ad_work, bond_3ad_state_machine_handler);
+}
+
+static void bond_work_cancel_all(struct bonding *bond)
+{
+ cancel_delayed_work_sync(&bond->mii_work);
+ cancel_delayed_work_sync(&bond->arp_work);
+ cancel_delayed_work_sync(&bond->alb_work);
+ cancel_delayed_work_sync(&bond->ad_work);
+ cancel_delayed_work_sync(&bond->mcast_work);
+}
+
static int bond_open(struct net_device *bond_dev)
{
struct bonding *bond = netdev_priv(bond_dev);
+ struct slave *slave;
+ int i;
- bond->kill_timers = 0;
+ /* reset slave->backup and slave->inactive */
+ read_lock(&bond->lock);
+ if (bond->slave_cnt > 0) {
+ read_lock(&bond->curr_slave_lock);
+ bond_for_each_slave(bond, slave, i) {
+ if ((bond->params.mode == BOND_MODE_ACTIVEBACKUP)
+ && (slave != bond->curr_active_slave)) {
+ bond_set_slave_inactive_flags(slave);
+ } else {
+ bond_set_slave_active_flags(slave);
+ }
+ }
+ read_unlock(&bond->curr_slave_lock);
+ }
+ read_unlock(&bond->lock);
- INIT_DELAYED_WORK(&bond->mcast_work, bond_resend_igmp_join_requests_delayed);
+ bond_work_init_all(bond);
if (bond_is_lb(bond)) {
/* bond_alb_initialize must be called before the timer
* is started.
*/
- if (bond_alb_initialize(bond, (bond->params.mode == BOND_MODE_ALB))) {
- /* something went wrong - fail the open operation */
+ if (bond_alb_initialize(bond, (bond->params.mode == BOND_MODE_ALB)))
return -ENOMEM;
- }
-
- INIT_DELAYED_WORK(&bond->alb_work, bond_alb_monitor);
queue_delayed_work(bond->wq, &bond->alb_work, 0);
}
- if (bond->params.miimon) { /* link check interval, in milliseconds. */
- INIT_DELAYED_WORK(&bond->mii_work, bond_mii_monitor);
+ if (bond->params.miimon) /* link check interval, in milliseconds. */
queue_delayed_work(bond->wq, &bond->mii_work, 0);
- }
if (bond->params.arp_interval) { /* arp interval, in milliseconds. */
- if (bond->params.mode == BOND_MODE_ACTIVEBACKUP)
- INIT_DELAYED_WORK(&bond->arp_work,
- bond_activebackup_arp_mon);
- else
- INIT_DELAYED_WORK(&bond->arp_work,
- bond_loadbalance_arp_mon);
-
queue_delayed_work(bond->wq, &bond->arp_work, 0);
if (bond->params.arp_validate)
bond->recv_probe = bond_arp_rcv;
}
if (bond->params.mode == BOND_MODE_8023AD) {
- INIT_DELAYED_WORK(&bond->ad_work, bond_3ad_state_machine_handler);
queue_delayed_work(bond->wq, &bond->ad_work, 0);
/* register to receive LACPDUs */
bond->recv_probe = bond_3ad_lacpdu_recv;
@@ -3565,37 +3528,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;
-
- /* signal timers not to re-arm */
- bond->kill_timers = 1;
-
write_unlock_bh(&bond->lock);
- if (bond->params.miimon) { /* link check interval, in milliseconds. */
- cancel_delayed_work(&bond->mii_work);
- }
-
- if (bond->params.arp_interval) { /* arp interval, in milliseconds. */
- cancel_delayed_work(&bond->arp_work);
- }
-
- switch (bond->params.mode) {
- case BOND_MODE_8023AD:
- cancel_delayed_work(&bond->ad_work);
- break;
- case BOND_MODE_TLB:
- case BOND_MODE_ALB:
- cancel_delayed_work(&bond->alb_work);
- break;
- default:
- break;
- }
-
- if (delayed_work_pending(&bond->mcast_work))
- cancel_delayed_work(&bond->mcast_work);
-
+ bond_work_cancel_all(bond);
if (bond_is_lb(bond)) {
/* Must be called only after all
* slaves have been released
@@ -3780,44 +3716,27 @@ static bool bond_addr_in_mc_list(unsigned char *addr,
return false;
}
-static void bond_set_multicast_list(struct net_device *bond_dev)
+static void bond_change_rx_flags(struct net_device *bond_dev, int change)
{
struct bonding *bond = netdev_priv(bond_dev);
- struct netdev_hw_addr *ha;
- bool found;
-
- /*
- * Do promisc before checking multicast_mode
- */
- if ((bond_dev->flags & IFF_PROMISC) && !(bond->flags & IFF_PROMISC))
- /*
- * FIXME: Need to handle the error when one of the multi-slaves
- * encounters error.
- */
- bond_set_promiscuity(bond, 1);
+ if (change & IFF_PROMISC)
+ bond_set_promiscuity(bond,
+ bond_dev->flags & IFF_PROMISC ? 1 : -1);
- if (!(bond_dev->flags & IFF_PROMISC) && (bond->flags & IFF_PROMISC))
- bond_set_promiscuity(bond, -1);
-
-
- /* set allmulti flag to slaves */
- if ((bond_dev->flags & IFF_ALLMULTI) && !(bond->flags & IFF_ALLMULTI))
- /*
- * FIXME: Need to handle the error when one of the multi-slaves
- * encounters error.
- */
- bond_set_allmulti(bond, 1);
-
-
- if (!(bond_dev->flags & IFF_ALLMULTI) && (bond->flags & IFF_ALLMULTI))
- bond_set_allmulti(bond, -1);
+ if (change & IFF_ALLMULTI)
+ bond_set_allmulti(bond,
+ bond_dev->flags & IFF_ALLMULTI ? 1 : -1);
+}
+static void bond_set_multicast_list(struct net_device *bond_dev)
+{
+ struct bonding *bond = netdev_priv(bond_dev);
+ struct netdev_hw_addr *ha;
+ bool found;
read_lock(&bond->lock);
- bond->flags = bond_dev->flags;
-
/* looking for addresses to add to slaves' mc list */
netdev_for_each_mc_addr(ha, bond_dev) {
found = bond_addr_in_mc_list(ha->addr, &bond->mc_list,
@@ -4376,12 +4295,12 @@ static const struct net_device_ops bond_netdev_ops = {
.ndo_select_queue = bond_select_queue,
.ndo_get_stats64 = bond_get_stats,
.ndo_do_ioctl = bond_do_ioctl,
- .ndo_set_multicast_list = bond_set_multicast_list,
+ .ndo_change_rx_flags = bond_change_rx_flags,
+ .ndo_set_rx_mode = bond_set_multicast_list,
.ndo_change_mtu = bond_change_mtu,
- .ndo_set_mac_address = bond_set_mac_address,
+ .ndo_set_mac_address = bond_set_mac_address,
.ndo_neigh_setup = bond_neigh_setup,
- .ndo_vlan_rx_register = bond_vlan_rx_register,
- .ndo_vlan_rx_add_vid = bond_vlan_rx_add_vid,
+ .ndo_vlan_rx_add_vid = bond_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = bond_vlan_rx_kill_vid,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_netpoll_setup = bond_netpoll_setup,
@@ -4456,30 +4375,6 @@ static void bond_setup(struct net_device *bond_dev)
bond_dev->features |= bond_dev->hw_features;
}
-static void bond_work_cancel_all(struct bonding *bond)
-{
- write_lock_bh(&bond->lock);
- bond->kill_timers = 1;
- write_unlock_bh(&bond->lock);
-
- if (bond->params.miimon && delayed_work_pending(&bond->mii_work))
- cancel_delayed_work(&bond->mii_work);
-
- if (bond->params.arp_interval && delayed_work_pending(&bond->arp_work))
- cancel_delayed_work(&bond->arp_work);
-
- if (bond->params.mode == BOND_MODE_ALB &&
- delayed_work_pending(&bond->alb_work))
- cancel_delayed_work(&bond->alb_work);
-
- if (bond->params.mode == BOND_MODE_8023AD &&
- delayed_work_pending(&bond->ad_work))
- cancel_delayed_work(&bond->ad_work);
-
- if (delayed_work_pending(&bond->mcast_work))
- cancel_delayed_work(&bond->mcast_work);
-}
-
/*
* Destroy a bonding device.
* Must be under rtnl_lock when this function is called.
@@ -4498,8 +4393,6 @@ static void bond_uninit(struct net_device *bond_dev)
bond_work_cancel_all(bond);
- bond_remove_proc_entry(bond);
-
bond_debug_unregister(bond);
__hw_addr_flush(&bond->mc_list);
@@ -4788,7 +4681,7 @@ static int bond_check_params(struct bond_params *params)
/* miimon and arp_interval not set, we need one so things
* work as expected, see bonding.txt for details
*/
- pr_warning("Warning: either miimon or arp_interval and arp_ip_target module parameters must be specified, otherwise bonding will not detect link failures! see bonding.txt for details.\n");
+ pr_debug("Warning: either miimon or arp_interval and arp_ip_target module parameters must be specified, otherwise bonding will not detect link failures! see bonding.txt for details.\n");
}
if (primary && !USES_PRIMARY(bond_mode)) {
@@ -4845,6 +4738,7 @@ static int bond_check_params(struct bond_params *params)
params->tx_queues = tx_queues;
params->all_slaves_active = all_slaves_active;
params->resend_igmp = resend_igmp;
+ params->min_links = min_links;
if (primary) {
strncpy(params->primary, primary, IFNAMSIZ);
@@ -4900,7 +4794,6 @@ static int bond_init(struct net_device *bond_dev)
bond_set_lockdep_class(bond_dev);
- bond_create_proc_entry(bond);
list_add_tail(&bond->bond_list, &bn->dev_list);
bond_prepare_sysfs_group(bond);
@@ -4922,11 +4815,20 @@ static int bond_validate(struct nlattr *tb[], struct nlattr *data[])
return 0;
}
+static int bond_get_tx_queues(struct net *net, struct nlattr *tb[],
+ unsigned int *num_queues,
+ unsigned int *real_num_queues)
+{
+ *num_queues = tx_queues;
+ return 0;
+}
+
static struct rtnl_link_ops bond_link_ops __read_mostly = {
.kind = "bond",
.priv_size = sizeof(struct bonding),
.setup = bond_setup,
.validate = bond_validate,
+ .get_tx_queues = bond_get_tx_queues,
};
/* Create a new bond based on the specified name and bonding parameters.
@@ -4971,6 +4873,7 @@ static int __net_init bond_net_init(struct net *net)
INIT_LIST_HEAD(&bn->dev_list);
bond_create_proc_dir(bn);
+ bond_create_sysfs(bn);
return 0;
}
@@ -4978,8 +4881,18 @@ static int __net_init bond_net_init(struct net *net)
static void __net_exit bond_net_exit(struct net *net)
{
struct bond_net *bn = net_generic(net, bond_net_id);
+ struct bonding *bond, *tmp_bond;
+ LIST_HEAD(list);
+ bond_destroy_sysfs(bn);
bond_destroy_proc_dir(bn);
+
+ /* Kill off any bonds created after unregistering bond rtnl ops */
+ rtnl_lock();
+ list_for_each_entry_safe(bond, tmp_bond, &bn->dev_list, bond_list)
+ unregister_netdevice_queue(bond->dev, &list);
+ unregister_netdevice_many(&list);
+ rtnl_unlock();
}
static struct pernet_operations bond_net_ops = {
@@ -5016,15 +4929,12 @@ static int __init bonding_init(void)
goto err;
}
- res = bond_create_sysfs();
- if (res)
- goto err;
-
register_netdevice_notifier(&bond_netdev_notifier);
register_inetaddr_notifier(&bond_inetaddr_notifier);
out:
return res;
err:
+ bond_destroy_debugfs();
rtnl_link_unregister(&bond_link_ops);
err_link:
unregister_pernet_subsys(&bond_net_ops);
@@ -5037,7 +4947,6 @@ static void __exit bonding_exit(void)
unregister_netdevice_notifier(&bond_netdev_notifier);
unregister_inetaddr_notifier(&bond_inetaddr_notifier);
- bond_destroy_sysfs();
bond_destroy_debugfs();
rtnl_link_unregister(&bond_link_ops);
diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c
index c97307d..ad284ba 100644
--- a/drivers/net/bonding/bond_procfs.c
+++ b/drivers/net/bonding/bond_procfs.c
@@ -1,4 +1,5 @@
#include <linux/proc_fs.h>
+#include <linux/export.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include "bonding.h"
@@ -125,6 +126,7 @@ static void bond_info_show_master(struct seq_file *seq)
seq_puts(seq, "\n802.3ad info\n");
seq_printf(seq, "LACP rate: %s\n",
(bond->params.lacp_fast) ? "fast" : "slow");
+ seq_printf(seq, "Min links: %d\n", bond->params.min_links);
seq_printf(seq, "Aggregator selection policy (ad_select): %s\n",
ad_select_tbl[bond->params.ad_select].modename);
@@ -156,8 +158,16 @@ static void bond_info_show_slave(struct seq_file *seq,
seq_printf(seq, "\nSlave Interface: %s\n", slave->dev->name);
seq_printf(seq, "MII Status: %s\n",
(slave->link == BOND_LINK_UP) ? "up" : "down");
- seq_printf(seq, "Speed: %d Mbps\n", slave->speed);
- seq_printf(seq, "Duplex: %s\n", slave->duplex ? "full" : "half");
+ if (slave->speed == SPEED_UNKNOWN)
+ seq_printf(seq, "Speed: %s\n", "Unknown");
+ else
+ seq_printf(seq, "Speed: %d Mbps\n", slave->speed);
+
+ if (slave->duplex == DUPLEX_UNKNOWN)
+ seq_printf(seq, "Duplex: %s\n", "Unknown");
+ else
+ seq_printf(seq, "Duplex: %s\n", slave->duplex ? "full" : "half");
+
seq_printf(seq, "Link Failure Count: %u\n",
slave->link_failure_count);
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index 8a96735..cf95bd8 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -55,8 +55,8 @@ static ssize_t bonding_show_bonds(struct class *cls,
struct class_attribute *attr,
char *buf)
{
- struct net *net = current->nsproxy->net_ns;
- struct bond_net *bn = net_generic(net, bond_net_id);
+ struct bond_net *bn =
+ container_of(attr, struct bond_net, class_attr_bonding_masters);
int res = 0;
struct bonding *bond;
@@ -79,9 +79,8 @@ static ssize_t bonding_show_bonds(struct class *cls,
return res;
}
-static struct net_device *bond_get_by_name(struct net *net, const char *ifname)
+static struct net_device *bond_get_by_name(struct bond_net *bn, const char *ifname)
{
- struct bond_net *bn = net_generic(net, bond_net_id);
struct bonding *bond;
list_for_each_entry(bond, &bn->dev_list, bond_list) {
@@ -103,7 +102,8 @@ static ssize_t bonding_store_bonds(struct class *cls,
struct class_attribute *attr,
const char *buffer, size_t count)
{
- struct net *net = current->nsproxy->net_ns;
+ struct bond_net *bn =
+ container_of(attr, struct bond_net, class_attr_bonding_masters);
char command[IFNAMSIZ + 1] = {0, };
char *ifname;
int rv, res = count;
@@ -116,7 +116,7 @@ static ssize_t bonding_store_bonds(struct class *cls,
if (command[0] == '+') {
pr_info("%s is being created...\n", ifname);
- rv = bond_create(net, ifname);
+ rv = bond_create(bn->net, ifname);
if (rv) {
if (rv == -EEXIST)
pr_info("%s already exists.\n", ifname);
@@ -128,7 +128,7 @@ static ssize_t bonding_store_bonds(struct class *cls,
struct net_device *bond_dev;
rtnl_lock();
- bond_dev = bond_get_by_name(net, ifname);
+ bond_dev = bond_get_by_name(bn, ifname);
if (bond_dev) {
pr_info("%s is being deleted...\n", ifname);
unregister_netdevice(bond_dev);
@@ -150,9 +150,24 @@ err_no_cmd:
return -EPERM;
}
+static const void *bonding_namespace(struct class *cls,
+ const struct class_attribute *attr)
+{
+ const struct bond_net *bn =
+ container_of(attr, struct bond_net, class_attr_bonding_masters);
+ return bn->net;
+}
+
/* class attribute for bond_masters file. This ends up in /sys/class/net */
-static CLASS_ATTR(bonding_masters, S_IWUSR | S_IRUGO,
- bonding_show_bonds, bonding_store_bonds);
+static const struct class_attribute class_attr_bonding_masters = {
+ .attr = {
+ .name = "bonding_masters",
+ .mode = S_IWUSR | S_IRUGO,
+ },
+ .show = bonding_show_bonds,
+ .store = bonding_store_bonds,
+ .namespace = bonding_namespace,
+};
int bond_create_slave_symlinks(struct net_device *master,
struct net_device *slave)
@@ -169,6 +184,11 @@ int bond_create_slave_symlinks(struct net_device *master,
sprintf(linkname, "slave_%s", slave->name);
ret = sysfs_create_link(&(master->dev.kobj), &(slave->dev.kobj),
linkname);
+
+ /* free the master link created earlier in case of error */
+ if (ret)
+ sysfs_remove_link(&(slave->dev.kobj), "master");
+
return ret;
}
@@ -304,6 +324,13 @@ static ssize_t bonding_store_mode(struct device *d,
goto out;
}
+ if (bond->slave_cnt > 0) {
+ pr_err("unable to update mode of %s because it has slaves.\n",
+ bond->dev->name);
+ ret = -EPERM;
+ goto out;
+ }
+
new_value = bond_parse_parm(buf, bond_mode_tbl);
if (new_value < 0) {
pr_err("%s: Ignoring invalid mode value %.*s.\n",
@@ -492,6 +519,8 @@ static ssize_t bonding_store_arp_interval(struct device *d,
int new_value, ret = count;
struct bonding *bond = to_bond(d);
+ if (!rtnl_trylock())
+ return restart_syscall();
if (sscanf(buf, "%d", &new_value) != 1) {
pr_err("%s: no arp_interval value specified.\n",
bond->dev->name);
@@ -499,14 +528,15 @@ static ssize_t bonding_store_arp_interval(struct device *d,
goto out;
}
if (new_value < 0) {
- pr_err("%s: Invalid arp_interval value %d not in range 1-%d; rejected.\n",
+ pr_err("%s: Invalid arp_interval value %d not in range 0-%d; rejected.\n",
bond->dev->name, new_value, INT_MAX);
ret = -EINVAL;
goto out;
}
if (bond->params.mode == BOND_MODE_ALB ||
- bond->params.mode == BOND_MODE_TLB) {
- pr_info("%s: ARP monitoring cannot be used with ALB/TLB. Only MII monitoring is supported on %s.\n",
+ bond->params.mode == BOND_MODE_TLB ||
+ bond->params.mode == BOND_MODE_8023AD) {
+ pr_info("%s: ARP monitoring cannot be used with ALB/TLB/802.3ad. Only MII monitoring is supported on %s.\n",
bond->dev->name, bond->dev->name);
ret = -EINVAL;
goto out;
@@ -514,18 +544,15 @@ static ssize_t bonding_store_arp_interval(struct device *d,
pr_info("%s: Setting ARP monitoring interval to %d.\n",
bond->dev->name, new_value);
bond->params.arp_interval = new_value;
- if (bond->params.miimon) {
- pr_info("%s: ARP monitoring cannot be used with MII monitoring. %s Disabling MII monitoring.\n",
- bond->dev->name, bond->dev->name);
- bond->params.miimon = 0;
- if (delayed_work_pending(&bond->mii_work)) {
- cancel_delayed_work(&bond->mii_work);
- flush_workqueue(bond->wq);
+ if (new_value) {
+ if (bond->params.miimon) {
+ pr_info("%s: ARP monitoring cannot be used with MII monitoring. %s Disabling MII monitoring.\n",
+ bond->dev->name, bond->dev->name);
+ bond->params.miimon = 0;
}
- }
- if (!bond->params.arp_targets[0]) {
- pr_info("%s: ARP monitoring has been set up, but no ARP targets have been specified.\n",
- bond->dev->name);
+ if (!bond->params.arp_targets[0])
+ pr_info("%s: ARP monitoring has been set up, but no ARP targets have been specified.\n",
+ bond->dev->name);
}
if (bond->dev->flags & IFF_UP) {
/* If the interface is up, we may need to fire off
@@ -533,19 +560,15 @@ static ssize_t bonding_store_arp_interval(struct device *d,
* timer will get fired off when the open function
* is called.
*/
- if (!delayed_work_pending(&bond->arp_work)) {
- if (bond->params.mode == BOND_MODE_ACTIVEBACKUP)
- INIT_DELAYED_WORK(&bond->arp_work,
- bond_activebackup_arp_mon);
- else
- INIT_DELAYED_WORK(&bond->arp_work,
- bond_loadbalance_arp_mon);
-
+ if (!new_value) {
+ cancel_delayed_work_sync(&bond->arp_work);
+ } else {
+ cancel_delayed_work_sync(&bond->mii_work);
queue_delayed_work(bond->wq, &bond->arp_work, 0);
}
}
-
out:
+ rtnl_unlock();
return ret;
}
static DEVICE_ATTR(arp_interval, S_IRUGO | S_IWUSR,
@@ -671,6 +694,8 @@ static ssize_t bonding_store_downdelay(struct device *d,
int new_value, ret = count;
struct bonding *bond = to_bond(d);
+ if (!rtnl_trylock())
+ return restart_syscall();
if (!(bond->params.miimon)) {
pr_err("%s: Unable to set down delay as MII monitoring is disabled\n",
bond->dev->name);
@@ -685,7 +710,7 @@ static ssize_t bonding_store_downdelay(struct device *d,
}
if (new_value < 0) {
pr_err("%s: Invalid down delay value %d not in range %d-%d; rejected.\n",
- bond->dev->name, new_value, 1, INT_MAX);
+ bond->dev->name, new_value, 0, INT_MAX);
ret = -EINVAL;
goto out;
} else {
@@ -704,6 +729,7 @@ static ssize_t bonding_store_downdelay(struct device *d,
}
out:
+ rtnl_unlock();
return ret;
}
static DEVICE_ATTR(downdelay, S_IRUGO | S_IWUSR,
@@ -726,6 +752,8 @@ static ssize_t bonding_store_updelay(struct device *d,
int new_value, ret = count;
struct bonding *bond = to_bond(d);
+ if (!rtnl_trylock())
+ return restart_syscall();
if (!(bond->params.miimon)) {
pr_err("%s: Unable to set up delay as MII monitoring is disabled\n",
bond->dev->name);
@@ -740,8 +768,8 @@ static ssize_t bonding_store_updelay(struct device *d,
goto out;
}
if (new_value < 0) {
- pr_err("%s: Invalid down delay value %d not in range %d-%d; rejected.\n",
- bond->dev->name, new_value, 1, INT_MAX);
+ pr_err("%s: Invalid up delay value %d not in range %d-%d; rejected.\n",
+ bond->dev->name, new_value, 0, INT_MAX);
ret = -EINVAL;
goto out;
} else {
@@ -759,6 +787,7 @@ static ssize_t bonding_store_updelay(struct device *d,
}
out:
+ rtnl_unlock();
return ret;
}
static DEVICE_ATTR(updelay, S_IRUGO | S_IWUSR,
@@ -804,6 +833,7 @@ static ssize_t bonding_store_lacp(struct device *d,
if ((new_value == 1) || (new_value == 0)) {
bond->params.lacp_fast = new_value;
+ bond_3ad_update_lacp_rate(bond);
pr_info("%s: Setting LACP rate to %s (%d).\n",
bond->dev->name, bond_lacp_tbl[new_value].modename,
new_value);
@@ -818,6 +848,38 @@ out:
static DEVICE_ATTR(lacp_rate, S_IRUGO | S_IWUSR,
bonding_show_lacp, bonding_store_lacp);
+static ssize_t bonding_show_min_links(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bonding *bond = to_bond(d);
+
+ return sprintf(buf, "%d\n", bond->params.min_links);
+}
+
+static ssize_t bonding_store_min_links(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bonding *bond = to_bond(d);
+ int ret;
+ unsigned int new_value;
+
+ ret = kstrtouint(buf, 0, &new_value);
+ if (ret < 0) {
+ pr_err("%s: Ignoring invalid min links value %s.\n",
+ bond->dev->name, buf);
+ return ret;
+ }
+
+ pr_info("%s: Setting min links value to %u\n",
+ bond->dev->name, new_value);
+ bond->params.min_links = new_value;
+ return count;
+}
+static DEVICE_ATTR(min_links, S_IRUGO | S_IWUSR,
+ bonding_show_min_links, bonding_store_min_links);
+
static ssize_t bonding_show_ad_select(struct device *d,
struct device_attribute *attr,
char *buf)
@@ -908,6 +970,8 @@ static ssize_t bonding_store_miimon(struct device *d,
int new_value, ret = count;
struct bonding *bond = to_bond(d);
+ if (!rtnl_trylock())
+ return restart_syscall();
if (sscanf(buf, "%d", &new_value) != 1) {
pr_err("%s: no miimon value specified.\n",
bond->dev->name);
@@ -916,50 +980,43 @@ static ssize_t bonding_store_miimon(struct device *d,
}
if (new_value < 0) {
pr_err("%s: Invalid miimon value %d not in range %d-%d; rejected.\n",
- bond->dev->name, new_value, 1, INT_MAX);
+ bond->dev->name, new_value, 0, INT_MAX);
ret = -EINVAL;
goto out;
- } else {
- pr_info("%s: Setting MII monitoring interval to %d.\n",
- bond->dev->name, new_value);
- bond->params.miimon = new_value;
- if (bond->params.updelay)
- pr_info("%s: Note: Updating updelay (to %d) since it is a multiple of the miimon value.\n",
- bond->dev->name,
- bond->params.updelay * bond->params.miimon);
- if (bond->params.downdelay)
- pr_info("%s: Note: Updating downdelay (to %d) since it is a multiple of the miimon value.\n",
- bond->dev->name,
- bond->params.downdelay * bond->params.miimon);
- if (bond->params.arp_interval) {
- pr_info("%s: MII monitoring cannot be used with ARP monitoring. Disabling ARP monitoring...\n",
- bond->dev->name);
- bond->params.arp_interval = 0;
- if (bond->params.arp_validate) {
- bond->params.arp_validate =
- BOND_ARP_VALIDATE_NONE;
- }
- if (delayed_work_pending(&bond->arp_work)) {
- cancel_delayed_work(&bond->arp_work);
- flush_workqueue(bond->wq);
- }
- }
-
- if (bond->dev->flags & IFF_UP) {
- /* If the interface is up, we may need to fire off
- * the MII timer. If the interface is down, the
- * timer will get fired off when the open function
- * is called.
- */
- if (!delayed_work_pending(&bond->mii_work)) {
- INIT_DELAYED_WORK(&bond->mii_work,
- bond_mii_monitor);
- queue_delayed_work(bond->wq,
- &bond->mii_work, 0);
- }
+ }
+ pr_info("%s: Setting MII monitoring interval to %d.\n",
+ bond->dev->name, new_value);
+ bond->params.miimon = new_value;
+ if (bond->params.updelay)
+ pr_info("%s: Note: Updating updelay (to %d) since it is a multiple of the miimon value.\n",
+ bond->dev->name,
+ bond->params.updelay * bond->params.miimon);
+ if (bond->params.downdelay)
+ pr_info("%s: Note: Updating downdelay (to %d) since it is a multiple of the miimon value.\n",
+ bond->dev->name,
+ bond->params.downdelay * bond->params.miimon);
+ if (new_value && bond->params.arp_interval) {
+ pr_info("%s: MII monitoring cannot be used with ARP monitoring. Disabling ARP monitoring...\n",
+ bond->dev->name);
+ bond->params.arp_interval = 0;
+ if (bond->params.arp_validate)
+ bond->params.arp_validate = BOND_ARP_VALIDATE_NONE;
+ }
+ if (bond->dev->flags & IFF_UP) {
+ /* If the interface is up, we may need to fire off
+ * the MII timer. If the interface is down, the
+ * timer will get fired off when the open function
+ * is called.
+ */
+ if (!new_value) {
+ cancel_delayed_work_sync(&bond->mii_work);
+ } else {
+ cancel_delayed_work_sync(&bond->arp_work);
+ queue_delayed_work(bond->wq, &bond->mii_work, 0);
}
}
out:
+ rtnl_unlock();
return ret;
}
static DEVICE_ATTR(miimon, S_IRUGO | S_IWUSR,
@@ -1611,6 +1668,7 @@ static struct attribute *per_bond_attrs[] = {
&dev_attr_queue_id.attr,
&dev_attr_all_slaves_active.attr,
&dev_attr_resend_igmp.attr,
+ &dev_attr_min_links.attr,
NULL,
};
@@ -1623,11 +1681,14 @@ static struct attribute_group bonding_group = {
* Initialize sysfs. This sets up the bonding_masters file in
* /sys/class/net.
*/
-int bond_create_sysfs(void)
+int bond_create_sysfs(struct bond_net *bn)
{
int ret;
- ret = netdev_class_create_file(&class_attr_bonding_masters);
+ bn->class_attr_bonding_masters = class_attr_bonding_masters;
+ sysfs_attr_init(&bn->class_attr_bonding_masters.attr);
+
+ ret = netdev_class_create_file(&bn->class_attr_bonding_masters);
/*
* Permit multiple loads of the module by ignoring failures to
* create the bonding_masters sysfs file. Bonding devices
@@ -1641,7 +1702,7 @@ int bond_create_sysfs(void)
*/
if (ret == -EEXIST) {
/* Is someone being kinky and naming a device bonding_master? */
- if (__dev_get_by_name(&init_net,
+ if (__dev_get_by_name(bn->net,
class_attr_bonding_masters.attr.name))
pr_err("network device named %s already exists in sysfs",
class_attr_bonding_masters.attr.name);
@@ -1655,9 +1716,9 @@ int bond_create_sysfs(void)
/*
* Remove /sys/class/net/bonding_masters.
*/
-void bond_destroy_sysfs(void)
+void bond_destroy_sysfs(struct bond_net *bn)
{
- netdev_class_remove_file(&class_attr_bonding_masters);
+ netdev_class_remove_file(&bn->class_attr_bonding_masters);
}
/*
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index ea1d005..1aecc37 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -147,6 +147,7 @@ struct bond_params {
int updelay;
int downdelay;
int lacp_fast;
+ unsigned int min_links;
int ad_select;
char primary[IFNAMSIZ];
int primary_reselect;
@@ -221,7 +222,6 @@ struct bonding {
struct slave *);
rwlock_t lock;
rwlock_t curr_slave_lock;
- s8 kill_timers;
u8 send_peer_notif;
s8 setup_by_slave;
s8 igmp_retrans;
@@ -233,14 +233,11 @@ struct bonding {
struct netdev_hw_addr_list mc_list;
int (*xmit_hash_policy)(struct sk_buff *, int);
__be32 master_ip;
- u16 flags;
u16 rr_tx_counter;
struct ad_bond_info ad_info;
struct alb_bond_info alb_info;
struct bond_params params;
struct list_head vlan_list;
- struct vlan_group *vlgrp;
- struct packet_type arp_mon_pt;
struct workqueue_struct *wq;
struct delayed_work mii_work;
struct delayed_work arp_work;
@@ -253,6 +250,11 @@ struct bonding {
#endif /* CONFIG_DEBUG_FS */
};
+static inline bool bond_vlan_used(struct bonding *bond)
+{
+ return !list_empty(&bond->vlan_list);
+}
+
#define bond_slave_get_rcu(dev) \
((struct slave *) rcu_dereference(dev->rx_handler_data))
@@ -376,11 +378,13 @@ static inline bool bond_is_slave_inactive(struct slave *slave)
return slave->inactive;
}
+struct bond_net;
+
struct vlan_entry *bond_next_vlan(struct bonding *bond, struct vlan_entry *curr);
int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev);
int bond_create(struct net *net, const char *name);
-int bond_create_sysfs(void);
-void bond_destroy_sysfs(void);
+int bond_create_sysfs(struct bond_net *net);
+void bond_destroy_sysfs(struct bond_net *net);
void bond_prepare_sysfs_group(struct bonding *bond);
int bond_create_slave_symlinks(struct net_device *master, struct net_device *slave);
void bond_destroy_slave_symlinks(struct net_device *master, struct net_device *slave);
@@ -406,6 +410,7 @@ struct bond_net {
#ifdef CONFIG_PROC_FS
struct proc_dir_entry * proc_dir;
#endif
+ struct class_attribute class_attr_bonding_masters;
};
#ifdef CONFIG_PROC_FS