aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-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)
104{ 104{
105 struct net_device *dev = p->dev; 105 struct net_device *dev = p->dev;
106 106
107 dev->br_port = NULL;
107 p->br = NULL; 108 p->br = NULL;
108 p->dev = NULL; 109 p->dev = NULL;
109 dev_put(dev); 110 dev_put(dev);
@@ -118,13 +119,24 @@ static void destroy_nbp_rcu(struct rcu_head *head)
118 destroy_nbp(p); 119 destroy_nbp(p);
119} 120}
120 121
121/* called with RTNL */ 122/* Delete port(interface) from bridge is done in two steps.
123 * via RCU. First step, marks device as down. That deletes
124 * all the timers and stops new packets from flowing through.
125 *
126 * Final cleanup doesn't occur until after all CPU's finished
127 * processing packets.
128 *
129 * Protected from multiple admin operations by RTNL mutex
130 */
122static void del_nbp(struct net_bridge_port *p) 131static void del_nbp(struct net_bridge_port *p)
123{ 132{
124 struct net_bridge *br = p->br; 133 struct net_bridge *br = p->br;
125 struct net_device *dev = p->dev; 134 struct net_device *dev = p->dev;
126 135
127 dev->br_port = NULL; 136 /* Race between RTNL notify and RCU callback */
137 if (p->deleted)
138 return;
139
128 dev_set_promiscuity(dev, -1); 140 dev_set_promiscuity(dev, -1);
129 141
130 cancel_delayed_work(&p->carrier_check); 142 cancel_delayed_work(&p->carrier_check);
@@ -132,16 +144,13 @@ static void del_nbp(struct net_bridge_port *p)
132 144
133 spin_lock_bh(&br->lock); 145 spin_lock_bh(&br->lock);
134 br_stp_disable_port(p); 146 br_stp_disable_port(p);
147 p->deleted = 1;
135 spin_unlock_bh(&br->lock); 148 spin_unlock_bh(&br->lock);
136 149
137 br_fdb_delete_by_port(br, p); 150 br_fdb_delete_by_port(br, p);
138 151
139 list_del_rcu(&p->list); 152 list_del_rcu(&p->list);
140 153
141 del_timer_sync(&p->message_age_timer);
142 del_timer_sync(&p->forward_delay_timer);
143 del_timer_sync(&p->hold_timer);
144
145 call_rcu(&p->rcu, destroy_nbp_rcu); 154 call_rcu(&p->rcu, destroy_nbp_rcu);
146} 155}
147 156
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
68 /* STP */ 68 /* STP */
69 u8 priority; 69 u8 priority;
70 u8 state; 70 u8 state;
71 u8 deleted;
71 u16 port_no; 72 u16 port_no;
72 unsigned char topology_change_ack; 73 unsigned char topology_change_ack;
73 unsigned char config_pending; 74 unsigned char config_pending;