aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
authorVasanthakumar Thiagarajan <vthiagar@qca.qualcomm.com>2011-12-08 03:58:47 -0500
committerJohn W. Linville <linville@tuxdriver.com>2011-12-13 15:30:28 -0500
commitadbde344dc12514d68620afae8d34035e72544b1 (patch)
treed3685186492aaef49a0373f82a1087c15b5cea86 /net/wireless
parentfb03c5eb8c0bbf4561cb5aa72e0a9546e9574661 (diff)
cfg80211: Fix race in bss timeout
It is quite possible to run into a race in bss timeout where the drivers see the bss entry just before notifying cfg80211 of a roaming event but it got timed out by the time rdev->event_work got scehduled from cfg80211_wq. This would result in the following WARN-ON() along with the failure to notify the user space of the roaming. The other situation which is happening with ath6kl that runs into issue is when the driver reports roam to same AP event where the AP bss entry already got expired. To fix this, move cfg80211_get_bss() from __cfg80211_roamed() to cfg80211_roamed(). [158645.538384] WARNING: at net/wireless/sme.c:586 __cfg80211_roamed+0xc2/0x1b1() [158645.538810] Call Trace: [158645.538838] [<c1033527>] warn_slowpath_common+0x65/0x7a [158645.538917] [<c14cfacf>] ? __cfg80211_roamed+0xc2/0x1b1 [158645.538946] [<c103354b>] warn_slowpath_null+0xf/0x13 [158645.539055] [<c14cfacf>] __cfg80211_roamed+0xc2/0x1b1 [158645.539086] [<c14beb5b>] cfg80211_process_rdev_events+0x153/0x1cc [158645.539166] [<c14bd57b>] cfg80211_event_work+0x26/0x36 [158645.539195] [<c10482ae>] process_one_work+0x219/0x38b [158645.539273] [<c14bd555>] ? wiphy_new+0x419/0x419 [158645.539301] [<c10486cb>] worker_thread+0xf6/0x1bf [158645.539379] [<c10485d5>] ? rescuer_thread+0x1b5/0x1b5 [158645.539407] [<c104b3e2>] kthread+0x62/0x67 [158645.539484] [<c104b380>] ? __init_kthread_worker+0x42/0x42 [158645.539514] [<c151309a>] kernel_thread_helper+0x6/0xd Reported-by: Kalle Valo <kvalo@qca.qualcomm.com> Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qca.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/core.h6
-rw-r--r--net/wireless/sme.c61
-rw-r--r--net/wireless/util.c6
3 files changed, 45 insertions, 28 deletions
diff --git a/net/wireless/core.h b/net/wireless/core.h
index fb08c28fc90a..43ad9c81efcf 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -249,12 +249,11 @@ struct cfg80211_event {
249 u16 status; 249 u16 status;
250 } cr; 250 } cr;
251 struct { 251 struct {
252 struct ieee80211_channel *channel;
253 u8 bssid[ETH_ALEN];
254 const u8 *req_ie; 252 const u8 *req_ie;
255 const u8 *resp_ie; 253 const u8 *resp_ie;
256 size_t req_ie_len; 254 size_t req_ie_len;
257 size_t resp_ie_len; 255 size_t resp_ie_len;
256 struct cfg80211_bss *bss;
258 } rm; 257 } rm;
259 struct { 258 struct {
260 const u8 *ie; 259 const u8 *ie;
@@ -403,8 +402,7 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
403 struct net_device *dev, u16 reason, 402 struct net_device *dev, u16 reason,
404 bool wextev); 403 bool wextev);
405void __cfg80211_roamed(struct wireless_dev *wdev, 404void __cfg80211_roamed(struct wireless_dev *wdev,
406 struct ieee80211_channel *channel, 405 struct cfg80211_bss *bss,
407 const u8 *bssid,
408 const u8 *req_ie, size_t req_ie_len, 406 const u8 *req_ie, size_t req_ie_len,
409 const u8 *resp_ie, size_t resp_ie_len); 407 const u8 *resp_ie, size_t resp_ie_len);
410int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, 408int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index f0c900ce2fb9..7b9ecaed96be 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -553,45 +553,35 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
553EXPORT_SYMBOL(cfg80211_connect_result); 553EXPORT_SYMBOL(cfg80211_connect_result);
554 554
555void __cfg80211_roamed(struct wireless_dev *wdev, 555void __cfg80211_roamed(struct wireless_dev *wdev,
556 struct ieee80211_channel *channel, 556 struct cfg80211_bss *bss,
557 const u8 *bssid,
558 const u8 *req_ie, size_t req_ie_len, 557 const u8 *req_ie, size_t req_ie_len,
559 const u8 *resp_ie, size_t resp_ie_len) 558 const u8 *resp_ie, size_t resp_ie_len)
560{ 559{
561 struct cfg80211_bss *bss;
562#ifdef CONFIG_CFG80211_WEXT 560#ifdef CONFIG_CFG80211_WEXT
563 union iwreq_data wrqu; 561 union iwreq_data wrqu;
564#endif 562#endif
565
566 ASSERT_WDEV_LOCK(wdev); 563 ASSERT_WDEV_LOCK(wdev);
567 564
568 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && 565 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
569 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) 566 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
570 return; 567 goto out;
571 568
572 if (wdev->sme_state != CFG80211_SME_CONNECTED) 569 if (wdev->sme_state != CFG80211_SME_CONNECTED)
573 return; 570 goto out;
574 571
575 /* internal error -- how did we get to CONNECTED w/o BSS? */ 572 /* internal error -- how did we get to CONNECTED w/o BSS? */
576 if (WARN_ON(!wdev->current_bss)) { 573 if (WARN_ON(!wdev->current_bss)) {
577 return; 574 goto out;
578 } 575 }
579 576
580 cfg80211_unhold_bss(wdev->current_bss); 577 cfg80211_unhold_bss(wdev->current_bss);
581 cfg80211_put_bss(&wdev->current_bss->pub); 578 cfg80211_put_bss(&wdev->current_bss->pub);
582 wdev->current_bss = NULL; 579 wdev->current_bss = NULL;
583 580
584 bss = cfg80211_get_bss(wdev->wiphy, channel, bssid,
585 wdev->ssid, wdev->ssid_len,
586 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
587
588 if (WARN_ON(!bss))
589 return;
590
591 cfg80211_hold_bss(bss_from_pub(bss)); 581 cfg80211_hold_bss(bss_from_pub(bss));
592 wdev->current_bss = bss_from_pub(bss); 582 wdev->current_bss = bss_from_pub(bss);
593 583
594 nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bssid, 584 nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bss->bssid,
595 req_ie, req_ie_len, resp_ie, resp_ie_len, 585 req_ie, req_ie_len, resp_ie, resp_ie_len,
596 GFP_KERNEL); 586 GFP_KERNEL);
597 587
@@ -612,11 +602,15 @@ void __cfg80211_roamed(struct wireless_dev *wdev,
612 602
613 memset(&wrqu, 0, sizeof(wrqu)); 603 memset(&wrqu, 0, sizeof(wrqu));
614 wrqu.ap_addr.sa_family = ARPHRD_ETHER; 604 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
615 memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); 605 memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN);
616 memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); 606 memcpy(wdev->wext.prev_bssid, bss->bssid, ETH_ALEN);
617 wdev->wext.prev_bssid_valid = true; 607 wdev->wext.prev_bssid_valid = true;
618 wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); 608 wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
619#endif 609#endif
610
611 return;
612out:
613 cfg80211_put_bss(bss);
620} 614}
621 615
622void cfg80211_roamed(struct net_device *dev, 616void cfg80211_roamed(struct net_device *dev,
@@ -626,32 +620,57 @@ void cfg80211_roamed(struct net_device *dev,
626 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) 620 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
627{ 621{
628 struct wireless_dev *wdev = dev->ieee80211_ptr; 622 struct wireless_dev *wdev = dev->ieee80211_ptr;
623 struct cfg80211_bss *bss;
624
625 CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
626
627 bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid,
628 wdev->ssid_len, WLAN_CAPABILITY_ESS,
629 WLAN_CAPABILITY_ESS);
630 if (WARN_ON(!bss))
631 return;
632
633 cfg80211_roamed_bss(dev, bss, req_ie, req_ie_len, resp_ie,
634 resp_ie_len, gfp);
635}
636EXPORT_SYMBOL(cfg80211_roamed);
637
638void cfg80211_roamed_bss(struct net_device *dev,
639 struct cfg80211_bss *bss, const u8 *req_ie,
640 size_t req_ie_len, const u8 *resp_ie,
641 size_t resp_ie_len, gfp_t gfp)
642{
643 struct wireless_dev *wdev = dev->ieee80211_ptr;
629 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 644 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
630 struct cfg80211_event *ev; 645 struct cfg80211_event *ev;
631 unsigned long flags; 646 unsigned long flags;
632 647
633 CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); 648 CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
634 649
650 if (WARN_ON(!bss))
651 return;
652
635 ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); 653 ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
636 if (!ev) 654 if (!ev) {
655 cfg80211_put_bss(bss);
637 return; 656 return;
657 }
638 658
639 ev->type = EVENT_ROAMED; 659 ev->type = EVENT_ROAMED;
640 ev->rm.channel = channel;
641 memcpy(ev->rm.bssid, bssid, ETH_ALEN);
642 ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); 660 ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev);
643 ev->rm.req_ie_len = req_ie_len; 661 ev->rm.req_ie_len = req_ie_len;
644 memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len); 662 memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len);
645 ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; 663 ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
646 ev->rm.resp_ie_len = resp_ie_len; 664 ev->rm.resp_ie_len = resp_ie_len;
647 memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len); 665 memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len);
666 ev->rm.bss = bss;
648 667
649 spin_lock_irqsave(&wdev->event_lock, flags); 668 spin_lock_irqsave(&wdev->event_lock, flags);
650 list_add_tail(&ev->list, &wdev->event_list); 669 list_add_tail(&ev->list, &wdev->event_list);
651 spin_unlock_irqrestore(&wdev->event_lock, flags); 670 spin_unlock_irqrestore(&wdev->event_lock, flags);
652 queue_work(cfg80211_wq, &rdev->event_work); 671 queue_work(cfg80211_wq, &rdev->event_work);
653} 672}
654EXPORT_SYMBOL(cfg80211_roamed); 673EXPORT_SYMBOL(cfg80211_roamed_bss);
655 674
656void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, 675void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
657 size_t ie_len, u16 reason, bool from_ap) 676 size_t ie_len, u16 reason, bool from_ap)
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 9c601d59b77a..e77df7585004 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -740,9 +740,9 @@ static void cfg80211_process_wdev_events(struct wireless_dev *wdev)
740 NULL); 740 NULL);
741 break; 741 break;
742 case EVENT_ROAMED: 742 case EVENT_ROAMED:
743 __cfg80211_roamed(wdev, ev->rm.channel, ev->rm.bssid, 743 __cfg80211_roamed(wdev, ev->rm.bss, ev->rm.req_ie,
744 ev->rm.req_ie, ev->rm.req_ie_len, 744 ev->rm.req_ie_len, ev->rm.resp_ie,
745 ev->rm.resp_ie, ev->rm.resp_ie_len); 745 ev->rm.resp_ie_len);
746 break; 746 break;
747 case EVENT_DISCONNECTED: 747 case EVENT_DISCONNECTED:
748 __cfg80211_disconnected(wdev->netdev, 748 __cfg80211_disconnected(wdev->netdev,