diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-07-06 21:56:11 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-07-10 15:02:32 -0400 |
commit | 667503ddcb96f3b10211f997fe55907fa7509841 (patch) | |
tree | 5e2559e94a716bb81bfc7566e3e3a05267810c31 /net/wireless/wext-sme.c | |
parent | 4f5dadcebb55fccef34722bbbf6401d39124c8a4 (diff) |
cfg80211: fix locking
Over time, a lot of locking issues have crept into
the smarts of cfg80211, so e.g. scan completion can
race against a new scan, IBSS join can race against
leaving an IBSS, etc.
Introduce a new per-interface lock that protects
most of the per-interface data that we need to keep
track of, and sprinkle assertions about that lock
everywhere. Some things now need to be offloaded to
work structs so that we don't require being able to
sleep in functions the drivers call. The exception
to that are the MLME callbacks (rx_auth etc.) that
currently only mac80211 calls because it was easier
to do that there instead of in cfg80211, and future
drivers implementing those calls will, if they ever
exist, probably need to use a similar scheme like
mac80211 anyway...
In order to be able to handle _deauth and _disassoc
properly, introduce a cookie passed to it that will
determine locking requirements.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless/wext-sme.c')
-rw-r--r-- | net/wireless/wext-sme.c | 125 |
1 files changed, 88 insertions, 37 deletions
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index fe1987acb891..6f75aaa7f795 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c | |||
@@ -15,6 +15,9 @@ static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, | |||
15 | { | 15 | { |
16 | int err; | 16 | int err; |
17 | 17 | ||
18 | ASSERT_RDEV_LOCK(rdev); | ||
19 | ASSERT_WDEV_LOCK(wdev); | ||
20 | |||
18 | if (!netif_running(wdev->netdev)) | 21 | if (!netif_running(wdev->netdev)) |
19 | return 0; | 22 | return 0; |
20 | 23 | ||
@@ -24,8 +27,8 @@ static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, | |||
24 | 27 | ||
25 | err = 0; | 28 | err = 0; |
26 | if (wdev->wext.connect.ssid_len != 0) | 29 | if (wdev->wext.connect.ssid_len != 0) |
27 | err = cfg80211_connect(rdev, wdev->netdev, | 30 | err = __cfg80211_connect(rdev, wdev->netdev, |
28 | &wdev->wext.connect); | 31 | &wdev->wext.connect); |
29 | 32 | ||
30 | return err; | 33 | return err; |
31 | } | 34 | } |
@@ -50,33 +53,43 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, | |||
50 | if (chan && (chan->flags & IEEE80211_CHAN_DISABLED)) | 53 | if (chan && (chan->flags & IEEE80211_CHAN_DISABLED)) |
51 | return -EINVAL; | 54 | return -EINVAL; |
52 | 55 | ||
53 | if (wdev->wext.connect.channel == chan) | 56 | cfg80211_lock_rdev(rdev); |
54 | return 0; | 57 | wdev_lock(wdev); |
58 | |||
59 | if (wdev->wext.connect.channel == chan) { | ||
60 | err = 0; | ||
61 | goto out; | ||
62 | } | ||
55 | 63 | ||
56 | if (wdev->sme_state != CFG80211_SME_IDLE) { | 64 | if (wdev->sme_state != CFG80211_SME_IDLE) { |
57 | bool event = true; | 65 | bool event = true; |
58 | /* if SSID set, we'll try right again, avoid event */ | 66 | /* if SSID set, we'll try right again, avoid event */ |
59 | if (wdev->wext.connect.ssid_len) | 67 | if (wdev->wext.connect.ssid_len) |
60 | event = false; | 68 | event = false; |
61 | err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), | 69 | err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), |
62 | dev, WLAN_REASON_DEAUTH_LEAVING, | 70 | dev, WLAN_REASON_DEAUTH_LEAVING, |
63 | event); | 71 | event); |
64 | if (err) | 72 | if (err) |
65 | return err; | 73 | goto out; |
66 | } | 74 | } |
67 | 75 | ||
76 | |||
68 | wdev->wext.connect.channel = chan; | 77 | wdev->wext.connect.channel = chan; |
69 | 78 | ||
70 | /* SSID is not set, we just want to switch channel */ | 79 | /* SSID is not set, we just want to switch channel */ |
71 | if (wdev->wext.connect.ssid_len && chan) { | 80 | if (wdev->wext.connect.ssid_len && chan) { |
72 | if (!rdev->ops->set_channel) | 81 | err = -EOPNOTSUPP; |
73 | return -EOPNOTSUPP; | 82 | if (rdev->ops->set_channel) |
74 | 83 | err = rdev->ops->set_channel(wdev->wiphy, chan, | |
75 | return rdev->ops->set_channel(wdev->wiphy, chan, | 84 | NL80211_CHAN_NO_HT); |
76 | NL80211_CHAN_NO_HT); | 85 | goto out; |
77 | } | 86 | } |
78 | 87 | ||
79 | return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); | 88 | err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); |
89 | out: | ||
90 | wdev_unlock(wdev); | ||
91 | cfg80211_unlock_rdev(rdev); | ||
92 | return err; | ||
80 | } | 93 | } |
81 | /* temporary symbol - mark GPL - in the future the handler won't be */ | 94 | /* temporary symbol - mark GPL - in the future the handler won't be */ |
82 | EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwfreq); | 95 | EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwfreq); |
@@ -92,10 +105,12 @@ int cfg80211_mgd_wext_giwfreq(struct net_device *dev, | |||
92 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) | 105 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) |
93 | return -EINVAL; | 106 | return -EINVAL; |
94 | 107 | ||
108 | wdev_lock(wdev); | ||
95 | if (wdev->current_bss) | 109 | if (wdev->current_bss) |
96 | chan = wdev->current_bss->pub.channel; | 110 | chan = wdev->current_bss->pub.channel; |
97 | else if (wdev->wext.connect.channel) | 111 | else if (wdev->wext.connect.channel) |
98 | chan = wdev->wext.connect.channel; | 112 | chan = wdev->wext.connect.channel; |
113 | wdev_unlock(wdev); | ||
99 | 114 | ||
100 | if (chan) { | 115 | if (chan) { |
101 | freq->m = chan->center_freq; | 116 | freq->m = chan->center_freq; |
@@ -128,21 +143,26 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, | |||
128 | if (len > 0 && ssid[len - 1] == '\0') | 143 | if (len > 0 && ssid[len - 1] == '\0') |
129 | len--; | 144 | len--; |
130 | 145 | ||
146 | cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy)); | ||
147 | wdev_lock(wdev); | ||
148 | |||
149 | err = 0; | ||
150 | |||
131 | if (wdev->wext.connect.ssid && len && | 151 | if (wdev->wext.connect.ssid && len && |
132 | len == wdev->wext.connect.ssid_len && | 152 | len == wdev->wext.connect.ssid_len && |
133 | memcmp(wdev->wext.connect.ssid, ssid, len)) | 153 | memcmp(wdev->wext.connect.ssid, ssid, len)) |
134 | return 0; | 154 | goto out; |
135 | 155 | ||
136 | if (wdev->sme_state != CFG80211_SME_IDLE) { | 156 | if (wdev->sme_state != CFG80211_SME_IDLE) { |
137 | bool event = true; | 157 | bool event = true; |
138 | /* if SSID set now, we'll try to connect, avoid event */ | 158 | /* if SSID set now, we'll try to connect, avoid event */ |
139 | if (len) | 159 | if (len) |
140 | event = false; | 160 | event = false; |
141 | err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), | 161 | err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), |
142 | dev, WLAN_REASON_DEAUTH_LEAVING, | 162 | dev, WLAN_REASON_DEAUTH_LEAVING, |
143 | event); | 163 | event); |
144 | if (err) | 164 | if (err) |
145 | return err; | 165 | goto out; |
146 | } | 166 | } |
147 | 167 | ||
148 | wdev->wext.connect.ssid = wdev->wext.ssid; | 168 | wdev->wext.connect.ssid = wdev->wext.ssid; |
@@ -151,7 +171,11 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, | |||
151 | 171 | ||
152 | wdev->wext.connect.crypto.control_port = false; | 172 | wdev->wext.connect.crypto.control_port = false; |
153 | 173 | ||
154 | return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); | 174 | err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); |
175 | out: | ||
176 | wdev_unlock(wdev); | ||
177 | cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy)); | ||
178 | return err; | ||
155 | } | 179 | } |
156 | /* temporary symbol - mark GPL - in the future the handler won't be */ | 180 | /* temporary symbol - mark GPL - in the future the handler won't be */ |
157 | EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwessid); | 181 | EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwessid); |
@@ -168,6 +192,7 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, | |||
168 | 192 | ||
169 | data->flags = 0; | 193 | data->flags = 0; |
170 | 194 | ||
195 | wdev_lock(wdev); | ||
171 | if (wdev->ssid_len) { | 196 | if (wdev->ssid_len) { |
172 | data->flags = 1; | 197 | data->flags = 1; |
173 | data->length = wdev->ssid_len; | 198 | data->length = wdev->ssid_len; |
@@ -178,6 +203,7 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, | |||
178 | memcpy(ssid, wdev->wext.connect.ssid, data->length); | 203 | memcpy(ssid, wdev->wext.connect.ssid, data->length); |
179 | } else | 204 | } else |
180 | data->flags = 0; | 205 | data->flags = 0; |
206 | wdev_unlock(wdev); | ||
181 | 207 | ||
182 | return 0; | 208 | return 0; |
183 | } | 209 | } |
@@ -203,21 +229,25 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, | |||
203 | if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) | 229 | if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) |
204 | bssid = NULL; | 230 | bssid = NULL; |
205 | 231 | ||
232 | cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy)); | ||
233 | wdev_lock(wdev); | ||
234 | |||
235 | err = 0; | ||
206 | /* both automatic */ | 236 | /* both automatic */ |
207 | if (!bssid && !wdev->wext.connect.bssid) | 237 | if (!bssid && !wdev->wext.connect.bssid) |
208 | return 0; | 238 | goto out; |
209 | 239 | ||
210 | /* fixed already - and no change */ | 240 | /* fixed already - and no change */ |
211 | if (wdev->wext.connect.bssid && bssid && | 241 | if (wdev->wext.connect.bssid && bssid && |
212 | compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0) | 242 | compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0) |
213 | return 0; | 243 | goto out; |
214 | 244 | ||
215 | if (wdev->sme_state != CFG80211_SME_IDLE) { | 245 | if (wdev->sme_state != CFG80211_SME_IDLE) { |
216 | err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), | 246 | err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), |
217 | dev, WLAN_REASON_DEAUTH_LEAVING, | 247 | dev, WLAN_REASON_DEAUTH_LEAVING, |
218 | false); | 248 | false); |
219 | if (err) | 249 | if (err) |
220 | return err; | 250 | goto out; |
221 | } | 251 | } |
222 | 252 | ||
223 | if (bssid) { | 253 | if (bssid) { |
@@ -226,7 +256,11 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, | |||
226 | } else | 256 | } else |
227 | wdev->wext.connect.bssid = NULL; | 257 | wdev->wext.connect.bssid = NULL; |
228 | 258 | ||
229 | return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); | 259 | err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); |
260 | out: | ||
261 | wdev_unlock(wdev); | ||
262 | cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy)); | ||
263 | return err; | ||
230 | } | 264 | } |
231 | /* temporary symbol - mark GPL - in the future the handler won't be */ | 265 | /* temporary symbol - mark GPL - in the future the handler won't be */ |
232 | EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwap); | 266 | EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwap); |
@@ -243,12 +277,14 @@ int cfg80211_mgd_wext_giwap(struct net_device *dev, | |||
243 | 277 | ||
244 | ap_addr->sa_family = ARPHRD_ETHER; | 278 | ap_addr->sa_family = ARPHRD_ETHER; |
245 | 279 | ||
280 | wdev_lock(wdev); | ||
246 | if (wdev->current_bss) | 281 | if (wdev->current_bss) |
247 | memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); | 282 | memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); |
248 | else if (wdev->wext.connect.bssid) | 283 | else if (wdev->wext.connect.bssid) |
249 | memcpy(ap_addr->sa_data, wdev->wext.connect.bssid, ETH_ALEN); | 284 | memcpy(ap_addr->sa_data, wdev->wext.connect.bssid, ETH_ALEN); |
250 | else | 285 | else |
251 | memset(ap_addr->sa_data, 0, ETH_ALEN); | 286 | memset(ap_addr->sa_data, 0, ETH_ALEN); |
287 | wdev_unlock(wdev); | ||
252 | 288 | ||
253 | return 0; | 289 | return 0; |
254 | } | 290 | } |
@@ -270,15 +306,20 @@ int cfg80211_wext_siwgenie(struct net_device *dev, | |||
270 | if (!ie_len) | 306 | if (!ie_len) |
271 | ie = NULL; | 307 | ie = NULL; |
272 | 308 | ||
309 | wdev_lock(wdev); | ||
310 | |||
273 | /* no change */ | 311 | /* no change */ |
312 | err = 0; | ||
274 | if (wdev->wext.ie_len == ie_len && | 313 | if (wdev->wext.ie_len == ie_len && |
275 | memcmp(wdev->wext.ie, ie, ie_len) == 0) | 314 | memcmp(wdev->wext.ie, ie, ie_len) == 0) |
276 | return 0; | 315 | goto out; |
277 | 316 | ||
278 | if (ie_len) { | 317 | if (ie_len) { |
279 | ie = kmemdup(extra, ie_len, GFP_KERNEL); | 318 | ie = kmemdup(extra, ie_len, GFP_KERNEL); |
280 | if (!ie) | 319 | if (!ie) { |
281 | return -ENOMEM; | 320 | err = -ENOMEM; |
321 | goto out; | ||
322 | } | ||
282 | } else | 323 | } else |
283 | ie = NULL; | 324 | ie = NULL; |
284 | 325 | ||
@@ -287,14 +328,17 @@ int cfg80211_wext_siwgenie(struct net_device *dev, | |||
287 | wdev->wext.ie_len = ie_len; | 328 | wdev->wext.ie_len = ie_len; |
288 | 329 | ||
289 | if (wdev->sme_state != CFG80211_SME_IDLE) { | 330 | if (wdev->sme_state != CFG80211_SME_IDLE) { |
290 | err = cfg80211_disconnect(rdev, dev, | 331 | err = __cfg80211_disconnect(rdev, dev, |
291 | WLAN_REASON_DEAUTH_LEAVING, false); | 332 | WLAN_REASON_DEAUTH_LEAVING, false); |
292 | if (err) | 333 | if (err) |
293 | return err; | 334 | goto out; |
294 | } | 335 | } |
295 | 336 | ||
296 | /* userspace better not think we'll reconnect */ | 337 | /* userspace better not think we'll reconnect */ |
297 | return 0; | 338 | err = 0; |
339 | out: | ||
340 | wdev_unlock(wdev); | ||
341 | return err; | ||
298 | } | 342 | } |
299 | EXPORT_SYMBOL_GPL(cfg80211_wext_siwgenie); | 343 | EXPORT_SYMBOL_GPL(cfg80211_wext_siwgenie); |
300 | 344 | ||
@@ -305,6 +349,7 @@ int cfg80211_wext_siwmlme(struct net_device *dev, | |||
305 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 349 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
306 | struct iw_mlme *mlme = (struct iw_mlme *)extra; | 350 | struct iw_mlme *mlme = (struct iw_mlme *)extra; |
307 | struct cfg80211_registered_device *rdev; | 351 | struct cfg80211_registered_device *rdev; |
352 | int err; | ||
308 | 353 | ||
309 | if (!wdev) | 354 | if (!wdev) |
310 | return -EOPNOTSUPP; | 355 | return -EOPNOTSUPP; |
@@ -317,13 +362,19 @@ int cfg80211_wext_siwmlme(struct net_device *dev, | |||
317 | if (mlme->addr.sa_family != ARPHRD_ETHER) | 362 | if (mlme->addr.sa_family != ARPHRD_ETHER) |
318 | return -EINVAL; | 363 | return -EINVAL; |
319 | 364 | ||
365 | wdev_lock(wdev); | ||
320 | switch (mlme->cmd) { | 366 | switch (mlme->cmd) { |
321 | case IW_MLME_DEAUTH: | 367 | case IW_MLME_DEAUTH: |
322 | case IW_MLME_DISASSOC: | 368 | case IW_MLME_DISASSOC: |
323 | return cfg80211_disconnect(rdev, dev, mlme->reason_code, | 369 | err = __cfg80211_disconnect(rdev, dev, mlme->reason_code, |
324 | true); | 370 | true); |
371 | break; | ||
325 | default: | 372 | default: |
326 | return -EOPNOTSUPP; | 373 | err = -EOPNOTSUPP; |
374 | break; | ||
327 | } | 375 | } |
376 | wdev_unlock(wdev); | ||
377 | |||
378 | return err; | ||
328 | } | 379 | } |
329 | EXPORT_SYMBOL_GPL(cfg80211_wext_siwmlme); | 380 | EXPORT_SYMBOL_GPL(cfg80211_wext_siwmlme); |