aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2008-09-10 18:01:51 -0400
committerJohn W. Linville <linville@tuxdriver.com>2008-09-15 16:48:20 -0400
commit5bc75728fd43bb15b46f16ef465bcf9d487393cf (patch)
tree5732adee3965970390bf7953d214c757bbdba2a2 /net/mac80211
parentb7413430d4d2a6168e68231d9f93763047b6d60c (diff)
mac80211: fix scan vs. interface removal race
When we remove an interface, we can currently end up having a pointer to it left in local->scan_sdata after it has been set down, and then with a hardware scan the scan completion can try to access it which is a bug. Alternatively, a scan that started as a hardware scan may terminate as though it was a software scan, if the timing is just right. On SMP systems, software scan also has a similar problem, just canceling the delayed work and setting a flag isn't enough since it may be running concurrently; in this case we would also never restore state of other interfaces. This patch hopefully fixes the problems by always invoking ieee80211_scan_completed or requiring it to be invoked by the driver, I suspect the drivers that have ->hw_scan() are buggy. The bug will not manifest itself unless you remove the interface while hw-scanning which will also turn off the hw, and then add a new interface which will be unusable until you scan once. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/main.c33
-rw-r--r--net/mac80211/mlme.c2
-rw-r--r--net/mac80211/scan.c38
3 files changed, 53 insertions, 20 deletions
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index ebdec7106d63..4bfac4b41c51 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -564,14 +564,6 @@ static int ieee80211_stop(struct net_device *dev)
564 synchronize_rcu(); 564 synchronize_rcu();
565 skb_queue_purge(&sdata->u.sta.skb_queue); 565 skb_queue_purge(&sdata->u.sta.skb_queue);
566 566
567 if (local->scan_sdata == sdata) {
568 if (!local->ops->hw_scan) {
569 local->sta_sw_scanning = 0;
570 cancel_delayed_work(&local->scan_work);
571 } else
572 local->sta_hw_scanning = 0;
573 }
574
575 sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED; 567 sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED;
576 kfree(sdata->u.sta.extra_ie); 568 kfree(sdata->u.sta.extra_ie);
577 sdata->u.sta.extra_ie = NULL; 569 sdata->u.sta.extra_ie = NULL;
@@ -585,6 +577,31 @@ static int ieee80211_stop(struct net_device *dev)
585 } 577 }
586 /* fall through */ 578 /* fall through */
587 default: 579 default:
580 if (local->scan_sdata == sdata) {
581 if (!local->ops->hw_scan)
582 cancel_delayed_work_sync(&local->scan_work);
583 /*
584 * The software scan can no longer run now, so we can
585 * clear out the scan_sdata reference. However, the
586 * hardware scan may still be running. The complete
587 * function must be prepared to handle a NULL value.
588 */
589 local->scan_sdata = NULL;
590 /*
591 * The memory barrier guarantees that another CPU
592 * that is hardware-scanning will now see the fact
593 * that this interface is gone.
594 */
595 smp_mb();
596 /*
597 * If software scanning, complete the scan but since
598 * the scan_sdata is NULL already don't send out a
599 * scan event to userspace -- the scan is incomplete.
600 */
601 if (local->sta_sw_scanning)
602 ieee80211_scan_completed(&local->hw);
603 }
604
588 conf.vif = &sdata->vif; 605 conf.vif = &sdata->vif;
589 conf.type = sdata->vif.type; 606 conf.type = sdata->vif.type;
590 conf.mac_addr = dev->dev_addr; 607 conf.mac_addr = dev->dev_addr;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 9e20a0c20a46..19c7f21e49d1 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -2530,7 +2530,7 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
2530 struct ieee80211_sub_if_data *sdata = local->scan_sdata; 2530 struct ieee80211_sub_if_data *sdata = local->scan_sdata;
2531 struct ieee80211_if_sta *ifsta; 2531 struct ieee80211_if_sta *ifsta;
2532 2532
2533 if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS) { 2533 if (sdata && sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
2534 ifsta = &sdata->u.sta; 2534 ifsta = &sdata->u.sta;
2535 if (!(ifsta->flags & IEEE80211_STA_BSSID_SET) || 2535 if (!(ifsta->flags & IEEE80211_STA_BSSID_SET) ||
2536 (!(ifsta->state == IEEE80211_STA_MLME_IBSS_JOINED) && 2536 (!(ifsta->state == IEEE80211_STA_MLME_IBSS_JOINED) &&
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index f4399e9ac928..27727027d76d 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -430,9 +430,20 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
430 struct ieee80211_sub_if_data *sdata; 430 struct ieee80211_sub_if_data *sdata;
431 union iwreq_data wrqu; 431 union iwreq_data wrqu;
432 432
433 if (WARN_ON(!local->sta_hw_scanning && !local->sta_sw_scanning))
434 return;
435
433 local->last_scan_completed = jiffies; 436 local->last_scan_completed = jiffies;
434 memset(&wrqu, 0, sizeof(wrqu)); 437 memset(&wrqu, 0, sizeof(wrqu));
435 wireless_send_event(local->scan_sdata->dev, SIOCGIWSCAN, &wrqu, NULL); 438
439 /*
440 * local->scan_sdata could have been NULLed by the interface
441 * down code in case we were scanning on an interface that is
442 * being taken down.
443 */
444 sdata = local->scan_sdata;
445 if (sdata)
446 wireless_send_event(sdata->dev, SIOCGIWSCAN, &wrqu, NULL);
436 447
437 if (local->sta_hw_scanning) { 448 if (local->sta_hw_scanning) {
438 local->sta_hw_scanning = 0; 449 local->sta_hw_scanning = 0;
@@ -491,7 +502,10 @@ void ieee80211_sta_scan_work(struct work_struct *work)
491 int skip; 502 int skip;
492 unsigned long next_delay = 0; 503 unsigned long next_delay = 0;
493 504
494 if (!local->sta_sw_scanning) 505 /*
506 * Avoid re-scheduling when the sdata is going away.
507 */
508 if (!netif_running(sdata->dev))
495 return; 509 return;
496 510
497 switch (local->scan_state) { 511 switch (local->scan_state) {
@@ -570,9 +584,8 @@ void ieee80211_sta_scan_work(struct work_struct *work)
570 break; 584 break;
571 } 585 }
572 586
573 if (local->sta_sw_scanning) 587 queue_delayed_work(local->hw.workqueue, &local->scan_work,
574 queue_delayed_work(local->hw.workqueue, &local->scan_work, 588 next_delay);
575 next_delay);
576} 589}
577 590
578 591
@@ -609,13 +622,16 @@ int ieee80211_sta_start_scan(struct ieee80211_sub_if_data *scan_sdata,
609 } 622 }
610 623
611 if (local->ops->hw_scan) { 624 if (local->ops->hw_scan) {
612 int rc = local->ops->hw_scan(local_to_hw(local), 625 int rc;
613 ssid, ssid_len); 626
614 if (!rc) { 627 local->sta_hw_scanning = 1;
615 local->sta_hw_scanning = 1; 628 rc = local->ops->hw_scan(local_to_hw(local), ssid, ssid_len);
616 local->scan_sdata = scan_sdata; 629 if (rc) {
630 local->sta_hw_scanning = 0;
631 return rc;
617 } 632 }
618 return rc; 633 local->scan_sdata = scan_sdata;
634 return 0;
619 } 635 }
620 636
621 local->sta_sw_scanning = 1; 637 local->sta_sw_scanning = 1;