linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH] cfg80211: fix duplicated scan entries after channel switch
@ 2019-06-13 11:21 Sergey Matyukevich
  2019-06-28 14:39 ` Johannes Berg
  0 siblings, 1 reply; 4+ messages in thread
From: Sergey Matyukevich @ 2019-06-13 11:21 UTC (permalink / raw)
  To: linux-wireless
  Cc: Johannes Berg, Igor Mitsyanko, Mikhail Karpenko, Sergey Matyukevich

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 this solution 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 remove it completely
- use rb_erase/rb_insert_bss reinstall updated BSS in bss_tree

Finally, next scan operation will find BSS entry in expected location
in rb_tree. So all the IEs, including HT/VHT operation IEs,
will be properly updated.

Signed-off-by: Sergey Matyukevich <sergey.matyukevich.os@quantenna.com>

---

Misc notes.

1. Tested using iwlwifi and qtnfmac drivers, looks good

2. Alternative approach: remove old BSS entry and keep new a one
This approach may have certain benefits for mac80211 drivers.
For instance, in this case HT/VHT operation IEs are going to be
valid from the start, no need to wait for the next scan.

However the following procedure for replacing current_bss, protected
by wdev->mtx and rdev->bss_lock locks, seems to be insufficient:

  bss_ref_get(rdev, new);
  cfg80211_hold_bss(new);
  wdev->current_bss = new;

  cfg80211_unhold_bss(old);
  bss_ref_put(rdev, old);
  __cfg80211_unlink_bss(rdev, old);

When testing this alternative approach using iwlwifi driver,
occasional general protection fault crashes have been observed
on ieee80211_rx_mgmt_beacon/ieee80211_bss_info_update code paths.
So far I haven't yet root caused them.

---
 net/wireless/core.h    |  2 ++
 net/wireless/nl80211.c |  5 +++--
 net/wireless/scan.c    | 45 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 50 insertions(+), 2 deletions(-)

diff --git a/net/wireless/core.h b/net/wireless/core.h
index 84d36ca7a7ab..763edce6b34f 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -306,6 +306,8 @@ void ieee80211_set_bitrate_flags(struct wiphy *wiphy);
 void cfg80211_bss_expire(struct cfg80211_registered_device *rdev);
 void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
                       unsigned long age_secs);
+void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,
+				     struct ieee80211_channel *channel);
 
 /* IBSS */
 int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index c391b560d986..b3b13131e42e 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -15971,8 +15971,9 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
 	wdev->preset_chandef = *chandef;
 
 	if (wdev->iftype == NL80211_IFTYPE_STATION &&
-	    !WARN_ON(!wdev->current_bss))
-		wdev->current_bss->pub.channel = chandef->chan;
+	    !WARN_ON(!wdev->current_bss)) {
+		cfg80211_update_assoc_bss_entry(wdev, chandef->chan);
+	}
 
 	nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
 				 NL80211_CMD_CH_SWITCH_NOTIFY, 0);
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index c04f5451f89b..9a16c42296d0 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -1972,6 +1972,51 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 }
 EXPORT_SYMBOL(cfg80211_unlink_bss);
 
+void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,
+				     struct ieee80211_channel *chan)
+{
+	struct wiphy *wiphy = wdev->wiphy;
+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+	struct cfg80211_internal_bss *cbss = wdev->current_bss;
+	struct cfg80211_internal_bss *new = NULL;
+	struct cfg80211_internal_bss *bss;
+
+	spin_lock_bh(&rdev->bss_lock);
+
+	if (WARN_ON(cbss->pub.channel == chan))
+		goto done;
+
+	cbss->pub.channel = chan;
+	cbss->ts = jiffies;
+
+	list_for_each_entry(bss, &rdev->bss_list, list) {
+		if (!cfg80211_bss_type_match(bss->pub.capability,
+					     bss->pub.channel->band,
+					     wdev->conn_bss_type))
+			continue;
+
+		if (bss == cbss)
+			continue;
+
+		if (!cmp_bss(&bss->pub, &cbss->pub, BSS_CMP_REGULAR)) {
+			new = bss;
+			break;
+		}
+	}
+
+	if (new) {
+		WARN_ON(atomic_read(&new->hold));
+		WARN_ON(!__cfg80211_unlink_bss(rdev, new));
+	}
+
+	rb_erase(&cbss->rbn, &rdev->bss_tree);
+	rb_insert_bss(rdev, cbss);
+	rdev->bss_generation++;
+
+done:
+	spin_unlock_bh(&rdev->bss_lock);
+}
+
 #ifdef CONFIG_CFG80211_WEXT
 static struct cfg80211_registered_device *
 cfg80211_get_dev_from_ifindex(struct net *net, int ifindex)
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [RFC PATCH] cfg80211: fix duplicated scan entries after channel switch
  2019-06-13 11:21 [RFC PATCH] cfg80211: fix duplicated scan entries after channel switch Sergey Matyukevich
@ 2019-06-28 14:39 ` Johannes Berg
  2019-07-02 11:50   ` Sergey Matyukevich
  0 siblings, 1 reply; 4+ messages in thread
From: Johannes Berg @ 2019-06-28 14:39 UTC (permalink / raw)
  To: Sergey Matyukevich, linux-wireless; +Cc: Igor Mitsyanko, Mikhail Karpenko

On Thu, 2019-06-13 at 11:21 +0000, Sergey Matyukevich wrote:
> 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 this solution 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.

Good catch!

> This commit suggests the following straightforward solution:
> - if new entry has been already created for BSS after channel switch,
>   then remove it completely

Shouldn't we prefer the new entry?

OTOH, the old entry will likely have a "hold", so it doesn't get removed
while we're connected ... and the driver etc. might be referencing it.
So I guess the old entry should be updated with info from the newer one?

> Finally, next scan operation will find BSS entry in expected location
> in rb_tree. So all the IEs, including HT/VHT operation IEs,
> will be properly updated.

Right. Although if it was there before, then it already has been updated
in a sense... But I guess it's a corner case to even get there?

> 1. Tested using iwlwifi and qtnfmac drivers, looks good

Great.

> 2. Alternative approach: remove old BSS entry and keep new a one
> This approach may have certain benefits for mac80211 drivers.
> For instance, in this case HT/VHT operation IEs are going to be
> valid from the start, no need to wait for the next scan.

> However the following procedure for replacing current_bss, protected
> by wdev->mtx and rdev->bss_lock locks, seems to be insufficient:
> 
>   bss_ref_get(rdev, new);
>   cfg80211_hold_bss(new);
>   wdev->current_bss = new;
> 
>   cfg80211_unhold_bss(old);
>   bss_ref_put(rdev, old);
>   __cfg80211_unlink_bss(rdev, old);
> 
> When testing this alternative approach using iwlwifi driver,
> occasional general protection fault crashes have been observed
> on ieee80211_rx_mgmt_beacon/ieee80211_bss_info_update code paths.
> So far I haven't yet root caused them.

At the very least you'd also have to update ifmgd->associated in
mac80211, and that's basically not really possible? Well, I guess we
could change the channel switch API to return the new one or something.

I guess the better thing would be to go update the old entry with the
new one's data, before killing the new one.

Not sure it's worth the extra complexity though.

johannes


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [RFC PATCH] cfg80211: fix duplicated scan entries after channel switch
  2019-06-28 14:39 ` Johannes Berg
