lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Date: Mon, 7 Mar 2011 22:11:50 +0800 From: Amerigo Wang <amwang@...hat.com> To: linux-kernel@...r.kernel.org Cc: WANG Cong <amwang@...hat.com>, Jay Vosburgh <fubar@...ibm.com>, "David S. Miller" <davem@...emloft.net>, Herbert Xu <herbert@...dor.hengli.com.au>, "Paul E. McKenney" <paulmck@...ux.vnet.ibm.com>, Neil Horman <nhorman@...driver.com>, "John W. Linville" <linville@...driver.com>, Eric Dumazet <eric.dumazet@...il.com>, netdev@...r.kernel.org Subject: [Patch] bonding: fix netpoll in active-backup mode netconsole doesn't work in active-backup mode, because we don't do anything for nic failover in active-backup mode. This patch fixes the problem by: 1) make slave_enable_netpoll() and slave_disable_netpoll() callable in softirq context, that is, moving code after synchronize_rcu_bh() into call_rcu_bh() callback function, teaching kzalloc() to use GFP_ATOMIC. 2) disable netpoll on old slave and enable netpoll on the new slave. Tested by ifdown the current active slave and ifup it again for several times, netconsole works well. Signed-off-by: WANG Cong <amwang@...hat.com> --- drivers/net/bonding/bond_main.c | 236 +++++++++++++++++++++------------------ include/linux/netpoll.h | 2 + net/core/netpoll.c | 22 ++-- 3 files changed, 140 insertions(+), 120 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 0592e6d..2d6ec1b 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -907,6 +907,121 @@ static void bond_mc_list_flush(struct net_device *bond_dev, } } +/*--------------------------- Netpoll code ---------------------------*/ +#ifdef CONFIG_NET_POLL_CONTROLLER +static inline int slave_enable_netpoll(struct slave *slave) +{ + struct netpoll *np; + int err = 0; + + np = kzalloc(sizeof(*np), GFP_ATOMIC); + err = -ENOMEM; + if (!np) + goto out; + + np->dev = slave->dev; + err = __netpoll_setup(np); + if (err) { + kfree(np); + goto out; + } + slave->np = np; +out: + return err; +} +static void slave_netpoll_reclaim(struct rcu_head *rp) +{ + struct netpoll *np = container_of(rp, struct netpoll, rcu); + __netpoll_cleanup(np); + kfree(np); +} +static inline void slave_disable_netpoll(struct slave *slave) +{ + struct netpoll *np = slave->np; + + if (!np) + return; + + slave->np = NULL; + call_rcu_bh(&np->rcu, slave_netpoll_reclaim); +} +static inline bool slave_dev_support_netpoll(struct net_device *slave_dev) +{ + if (slave_dev->priv_flags & IFF_DISABLE_NETPOLL) + return false; + if (!slave_dev->netdev_ops->ndo_poll_controller) + return false; + return true; +} + +static void bond_poll_controller(struct net_device *bond_dev) +{ +} + +static void __bond_netpoll_cleanup(struct bonding *bond) +{ + struct slave *slave; + int i; + + bond_for_each_slave(bond, slave, i) + 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) +{ + struct bonding *bond = netdev_priv(dev); + struct slave *slave; + int i, err = 0; + + read_lock(&bond->lock); + bond_for_each_slave(bond, slave, i) { + if (!IS_UP(slave->dev)) + continue; + err = slave_enable_netpoll(slave); + if (err) { + __bond_netpoll_cleanup(bond); + 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) +{ + return 0; +} +static inline void slave_disable_netpoll(struct slave *slave) +{ +} +static void bond_netpoll_cleanup(struct net_device *bond_dev) +{ +} +static int bond_netpoll_setup(struct net_device *dev, struct netpoll_info *ni) +{ + return 0; +} +static struct netpoll_info *bond_netpoll_info(struct bonding *bond) +{ + return NULL; +} +#endif + /*--------------------------- Active slave change ---------------------------*/ /* @@ -1155,11 +1270,20 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) } if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) { - if (old_active) + if (old_active) { bond_set_slave_inactive_flags(old_active); + if (bond_netpoll_info(bond)) + slave_disable_netpoll(old_active); + } if (new_active) { + struct netpoll_info *ni; bond_set_slave_active_flags(new_active); + ni = bond_netpoll_info(bond); + if (ni) { + new_active->dev->npinfo = ni; + slave_enable_netpoll(new_active); + } if (bond->params.fail_over_mac) bond_do_fail_over_mac(bond, new_active, @@ -1280,116 +1404,6 @@ static void bond_detach_slave(struct bonding *bond, struct slave *slave) bond->slave_cnt--; } -#ifdef CONFIG_NET_POLL_CONTROLLER -static inline int slave_enable_netpoll(struct slave *slave) -{ - struct netpoll *np; - int err = 0; - - np = kzalloc(sizeof(*np), GFP_KERNEL); - err = -ENOMEM; - if (!np) - goto out; - - np->dev = slave->dev; - err = __netpoll_setup(np); - if (err) { - kfree(np); - goto out; - } - slave->np = np; -out: - return err; -} -static inline void slave_disable_netpoll(struct slave *slave) -{ - struct netpoll *np = slave->np; - - if (!np) - return; - - slave->np = NULL; - synchronize_rcu_bh(); - __netpoll_cleanup(np); - kfree(np); -} -static inline bool slave_dev_support_netpoll(struct net_device *slave_dev) -{ - if (slave_dev->priv_flags & IFF_DISABLE_NETPOLL) - return false; - if (!slave_dev->netdev_ops->ndo_poll_controller) - return false; - return true; -} - -static void bond_poll_controller(struct net_device *bond_dev) -{ -} - -static void __bond_netpoll_cleanup(struct bonding *bond) -{ - struct slave *slave; - int i; - - bond_for_each_slave(bond, slave, i) - 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) -{ - struct bonding *bond = netdev_priv(dev); - struct slave *slave; - int i, err = 0; - - read_lock(&bond->lock); - bond_for_each_slave(bond, slave, i) { - if (!IS_UP(slave->dev)) - continue; - err = slave_enable_netpoll(slave); - if (err) { - __bond_netpoll_cleanup(bond); - 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) -{ - return 0; -} -static inline void slave_disable_netpoll(struct slave *slave) -{ -} -static void bond_netpoll_cleanup(struct net_device *bond_dev) -{ -} -static int bond_netpoll_setup(struct net_device *dev, struct netpoll_info *ni) -{ - return 0; -} -static struct netpoll_info *bond_netpoll_info(struct bonding *bond) -{ - return NULL; -} -#endif - /*---------------------------------- IOCTL ----------------------------------*/ static int bond_sethwaddr(struct net_device *bond_dev, diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index 79358bb..9412aa5 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -23,6 +23,7 @@ struct netpoll { u8 remote_mac[ETH_ALEN]; struct list_head rx; /* rx_np list element */ + struct rcu_head rcu; }; struct netpoll_info { @@ -38,6 +39,7 @@ struct netpoll_info { struct delayed_work tx_work; struct netpoll *netpoll; + struct rcu_head rcu; }; void netpoll_poll_dev(struct net_device *dev); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 06be243..9870dac 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -901,6 +901,18 @@ static int __init netpoll_init(void) } core_initcall(netpoll_init); +static void netpoll_reclaim(struct rcu_head *rp) +{ + struct netpoll_info *npinfo = container_of(rp, struct netpoll_info, rcu); + skb_queue_purge(&npinfo->arp_tx); + skb_queue_purge(&npinfo->txq); + cancel_delayed_work_sync(&npinfo->tx_work); + + /* clean after last, unfinished work */ + __skb_queue_purge(&npinfo->txq); + kfree(npinfo); +} + void __netpoll_cleanup(struct netpoll *np) { struct netpoll_info *npinfo; @@ -928,15 +940,7 @@ void __netpoll_cleanup(struct netpoll *np) rcu_assign_pointer(np->dev->npinfo, NULL); /* avoid racing with NAPI reading npinfo */ - synchronize_rcu_bh(); - - skb_queue_purge(&npinfo->arp_tx); - skb_queue_purge(&npinfo->txq); - cancel_delayed_work_sync(&npinfo->tx_work); - - /* clean after last, unfinished work */ - __skb_queue_purge(&npinfo->txq); - kfree(npinfo); + call_rcu_bh(&npinfo->rcu, netpoll_reclaim); } } EXPORT_SYMBOL_GPL(__netpoll_cleanup); -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@...r.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists