diff options
author | Stephen Hemminger <shemminger@osdl.org> | 2006-02-09 20:08:52 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2006-02-09 20:08:52 -0500 |
commit | b3f1be4b5412e34647764457bec901e06b03e624 (patch) | |
tree | b3f1529ff0882ebb5aed190d8a61b52f703503ce /net/bridge/br_stp_bpdu.c | |
parent | 6fcf9412de64056238a6295f21db7aa9c37a532e (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_stp_bpdu.c')
-rw-r--r-- | net/bridge/br_stp_bpdu.c | 30 |
1 files changed, 18 insertions, 12 deletions
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 | ||
134 | static const unsigned char header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00}; | 134 | static 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) */ |
137 | int br_stp_handle_bpdu(struct sk_buff *skb) | 137 | int 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; |