diff mbox series

[2/2] wpa_supplicant: fix the constant roaming problem

Message ID 1536169473-19181-2-git-send-email-andrei.otcheretianski@intel.com
State Superseded
Headers show
Series [1/2] drivers: document struct wpa_signal_info | expand

Commit Message

Andrei Otcheretianski Sept. 5, 2018, 5:44 p.m. UTC
From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>

We saw that on certain platforms in certain places we keep
switching between two APs and eventually get the same RSSI.
Debugging showed that we have a very big difference between
the two antennas.

Ant A can hear AP A very well (-60) but AP B very bad (-80)
Ant B can hear AP B very well (-60) but AP A very bad (-80)

When the device associates to AP A, it'll learn to use Ant A.
If the device uses one single antenna to receive the scan
results, it may hear the AP it is currently associated to
on the second antenna and get bad results. Because of that,
the wpa_supplicant will roam to the other AP and the same
scenario will repeat itself:

Association to AP A (Ant A reports -60).
Scan on Ant A: AP A: -60, AP B: -80
Scan on Ant B: AP A: -80, AP A: -60 ==> ROAM.

Association to AP B (Ant B reports -60)
Scan on Ant A: AP A: -60, AP B: -80 ==> ROAM

Etc...

Fix this by querying the signal of the current AP using
drv_signal_poll() instead of relying on the signal that
we get from the scan results.

Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
---
 wpa_supplicant/events.c           | 83 +++++++++++++++++++++++++++++++++------
 wpa_supplicant/scan.c             | 57 +++++++++++++--------------
 wpa_supplicant/scan.h             |  4 ++
 wpa_supplicant/wpa_supplicant_i.h |  9 +++++
 4 files changed, 112 insertions(+), 41 deletions(-)

Comments

Andrei Otcheretianski Dec. 9, 2019, 7:42 a.m. UTC | #1
Hi Jouni,

We sent this patch about a year ago and it was unanswered.
As we still get complaints about constant roaming in the described scenario, I'd like to ask to you to take another look if possible.
We'd like to get any feedback, and if you think it may work  - we'll rebase and resubmit.

Thanks,
Andrei

