aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge/br_if.c
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@osdl.org>2006-02-09 20:08:52 -0500
committerDavid S. Miller <davem@davemloft.net>2006-02-09 20:08:52 -0500
commitb3f1be4b5412e34647764457bec901e06b03e624 (patch)
treeb3f1529ff0882ebb5aed190d8a61b52f703503ce /net/bridge/br_if.c
parent6fcf9412de64056238a6295f21db7aa9c37a532e (diff)
[BRIDGE]: fix for RCU and deadlock on device removal
Change Bridge receive path to correctly handle RCU removal of device from bridge. Also fixes deadlock between carrier_check and del_nbp. This replaces the previous deleted flag fix. Signed-off-by: Stephen Hemminger <shemminger@osdl.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge/br_if.c')
-rw-r--r--net/bridge/br_if.c21
1 files changed, 11 insertions, 10 deletions
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index da687c8dc6f..70b7ef91723 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -79,9 +79,14 @@ static int port_cost(struct net_device *dev)
79 */ 79 */
80static void port_carrier_check(void *arg) 80static void port_carrier_check(void *arg)
81{ 81{
82 struct net_bridge_port *p = arg; 82 struct net_device *dev = arg;
83 struct net_bridge_port *p;
83 84
84 rtnl_lock(); 85 rtnl_lock();
86 p = dev->br_port;
87 if (!p)
88 goto done;
89
85 if (netif_carrier_ok(p->dev)) { 90 if (netif_carrier_ok(p->dev)) {
86 u32 cost = port_cost(p->dev); 91 u32 cost = port_cost(p->dev);
87 92
@@ -97,6 +102,7 @@ static void port_carrier_check(void *arg)
97 br_stp_disable_port(p); 102 br_stp_disable_port(p);
98 spin_unlock_bh(&p->br->lock); 103 spin_unlock_bh(&p->br->lock);
99 } 104 }
105done:
100 rtnl_unlock(); 106 rtnl_unlock();
101} 107}
102 108
@@ -104,7 +110,6 @@ static void destroy_nbp(struct net_bridge_port *p)
104{ 110{
105 struct net_device *dev = p->dev; 111 struct net_device *dev = p->dev;
106 112
107 dev->br_port = NULL;
108 p->br = NULL; 113 p->br = NULL;
109 p->dev = NULL; 114 p->dev = NULL;
110 dev_put(dev); 115 dev_put(dev);
@@ -133,24 +138,20 @@ static void del_nbp(struct net_bridge_port *p)
133 struct net_bridge *br = p->br; 138 struct net_bridge *br = p->br;
134 struct net_device *dev = p->dev; 139 struct net_device *dev = p->dev;
135 140
136 /* Race between RTNL notify and RCU callback */
137 if (p->deleted)
138 return;
139
140 dev_set_promiscuity(dev, -1); 141 dev_set_promiscuity(dev, -1);
141 142
142 cancel_delayed_work(&p->carrier_check); 143 cancel_delayed_work(&p->carrier_check);
143 flush_scheduled_work();
144 144
145 spin_lock_bh(&br->lock); 145 spin_lock_bh(&br->lock);
146 br_stp_disable_port(p); 146 br_stp_disable_port(p);
147 p->deleted = 1;
148 spin_unlock_bh(&br->lock); 147 spin_unlock_bh(&br->lock);
149 148
150 br_fdb_delete_by_port(br, p); 149 br_fdb_delete_by_port(br, p);
151 150
152 list_del_rcu(&p->list); 151 list_del_rcu(&p->list);
153 152
153 rcu_assign_pointer(dev->br_port, NULL);
154
154 call_rcu(&p->rcu, destroy_nbp_rcu); 155 call_rcu(&p->rcu, destroy_nbp_rcu);
155} 156}
156 157
@@ -254,11 +255,10 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
254 p->dev = dev; 255 p->dev = dev;
255 p->path_cost = port_cost(dev); 256 p->path_cost = port_cost(dev);
256 p->priority = 0x8000 >> BR_PORT_BITS; 257 p->priority = 0x8000 >> BR_PORT_BITS;
257 dev->br_port = p;
258 p->port_no = index; 258 p->port_no = index;
259 br_init_port(p); 259 br_init_port(p);
260 p->state = BR_STATE_DISABLED; 260 p->state = BR_STATE_DISABLED;
261 INIT_WORK(&p->carrier_check, port_carrier_check, p); 261 INIT_WORK(&p->carrier_check, port_carrier_check, dev);
262 kobject_init(&p->kobj); 262 kobject_init(&p->kobj);
263 263
264 return p; 264 return p;
@@ -397,6 +397,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
397 else if ((err = br_sysfs_addif(p))) 397 else if ((err = br_sysfs_addif(p)))
398 del_nbp(p); 398 del_nbp(p);
399 else { 399 else {
400 rcu_assign_pointer(dev->br_port, p);
400 dev_set_promiscuity(dev, 1); 401 dev_set_promiscuity(dev, 1);
401 402
402 list_add_rcu(&p->list, &br->port_list); 403 list_add_rcu(&p->list, &br->port_list);