[136/262] bridge: fix hang on removal of bridge via netlink

From: Greg KH
Date: Thu Nov 10 2011 - 00:08:09 EST


3.0-stable review patch. If anyone has any objections, please let me know.

------------------


From: stephen hemminger <shemminger@xxxxxxxxxx>

[ 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@xxxxxxxxxx>
Signed-off-by: Stephen Hemminger <shemminger@xxxxxxxxxx>
Acked-by: Sridhar Samudrala <sri@xxxxxxxxxx>
Signed-off-by: David S. Miller <davem@xxxxxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxx>
---
net/bridge/br_if.c | 9 +++++----
net/bridge/br_netlink.c | 1 +
net/bridge/br_private.h | 1 +
3 files changed, 7 insertions(+), 4 deletions(-)

--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -161,9 +161,10 @@ static void del_nbp(struct net_bridge_po
call_rcu(&p->rcu, destroy_nbp_rcu);
}

-/* called with RTNL */
-static void del_br(struct net_bridge *br, struct list_head *head)
+/* Delete bridge device */
+void br_dev_delete(struct net_device *dev, struct list_head *head)
{
+ struct net_bridge *br = netdev_priv(dev);
struct net_bridge_port *p, *n;

list_for_each_entry_safe(p, n, &br->port_list, list) {
@@ -268,7 +269,7 @@ int br_del_bridge(struct net *net, const
}

else
- del_br(netdev_priv(dev), NULL);
+ br_dev_delete(dev, NULL);

rtnl_unlock();
return ret;
@@ -445,7 +446,7 @@ void __net_exit br_net_exit(struct net *
rtnl_lock();
for_each_netdev(net, dev)
if (dev->priv_flags & IFF_EBRIDGE)
- del_br(netdev_priv(dev), &list);
+ br_dev_delete(dev, &list);

unregister_netdevice_many(&list);
rtnl_unlock();
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -208,6 +208,7 @@ static struct rtnl_link_ops br_link_ops
.priv_size = sizeof(struct net_bridge),
.setup = br_dev_setup,
.validate = br_validate,
+ .dellink = br_dev_delete,
};

int __init br_netlink_init(void)
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -294,6 +294,7 @@ static inline int br_is_root_bridge(cons

/* br_device.c */
extern void br_dev_setup(struct net_device *dev);
+extern void br_dev_delete(struct net_device *dev, struct list_head *list);
extern netdev_tx_t br_dev_xmit(struct sk_buff *skb,
struct net_device *dev);
#ifdef CONFIG_NET_POLL_CONTROLLER


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/