> -----Original Message-----
> From: Hostap <hostap-bounces@lists.infradead.org> On Behalf Of Andrei
> Otcheretianski
> Sent: Wednesday, September 05, 2018 20:45
> To: hostap@lists.infradead.org
> Cc: Grumbach, Emmanuel <emmanuel.grumbach@intel.com>
> Subject: [PATCH 2/2] wpa_supplicant: fix the constant roaming problem
> 
> From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
> 
> We saw that on certain platforms in certain places we keep switching between
> two APs and eventually get the same RSSI.
> Debugging showed that we have a very big difference between the two
> antennas.
> 
> Ant A can hear AP A very well (-60) but AP B very bad (-80) Ant B can hear AP B
> very well (-60) but AP A very bad (-80)
> 
> When the device associates to AP A, it'll learn to use Ant A.
> If the device uses one single antenna to receive the scan results, it may hear the
> AP it is currently associated to on the second antenna and get bad results.
> Because of that, the wpa_supplicant will roam to the other AP and the same
> scenario will repeat itself:
> 
> Association to AP A (Ant A reports -60).
> Scan on Ant A: AP A: -60, AP B: -80
> Scan on Ant B: AP A: -80, AP A: -60 ==> ROAM.
> 
> Association to AP B (Ant B reports -60)
> Scan on Ant A: AP A: -60, AP B: -80 ==> ROAM
> 
> Etc...
> 
> Fix this by querying the signal of the current AP using
> drv_signal_poll() instead of relying on the signal that we get from the scan
> results.
> 
> Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
> ---
>  wpa_supplicant/events.c           | 83 +++++++++++++++++++++++++++++++++----
> --
>  wpa_supplicant/scan.c             | 57 +++++++++++++--------------
>  wpa_supplicant/scan.h             |  4 ++
>  wpa_supplicant/wpa_supplicant_i.h |  9 +++++
>  4 files changed, 112 insertions(+), 41 deletions(-)
> 
> diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index
> 4cdca34..a814041 100644
> --- a/wpa_supplicant/events.c
> +++ b/wpa_supplicant/events.c
> @@ -1617,6 +1617,33 @@ static void
> wpa_supplicant_rsn_preauth_scan_results(
>  }
> 
> 
> +static int wpas_get_snr_signal_info(const struct wpa_signal_info *si) {
> +	int noise = IS_5GHZ(si->frequency) ?
> +		DEFAULT_NOISE_FLOOR_5GHZ :
> +		DEFAULT_NOISE_FLOOR_2GHZ;
> +
> +	/*
> +	 * Since we take the average beacon signal, we can't use
> +	 * the current noise measurement (average vs. snapshot),
> +	 * so use the default values instead.
> +	 */
> +	return si->avg_beacon_signal - noise;
> +}
> +
> +
> +static unsigned int
> +wpas_get_est_throughput_from_bss_snr(const struct wpa_supplicant *wpa_s,
> +				     const struct wpa_bss *bss, int snr) {
> +	int rate = wpa_bss_get_max_rate(bss);
> +	const u8 *ies = (void *)(bss + 1);
> +	size_t ie_len = bss->ie_len ?: bss->beacon_ie_len;
> +
> +	return wpas_get_est_tpt(wpa_s, ies, ie_len, rate, snr); }
> +
> +
>  static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
>  				       struct wpa_bss *selected,
>  				       struct wpa_ssid *ssid)
> @@ -1625,7 +1652,9 @@ static int wpa_supplicant_need_to_roam(struct
> wpa_supplicant *wpa_s,  #ifndef CONFIG_NO_ROAMING
>  	int min_diff, diff;
>  	int to_5ghz;
> -	int cur_est, sel_est;
> +	int cur_level;
> +	unsigned int cur_est, sel_est;
> +	struct wpa_signal_info si;
>  #endif /* CONFIG_NO_ROAMING */
> 
>  	if (wpa_s->reassociate)
> @@ -1676,7 +1705,39 @@ static int wpa_supplicant_need_to_roam(struct
> wpa_supplicant *wpa_s,
>  		return 1;
>  	}
> 
> -	if (selected->est_throughput > current_bss->est_throughput + 5000) {
> +	cur_level = current_bss->level;
> +	cur_est = current_bss->est_throughput;
> +
> +	/*
> +	 * Try to poll the signal from the driver since this will allow to get
> +	 * more accurate values. In some cases, there can be big differences
> +	 * between the RSSI of the probe response of the AP we are associated
> +	 * to and the beacons we hear from the same AP after association. This
> +	 * can happen when there are two antennas that hear the AP very
> +	 * differently. If the driver chooses to hear the probe responses
> +	 * during the scan on the "bad" antenna because it wants to save power,
> +	 * but knows to choose the other antenna after association, we will
> +	 * hear our AP with a low RSSI as part of the scan, even we can hear
> +	 * it decently on the other antenna.
> +	 * To cope with this, ask the driver to teach us how it hears the AP.
> +	 * Also, the scan results may be a bit old, since we can very quickly
> +	 * get fresh information about our own AP, do that.
> +	 */
> +	if (!wpa_drv_signal_poll(wpa_s, &si)) {
> +		if (si.avg_beacon_signal) {
> +			int snr = wpas_get_snr_signal_info(&si);
> +
> +			cur_level = si.avg_beacon_signal;
> +			cur_est =
> wpas_get_est_throughput_from_bss_snr(wpa_s,
> +
> current_bss,
> +								       snr);
> +			wpa_dbg(wpa_s, MSG_DEBUG,
> +				"Using poll to get accurate level and est
> throughput: level=%d snr=%d est_throughput=%u",
> +				cur_level, snr, cur_est);
> +		}
> +	}
> +
> +	if (selected->est_throughput > cur_est + 5000) {
>  		wpa_dbg(wpa_s, MSG_DEBUG,
>  			"Allow reassociation - selected BSS has better
> estimated throughput");
>  		return 1;
> @@ -1684,30 +1745,28 @@ static int wpa_supplicant_need_to_roam(struct
> wpa_supplicant *wpa_s,
> 
>  	to_5ghz = selected->freq > 4000 && current_bss->freq < 4000;
> 
> -	if (current_bss->level < 0 &&
> -	    current_bss->level > selected->level + to_5ghz * 2) {
> +	if (cur_level < 0 && cur_level > selected->level + to_5ghz * 2) {
>  		wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has
> better "
>  			"signal level");
>  		return 0;
>  	}
> 
> -	if (current_bss->est_throughput > selected->est_throughput + 5000) {
> +	if (cur_est > selected->est_throughput + 5000) {
>  		wpa_dbg(wpa_s, MSG_DEBUG,
>  			"Skip roam - Current BSS has better estimated
> throughput");
>  		return 0;
>  	}
> 
> -	cur_est = current_bss->est_throughput;
>  	sel_est = selected->est_throughput;
>  	min_diff = 2;
> -	if (current_bss->level < 0) {
> -		if (current_bss->level < -85)
> +	if (cur_level < 0) {
> +		if (cur_level < -85)
>  			min_diff = 1;
> -		else if (current_bss->level < -80)
> +		else if (cur_level < -80)
>  			min_diff = 2;
> -		else if (current_bss->level < -75)
> +		else if (cur_level < -75)
>  			min_diff = 3;
> -		else if (current_bss->level < -70)
> +		else if (cur_level < -70)
>  			min_diff = 4;
>  		else
>  			min_diff = 5;
> @@ -1736,7 +1795,7 @@ static int wpa_supplicant_need_to_roam(struct
> wpa_supplicant *wpa_s,
>  		else
>  			min_diff = 0;
>  	}
> -	diff = abs(current_bss->level - selected->level);
> +	diff = abs(cur_level - selected->level);
>  	if (diff < min_diff) {
>  		wpa_dbg(wpa_s, MSG_DEBUG,
>  			"Skip roam - too small difference in signal level (%d <
> %d)", diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index
> 56d53fb..030e7ac 100644
> --- a/wpa_supplicant/scan.c
> +++ b/wpa_supplicant/scan.c
> @@ -1869,8 +1869,6 @@ struct wpabuf *
> wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
>   */
>  #define GREAT_SNR 25
> 
> -#define IS_5GHZ(n) (n > 4000)
> -
>  /* Compare function for sorting scan results. Return >0 if @b is considered
>   * better. */
>  static int wpa_scan_result_compar(const void *a, const void *b) @@ -2078,14
> +2076,6 @@ void filter_scan_res(struct wpa_supplicant *wpa_s,  }
> 
> 
> -/*
> - * Noise floor values to use when we have signal strength
> - * measurements, but no noise floor measurements. These values were
> - * measured in an office environment with many APs.
> - */
> -#define DEFAULT_NOISE_FLOOR_2GHZ (-89)
> -#define DEFAULT_NOISE_FLOOR_5GHZ (-92)
> -
>  void scan_snr(struct wpa_scan_res *res)  {
>  	if (res->flags & WPA_SCAN_NOISE_INVALID) { @@ -2169,21 +2159,13
> @@ static unsigned int max_vht80_rate(int snr)
>  	return 390000; /* VHT80 MCS9 */
>  }
> 
> -
> -void scan_est_throughput(struct wpa_supplicant *wpa_s,
> -			 struct wpa_scan_res *res)
> +unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s,
> +			      const u8 *ies, size_t ies_len, int rate,
> +			      int snr)
>  {
>  	enum local_hw_capab capab = wpa_s->hw_capab;
> -	int rate; /* max legacy rate in 500 kb/s units */
> -	const u8 *ie;
>  	unsigned int est, tmp;
> -	int snr = res->snr;
> -
> -	if (res->est_throughput)
> -		return;
> -
> -	/* Get maximum legacy rate */
> -	rate = wpa_scan_get_max_rate(res);
> +	const u8 *ie;
> 
>  	/* Limit based on estimated SNR */
>  	if (rate > 1 * 2 && snr < 1)
> @@ -2209,7 +2191,7 @@ void scan_est_throughput(struct wpa_supplicant
> *wpa_s,
>  	est = rate * 500;
> 
>  	if (capab == CAPAB_HT || capab == CAPAB_HT40 || capab ==
> CAPAB_VHT) {
> -		ie = wpa_scan_get_ie(res, WLAN_EID_HT_CAP);
> +		ie = get_ie(ies, ies_len, WLAN_EID_HT_CAP);
>  		if (ie) {
>  			tmp = max_ht20_rate(snr);
>  			if (tmp > est)
> @@ -2218,7 +2200,7 @@ void scan_est_throughput(struct wpa_supplicant
> *wpa_s,
>  	}
> 
>  	if (capab == CAPAB_HT40 || capab == CAPAB_VHT) {
> -		ie = wpa_scan_get_ie(res, WLAN_EID_HT_OPERATION);
> +		ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
>  		if (ie && ie[1] >= 2 &&
>  		    (ie[3] &
> HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
>  			tmp = max_ht40_rate(snr);
> @@ -2229,13 +2211,13 @@ void scan_est_throughput(struct wpa_supplicant
> *wpa_s,
> 
>  	if (capab == CAPAB_VHT) {
>  		/* Use +1 to assume VHT is always faster than HT */
> -		ie = wpa_scan_get_ie(res, WLAN_EID_VHT_CAP);
> +		ie = get_ie(ies, ies_len, WLAN_EID_VHT_CAP);
>  		if (ie) {
>  			tmp = max_ht20_rate(snr) + 1;
>  			if (tmp > est)
>  				est = tmp;
> 
> -			ie = wpa_scan_get_ie(res,
> WLAN_EID_HT_OPERATION);
> +			ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
>  			if (ie && ie[1] >= 2 &&
>  			    (ie[3] &
> 
> HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) { @@ -2244,7 +2226,7
> @@ void scan_est_throughput(struct wpa_supplicant *wpa_s,
>  					est = tmp;
>  			}
> 
> -			ie = wpa_scan_get_ie(res,
> WLAN_EID_VHT_OPERATION);
> +			ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION);
>  			if (ie && ie[1] >= 1 &&
>  			    (ie[2] & VHT_OPMODE_CHANNEL_WIDTH_MASK)) {
>  				tmp = max_vht80_rate(snr) + 1;
> @@ -2254,9 +2236,26 @@ void scan_est_throughput(struct wpa_supplicant
> *wpa_s,
>  		}
>  	}
> 
> -	/* TODO: channel utilization and AP load (e.g., from AP Beacon) */
> +	return est;
> +}
> +
> +void scan_est_throughput(struct wpa_supplicant *wpa_s,
> +			 struct wpa_scan_res *res)
> +{
> +	int rate; /* max legacy rate in 500 kb/s units */
> +	int snr = res->snr;
> +	const u8 *ies = (void *)(res + 1);
> 
> -	res->est_throughput = est;
> +	if (res->est_throughput)
> +		return;
> +
> +	/* Get maximum legacy rate */
> +	rate = wpa_scan_get_max_rate(res);
> +
> +	res->est_throughput =
> +		wpas_get_est_tpt(wpa_s, ies, res->ie_len, rate, snr);
> +
> +	/* TODO: channel utilization and AP load (e.g., from AP Beacon) */
>  }
> 
> 
> diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h index
> 2aa0a8b..204332a 100644
> --- a/wpa_supplicant/scan.h
> +++ b/wpa_supplicant/scan.h
> @@ -58,6 +58,10 @@ void filter_scan_res(struct wpa_supplicant *wpa_s,  void
> scan_snr(struct wpa_scan_res *res);  void scan_est_throughput(struct
> wpa_supplicant *wpa_s,
>  			 struct wpa_scan_res *res);
> +
> +unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s,
> +			      const u8 *ies, size_t ies_len, int rate,
> +			      int snr);
>  void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s);
> 
>  #endif /* SCAN_H */
> diff --git a/wpa_supplicant/wpa_supplicant_i.h
> b/wpa_supplicant/wpa_supplicant_i.h
> index 9b8d1fa..9255b2c 100644
> --- a/wpa_supplicant/wpa_supplicant_i.h
> +++ b/wpa_supplicant/wpa_supplicant_i.h
> @@ -1487,4 +1487,13 @@ int
> wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,  int
> wpa_is_fils_supported(struct wpa_supplicant *wpa_s);  int
> wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s);
> 
> +/*
> + * Noise floor values to use when we have signal strength
> + * measurements, but no noise floor measurements. These values were
> + * measured in an office environment with many APs.
> + */
> +#define DEFAULT_NOISE_FLOOR_2GHZ (-89)
> +#define DEFAULT_NOISE_FLOOR_5GHZ (-92)
> +#define IS_5GHZ(n) (n > 4000)
> +
>  #endif /* WPA_SUPPLICANT_I_H */
> --
> 2.7.4
> 
> 
> _______________________________________________
> Hostap mailing list
> Hostap@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/hostap
Pavel Nikulin Dec. 9, 2019, 4:19 p.m. UTC | #2
I do certainly remember the patch, I once used it and long though that
it was upstreamed.

While looking through it, please take a look on
https://www.spinics.net/lists/hostap/msg05792.html

Roaming behaviour in a dense office environment is borderline unusable
unless you sit right underneath an AP with Intel AX200 NIC. It seems
that background scan settings have no effect on roaming since a few
major versions ago.

For me, even a person passing by a corridor 10 metres away can cause a
reassociation. Adding a simple hysteresis mechanism should at least
solve that.


On Mon, Dec 9, 2019 at 1:42 PM Otcheretianski, Andrei
<andrei.otcheretianski@intel.com> wrote:
>
> Hi Jouni,
>
> We sent this patch about a year ago and it was unanswered.
> As we still get complaints about constant roaming in the described scenario, I'd like to ask to you to take another look if possible.
> We'd like to get any feedback, and if you think it may work  - we'll rebase and resubmit.
>
> Thanks,
> Andrei
>
> > -----Original Message-----
> > From: Hostap <hostap-bounces@lists.infradead.org> On Behalf Of Andrei
> > Otcheretianski
> > Sent: Wednesday, September 05, 2018 20:45
> > To: hostap@lists.infradead.org
> > Cc: Grumbach, Emmanuel <emmanuel.grumbach@intel.com>
> > Subject: [PATCH 2/2] wpa_supplicant: fix the constant roaming problem
> >
> > From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
> >
> > We saw that on certain platforms in certain places we keep switching between
> > two APs and eventually get the same RSSI.
> > Debugging showed that we have a very big difference between the two
> > antennas.
> >
> > Ant A can hear AP A very well (-60) but AP B very bad (-80) Ant B can hear AP B
> > very well (-60) but AP A very bad (-80)
> >
> > When the device associates to AP A, it'll learn to use Ant A.
> > If the device uses one single antenna to receive the scan results, it may hear the
> > AP it is currently associated to on the second antenna and get bad results.
> > Because of that, the wpa_supplicant will roam to the other AP and the same
> > scenario will repeat itself:
> >
> > Association to AP A (Ant A reports -60).
> > Scan on Ant A: AP A: -60, AP B: -80
> > Scan on Ant B: AP A: -80, AP A: -60 ==> ROAM.
> >
> > Association to AP B (Ant B reports -60)
> > Scan on Ant A: AP A: -60, AP B: -80 ==> ROAM
> >
> > Etc...
> >
> > Fix this by querying the signal of the current AP using
> > drv_signal_poll() instead of relying on the signal that we get from the scan
> > results.
> >
> > Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
> > ---
> >  wpa_supplicant/events.c           | 83 +++++++++++++++++++++++++++++++++----
> > --
> >  wpa_supplicant/scan.c             | 57 +++++++++++++--------------
> >  wpa_supplicant/scan.h             |  4 ++
> >  wpa_supplicant/wpa_supplicant_i.h |  9 +++++
> >  4 files changed, 112 insertions(+), 41 deletions(-)
> >
> > diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index
> > 4cdca34..a814041 100644
> > --- a/wpa_supplicant/events.c
> > +++ b/wpa_supplicant/events.c
> > @@ -1617,6 +1617,33 @@ static void
> > wpa_supplicant_rsn_preauth_scan_results(
> >  }
> >
> >
> > +static int wpas_get_snr_signal_info(const struct wpa_signal_info *si) {
> > +     int noise = IS_5GHZ(si->frequency) ?
> > +             DEFAULT_NOISE_FLOOR_5GHZ :
> > +             DEFAULT_NOISE_FLOOR_2GHZ;
> > +
> > +     /*
> > +      * Since we take the average beacon signal, we can't use
> > +      * the current noise measurement (average vs. snapshot),
> > +      * so use the default values instead.
> > +      */
> > +     return si->avg_beacon_signal - noise;
> > +}
> > +
> > +
> > +static unsigned int
> > +wpas_get_est_throughput_from_bss_snr(const struct wpa_supplicant *wpa_s,
> > +                                  const struct wpa_bss *bss, int snr) {
> > +     int rate = wpa_bss_get_max_rate(bss);
> > +     const u8 *ies = (void *)(bss + 1);
> > +     size_t ie_len = bss->ie_len ?: bss->beacon_ie_len;
> > +
> > +     return wpas_get_est_tpt(wpa_s, ies, ie_len, rate, snr); }
> > +
> > +
> >  static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
> >                                      struct wpa_bss *selected,
> >                                      struct wpa_ssid *ssid)
> > @@ -1625,7 +1652,9 @@ static int wpa_supplicant_need_to_roam(struct
> > wpa_supplicant *wpa_s,  #ifndef CONFIG_NO_ROAMING
> >       int min_diff, diff;
> >       int to_5ghz;
> > -     int cur_est, sel_est;
> > +     int cur_level;
> > +     unsigned int cur_est, sel_est;
> > +     struct wpa_signal_info si;
> >  #endif /* CONFIG_NO_ROAMING */
> >
> >       if (wpa_s->reassociate)
> > @@ -1676,7 +1705,39 @@ static int wpa_supplicant_need_to_roam(struct
> > wpa_supplicant *wpa_s,
> >               return 1;
> >       }
> >
> > -     if (selected->est_throughput > current_bss->est_throughput + 5000) {
> > +     cur_level = current_bss->level;
> > +     cur_est = current_bss->est_throughput;
> > +
> > +     /*
> > +      * Try to poll the signal from the driver since this will allow to get
> > +      * more accurate values. In some cases, there can be big differences
> > +      * between the RSSI of the probe response of the AP we are associated
> > +      * to and the beacons we hear from the same AP after association. This
> > +      * can happen when there are two antennas that hear the AP very
> > +      * differently. If the driver chooses to hear the probe responses
> > +      * during the scan on the "bad" antenna because it wants to save power,
> > +      * but knows to choose the other antenna after association, we will
> > +      * hear our AP with a low RSSI as part of the scan, even we can hear
> > +      * it decently on the other antenna.
> > +      * To cope with this, ask the driver to teach us how it hears the AP.
> > +      * Also, the scan results may be a bit old, since we can very quickly
> > +      * get fresh information about our own AP, do that.
> > +      */
> > +     if (!wpa_drv_signal_poll(wpa_s, &si)) {
> > +             if (si.avg_beacon_signal) {
> > +                     int snr = wpas_get_snr_signal_info(&si);
> > +
> > +                     cur_level = si.avg_beacon_signal;
> > +                     cur_est =
> > wpas_get_est_throughput_from_bss_snr(wpa_s,
> > +
> > current_bss,
> > +                                                                    snr);
> > +                     wpa_dbg(wpa_s, MSG_DEBUG,
> > +                             "Using poll to get accurate level and est
> > throughput: level=%d snr=%d est_throughput=%u",
> > +                             cur_level, snr, cur_est);
> > +             }
> > +     }
> > +
> > +     if (selected->est_throughput > cur_est + 5000) {
> >               wpa_dbg(wpa_s, MSG_DEBUG,
> >                       "Allow reassociation - selected BSS has better
> > estimated throughput");
> >               return 1;
> > @@ -1684,30 +1745,28 @@ static int wpa_supplicant_need_to_roam(struct
> > wpa_supplicant *wpa_s,
> >
> >       to_5ghz = selected->freq > 4000 && current_bss->freq < 4000;
> >
> > -     if (current_bss->level < 0 &&
> > -         current_bss->level > selected->level + to_5ghz * 2) {
> > +     if (cur_level < 0 && cur_level > selected->level + to_5ghz * 2) {
> >               wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has
> > better "
> >                       "signal level");
> >               return 0;
> >       }
> >
> > -     if (current_bss->est_throughput > selected->est_throughput + 5000) {
> > +     if (cur_est > selected->est_throughput + 5000) {
> >               wpa_dbg(wpa_s, MSG_DEBUG,
> >                       "Skip roam - Current BSS has better estimated
> > throughput");
> >               return 0;
> >       }
> >
> > -     cur_est = current_bss->est_throughput;
> >       sel_est = selected->est_throughput;
> >       min_diff = 2;
> > -     if (current_bss->level < 0) {
> > -             if (current_bss->level < -85)
> > +     if (cur_level < 0) {
> > +             if (cur_level < -85)
> >                       min_diff = 1;
> > -             else if (current_bss->level < -80)
> > +             else if (cur_level < -80)
> >                       min_diff = 2;
> > -             else if (current_bss->level < -75)
> > +             else if (cur_level < -75)
> >                       min_diff = 3;
> > -             else if (current_bss->level < -70)
> > +             else if (cur_level < -70)
> >                       min_diff = 4;
> >               else
> >                       min_diff = 5;
> > @@ -1736,7 +1795,7 @@ static int wpa_supplicant_need_to_roam(struct
> > wpa_supplicant *wpa_s,
> >               else
> >                       min_diff = 0;
> >       }
> > -     diff = abs(current_bss->level - selected->level);
> > +     diff = abs(cur_level - selected->level);
> >       if (diff < min_diff) {
> >               wpa_dbg(wpa_s, MSG_DEBUG,
> >                       "Skip roam - too small difference in signal level (%d <
> > %d)", diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index
> > 56d53fb..030e7ac 100644
> > --- a/wpa_supplicant/scan.c
> > +++ b/wpa_supplicant/scan.c
> > @@ -1869,8 +1869,6 @@ struct wpabuf *
> > wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
> >   */
> >  #define GREAT_SNR 25
> >
> > -#define IS_5GHZ(n) (n > 4000)
> > -
> >  /* Compare function for sorting scan results. Return >0 if @b is considered
> >   * better. */
> >  static int wpa_scan_result_compar(const void *a, const void *b) @@ -2078,14
> > +2076,6 @@ void filter_scan_res(struct wpa_supplicant *wpa_s,  }
> >
> >
> > -/*
> > - * Noise floor values to use when we have signal strength
> > - * measurements, but no noise floor measurements. These values were
> > - * measured in an office environment with many APs.
> > - */
> > -#define DEFAULT_NOISE_FLOOR_2GHZ (-89)
> > -#define DEFAULT_NOISE_FLOOR_5GHZ (-92)
> > -
> >  void scan_snr(struct wpa_scan_res *res)  {
> >       if (res->flags & WPA_SCAN_NOISE_INVALID) { @@ -2169,21 +2159,13
> > @@ static unsigned int max_vht80_rate(int snr)
> >       return 390000; /* VHT80 MCS9 */
> >  }
> >
> > -
> > -void scan_est_throughput(struct wpa_supplicant *wpa_s,
> > -                      struct wpa_scan_res *res)
> > +unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s,
> > +                           const u8 *ies, size_t ies_len, int rate,
> > +                           int snr)
> >  {
> >       enum local_hw_capab capab = wpa_s->hw_capab;
> > -     int rate; /* max legacy rate in 500 kb/s units */
> > -     const u8 *ie;
> >       unsigned int est, tmp;
> > -     int snr = res->snr;
> > -
> > -     if (res->est_throughput)
> > -             return;
> > -
> > -     /* Get maximum legacy rate */
> > -     rate = wpa_scan_get_max_rate(res);
> > +     const u8 *ie;
> >
> >       /* Limit based on estimated SNR */
> >       if (rate > 1 * 2 && snr < 1)
> > @@ -2209,7 +2191,7 @@ void scan_est_throughput(struct wpa_supplicant
> > *wpa_s,
> >       est = rate * 500;
> >
> >       if (capab == CAPAB_HT || capab == CAPAB_HT40 || capab ==
> > CAPAB_VHT) {
> > -             ie = wpa_scan_get_ie(res, WLAN_EID_HT_CAP);
> > +             ie = get_ie(ies, ies_len, WLAN_EID_HT_CAP);
> >               if (ie) {
> >                       tmp = max_ht20_rate(snr);
> >                       if (tmp > est)
> > @@ -2218,7 +2200,7 @@ void scan_est_throughput(struct wpa_supplicant
> > *wpa_s,
> >       }
> >
> >       if (capab == CAPAB_HT40 || capab == CAPAB_VHT) {
> > -             ie = wpa_scan_get_ie(res, WLAN_EID_HT_OPERATION);
> > +             ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
> >               if (ie && ie[1] >= 2 &&
> >                   (ie[3] &
> > HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
> >                       tmp = max_ht40_rate(snr);
> > @@ -2229,13 +2211,13 @@ void scan_est_throughput(struct wpa_supplicant
> > *wpa_s,
> >
> >       if (capab == CAPAB_VHT) {
> >               /* Use +1 to assume VHT is always faster than HT */
> > -             ie = wpa_scan_get_ie(res, WLAN_EID_VHT_CAP);
> > +             ie = get_ie(ies, ies_len, WLAN_EID_VHT_CAP);
> >               if (ie) {
> >                       tmp = max_ht20_rate(snr) + 1;
> >                       if (tmp > est)
> >                               est = tmp;
> >
> > -                     ie = wpa_scan_get_ie(res,
> > WLAN_EID_HT_OPERATION);
> > +                     ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
> >                       if (ie && ie[1] >= 2 &&
> >                           (ie[3] &
> >
> > HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) { @@ -2244,7 +2226,7
> > @@ void scan_est_throughput(struct wpa_supplicant *wpa_s,
> >                                       est = tmp;
> >                       }
> >
> > -                     ie = wpa_scan_get_ie(res,
> > WLAN_EID_VHT_OPERATION);
> > +                     ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION);
> >                       if (ie && ie[1] >= 1 &&
> >                           (ie[2] & VHT_OPMODE_CHANNEL_WIDTH_MASK)) {
> >                               tmp = max_vht80_rate(snr) + 1;
> > @@ -2254,9 +2236,26 @@ void scan_est_throughput(struct wpa_supplicant
> > *wpa_s,
> >               }
> >       }
> >
> > -     /* TODO: channel utilization and AP load (e.g., from AP Beacon) */
> > +     return est;
> > +}
> > +
> > +void scan_est_throughput(struct wpa_supplicant *wpa_s,
> > +                      struct wpa_scan_res *res)
> > +{
> > +     int rate; /* max legacy rate in 500 kb/s units */
> > +     int snr = res->snr;
> > +     const u8 *ies = (void *)(res + 1);
> >
> > -     res->est_throughput = est;
> > +     if (res->est_throughput)
> > +             return;
> > +
> > +     /* Get maximum legacy rate */
> > +     rate = wpa_scan_get_max_rate(res);
> > +
> > +     res->est_throughput =
> > +             wpas_get_est_tpt(wpa_s, ies, res->ie_len, rate, snr);
> > +
> > +     /* TODO: channel utilization and AP load (e.g., from AP Beacon) */
> >  }
> >
> >
> > diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h index
> > 2aa0a8b..204332a 100644
> > --- a/wpa_supplicant/scan.h
> > +++ b/wpa_supplicant/scan.h
> > @@ -58,6 +58,10 @@ void filter_scan_res(struct wpa_supplicant *wpa_s,  void
> > scan_snr(struct wpa_scan_res *res);  void scan_est_throughput(struct
> > wpa_supplicant *wpa_s,
> >                        struct wpa_scan_res *res);
> > +
> > +unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s,
> > +                           const u8 *ies, size_t ies_len, int rate,
> > +                           int snr);
> >  void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s);
> >
> >  #endif /* SCAN_H */
> > diff --git a/wpa_supplicant/wpa_supplicant_i.h
> > b/wpa_supplicant/wpa_supplicant_i.h
> > index 9b8d1fa..9255b2c 100644
> > --- a/wpa_supplicant/wpa_supplicant_i.h
> > +++ b/wpa_supplicant/wpa_supplicant_i.h
> > @@ -1487,4 +1487,13 @@ int
> > wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,  int
> > wpa_is_fils_supported(struct wpa_supplicant *wpa_s);  int
> > wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s);
> >
> > +/*
> > + * Noise floor values to use when we have signal strength
> > + * measurements, but no noise floor measurements. These values were
> > + * measured in an office environment with many APs.
> > + */
> > +#define DEFAULT_NOISE_FLOOR_2GHZ (-89)
> > +#define DEFAULT_NOISE_FLOOR_5GHZ (-92)
> > +#define IS_5GHZ(n) (n > 4000)
> > +
> >  #endif /* WPA_SUPPLICANT_I_H */
> > --
> > 2.7.4
> >
> >
> > _______________________________________________
> > Hostap mailing list
> > Hostap@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/hostap
>
> _______________________________________________
> Hostap mailing list
> Hostap@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/hostap
diff mbox series

Patch

diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 4cdca34..a814041 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1617,6 +1617,33 @@  static void wpa_supplicant_rsn_preauth_scan_results(
 }
 
 
+static int wpas_get_snr_signal_info(const struct wpa_signal_info *si)
+{
+	int noise = IS_5GHZ(si->frequency) ?
+		DEFAULT_NOISE_FLOOR_5GHZ :
+		DEFAULT_NOISE_FLOOR_2GHZ;
+
+	/*
+	 * Since we take the average beacon signal, we can't use
+	 * the current noise measurement (average vs. snapshot),
+	 * so use the default values instead.
+	 */
+	return si->avg_beacon_signal - noise;
+}
+
+
+static unsigned int
+wpas_get_est_throughput_from_bss_snr(const struct wpa_supplicant *wpa_s,
+				     const struct wpa_bss *bss, int snr)
+{
+	int rate = wpa_bss_get_max_rate(bss);
+	const u8 *ies = (void *)(bss + 1);
+	size_t ie_len = bss->ie_len ?: bss->beacon_ie_len;
+
+	return wpas_get_est_tpt(wpa_s, ies, ie_len, rate, snr);
+}
+
+
 static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
 				       struct wpa_bss *selected,
 				       struct wpa_ssid *ssid)
@@ -1625,7 +1652,9 @@  static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
 #ifndef CONFIG_NO_ROAMING
 	int min_diff, diff;
 	int to_5ghz;
-	int cur_est, sel_est;
+	int cur_level;
+	unsigned int cur_est, sel_est;
+	struct wpa_signal_info si;
 #endif /* CONFIG_NO_ROAMING */
 
 	if (wpa_s->reassociate)
@@ -1676,7 +1705,39 @@  static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
 		return 1;
 	}
 
-	if (selected->est_throughput > current_bss->est_throughput + 5000) {
+	cur_level = current_bss->level;
+	cur_est = current_bss->est_throughput;
+
+	/*
+	 * Try to poll the signal from the driver since this will allow to get
+	 * more accurate values. In some cases, there can be big differences
+	 * between the RSSI of the probe response of the AP we are associated
+	 * to and the beacons we hear from the same AP after association. This
+	 * can happen when there are two antennas that hear the AP very
+	 * differently. If the driver chooses to hear the probe responses
+	 * during the scan on the "bad" antenna because it wants to save power,
+	 * but knows to choose the other antenna after association, we will
+	 * hear our AP with a low RSSI as part of the scan, even we can hear
+	 * it decently on the other antenna.
+	 * To cope with this, ask the driver to teach us how it hears the AP.
+	 * Also, the scan results may be a bit old, since we can very quickly
+	 * get fresh information about our own AP, do that.
+	 */
+	if (!wpa_drv_signal_poll(wpa_s, &si)) {
+		if (si.avg_beacon_signal) {
+			int snr = wpas_get_snr_signal_info(&si);
+
+			cur_level = si.avg_beacon_signal;
+			cur_est = wpas_get_est_throughput_from_bss_snr(wpa_s,
+								       current_bss,
+								       snr);
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"Using poll to get accurate level and est throughput: level=%d snr=%d est_throughput=%u",
+				cur_level, snr, cur_est);
+		}
+	}
+
+	if (selected->est_throughput > cur_est + 5000) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"Allow reassociation - selected BSS has better estimated throughput");
 		return 1;
@@ -1684,30 +1745,28 @@  static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
 
 	to_5ghz = selected->freq > 4000 && current_bss->freq < 4000;
 
-	if (current_bss->level < 0 &&
-	    current_bss->level > selected->level + to_5ghz * 2) {
+	if (cur_level < 0 && cur_level > selected->level + to_5ghz * 2) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better "
 			"signal level");
 		return 0;
 	}
 
-	if (current_bss->est_throughput > selected->est_throughput + 5000) {
+	if (cur_est > selected->est_throughput + 5000) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"Skip roam - Current BSS has better estimated throughput");
 		return 0;
 	}
 
-	cur_est = current_bss->est_throughput;
 	sel_est = selected->est_throughput;
 	min_diff = 2;
-	if (current_bss->level < 0) {
-		if (current_bss->level < -85)
+	if (cur_level < 0) {
+		if (cur_level < -85)
 			min_diff = 1;
-		else if (current_bss->level < -80)
+		else if (cur_level < -80)
 			min_diff = 2;
-		else if (current_bss->level < -75)
+		else if (cur_level < -75)
 			min_diff = 3;
-		else if (current_bss->level < -70)
+		else if (cur_level < -70)
 			min_diff = 4;
 		else
 			min_diff = 5;
@@ -1736,7 +1795,7 @@  static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
 		else
 			min_diff = 0;
 	}
-	diff = abs(current_bss->level - selected->level);
+	diff = abs(cur_level - selected->level);
 	if (diff < min_diff) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"Skip roam - too small difference in signal level (%d < %d)",
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 56d53fb..030e7ac 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -1869,8 +1869,6 @@  struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
  */
 #define GREAT_SNR 25
 
-#define IS_5GHZ(n) (n > 4000)
-
 /* Compare function for sorting scan results. Return >0 if @b is considered
  * better. */
 static int wpa_scan_result_compar(const void *a, const void *b)
@@ -2078,14 +2076,6 @@  void filter_scan_res(struct wpa_supplicant *wpa_s,
 }
 
 
-/*
- * Noise floor values to use when we have signal strength
- * measurements, but no noise floor measurements. These values were
- * measured in an office environment with many APs.
- */
-#define DEFAULT_NOISE_FLOOR_2GHZ (-89)
-#define DEFAULT_NOISE_FLOOR_5GHZ (-92)
-
 void scan_snr(struct wpa_scan_res *res)
 {
 	if (res->flags & WPA_SCAN_NOISE_INVALID) {
@@ -2169,21 +2159,13 @@  static unsigned int max_vht80_rate(int snr)
 	return 390000; /* VHT80 MCS9 */
 }
 
-
-void scan_est_throughput(struct wpa_supplicant *wpa_s,
-			 struct wpa_scan_res *res)
+unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s,
+			      const u8 *ies, size_t ies_len, int rate,
+			      int snr)
 {
 	enum local_hw_capab capab = wpa_s->hw_capab;
-	int rate; /* max legacy rate in 500 kb/s units */
-	const u8 *ie;
 	unsigned int est, tmp;
-	int snr = res->snr;
-
-	if (res->est_throughput)
-		return;
-
-	/* Get maximum legacy rate */
-	rate = wpa_scan_get_max_rate(res);
+	const u8 *ie;
 
 	/* Limit based on estimated SNR */
 	if (rate > 1 * 2 && snr < 1)
@@ -2209,7 +2191,7 @@  void scan_est_throughput(struct wpa_supplicant *wpa_s,
 	est = rate * 500;
 
 	if (capab == CAPAB_HT || capab == CAPAB_HT40 || capab == CAPAB_VHT) {
-		ie = wpa_scan_get_ie(res, WLAN_EID_HT_CAP);
+		ie = get_ie(ies, ies_len, WLAN_EID_HT_CAP);
 		if (ie) {
 			tmp = max_ht20_rate(snr);
 			if (tmp > est)
@@ -2218,7 +2200,7 @@  void scan_est_throughput(struct wpa_supplicant *wpa_s,
 	}
 
 	if (capab == CAPAB_HT40 || capab == CAPAB_VHT) {
-		ie = wpa_scan_get_ie(res, WLAN_EID_HT_OPERATION);
+		ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
 		if (ie && ie[1] >= 2 &&
 		    (ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
 			tmp = max_ht40_rate(snr);
@@ -2229,13 +2211,13 @@  void scan_est_throughput(struct wpa_supplicant *wpa_s,
 
 	if (capab == CAPAB_VHT) {
 		/* Use +1 to assume VHT is always faster than HT */
-		ie = wpa_scan_get_ie(res, WLAN_EID_VHT_CAP);
+		ie = get_ie(ies, ies_len, WLAN_EID_VHT_CAP);
 		if (ie) {
 			tmp = max_ht20_rate(snr) + 1;
 			if (tmp > est)
 				est = tmp;
 
-			ie = wpa_scan_get_ie(res, WLAN_EID_HT_OPERATION);
+			ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
 			if (ie && ie[1] >= 2 &&
 			    (ie[3] &
 			     HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
@@ -2244,7 +2226,7 @@  void scan_est_throughput(struct wpa_supplicant *wpa_s,
 					est = tmp;
 			}
 
-			ie = wpa_scan_get_ie(res, WLAN_EID_VHT_OPERATION);
+			ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION);
 			if (ie && ie[1] >= 1 &&
 			    (ie[2] & VHT_OPMODE_CHANNEL_WIDTH_MASK)) {
 				tmp = max_vht80_rate(snr) + 1;
@@ -2254,9 +2236,26 @@  void scan_est_throughput(struct wpa_supplicant *wpa_s,
 		}
 	}
 
-	/* TODO: channel utilization and AP load (e.g., from AP Beacon) */
+	return est;
+}
+
+void scan_est_throughput(struct wpa_supplicant *wpa_s,
+			 struct wpa_scan_res *res)
+{
+	int rate; /* max legacy rate in 500 kb/s units */
+	int snr = res->snr;
+	const u8 *ies = (void *)(res + 1);
 
-	res->est_throughput = est;
+	if (res->est_throughput)
+		return;
+
+	/* Get maximum legacy rate */
+	rate = wpa_scan_get_max_rate(res);
+
+	res->est_throughput =
+		wpas_get_est_tpt(wpa_s, ies, res->ie_len, rate, snr);
+
+	/* TODO: channel utilization and AP load (e.g., from AP Beacon) */
 }
 
 
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index 2aa0a8b..204332a 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -58,6 +58,10 @@  void filter_scan_res(struct wpa_supplicant *wpa_s,
 void scan_snr(struct wpa_scan_res *res);
 void scan_est_throughput(struct wpa_supplicant *wpa_s,
 			 struct wpa_scan_res *res);
+
+unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s,
+			      const u8 *ies, size_t ies_len, int rate,
+			      int snr);
 void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s);
 
 #endif /* SCAN_H */
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 9b8d1fa..9255b2c 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1487,4 +1487,13 @@  int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
 int wpa_is_fils_supported(struct wpa_supplicant *wpa_s);
 int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s);
 
+/*
+ * Noise floor values to use when we have signal strength
+ * measurements, but no noise floor measurements. These values were
+ * measured in an office environment with many APs.
+ */
+#define DEFAULT_NOISE_FLOOR_2GHZ (-89)
+#define DEFAULT_NOISE_FLOOR_5GHZ (-92)
+#define IS_5GHZ(n) (n > 4000)
+
 #endif /* WPA_SUPPLICANT_I_H */