Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Neil Horman and committed by
David S. Miller
c2355e1a c6ce3854

+19 -11
+9 -6
drivers/net/bonding/bond_main.c
··· 449 449 if (unlikely(bond->dev->priv_flags & IFF_IN_NETPOLL)) { 450 450 struct netpoll *np = bond->dev->npinfo->netpoll; 451 451 slave_dev->npinfo = bond->dev->npinfo; 452 - np->real_dev = np->dev = skb->dev; 453 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 454 slave_dev->priv_flags &= ~IFF_IN_NETPOLL; 456 - np->dev = bond->dev; 457 455 } else 458 456 #endif 459 457 dev_queue_xmit(skb); ··· 1330 1332 1331 1333 static void bond_poll_controller(struct net_device *bond_dev) 1332 1334 { 1333 - struct net_device *dev = bond_dev->npinfo->netpoll->real_dev; 1334 - if (dev != bond_dev) 1335 - netpoll_poll_dev(dev); 1335 + struct bonding *bond = netdev_priv(bond_dev); 1336 + struct slave *slave; 1337 + int i; 1338 + 1339 + bond_for_each_slave(bond, slave, i) { 1340 + if (slave->dev && IS_UP(slave->dev)) 1341 + netpoll_poll_dev(slave->dev); 1342 + } 1336 1343 } 1337 1344 1338 1345 static void bond_netpoll_cleanup(struct net_device *bond_dev)
+7 -2
include/linux/netpoll.h
··· 14 14 15 15 struct netpoll { 16 16 struct net_device *dev; 17 - struct net_device *real_dev; 18 17 char dev_name[IFNAMSIZ]; 19 18 const char *name; 20 19 void (*rx_hook)(struct netpoll *, int, char *, int); ··· 52 53 void __netpoll_cleanup(struct netpoll *np); 53 54 void netpoll_cleanup(struct netpoll *np); 54 55 int __netpoll_rx(struct sk_buff *skb); 55 - void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb); 56 + void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, 57 + struct net_device *dev); 58 + static inline void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) 59 + { 60 + netpoll_send_skb_on_dev(np, skb, np->dev); 61 + } 62 + 56 63 57 64 58 65 #ifdef CONFIG_NETPOLL
+3 -3
net/core/netpoll.c
··· 288 288 return 0; 289 289 } 290 290 291 - void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) 291 + void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, 292 + struct net_device *dev) 292 293 { 293 294 int status = NETDEV_TX_BUSY; 294 295 unsigned long tries; 295 - struct net_device *dev = np->dev; 296 296 const struct net_device_ops *ops = dev->netdev_ops; 297 297 /* It is up to the caller to keep npinfo alive. */ 298 298 struct netpoll_info *npinfo = np->dev->npinfo; ··· 346 346 schedule_delayed_work(&npinfo->tx_work,0); 347 347 } 348 348 } 349 - EXPORT_SYMBOL(netpoll_send_skb); 349 + EXPORT_SYMBOL(netpoll_send_skb_on_dev); 350 350 351 351 void netpoll_send_udp(struct netpoll *np, const char *msg, int len) 352 352 {