diff options
Diffstat (limited to 'net/mac80211/iface.c')
-rw-r--r-- | net/mac80211/iface.c | 60 |
1 files changed, 41 insertions, 19 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index fbef7a1ada7a..bfb57dcc1538 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -112,10 +112,11 @@ static u32 __ieee80211_recalc_idle(struct ieee80211_local *local) | |||
112 | } | 112 | } |
113 | } | 113 | } |
114 | 114 | ||
115 | if (local->scan_sdata && | 115 | sdata = rcu_dereference_protected(local->scan_sdata, |
116 | !(local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE)) { | 116 | lockdep_is_held(&local->mtx)); |
117 | if (sdata && !(local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE)) { | ||
117 | scanning = true; | 118 | scanning = true; |
118 | local->scan_sdata->vif.bss_conf.idle = false; | 119 | sdata->vif.bss_conf.idle = false; |
119 | } | 120 | } |
120 | 121 | ||
121 | list_for_each_entry(sdata, &local->interfaces, list) { | 122 | list_for_each_entry(sdata, &local->interfaces, list) { |
@@ -330,20 +331,24 @@ static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata) | |||
330 | sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE; | 331 | sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE; |
331 | } | 332 | } |
332 | 333 | ||
333 | int ieee80211_add_virtual_monitor(struct ieee80211_local *local) | 334 | static int ieee80211_add_virtual_monitor(struct ieee80211_local *local) |
334 | { | 335 | { |
335 | struct ieee80211_sub_if_data *sdata; | 336 | struct ieee80211_sub_if_data *sdata; |
336 | int ret; | 337 | int ret = 0; |
337 | 338 | ||
338 | if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF)) | 339 | if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF)) |
339 | return 0; | 340 | return 0; |
340 | 341 | ||
342 | mutex_lock(&local->iflist_mtx); | ||
343 | |||
341 | if (local->monitor_sdata) | 344 | if (local->monitor_sdata) |
342 | return 0; | 345 | goto out_unlock; |
343 | 346 | ||
344 | sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL); | 347 | sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL); |
345 | if (!sdata) | 348 | if (!sdata) { |
346 | return -ENOMEM; | 349 | ret = -ENOMEM; |
350 | goto out_unlock; | ||
351 | } | ||
347 | 352 | ||
348 | /* set up data */ | 353 | /* set up data */ |
349 | sdata->local = local; | 354 | sdata->local = local; |
@@ -357,31 +362,34 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local) | |||
357 | if (WARN_ON(ret)) { | 362 | if (WARN_ON(ret)) { |
358 | /* ok .. stupid driver, it asked for this! */ | 363 | /* ok .. stupid driver, it asked for this! */ |
359 | kfree(sdata); | 364 | kfree(sdata); |
360 | return ret; | 365 | goto out_unlock; |
361 | } | 366 | } |
362 | 367 | ||
363 | ret = ieee80211_check_queues(sdata); | 368 | ret = ieee80211_check_queues(sdata); |
364 | if (ret) { | 369 | if (ret) { |
365 | kfree(sdata); | 370 | kfree(sdata); |
366 | return ret; | 371 | goto out_unlock; |
367 | } | 372 | } |
368 | 373 | ||
369 | rcu_assign_pointer(local->monitor_sdata, sdata); | 374 | rcu_assign_pointer(local->monitor_sdata, sdata); |
370 | 375 | out_unlock: | |
371 | return 0; | 376 | mutex_unlock(&local->iflist_mtx); |
377 | return ret; | ||
372 | } | 378 | } |
373 | 379 | ||
374 | void ieee80211_del_virtual_monitor(struct ieee80211_local *local) | 380 | static void ieee80211_del_virtual_monitor(struct ieee80211_local *local) |
375 | { | 381 | { |
376 | struct ieee80211_sub_if_data *sdata; | 382 | struct ieee80211_sub_if_data *sdata; |
377 | 383 | ||
378 | if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF)) | 384 | if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF)) |
379 | return; | 385 | return; |
380 | 386 | ||
381 | sdata = rtnl_dereference(local->monitor_sdata); | 387 | mutex_lock(&local->iflist_mtx); |
382 | 388 | ||
389 | sdata = rcu_dereference_protected(local->monitor_sdata, | ||
390 | lockdep_is_held(&local->iflist_mtx)); | ||
383 | if (!sdata) | 391 | if (!sdata) |
384 | return; | 392 | goto out_unlock; |
385 | 393 | ||
386 | rcu_assign_pointer(local->monitor_sdata, NULL); | 394 | rcu_assign_pointer(local->monitor_sdata, NULL); |
387 | synchronize_net(); | 395 | synchronize_net(); |
@@ -389,6 +397,8 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local) | |||
389 | drv_remove_interface(local, sdata); | 397 | drv_remove_interface(local, sdata); |
390 | 398 | ||
391 | kfree(sdata); | 399 | kfree(sdata); |
400 | out_unlock: | ||
401 | mutex_unlock(&local->iflist_mtx); | ||
392 | } | 402 | } |
393 | 403 | ||
394 | /* | 404 | /* |
@@ -487,6 +497,12 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) | |||
487 | break; | 497 | break; |
488 | } | 498 | } |
489 | 499 | ||
500 | if (local->monitors == 0 && local->open_count == 0) { | ||
501 | res = ieee80211_add_virtual_monitor(local); | ||
502 | if (res) | ||
503 | goto err_stop; | ||
504 | } | ||
505 | |||
490 | /* must be before the call to ieee80211_configure_filter */ | 506 | /* must be before the call to ieee80211_configure_filter */ |
491 | local->monitors++; | 507 | local->monitors++; |
492 | if (local->monitors == 1) { | 508 | if (local->monitors == 1) { |
@@ -501,6 +517,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) | |||
501 | break; | 517 | break; |
502 | default: | 518 | default: |
503 | if (coming_up) { | 519 | if (coming_up) { |
520 | ieee80211_del_virtual_monitor(local); | ||
521 | |||
504 | res = drv_add_interface(local, sdata); | 522 | res = drv_add_interface(local, sdata); |
505 | if (res) | 523 | if (res) |
506 | goto err_stop; | 524 | goto err_stop; |
@@ -628,7 +646,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, | |||
628 | 646 | ||
629 | clear_bit(SDATA_STATE_RUNNING, &sdata->state); | 647 | clear_bit(SDATA_STATE_RUNNING, &sdata->state); |
630 | 648 | ||
631 | if (local->scan_sdata == sdata) | 649 | if (rcu_access_pointer(local->scan_sdata) == sdata) |
632 | ieee80211_scan_cancel(local); | 650 | ieee80211_scan_cancel(local); |
633 | 651 | ||
634 | /* | 652 | /* |
@@ -735,6 +753,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, | |||
735 | if (local->monitors == 0) { | 753 | if (local->monitors == 0) { |
736 | local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR; | 754 | local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR; |
737 | hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; | 755 | hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; |
756 | ieee80211_del_virtual_monitor(local); | ||
738 | } | 757 | } |
739 | 758 | ||
740 | ieee80211_adjust_monitor_flags(sdata, -1); | 759 | ieee80211_adjust_monitor_flags(sdata, -1); |
@@ -808,6 +827,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, | |||
808 | } | 827 | } |
809 | } | 828 | } |
810 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); | 829 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); |
830 | |||
831 | if (local->monitors == local->open_count && local->monitors > 0) | ||
832 | ieee80211_add_virtual_monitor(local); | ||
811 | } | 833 | } |
812 | 834 | ||
813 | static int ieee80211_stop(struct net_device *dev) | 835 | static int ieee80211_stop(struct net_device *dev) |
@@ -1373,7 +1395,7 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, | |||
1373 | } | 1395 | } |
1374 | 1396 | ||
1375 | int ieee80211_if_add(struct ieee80211_local *local, const char *name, | 1397 | int ieee80211_if_add(struct ieee80211_local *local, const char *name, |
1376 | struct net_device **new_dev, enum nl80211_iftype type, | 1398 | struct wireless_dev **new_wdev, enum nl80211_iftype type, |
1377 | struct vif_params *params) | 1399 | struct vif_params *params) |
1378 | { | 1400 | { |
1379 | struct net_device *ndev; | 1401 | struct net_device *ndev; |
@@ -1463,8 +1485,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, | |||
1463 | list_add_tail_rcu(&sdata->list, &local->interfaces); | 1485 | list_add_tail_rcu(&sdata->list, &local->interfaces); |
1464 | mutex_unlock(&local->iflist_mtx); | 1486 | mutex_unlock(&local->iflist_mtx); |
1465 | 1487 | ||
1466 | if (new_dev) | 1488 | if (new_wdev) |
1467 | *new_dev = ndev; | 1489 | *new_wdev = &sdata->wdev; |
1468 | 1490 | ||
1469 | return 0; | 1491 | return 0; |
1470 | 1492 | ||