@ 2019-07-02 11:50   ` Sergey Matyukevich
  2019-07-02 12:40     ` Johannes Berg
  0 siblings, 1 reply; 4+ messages in thread
From: Sergey Matyukevich @ 2019-07-02 11:50 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless, Igor Mitsyanko, Mikhail Karpenko

...

> > 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.
> 
> Good catch!
> 
> > This commit suggests the following straightforward solution:
> > - if new entry has been already created for BSS after channel switch,
> >   then remove it completely
> 
> Shouldn't we prefer the new entry?
> 
> OTOH, the old entry will likely have a "hold", so it doesn't get removed
> while we're connected ... and the driver etc. might be referencing it.
> So I guess the old entry should be updated with info from the newer one?
> 
> > Finally, next scan operation will find BSS entry in expected location
> > in rb_tree. So all the IEs, including HT/VHT operation IEs,
> > will be properly updated.
> 
> Right. Although if it was there before, then it already has been updated
> in a sense... But I guess it's a corner case to even get there?
> 
> > 1. Tested using iwlwifi and qtnfmac drivers, looks good
> 
> Great.
> 
> > 2. Alternative approach: remove old BSS entry and keep new a one
> > This approach may have certain benefits for mac80211 drivers.
> > For instance, in this case HT/VHT operation IEs are going to be
> > valid from the start, no need to wait for the next scan.
> 
> > However the following procedure for replacing current_bss, protected
> > by wdev->mtx and rdev->bss_lock locks, seems to be insufficient:
> > 
> >   bss_ref_get(rdev, new);
> >   cfg80211_hold_bss(new);
> >   wdev->current_bss = new;
> > 
> >   cfg80211_unhold_bss(old);
> >   bss_ref_put(rdev, old);
> >   __cfg80211_unlink_bss(rdev, old);
> > 
> > When testing this alternative approach using iwlwifi driver,
> > occasional general protection fault crashes have been observed
> > on ieee80211_rx_mgmt_beacon/ieee80211_bss_info_update code paths.
> > So far I haven't yet root caused them.
> 
> At the very least you'd also have to update ifmgd->associated in
> mac80211, and that's basically not really possible? Well, I guess we
> could change the channel switch API to return the new one or something.
> 
> I guess the better thing would be to go update the old entry with the
> new one's data, before killing the new one.
> 
> Not sure it's worth the extra complexity though.

