diff options
author | Johannes Berg <johannes.berg@intel.com> | 2013-03-20 17:25:17 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-03-20 17:26:35 -0400 |
commit | 8b305780ed0c49a49c6bd58a4372fd6b22a5a71e (patch) | |
tree | 865d7c5105a504f5466d68456770d2e54525954c | |
parent | ce1eadda6badef9e4e3460097ede674fca47383d (diff) |
mac80211: fix virtual monitor interface locking
The virtual monitor interface has a locking issue, it calls
into the channel context code with the iflist mutex held
which isn't allowed since it is usually acquired the other
way around. The mutex is still required for the interface
iteration, but need not be held across the channel calls.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r-- | net/mac80211/iface.c | 35 |
1 files changed, 19 insertions, 16 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index baaa8608e52d..3bfe2612c8c2 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -349,21 +349,19 @@ static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata) | |||
349 | static int ieee80211_add_virtual_monitor(struct ieee80211_local *local) | 349 | static int ieee80211_add_virtual_monitor(struct ieee80211_local *local) |
350 | { | 350 | { |
351 | struct ieee80211_sub_if_data *sdata; | 351 | struct ieee80211_sub_if_data *sdata; |
352 | int ret = 0; | 352 | int ret; |
353 | 353 | ||
354 | if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF)) | 354 | if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF)) |
355 | return 0; | 355 | return 0; |
356 | 356 | ||
357 | mutex_lock(&local->iflist_mtx); | 357 | ASSERT_RTNL(); |
358 | 358 | ||
359 | if (local->monitor_sdata) | 359 | if (local->monitor_sdata) |
360 | goto out_unlock; | 360 | return 0; |
361 | 361 | ||
362 | sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL); | 362 | sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL); |
363 | if (!sdata) { | 363 | if (!sdata) |
364 | ret = -ENOMEM; | 364 | return -ENOMEM; |
365 | goto out_unlock; | ||
366 | } | ||
367 | 365 | ||
368 | /* set up data */ | 366 | /* set up data */ |
369 | sdata->local = local; | 367 | sdata->local = local; |
@@ -377,13 +375,13 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local) | |||
377 | if (WARN_ON(ret)) { | 375 | if (WARN_ON(ret)) { |
378 | /* ok .. stupid driver, it asked for this! */ | 376 | /* ok .. stupid driver, it asked for this! */ |
379 | kfree(sdata); | 377 | kfree(sdata); |
380 | goto out_unlock; | 378 | return ret; |
381 | } | 379 | } |
382 | 380 | ||
383 | ret = ieee80211_check_queues(sdata); | 381 | ret = ieee80211_check_queues(sdata); |
384 | if (ret) { | 382 | if (ret) { |
385 | kfree(sdata); | 383 | kfree(sdata); |
386 | goto out_unlock; | 384 | return ret; |
387 | } | 385 | } |
388 | 386 | ||
389 | ret = ieee80211_vif_use_channel(sdata, &local->monitor_chandef, | 387 | ret = ieee80211_vif_use_channel(sdata, &local->monitor_chandef, |
@@ -391,13 +389,14 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local) | |||
391 | if (ret) { | 389 | if (ret) { |
392 | drv_remove_interface(local, sdata); | 390 | drv_remove_interface(local, sdata); |
393 | kfree(sdata); | 391 | kfree(sdata); |
394 | goto out_unlock; | 392 | return ret; |
395 | } | 393 | } |
396 | 394 | ||
395 | mutex_lock(&local->iflist_mtx); | ||
397 | rcu_assign_pointer(local->monitor_sdata, sdata); | 396 | rcu_assign_pointer(local->monitor_sdata, sdata); |
398 | out_unlock: | ||
399 | mutex_unlock(&local->iflist_mtx); | 397 | mutex_unlock(&local->iflist_mtx); |
400 | return ret; | 398 | |
399 | return 0; | ||
401 | } | 400 | } |
402 | 401 | ||
403 | static void ieee80211_del_virtual_monitor(struct ieee80211_local *local) | 402 | static void ieee80211_del_virtual_monitor(struct ieee80211_local *local) |
@@ -407,14 +406,20 @@ static void ieee80211_del_virtual_monitor(struct ieee80211_local *local) | |||
407 | if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF)) | 406 | if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF)) |
408 | return; | 407 | return; |
409 | 408 | ||
409 | ASSERT_RTNL(); | ||
410 | |||
410 | mutex_lock(&local->iflist_mtx); | 411 | mutex_lock(&local->iflist_mtx); |
411 | 412 | ||
412 | sdata = rcu_dereference_protected(local->monitor_sdata, | 413 | sdata = rcu_dereference_protected(local->monitor_sdata, |
413 | lockdep_is_held(&local->iflist_mtx)); | 414 | lockdep_is_held(&local->iflist_mtx)); |
414 | if (!sdata) | 415 | if (!sdata) { |
415 | goto out_unlock; | 416 | mutex_unlock(&local->iflist_mtx); |
417 | return; | ||
418 | } | ||
416 | 419 | ||
417 | rcu_assign_pointer(local->monitor_sdata, NULL); | 420 | rcu_assign_pointer(local->monitor_sdata, NULL); |
421 | mutex_unlock(&local->iflist_mtx); | ||
422 | |||
418 | synchronize_net(); | 423 | synchronize_net(); |
419 | 424 | ||
420 | ieee80211_vif_release_channel(sdata); | 425 | ieee80211_vif_release_channel(sdata); |
@@ -422,8 +427,6 @@ static void ieee80211_del_virtual_monitor(struct ieee80211_local *local) | |||
422 | drv_remove_interface(local, sdata); | 427 | drv_remove_interface(local, sdata); |
423 | 428 | ||
424 | kfree(sdata); | 429 | kfree(sdata); |
425 | out_unlock: | ||
426 | mutex_unlock(&local->iflist_mtx); | ||
427 | } | 430 | } |
428 | 431 | ||
429 | /* | 432 | /* |