aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergey Matyukevich <sergey.matyukevich.os@quantenna.com>2019-07-26 12:39:34 -0400
committerJohannes Berg <johannes.berg@intel.com>2019-07-29 10:23:13 -0400
commit0afd425b1b64251f19b5d8d8b49bf56fefbc643f (patch)
tree631452f22aaad867bc752106de595588f493612e
parent3ab8227d3e7d1d2bf1829675d3197e3cb600e9f6 (diff)
cfg80211: fix duplicated scan entries after channel switch
When associated BSS completes channel switch procedure, its channel record needs to be updated. The existing mac80211 solution was extended to cfg80211 in commit 5dc8cdce1d72 ("mac80211/cfg80211: update bss channel on channel switch"). However that solution still appears to be incomplete as it may lead to duplicated scan entries for associated BSS after channel switch. The root cause of the problem is as follows. Each BSS entry is included into the following data structures: - bss list rdev->bss_list - bss search tree rdev->bss_tree Updating BSS channel record without rebuilding bss_tree may break tree search since cmp_bss considers all of the following: channel, bssid, ssid. When BSS channel is updated, but its location in bss_tree is not updated, then subsequent search operations may fail to locate this BSS since they will be traversing bss_tree in wrong direction. As a result, for scan performed after associated BSS channel switch, cfg80211_bss_update may add the second entry for the same BSS to both bss_list and bss_tree, rather then update the existing one. To summarize, if BSS channel needs to be updated, then bss_tree should be rebuilt in order to put updated BSS entry into a proper location. This commit suggests the following straightforward solution: - if new entry has been already created for BSS after channel switch, then use its IEs to update known BSS entry and then remove new entry completely - use rb_erase/rb_insert_bss reinstall updated BSS in bss_tree - for nontransmit BSS entry, the whole transmit BSS hierarchy is updated Signed-off-by: Sergey Matyukevich <sergey.matyukevich.os@quantenna.com> Link: https://lore.kernel.org/r/20190726163922.27509-3-sergey.matyukevich.os@quantenna.com Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--net/wireless/core.h2
-rw-r--r--net/wireless/nl80211.c2
-rw-r--r--net/wireless/scan.c79
3 files changed, 82 insertions, 1 deletions
diff --git a/net/wireless/core.h b/net/wireless/core.h
index ee8388fe4a92..77556c58d9ac 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -306,6 +306,8 @@ void ieee80211_set_bitrate_flags(struct wiphy *wiphy);
306void cfg80211_bss_expire(struct cfg80211_registered_device *rdev); 306void cfg80211_bss_expire(struct cfg80211_registered_device *rdev);
307void cfg80211_bss_age(struct cfg80211_registered_device *rdev, 307void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
308 unsigned long age_secs); 308 unsigned long age_secs);
309void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,
310 struct ieee80211_channel *channel);
309 311
310/* IBSS */ 312/* IBSS */
311int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, 313int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 10b57aa10227..a8d4b2b6b3ec 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -16116,7 +16116,7 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
16116 16116
16117 if (wdev->iftype == NL80211_IFTYPE_STATION && 16117 if (wdev->iftype == NL80211_IFTYPE_STATION &&
16118 !WARN_ON(!wdev->current_bss)) 16118 !WARN_ON(!wdev->current_bss))
16119 wdev->current_bss->pub.channel = chandef->chan; 16119 cfg80211_update_assoc_bss_entry(wdev, chandef->chan);
16120 16120
16121 nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL, 16121 nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
16122 NL80211_CMD_CH_SWITCH_NOTIFY, 0); 16122 NL80211_CMD_CH_SWITCH_NOTIFY, 0);
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 9119f5ce3677..d313c9befa23 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -2001,6 +2001,85 @@ void cfg80211_bss_iter(struct wiphy *wiphy,
2001} 2001}
2002EXPORT_SYMBOL(cfg80211_bss_iter); 2002EXPORT_SYMBOL(cfg80211_bss_iter);
2003 2003
2004void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,
2005 struct ieee80211_channel *chan)
2006{
2007 struct wiphy *wiphy = wdev->wiphy;
2008 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
2009 struct cfg80211_internal_bss *cbss = wdev->current_bss;
2010 struct cfg80211_internal_bss *new = NULL;
2011 struct cfg80211_internal_bss *bss;
2012 struct cfg80211_bss *nontrans_bss;
2013 struct cfg80211_bss *tmp;
2014
2015 spin_lock_bh(&rdev->bss_lock);
2016
2017 if (WARN_ON(cbss->pub.channel == chan))
2018 goto done;
2019
2020 /* use transmitting bss */
2021 if (cbss->pub.transmitted_bss)
2022 cbss = container_of(cbss->pub.transmitted_bss,
2023 struct cfg80211_internal_bss,
2024 pub);
2025
2026 cbss->pub.channel = chan;
2027
2028 list_for_each_entry(bss, &rdev->bss_list, list) {
2029 if (!cfg80211_bss_type_match(bss->pub.capability,
2030 bss->pub.channel->band,
2031 wdev->conn_bss_type))
2032 continue;
2033
2034 if (bss == cbss)
2035 continue;
2036
2037 if (!cmp_bss(&bss->pub, &cbss->pub, BSS_CMP_REGULAR)) {
2038 new = bss;
2039 break;
2040 }
2041 }
2042
2043 if (new) {
2044 /* to save time, update IEs for transmitting bss only */
2045 if (cfg80211_update_known_bss(rdev, cbss, new, false)) {
2046 new->pub.proberesp_ies = NULL;
2047 new->pub.beacon_ies = NULL;
2048 }
2049
2050 list_for_each_entry_safe(nontrans_bss, tmp,
2051 &new->pub.nontrans_list,
2052 nontrans_list) {
2053 bss = container_of(nontrans_bss,
2054 struct cfg80211_internal_bss, pub);
2055 if (__cfg80211_unlink_bss(rdev, bss))
2056 rdev->bss_generation++;
2057 }
2058
2059 WARN_ON(atomic_read(&new->hold));
2060 if (!WARN_ON(!__cfg80211_unlink_bss(rdev, new)))
2061 rdev->bss_generation++;
2062 }
2063
2064 rb_erase(&cbss->rbn, &rdev->bss_tree);
2065 rb_insert_bss(rdev, cbss);
2066 rdev->bss_generation++;
2067
2068 list_for_each_entry_safe(nontrans_bss, tmp,
2069 &cbss->pub.nontrans_list,
2070 nontrans_list) {
2071 bss = container_of(nontrans_bss,
2072 struct cfg80211_internal_bss, pub);
2073 bss->pub.channel = chan;
2074 rb_erase(&bss->rbn, &rdev->bss_tree);
2075 rb_insert_bss(rdev, bss);
2076 rdev->bss_generation++;
2077 }
2078
2079done:
2080 spin_unlock_bh(&rdev->bss_lock);
2081}
2082
2004#ifdef CONFIG_CFG80211_WEXT 2083#ifdef CONFIG_CFG80211_WEXT
2005static struct cfg80211_registered_device * 2084static struct cfg80211_registered_device *
2006cfg80211_get_dev_from_ifindex(struct net *net, int ifindex) 2085cfg80211_get_dev_from_ifindex(struct net *net, int ifindex)