Hello Johannes,

Thanks for review! Summarizing your comments, here are the options
and some of their highlights:

1. replace old entry with the new entry
   - no easy way to update ifmgd->associated w/o rework of mac80211 csa

2. keep old entry, remove new entry
   - this is what suggested RFC patch does
   - works, but update of current_bss entry after csa may be delayed

3. keep old entry, update it using data from new entry, then remove new entry
   - this looks like a better approach

Unless I am missing something, the last option can be implemented on top of
the current RFC patch w/o extra complexity. The required bss entry update is
what cfg80211_bss_update function does when bss in question already exists.
So it should be possible to reuse that code.

I will post RFC patch v2 after more testing for both mac80211 and
fullmac cases.

Regards,
Sergey

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [RFC PATCH] cfg80211: fix duplicated scan entries after channel switch
  2019-07-02 11:50   ` Sergey Matyukevich
@ 2019-07-02 12:40     ` Johannes Berg
  0 siblings, 0 replies; 4+ messages in thread
From: Johannes Berg @ 2019-07-02 12:40 UTC (permalink / raw)
  To: Sergey Matyukevich; +Cc: linux-wireless, Igor Mitsyanko, Mikhail Karpenko

Hi Sergey,

On Tue, 2019-07-02 at 11:50 +0000, Sergey Matyukevich wrote:

> Thanks for review! Summarizing your comments, here are the options
> and some of their highlights:
> 
> 1. replace old entry with the new entry
>    - no easy way to update ifmgd->associated w/o rework of mac80211 csa

Yes, this one's the tricky one. I don't think you can make this work
easily.

> 2. keep old entry, remove new entry
>    - this is what suggested RFC patch does
>    - works, but update of current_bss entry after csa may be delayed

Correct.

> 3. keep old entry, update it using data from new entry, then remove new entry
>    - this looks like a better approach
> 
> Unless I am missing something, the last option can be implemented on top of
> the current RFC patch w/o extra complexity. The required bss entry update is
> what cfg80211_bss_update function does when bss in question already exists.
> So it should be possible to reuse that code.

Agree, you just need to sort of invert it, or call it like this:

 * relink the old entry, unlink a new entry (if any)
 * bss_update() with the new entry if there was one
 * it should free one of the entries if I remember correctly

> I will post RFC patch v2 after more testing for both mac80211 and
> fullmac cases.

Sounds good, thanks a lot for working on this!

johannes


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2019-07-02 12:41 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-13 11:21 [RFC PATCH] cfg80211: fix duplicated scan entries after channel switch Sergey Matyukevich
2019-06-28 14:39 ` Johannes Berg
2019-07-02 11:50   ` Sergey Matyukevich
2019-07-02 12:40     ` Johannes Berg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).