aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/iface.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-03-20 17:25:17 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-03-20 17:26:35 -0400
commit8b305780ed0c49a49c6bd58a4372fd6b22a5a71e (patch)
tree865d7c5105a504f5466d68456770d2e54525954c /net/mac80211/iface.c
parentce1eadda6badef9e4e3460097ede674fca47383d (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>
Diffstat (limited to 'net/mac80211/iface.c')
-rw-r--r--net/mac80211/iface.c35
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)
349static int ieee80211_add_virtual_monitor(struct ieee80211_local *local) 349static 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
403static void ieee80211_del_virtual_monitor(struct ieee80211_local *local) 402static 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/*