summaryrefslogtreecommitdiffstats
path: root/net/bridge
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@osdl.org>2006-01-31 17:44:07 -0800
committerDavid S. Miller <davem@davemloft.net>2006-01-31 17:44:07 -0800
commit3f4cfc2d11c9e29709e6f0f3add54039614d847a (patch)
treefe3131288787952d95b578f151dbbf20791951b7 /net/bridge
parent5d39a795bfa217b5f7637028c83ab5cb291f37bf (diff)
downloadlinux-3f4cfc2d11c9e29709e6f0f3add54039614d847a.tar.bz2
[BRIDGE]: Fix device delete race.
This is a simpler fix for the two races in bridge device removal. The Xen race of delif and notify is managed now by a new deleted flag. No need for barriers or other locking because of rtnl mutex. The del_timer_sync()'s are unnecessary, because br_stp_disable_port delete's the timers, and they will finish running before RCU callback. Signed-off-by: Stephen Hemminger <shemminger@osdl.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge')
-rw-r--r--net/bridge/br_if.c21
-rw-r--r--net/bridge/br_private.h1
2 files changed, 16 insertions, 6 deletions
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index ba442883e877..da687c8dc6ff 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -104,6 +104,7 @@ static void destroy_nbp(struct net_bridge_port *p)
{
struct net_device *dev = p->dev;
+ dev->br_port = NULL;
p->br = NULL;
p->dev = NULL;
dev_put(dev);
@@ -118,13 +119,24 @@ static void destroy_nbp_rcu(struct rcu_head *head)
destroy_nbp(p);
}
-/* called with RTNL */
+/* Delete port(interface) from bridge is done in two steps.
+ * via RCU. First step, marks device as down. That deletes
+ * all the timers and stops new packets from flowing through.
+ *
+ * Final cleanup doesn't occur until after all CPU's finished
+ * processing packets.
+ *
+ * Protected from multiple admin operations by RTNL mutex
+ */
static void del_nbp(struct net_bridge_port *p)
{
struct net_bridge *br = p->br;
struct net_device *dev = p->dev;
- dev->br_port = NULL;
+ /* Race between RTNL notify and RCU callback */
+ if (p->deleted)
+ return;
+
dev_set_promiscuity(dev, -1);
cancel_delayed_work(&p->carrier_check);
@@ -132,16 +144,13 @@ static void del_nbp(struct net_bridge_port *p)
spin_lock_bh(&br->lock);
br_stp_disable_port(p);
+ p->deleted = 1;
spin_unlock_bh(&br->lock);
br_fdb_delete_by_port(br, p);
list_del_rcu(&p->list);
- del_timer_sync(&p->message_age_timer);
- del_timer_sync(&p->forward_delay_timer);
- del_timer_sync(&p->hold_timer);
-
call_rcu(&p->rcu, destroy_nbp_rcu);
}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index c5bd631ffcd5..e330b17b6d81 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -68,6 +68,7 @@ struct net_bridge_port
/* STP */
u8 priority;
u8 state;
+ u8 deleted;
u16 port_no;
unsigned char topology_change_ack;
unsigned char config_pending;