[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>
This commit is contained in:
Stephen Hemminger 2006-01-31 17:44:07 -08:00 committed by David S. Miller
parent 5d39a795bf
commit 3f4cfc2d11
2 changed files with 16 additions and 6 deletions

View File

@ -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);
}

View File

@ -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;