bridge: fix hang on removal of bridge via netlink
authorstephen hemminger <shemminger@vyatta.com>
Thu, 6 Oct 2011 11:19:41 +0000 (11:19 +0000)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 11 Nov 2011 17:36:24 +0000 (09:36 -0800)
[ 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>
net/bridge/br_if.c
net/bridge/br_netlink.c
net/bridge/br_private.h

index 6f156c199998b6d197ee9d92a46eb2a2c23ba94a..449087373d883114f4907e5df2e50f362a0e067c 100644 (file)
@@ -161,9 +161,10 @@ static void del_nbp(struct net_bridge_port *p)
        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 char *name)
        }
 
        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 *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();
index ffb0dc4cc0e80691c1264e2344064b89d01c2f2f..2c16055256802a053b311dac8f4c92e754b63c4c 100644 (file)
@@ -208,6 +208,7 @@ static struct rtnl_link_ops br_link_ops __read_mostly = {
        .priv_size      = sizeof(struct net_bridge),
        .setup          = br_dev_setup,
        .validate       = br_validate,
+       .dellink        = br_dev_delete,
 };
 
 int __init br_netlink_init(void)
index 78cc364997d9888bc4a1e9869c7c64edb6378ee9..857a021deea9fb6b1d2351fe7a8d3b5d84667947 100644 (file)
@@ -294,6 +294,7 @@ static inline int br_is_root_bridge(const struct net_bridge *br)
 
 /* 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