aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--net/bridge/br_if.c21
-rw-r--r--net/bridge/br_input.c19
-rw-r--r--net/bridge/br_private.h1
-rw-r--r--net/bridge/br_stp_bpdu.c30
4 files changed, 41 insertions, 30 deletions
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index da687c8dc6ff..70b7ef917234 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);
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index e3a73cead6b6..4eef83755315 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -45,18 +45,20 @@ static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
45int br_handle_frame_finish(struct sk_buff *skb) 45int br_handle_frame_finish(struct sk_buff *skb)
46{ 46{
47 const unsigned char *dest = eth_hdr(skb)->h_dest; 47 const unsigned char *dest = eth_hdr(skb)->h_dest;
48 struct net_bridge_port *p = skb->dev->br_port; 48 struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
49 struct net_bridge *br = p->br; 49 struct net_bridge *br;
50 struct net_bridge_fdb_entry *dst; 50 struct net_bridge_fdb_entry *dst;
51 int passedup = 0; 51 int passedup = 0;
52 52
53 if (!p || p->state == BR_STATE_DISABLED)
54 goto drop;
55
53 /* insert into forwarding database after filtering to avoid spoofing */ 56 /* insert into forwarding database after filtering to avoid spoofing */
54 br_fdb_update(p->br, p, eth_hdr(skb)->h_source); 57 br = p->br;
58 br_fdb_update(br, p, eth_hdr(skb)->h_source);
55 59
56 if (p->state == BR_STATE_LEARNING) { 60 if (p->state == BR_STATE_LEARNING)
57 kfree_skb(skb); 61 goto drop;
58 goto out;
59 }
60 62
61 if (br->dev->flags & IFF_PROMISC) { 63 if (br->dev->flags & IFF_PROMISC) {
62 struct sk_buff *skb2; 64 struct sk_buff *skb2;
@@ -93,6 +95,9 @@ int br_handle_frame_finish(struct sk_buff *skb)
93 95
94out: 96out:
95 return 0; 97 return 0;
98drop:
99 kfree_skb(skb);
100 goto out;
96} 101}
97 102
98/* 103/*
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index e330b17b6d81..c5bd631ffcd5 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -68,7 +68,6 @@ struct net_bridge_port
68 /* STP */ 68 /* STP */
69 u8 priority; 69 u8 priority;
70 u8 state; 70 u8 state;
71 u8 deleted;
72 u16 port_no; 71 u16 port_no;
73 unsigned char topology_change_ack; 72 unsigned char topology_change_ack;
74 unsigned char config_pending; 73 unsigned char config_pending;
diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c
index d071f1c9ad0b..296f6a487c52 100644
--- a/net/bridge/br_stp_bpdu.c
+++ b/net/bridge/br_stp_bpdu.c
@@ -133,29 +133,35 @@ void br_send_tcn_bpdu(struct net_bridge_port *p)
133 133
134static const unsigned char header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00}; 134static const unsigned char header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00};
135 135
136/* NO locks */ 136/* NO locks, but rcu_read_lock (preempt_disabled) */
137int br_stp_handle_bpdu(struct sk_buff *skb) 137int br_stp_handle_bpdu(struct sk_buff *skb)
138{ 138{
139 struct net_bridge_port *p = skb->dev->br_port; 139 struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
140 struct net_bridge *br = p->br; 140 struct net_bridge *br;
141 unsigned char *buf; 141 unsigned char *buf;
142 142
143 if (!p)
144 goto err;
145
146 br = p->br;
147 spin_lock(&br->lock);
148
149 if (p->state == BR_STATE_DISABLED || !(br->dev->flags & IFF_UP))
150 goto out;
151
143 /* insert into forwarding database after filtering to avoid spoofing */ 152 /* insert into forwarding database after filtering to avoid spoofing */
144 br_fdb_update(p->br, p, eth_hdr(skb)->h_source); 153 br_fdb_update(br, p, eth_hdr(skb)->h_source);
154
155 if (!br->stp_enabled)
156 goto out;
145 157
146 /* need at least the 802 and STP headers */ 158 /* need at least the 802 and STP headers */
147 if (!pskb_may_pull(skb, sizeof(header)+1) || 159 if (!pskb_may_pull(skb, sizeof(header)+1) ||
148 memcmp(skb->data, header, sizeof(header))) 160 memcmp(skb->data, header, sizeof(header)))
149 goto err; 161 goto out;
150 162
151 buf = skb_pull(skb, sizeof(header)); 163 buf = skb_pull(skb, sizeof(header));
152 164
153 spin_lock_bh(&br->lock);
154 if (p->state == BR_STATE_DISABLED
155 || !(br->dev->flags & IFF_UP)
156 || !br->stp_enabled)
157 goto out;
158
159 if (buf[0] == BPDU_TYPE_CONFIG) { 165 if (buf[0] == BPDU_TYPE_CONFIG) {
160 struct br_config_bpdu bpdu; 166 struct br_config_bpdu bpdu;
161 167
@@ -201,7 +207,7 @@ int br_stp_handle_bpdu(struct sk_buff *skb)
201 br_received_tcn_bpdu(p); 207 br_received_tcn_bpdu(p);
202 } 208 }
203 out: 209 out:
204 spin_unlock_bh(&br->lock); 210 spin_unlock(&br->lock);
205 err: 211 err:
206 kfree_skb(skb); 212 kfree_skb(skb);
207 return 0; 213 return 0;