aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorstephen hemminger <shemminger@vyatta.com>2011-10-06 07:19:41 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-11-11 12:36:24 -0500
commit99dfac8ab234555a54f7fa6e4b7bb08bb355158b (patch)
tree13e533ddb167623485a1eed2cf505f7ae8b30cca
parente8c492bd9cbc8dd1002bf4bc316f21f0a002b10f (diff)
bridge: fix hang on removal of bridge via netlink
[ Upstream commit 1ce5cce895309862d2c35d922816adebe094fe4a ] Need to cleanup bridge device timers and ports when being bridge device is being removed via netlink. This fixes the problem of observed when doing: ip link add br0 type bridge ip link set dev eth1 master br0 ip link set br0 up ip link del br0 which would cause br0 to hang in unregister_netdev because of leftover reference count. Reported-by: Sridhar Samudrala <sri@us.ibm.com> Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> Acked-by: Sridhar Samudrala <sri@us.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--net/bridge/br_if.c9
-rw-r--r--net/bridge/br_netlink.c1
-rw-r--r--net/bridge/br_private.h1
3 files changed, 7 insertions, 4 deletions
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 6f156c19999..449087373d8 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -161,9 +161,10 @@ static void del_nbp(struct net_bridge_port *p)
161 call_rcu(&p->rcu, destroy_nbp_rcu); 161 call_rcu(&p->rcu, destroy_nbp_rcu);
162} 162}
163 163
164/* called with RTNL */ 164/* Delete bridge device */
165static void del_br(struct net_bridge *br, struct list_head *head) 165void br_dev_delete(struct net_device *dev, struct list_head *head)
166{ 166{
167 struct net_bridge *br = netdev_priv(dev);
167 struct net_bridge_port *p, *n; 168 struct net_bridge_port *p, *n;
168 169
169 list_for_each_entry_safe(p, n, &br->port_list, list) { 170 list_for_each_entry_safe(p, n, &br->port_list, list) {
@@ -268,7 +269,7 @@ int br_del_bridge(struct net *net, const char *name)
268 } 269 }
269 270
270 else 271 else
271 del_br(netdev_priv(dev), NULL); 272 br_dev_delete(dev, NULL);
272 273
273 rtnl_unlock(); 274 rtnl_unlock();
274 return ret; 275 return ret;
@@ -445,7 +446,7 @@ void __net_exit br_net_exit(struct net *net)
445 rtnl_lock(); 446 rtnl_lock();
446 for_each_netdev(net, dev) 447 for_each_netdev(net, dev)
447 if (dev->priv_flags & IFF_EBRIDGE) 448 if (dev->priv_flags & IFF_EBRIDGE)
448 del_br(netdev_priv(dev), &list); 449 br_dev_delete(dev, &list);
449 450
450 unregister_netdevice_many(&list); 451 unregister_netdevice_many(&list);
451 rtnl_unlock(); 452 rtnl_unlock();
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index ffb0dc4cc0e..2c160552568 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -208,6 +208,7 @@ static struct rtnl_link_ops br_link_ops __read_mostly = {
208 .priv_size = sizeof(struct net_bridge), 208 .priv_size = sizeof(struct net_bridge),
209 .setup = br_dev_setup, 209 .setup = br_dev_setup,
210 .validate = br_validate, 210 .validate = br_validate,
211 .dellink = br_dev_delete,
211}; 212};
212 213
213int __init br_netlink_init(void) 214int __init br_netlink_init(void)
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 78cc364997d..857a021deea 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -294,6 +294,7 @@ static inline int br_is_root_bridge(const struct net_bridge *br)
294 294
295/* br_device.c */ 295/* br_device.c */
296extern void br_dev_setup(struct net_device *dev); 296extern void br_dev_setup(struct net_device *dev);
297extern void br_dev_delete(struct net_device *dev, struct list_head *list);
297extern netdev_tx_t br_dev_xmit(struct sk_buff *skb, 298extern netdev_tx_t br_dev_xmit(struct sk_buff *skb,
298 struct net_device *dev); 299 struct net_device *dev);
299#ifdef CONFIG_NET_POLL_CONTROLLER 300#ifdef CONFIG_NET_POLL_CONTROLLER