|
@@ -76,6 +76,7 @@
|
|
|
#include <linux/if_vlan.h>
|
|
|
#include <linux/if_bonding.h>
|
|
|
#include <linux/jiffies.h>
|
|
|
+#include <linux/preempt.h>
|
|
|
#include <net/route.h>
|
|
|
#include <net/net_namespace.h>
|
|
|
#include <net/netns/generic.h>
|
|
@@ -169,6 +170,10 @@ MODULE_PARM_DESC(resend_igmp, "Number of IGMP membership reports to send on link
|
|
|
|
|
|
/*----------------------------- Global variables ----------------------------*/
|
|
|
|
|
|
+#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
+cpumask_var_t netpoll_block_tx;
|
|
|
+#endif
|
|
|
+
|
|
|
static const char * const version =
|
|
|
DRV_DESCRIPTION ": v" DRV_VERSION " (" DRV_RELDATE ")\n";
|
|
|
|
|
@@ -310,6 +315,7 @@ static int bond_del_vlan(struct bonding *bond, unsigned short vlan_id)
|
|
|
|
|
|
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) {
|
|
@@ -344,6 +350,7 @@ static int bond_del_vlan(struct bonding *bond, unsigned short vlan_id)
|
|
|
|
|
|
out:
|
|
|
write_unlock_bh(&bond->lock);
|
|
|
+ unblock_netpoll_tx();
|
|
|
return res;
|
|
|
}
|
|
|
|
|
@@ -1804,10 +1811,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|
|
bond_set_carrier(bond);
|
|
|
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
- /*
|
|
|
- * Netpoll and bonding is broken, make sure it is not initialized
|
|
|
- * until it is fixed.
|
|
|
- */
|
|
|
if (disable_netpoll) {
|
|
|
bond_dev->priv_flags |= IFF_DISABLE_NETPOLL;
|
|
|
} else {
|
|
@@ -1892,6 +1895,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
+ block_netpoll_tx();
|
|
|
netdev_bonding_change(bond_dev, NETDEV_BONDING_DESLAVE);
|
|
|
write_lock_bh(&bond->lock);
|
|
|
|
|
@@ -1901,6 +1905,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
|
|
|
pr_info("%s: %s not enslaved\n",
|
|
|
bond_dev->name, slave_dev->name);
|
|
|
write_unlock_bh(&bond->lock);
|
|
|
+ unblock_netpoll_tx();
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
@@ -1994,6 +1999,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
|
|
|
}
|
|
|
|
|
|
write_unlock_bh(&bond->lock);
|
|
|
+ unblock_netpoll_tx();
|
|
|
|
|
|
/* must do this from outside any spinlocks */
|
|
|
bond_destroy_slave_symlinks(bond_dev, slave_dev);
|
|
@@ -2085,6 +2091,7 @@ static int bond_release_all(struct net_device *bond_dev)
|
|
|
struct net_device *slave_dev;
|
|
|
struct sockaddr addr;
|
|
|
|
|
|
+ block_netpoll_tx();
|
|
|
write_lock_bh(&bond->lock);
|
|
|
|
|
|
netif_carrier_off(bond_dev);
|
|
@@ -2183,6 +2190,7 @@ static int bond_release_all(struct net_device *bond_dev)
|
|
|
|
|
|
out:
|
|
|
write_unlock_bh(&bond->lock);
|
|
|
+ unblock_netpoll_tx();
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -2232,9 +2240,11 @@ static int bond_ioctl_change_active(struct net_device *bond_dev, struct net_devi
|
|
|
(old_active) &&
|
|
|
(new_active->link == BOND_LINK_UP) &&
|
|
|
IS_UP(new_active->dev)) {
|
|
|
+ block_netpoll_tx();
|
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
|
bond_change_active_slave(bond, new_active);
|
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
|
|
+ unblock_netpoll_tx();
|
|
|
} else
|
|
|
res = -EINVAL;
|
|
|
|
|
@@ -2466,9 +2476,11 @@ static void bond_miimon_commit(struct bonding *bond)
|
|
|
|
|
|
do_failover:
|
|
|
ASSERT_RTNL();
|
|
|
+ block_netpoll_tx();
|
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
|
bond_select_active_slave(bond);
|
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
|
|
+ unblock_netpoll_tx();
|
|
|
}
|
|
|
|
|
|
bond_set_carrier(bond);
|
|
@@ -2911,11 +2923,13 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
|
|
|
}
|
|
|
|
|
|
if (do_failover) {
|
|
|
+ block_netpoll_tx();
|
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
|
|
|
|
bond_select_active_slave(bond);
|
|
|
|
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
|
|
+ unblock_netpoll_tx();
|
|
|
}
|
|
|
|
|
|
re_arm:
|
|
@@ -3074,9 +3088,11 @@ static void bond_ab_arp_commit(struct bonding *bond, int delta_in_ticks)
|
|
|
|
|
|
do_failover:
|
|
|
ASSERT_RTNL();
|
|
|
+ block_netpoll_tx();
|
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
|
bond_select_active_slave(bond);
|
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
|
|
+ unblock_netpoll_tx();
|
|
|
}
|
|
|
|
|
|
bond_set_carrier(bond);
|
|
@@ -4564,6 +4580,13 @@ static netdev_tx_t bond_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
{
|
|
|
struct bonding *bond = netdev_priv(dev);
|
|
|
|
|
|
+ /*
|
|
|
+ * If we risk deadlock from transmitting this in the
|
|
|
+ * netpoll path, tell netpoll to queue the frame for later tx
|
|
|
+ */
|
|
|
+ if (is_netpoll_tx_blocked(dev))
|
|
|
+ return NETDEV_TX_BUSY;
|
|
|
+
|
|
|
if (TX_QUEUE_OVERRIDE(bond->params.mode)) {
|
|
|
if (!bond_slave_override(bond, skb))
|
|
|
return NETDEV_TX_OK;
|
|
@@ -5286,6 +5309,13 @@ static int __init bonding_init(void)
|
|
|
if (res)
|
|
|
goto out;
|
|
|
|
|
|
+#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
+ if (!alloc_cpumask_var(&netpoll_block_tx, GFP_KERNEL)) {
|
|
|
+ res = -ENOMEM;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
res = register_pernet_subsys(&bond_net_ops);
|
|
|
if (res)
|
|
|
goto out;
|
|
@@ -5304,6 +5334,7 @@ static int __init bonding_init(void)
|
|
|
if (res)
|
|
|
goto err;
|
|
|
|
|
|
+
|
|
|
register_netdevice_notifier(&bond_netdev_notifier);
|
|
|
register_inetaddr_notifier(&bond_inetaddr_notifier);
|
|
|
bond_register_ipv6_notifier();
|
|
@@ -5313,6 +5344,9 @@ err:
|
|
|
rtnl_link_unregister(&bond_link_ops);
|
|
|
err_link:
|
|
|
unregister_pernet_subsys(&bond_net_ops);
|
|
|
+#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
+ free_cpumask_var(netpoll_block_tx);
|
|
|
+#endif
|
|
|
goto out;
|
|
|
|
|
|
}
|
|
@@ -5327,6 +5361,10 @@ static void __exit bonding_exit(void)
|
|
|
|
|
|
rtnl_link_unregister(&bond_link_ops);
|
|
|
unregister_pernet_subsys(&bond_net_ops);
|
|
|
+
|
|
|
+#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
+ free_cpumask_var(netpoll_block_tx);
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
module_init(bonding_init);
|