diff options
Diffstat (limited to 'net/wireless/scan.c')
-rw-r--r-- | net/wireless/scan.c | 295 |
1 files changed, 229 insertions, 66 deletions
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 36daacb31788..b7a167984986 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c | |||
@@ -19,46 +19,124 @@ | |||
19 | #include "wext-compat.h" | 19 | #include "wext-compat.h" |
20 | #include "rdev-ops.h" | 20 | #include "rdev-ops.h" |
21 | 21 | ||
22 | /** | ||
23 | * DOC: BSS tree/list structure | ||
24 | * | ||
25 | * At the top level, the BSS list is kept in both a list in each | ||
26 | * registered device (@bss_list) as well as an RB-tree for faster | ||
27 | * lookup. In the RB-tree, entries can be looked up using their | ||
28 | * channel, MESHID, MESHCONF (for MBSSes) or channel, BSSID, SSID | ||
29 | * for other BSSes. | ||
30 | * | ||
31 | * Due to the possibility of hidden SSIDs, there's a second level | ||
32 | * structure, the "hidden_list" and "hidden_beacon_bss" pointer. | ||
33 | * The hidden_list connects all BSSes belonging to a single AP | ||
34 | * that has a hidden SSID, and connects beacon and probe response | ||
35 | * entries. For a probe response entry for a hidden SSID, the | ||
36 | * hidden_beacon_bss pointer points to the BSS struct holding the | ||
37 | * beacon's information. | ||
38 | * | ||
39 | * Reference counting is done for all these references except for | ||
40 | * the hidden_list, so that a beacon BSS struct that is otherwise | ||
41 | * not referenced has one reference for being on the bss_list and | ||
42 | * one for each probe response entry that points to it using the | ||
43 | * hidden_beacon_bss pointer. When a BSS struct that has such a | ||
44 | * pointer is get/put, the refcount update is also propagated to | ||
45 | * the referenced struct, this ensure that it cannot get removed | ||
46 | * while somebody is using the probe response version. | ||
47 | * | ||
48 | * Note that the hidden_beacon_bss pointer never changes, due to | ||
49 | * the reference counting. Therefore, no locking is needed for | ||
50 | * it. | ||
51 | * | ||
52 | * Also note that the hidden_beacon_bss pointer is only relevant | ||
53 | * if the driver uses something other than the IEs, e.g. private | ||
54 | * data stored stored in the BSS struct, since the beacon IEs are | ||
55 | * also linked into the probe response struct. | ||
56 | */ | ||
57 | |||
22 | #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) | 58 | #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) |
23 | 59 | ||
24 | static void bss_release(struct kref *ref) | 60 | static void bss_free(struct cfg80211_internal_bss *bss) |
25 | { | 61 | { |
26 | struct cfg80211_bss_ies *ies; | 62 | struct cfg80211_bss_ies *ies; |
27 | struct cfg80211_internal_bss *bss; | ||
28 | |||
29 | bss = container_of(ref, struct cfg80211_internal_bss, ref); | ||
30 | 63 | ||
31 | if (WARN_ON(atomic_read(&bss->hold))) | 64 | if (WARN_ON(atomic_read(&bss->hold))) |
32 | return; | 65 | return; |
33 | 66 | ||
34 | ies = (void *)rcu_access_pointer(bss->pub.beacon_ies); | 67 | ies = (void *)rcu_access_pointer(bss->pub.beacon_ies); |
35 | if (ies) | 68 | if (ies && !bss->pub.hidden_beacon_bss) |
36 | kfree_rcu(ies, rcu_head); | 69 | kfree_rcu(ies, rcu_head); |
37 | ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies); | 70 | ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies); |
38 | if (ies) | 71 | if (ies) |
39 | kfree_rcu(ies, rcu_head); | 72 | kfree_rcu(ies, rcu_head); |
40 | 73 | ||
74 | /* | ||
75 | * This happens when the module is removed, it doesn't | ||
76 | * really matter any more save for completeness | ||
77 | */ | ||
78 | if (!list_empty(&bss->hidden_list)) | ||
79 | list_del(&bss->hidden_list); | ||
80 | |||
41 | kfree(bss); | 81 | kfree(bss); |
42 | } | 82 | } |
43 | 83 | ||
44 | static inline void bss_ref_get(struct cfg80211_internal_bss *bss) | 84 | static inline void bss_ref_get(struct cfg80211_registered_device *dev, |
85 | struct cfg80211_internal_bss *bss) | ||
45 | { | 86 | { |
46 | kref_get(&bss->ref); | 87 | lockdep_assert_held(&dev->bss_lock); |
88 | |||
89 | bss->refcount++; | ||
90 | if (bss->pub.hidden_beacon_bss) { | ||
91 | bss = container_of(bss->pub.hidden_beacon_bss, | ||
92 | struct cfg80211_internal_bss, | ||
93 | pub); | ||
94 | bss->refcount++; | ||
95 | } | ||
47 | } | 96 | } |
48 | 97 | ||
49 | static inline void bss_ref_put(struct cfg80211_internal_bss *bss) | 98 | static inline void bss_ref_put(struct cfg80211_registered_device *dev, |
99 | struct cfg80211_internal_bss *bss) | ||
50 | { | 100 | { |
51 | kref_put(&bss->ref, bss_release); | 101 | lockdep_assert_held(&dev->bss_lock); |
102 | |||
103 | if (bss->pub.hidden_beacon_bss) { | ||
104 | struct cfg80211_internal_bss *hbss; | ||
105 | hbss = container_of(bss->pub.hidden_beacon_bss, | ||
106 | struct cfg80211_internal_bss, | ||
107 | pub); | ||
108 | hbss->refcount--; | ||
109 | if (hbss->refcount == 0) | ||
110 | bss_free(hbss); | ||
111 | } | ||
112 | bss->refcount--; | ||
113 | if (bss->refcount == 0) | ||
114 | bss_free(bss); | ||
52 | } | 115 | } |
53 | 116 | ||
54 | static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, | 117 | static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, |
55 | struct cfg80211_internal_bss *bss) | 118 | struct cfg80211_internal_bss *bss) |
56 | { | 119 | { |
57 | lockdep_assert_held(&dev->bss_lock); | 120 | lockdep_assert_held(&dev->bss_lock); |
58 | 121 | ||
122 | if (!list_empty(&bss->hidden_list)) { | ||
123 | /* | ||
124 | * don't remove the beacon entry if it has | ||
125 | * probe responses associated with it | ||
126 | */ | ||
127 | if (!bss->pub.hidden_beacon_bss) | ||
128 | return false; | ||
129 | /* | ||
130 | * if it's a probe response entry break its | ||
131 | * link to the other entries in the group | ||
132 | */ | ||
133 | list_del_init(&bss->hidden_list); | ||
134 | } | ||
135 | |||
59 | list_del_init(&bss->list); | 136 | list_del_init(&bss->list); |
60 | rb_erase(&bss->rbn, &dev->bss_tree); | 137 | rb_erase(&bss->rbn, &dev->bss_tree); |
61 | bss_ref_put(bss); | 138 | bss_ref_put(dev, bss); |
139 | return true; | ||
62 | } | 140 | } |
63 | 141 | ||
64 | static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev, | 142 | static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev, |
@@ -75,8 +153,8 @@ static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev, | |||
75 | if (!time_after(expire_time, bss->ts)) | 153 | if (!time_after(expire_time, bss->ts)) |
76 | continue; | 154 | continue; |
77 | 155 | ||
78 | __cfg80211_unlink_bss(dev, bss); | 156 | if (__cfg80211_unlink_bss(dev, bss)) |
79 | expired = true; | 157 | expired = true; |
80 | } | 158 | } |
81 | 159 | ||
82 | if (expired) | 160 | if (expired) |
@@ -466,7 +544,7 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, | |||
466 | continue; | 544 | continue; |
467 | if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { | 545 | if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { |
468 | res = bss; | 546 | res = bss; |
469 | bss_ref_get(res); | 547 | bss_ref_get(dev, res); |
470 | break; | 548 | break; |
471 | } | 549 | } |
472 | } | 550 | } |
@@ -532,23 +610,67 @@ rb_find_bss(struct cfg80211_registered_device *dev, | |||
532 | return NULL; | 610 | return NULL; |
533 | } | 611 | } |
534 | 612 | ||
535 | static void | 613 | static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev, |
536 | copy_hidden_ies(struct cfg80211_internal_bss *res, | 614 | struct cfg80211_internal_bss *new) |
537 | struct cfg80211_internal_bss *hidden) | ||
538 | { | 615 | { |
539 | const struct cfg80211_bss_ies *ies; | 616 | const struct cfg80211_bss_ies *ies; |
617 | struct cfg80211_internal_bss *bss; | ||
618 | const u8 *ie; | ||
619 | int i, ssidlen; | ||
620 | u8 fold = 0; | ||
540 | 621 | ||
541 | if (rcu_access_pointer(res->pub.beacon_ies)) | 622 | ies = rcu_access_pointer(new->pub.beacon_ies); |
542 | return; | ||
543 | |||
544 | ies = rcu_access_pointer(hidden->pub.beacon_ies); | ||
545 | if (WARN_ON(!ies)) | 623 | if (WARN_ON(!ies)) |
546 | return; | 624 | return false; |
547 | 625 | ||
548 | ies = kmemdup(ies, sizeof(*ies) + ies->len, GFP_ATOMIC); | 626 | ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); |
549 | if (unlikely(!ies)) | 627 | if (!ie) { |
550 | return; | 628 | /* nothing to do */ |
551 | rcu_assign_pointer(res->pub.beacon_ies, ies); | 629 | return true; |
630 | } | ||
631 | |||
632 | ssidlen = ie[1]; | ||
633 | for (i = 0; i < ssidlen; i++) | ||
634 | fold |= ie[2 + i]; | ||
635 | |||
636 | if (fold) { | ||
637 | /* not a hidden SSID */ | ||
638 | return true; | ||
639 | } | ||
640 | |||
641 | /* This is the bad part ... */ | ||
642 | |||
643 | list_for_each_entry(bss, &dev->bss_list, list) { | ||
644 | if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid)) | ||
645 | continue; | ||
646 | if (bss->pub.channel != new->pub.channel) | ||
647 | continue; | ||
648 | if (rcu_access_pointer(bss->pub.beacon_ies)) | ||
649 | continue; | ||
650 | ies = rcu_access_pointer(bss->pub.ies); | ||
651 | if (!ies) | ||
652 | continue; | ||
653 | ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); | ||
654 | if (!ie) | ||
655 | continue; | ||
656 | if (ssidlen && ie[1] != ssidlen) | ||
657 | continue; | ||
658 | /* that would be odd ... */ | ||
659 | if (bss->pub.beacon_ies) | ||
660 | continue; | ||
661 | if (WARN_ON_ONCE(bss->pub.hidden_beacon_bss)) | ||
662 | continue; | ||
663 | if (WARN_ON_ONCE(!list_empty(&bss->hidden_list))) | ||
664 | list_del(&bss->hidden_list); | ||
665 | /* combine them */ | ||
666 | list_add(&bss->hidden_list, &new->hidden_list); | ||
667 | bss->pub.hidden_beacon_bss = &new->pub; | ||
668 | new->refcount += bss->refcount; | ||
669 | rcu_assign_pointer(bss->pub.beacon_ies, | ||
670 | new->pub.beacon_ies); | ||
671 | } | ||
672 | |||
673 | return true; | ||
552 | } | 674 | } |
553 | 675 | ||
554 | static struct cfg80211_internal_bss * | 676 | static struct cfg80211_internal_bss * |
@@ -573,7 +695,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
573 | 695 | ||
574 | if (found) { | 696 | if (found) { |
575 | found->pub.beacon_interval = tmp->pub.beacon_interval; | 697 | found->pub.beacon_interval = tmp->pub.beacon_interval; |
576 | found->pub.tsf = tmp->pub.tsf; | ||
577 | found->pub.signal = tmp->pub.signal; | 698 | found->pub.signal = tmp->pub.signal; |
578 | found->pub.capability = tmp->pub.capability; | 699 | found->pub.capability = tmp->pub.capability; |
579 | found->ts = tmp->ts; | 700 | found->ts = tmp->ts; |
@@ -594,6 +715,21 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
594 | rcu_head); | 715 | rcu_head); |
595 | } else if (rcu_access_pointer(tmp->pub.beacon_ies)) { | 716 | } else if (rcu_access_pointer(tmp->pub.beacon_ies)) { |
596 | const struct cfg80211_bss_ies *old; | 717 | const struct cfg80211_bss_ies *old; |
718 | struct cfg80211_internal_bss *bss; | ||
719 | |||
720 | if (found->pub.hidden_beacon_bss && | ||
721 | !list_empty(&found->hidden_list)) { | ||
722 | /* | ||
723 | * The found BSS struct is one of the probe | ||
724 | * response members of a group, but we're | ||
725 | * receiving a beacon (beacon_ies in the tmp | ||
726 | * bss is used). This can only mean that the | ||
727 | * AP changed its beacon from not having an | ||
728 | * SSID to showing it, which is confusing so | ||
729 | * drop this information. | ||
730 | */ | ||
731 | goto drop; | ||
732 | } | ||
597 | 733 | ||
598 | old = rcu_access_pointer(found->pub.beacon_ies); | 734 | old = rcu_access_pointer(found->pub.beacon_ies); |
599 | 735 | ||
@@ -605,6 +741,18 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
605 | rcu_assign_pointer(found->pub.ies, | 741 | rcu_assign_pointer(found->pub.ies, |
606 | tmp->pub.beacon_ies); | 742 | tmp->pub.beacon_ies); |
607 | 743 | ||
744 | /* Assign beacon IEs to all sub entries */ | ||
745 | list_for_each_entry(bss, &found->hidden_list, | ||
746 | hidden_list) { | ||
747 | const struct cfg80211_bss_ies *ies; | ||
748 | |||
749 | ies = rcu_access_pointer(bss->pub.beacon_ies); | ||
750 | WARN_ON(ies != old); | ||
751 | |||
752 | rcu_assign_pointer(bss->pub.beacon_ies, | ||
753 | tmp->pub.beacon_ies); | ||
754 | } | ||
755 | |||
608 | if (old) | 756 | if (old) |
609 | kfree_rcu((struct cfg80211_bss_ies *)old, | 757 | kfree_rcu((struct cfg80211_bss_ies *)old, |
610 | rcu_head); | 758 | rcu_head); |
@@ -614,24 +762,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
614 | struct cfg80211_internal_bss *hidden; | 762 | struct cfg80211_internal_bss *hidden; |
615 | struct cfg80211_bss_ies *ies; | 763 | struct cfg80211_bss_ies *ies; |
616 | 764 | ||
617 | /* First check if the beacon is a probe response from | ||
618 | * a hidden bss. If so, copy beacon ies (with nullified | ||
619 | * ssid) into the probe response bss entry (with real ssid). | ||
620 | * It is required basically for PSM implementation | ||
621 | * (probe responses do not contain tim ie) */ | ||
622 | |||
623 | /* TODO: The code is not trying to update existing probe | ||
624 | * response bss entries when beacon ies are | ||
625 | * getting changed. */ | ||
626 | hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN); | ||
627 | if (hidden) { | ||
628 | copy_hidden_ies(tmp, hidden); | ||
629 | } else { | ||
630 | hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_NUL); | ||
631 | if (hidden) | ||
632 | copy_hidden_ies(tmp, hidden); | ||
633 | } | ||
634 | |||
635 | /* | 765 | /* |
636 | * create a copy -- the "res" variable that is passed in | 766 | * create a copy -- the "res" variable that is passed in |
637 | * is allocated on the stack since it's not needed in the | 767 | * is allocated on the stack since it's not needed in the |
@@ -646,21 +776,51 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
646 | ies = (void *)rcu_dereference(tmp->pub.proberesp_ies); | 776 | ies = (void *)rcu_dereference(tmp->pub.proberesp_ies); |
647 | if (ies) | 777 | if (ies) |
648 | kfree_rcu(ies, rcu_head); | 778 | kfree_rcu(ies, rcu_head); |
649 | spin_unlock_bh(&dev->bss_lock); | 779 | goto drop; |
650 | return NULL; | ||
651 | } | 780 | } |
652 | memcpy(new, tmp, sizeof(*new)); | 781 | memcpy(new, tmp, sizeof(*new)); |
653 | kref_init(&new->ref); | 782 | new->refcount = 1; |
783 | INIT_LIST_HEAD(&new->hidden_list); | ||
784 | |||
785 | if (rcu_access_pointer(tmp->pub.proberesp_ies)) { | ||
786 | hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN); | ||
787 | if (!hidden) | ||
788 | hidden = rb_find_bss(dev, tmp, | ||
789 | BSS_CMP_HIDE_NUL); | ||
790 | if (hidden) { | ||
791 | new->pub.hidden_beacon_bss = &hidden->pub; | ||
792 | list_add(&new->hidden_list, | ||
793 | &hidden->hidden_list); | ||
794 | hidden->refcount++; | ||
795 | rcu_assign_pointer(new->pub.beacon_ies, | ||
796 | hidden->pub.beacon_ies); | ||
797 | } | ||
798 | } else { | ||
799 | /* | ||
800 | * Ok so we found a beacon, and don't have an entry. If | ||
801 | * it's a beacon with hidden SSID, we might be in for an | ||
802 | * expensive search for any probe responses that should | ||
803 | * be grouped with this beacon for updates ... | ||
804 | */ | ||
805 | if (!cfg80211_combine_bsses(dev, new)) { | ||
806 | kfree(new); | ||
807 | goto drop; | ||
808 | } | ||
809 | } | ||
810 | |||
654 | list_add_tail(&new->list, &dev->bss_list); | 811 | list_add_tail(&new->list, &dev->bss_list); |
655 | rb_insert_bss(dev, new); | 812 | rb_insert_bss(dev, new); |
656 | found = new; | 813 | found = new; |
657 | } | 814 | } |
658 | 815 | ||
659 | dev->bss_generation++; | 816 | dev->bss_generation++; |
817 | bss_ref_get(dev, found); | ||
660 | spin_unlock_bh(&dev->bss_lock); | 818 | spin_unlock_bh(&dev->bss_lock); |
661 | 819 | ||
662 | bss_ref_get(found); | ||
663 | return found; | 820 | return found; |
821 | drop: | ||
822 | spin_unlock_bh(&dev->bss_lock); | ||
823 | return NULL; | ||
664 | } | 824 | } |
665 | 825 | ||
666 | static struct ieee80211_channel * | 826 | static struct ieee80211_channel * |
@@ -719,7 +879,6 @@ cfg80211_inform_bss(struct wiphy *wiphy, | |||
719 | memcpy(tmp.pub.bssid, bssid, ETH_ALEN); | 879 | memcpy(tmp.pub.bssid, bssid, ETH_ALEN); |
720 | tmp.pub.channel = channel; | 880 | tmp.pub.channel = channel; |
721 | tmp.pub.signal = signal; | 881 | tmp.pub.signal = signal; |
722 | tmp.pub.tsf = tsf; | ||
723 | tmp.pub.beacon_interval = beacon_interval; | 882 | tmp.pub.beacon_interval = beacon_interval; |
724 | tmp.pub.capability = capability; | 883 | tmp.pub.capability = capability; |
725 | /* | 884 | /* |
@@ -734,6 +893,7 @@ cfg80211_inform_bss(struct wiphy *wiphy, | |||
734 | if (!ies) | 893 | if (!ies) |
735 | return NULL; | 894 | return NULL; |
736 | ies->len = ielen; | 895 | ies->len = ielen; |
896 | ies->tsf = tsf; | ||
737 | memcpy(ies->data, ie, ielen); | 897 | memcpy(ies->data, ie, ielen); |
738 | 898 | ||
739 | rcu_assign_pointer(tmp.pub.beacon_ies, ies); | 899 | rcu_assign_pointer(tmp.pub.beacon_ies, ies); |
@@ -790,6 +950,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
790 | if (!ies) | 950 | if (!ies) |
791 | return NULL; | 951 | return NULL; |
792 | ies->len = ielen; | 952 | ies->len = ielen; |
953 | ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); | ||
793 | memcpy(ies->data, mgmt->u.probe_resp.variable, ielen); | 954 | memcpy(ies->data, mgmt->u.probe_resp.variable, ielen); |
794 | 955 | ||
795 | if (ieee80211_is_probe_resp(mgmt->frame_control)) | 956 | if (ieee80211_is_probe_resp(mgmt->frame_control)) |
@@ -801,7 +962,6 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
801 | memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN); | 962 | memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN); |
802 | tmp.pub.channel = channel; | 963 | tmp.pub.channel = channel; |
803 | tmp.pub.signal = signal; | 964 | tmp.pub.signal = signal; |
804 | tmp.pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); | ||
805 | tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); | 965 | tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); |
806 | tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); | 966 | tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); |
807 | 967 | ||
@@ -818,27 +978,35 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
818 | } | 978 | } |
819 | EXPORT_SYMBOL(cfg80211_inform_bss_frame); | 979 | EXPORT_SYMBOL(cfg80211_inform_bss_frame); |
820 | 980 | ||
821 | void cfg80211_ref_bss(struct cfg80211_bss *pub) | 981 | void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) |
822 | { | 982 | { |
983 | struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); | ||
823 | struct cfg80211_internal_bss *bss; | 984 | struct cfg80211_internal_bss *bss; |
824 | 985 | ||
825 | if (!pub) | 986 | if (!pub) |
826 | return; | 987 | return; |
827 | 988 | ||
828 | bss = container_of(pub, struct cfg80211_internal_bss, pub); | 989 | bss = container_of(pub, struct cfg80211_internal_bss, pub); |
829 | bss_ref_get(bss); | 990 | |
991 | spin_lock_bh(&dev->bss_lock); | ||
992 | bss_ref_get(dev, bss); | ||
993 | spin_unlock_bh(&dev->bss_lock); | ||
830 | } | 994 | } |
831 | EXPORT_SYMBOL(cfg80211_ref_bss); | 995 | EXPORT_SYMBOL(cfg80211_ref_bss); |
832 | 996 | ||
833 | void cfg80211_put_bss(struct cfg80211_bss *pub) | 997 | void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) |
834 | { | 998 | { |
999 | struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); | ||
835 | struct cfg80211_internal_bss *bss; | 1000 | struct cfg80211_internal_bss *bss; |
836 | 1001 | ||
837 | if (!pub) | 1002 | if (!pub) |
838 | return; | 1003 | return; |
839 | 1004 | ||
840 | bss = container_of(pub, struct cfg80211_internal_bss, pub); | 1005 | bss = container_of(pub, struct cfg80211_internal_bss, pub); |
841 | bss_ref_put(bss); | 1006 | |
1007 | spin_lock_bh(&dev->bss_lock); | ||
1008 | bss_ref_put(dev, bss); | ||
1009 | spin_unlock_bh(&dev->bss_lock); | ||
842 | } | 1010 | } |
843 | EXPORT_SYMBOL(cfg80211_put_bss); | 1011 | EXPORT_SYMBOL(cfg80211_put_bss); |
844 | 1012 | ||
@@ -854,8 +1022,8 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) | |||
854 | 1022 | ||
855 | spin_lock_bh(&dev->bss_lock); | 1023 | spin_lock_bh(&dev->bss_lock); |
856 | if (!list_empty(&bss->list)) { | 1024 | if (!list_empty(&bss->list)) { |
857 | __cfg80211_unlink_bss(dev, bss); | 1025 | if (__cfg80211_unlink_bss(dev, bss)) |
858 | dev->bss_generation++; | 1026 | dev->bss_generation++; |
859 | } | 1027 | } |
860 | spin_unlock_bh(&dev->bss_lock); | 1028 | spin_unlock_bh(&dev->bss_lock); |
861 | } | 1029 | } |
@@ -1124,15 +1292,10 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, | |||
1124 | 1292 | ||
1125 | rcu_read_lock(); | 1293 | rcu_read_lock(); |
1126 | ies = rcu_dereference(bss->pub.ies); | 1294 | ies = rcu_dereference(bss->pub.ies); |
1127 | if (ies) { | 1295 | rem = ies->len; |
1128 | rem = ies->len; | 1296 | ie = ies->data; |
1129 | ie = ies->data; | ||
1130 | } else { | ||
1131 | rem = 0; | ||
1132 | ie = NULL; | ||
1133 | } | ||
1134 | 1297 | ||
1135 | while (ies && rem >= 2) { | 1298 | while (rem >= 2) { |
1136 | /* invalid data */ | 1299 | /* invalid data */ |
1137 | if (ie[1] > rem - 2) | 1300 | if (ie[1] > rem - 2) |
1138 | break; | 1301 | break; |
@@ -1245,7 +1408,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, | |||
1245 | if (buf) { | 1408 | if (buf) { |
1246 | memset(&iwe, 0, sizeof(iwe)); | 1409 | memset(&iwe, 0, sizeof(iwe)); |
1247 | iwe.cmd = IWEVCUSTOM; | 1410 | iwe.cmd = IWEVCUSTOM; |
1248 | sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf)); | 1411 | sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf)); |
1249 | iwe.u.data.length = strlen(buf); | 1412 | iwe.u.data.length = strlen(buf); |
1250 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | 1413 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, |
1251 | &iwe, buf); | 1414 | &iwe, buf); |