diff options
author | stephen hemminger <shemminger@vyatta.com> | 2011-10-06 07:19:41 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-11-11 12:36:24 -0500 |
commit | 99dfac8ab234555a54f7fa6e4b7bb08bb355158b (patch) | |
tree | 13e533ddb167623485a1eed2cf505f7ae8b30cca | |
parent | e8c492bd9cbc8dd1002bf4bc316f21f0a002b10f (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.c | 9 | ||||
-rw-r--r-- | net/bridge/br_netlink.c | 1 | ||||
-rw-r--r-- | net/bridge/br_private.h | 1 |
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 */ |
165 | static void del_br(struct net_bridge *br, struct list_head *head) | 165 | void 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 | ||
213 | int __init br_netlink_init(void) | 214 | int __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 */ |
296 | extern void br_dev_setup(struct net_device *dev); | 296 | extern void br_dev_setup(struct net_device *dev); |
297 | extern void br_dev_delete(struct net_device *dev, struct list_head *list); | ||
297 | extern netdev_tx_t br_dev_xmit(struct sk_buff *skb, | 298 | extern 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 |