aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/bonding/bond_main.c
diff options
context:
space:
mode:
authorNeil Horman <nhorman@tuxdriver.com>2010-10-13 12:01:49 -0400
committerDavid S. Miller <davem@davemloft.net>2010-10-18 11:32:07 -0400
commitc2355e1ab910278a94d487b78590ee3c8eecd08a (patch)
tree6a3adce66355ad36483500475f9931d0e359695e /drivers/net/bonding/bond_main.c
parentc6ce3854f098e1307ecd3bde07903d65fb14a9cb (diff)
bonding: Fix bonding drivers improper modification of netpoll structure
The bonding driver currently modifies the netpoll structure in its xmit path while sending frames from netpoll. This is racy, as other cpus can access the netpoll structure in parallel. Since the bonding driver points np->dev to a slave device, other cpus can inadvertently attempt to send data directly to slave devices, leading to improper locking with the bonding master, lost frames, and deadlocks. This patch fixes that up. This patch also removes the real_dev pointer from the netpoll structure as that data is really only used by bonding in the poll_controller, and we can emulate its behavior by check each slave for IS_UP. Signed-off-by: Neil Horman <nhorman@tuxdriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/bonding/bond_main.c')
-rw-r--r--drivers/net/bonding/bond_main.c15
1 files changed, 9 insertions, 6 deletions
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 7703d35de65d..813cc2f8edd6 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -449,11 +449,9 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb,
449 if (unlikely(bond->dev->priv_flags & IFF_IN_NETPOLL)) { 449 if (unlikely(bond->dev->priv_flags & IFF_IN_NETPOLL)) {
450 struct netpoll *np = bond->dev->npinfo->netpoll; 450 struct netpoll *np = bond->dev->npinfo->netpoll;
451 slave_dev->npinfo = bond->dev->npinfo; 451 slave_dev->npinfo = bond->dev->npinfo;
452 np->real_dev = np->dev = skb->dev;
453 slave_dev->priv_flags |= IFF_IN_NETPOLL; 452 slave_dev->priv_flags |= IFF_IN_NETPOLL;
454 netpoll_send_skb(np, skb); 453 netpoll_send_skb_on_dev(np, skb, slave_dev);
455 slave_dev->priv_flags &= ~IFF_IN_NETPOLL; 454 slave_dev->priv_flags &= ~IFF_IN_NETPOLL;
456 np->dev = bond->dev;
457 } else 455 } else
458#endif 456#endif
459 dev_queue_xmit(skb); 457 dev_queue_xmit(skb);
@@ -1332,9 +1330,14 @@ static bool slaves_support_netpoll(struct net_device *bond_dev)
1332 1330
1333static void bond_poll_controller(struct net_device *bond_dev) 1331static void bond_poll_controller(struct net_device *bond_dev)
1334{ 1332{
1335 struct net_device *dev = bond_dev->npinfo->netpoll->real_dev; 1333 struct bonding *bond = netdev_priv(bond_dev);
1336 if (dev != bond_dev) 1334 struct slave *slave;
1337 netpoll_poll_dev(dev); 1335 int i;
1336
1337 bond_for_each_slave(bond, slave, i) {
1338 if (slave->dev && IS_UP(slave->dev))
1339 netpoll_poll_dev(slave->dev);
1340 }
1338} 1341}
1339 1342
1340static void bond_netpoll_cleanup(struct net_device *bond_dev) 1343static void bond_netpoll_cleanup(struct net_device *bond_dev)