diff mbox

[RFC] nl80211/mac80211: Rounded RSSI reporting

Message ID 1477010947-6207-1-git-send-email-andrew.zaborowski@intel.com (mailing list archive)
State RFC
Delegated to: Johannes Berg
Headers show

Commit Message

Andrew Zaborowski Oct. 21, 2016, 12:49 a.m. UTC
Hi,

I'm looking for a way to enable userspace to receive RSSI updates
without polling and waking up only as often as needed, for use in a
wifi daemon oriented for low power.  Userspace tends to display a wifi
icon with the rssi shown as a number of "signal bars", usually 4, 5,
or 6.  This patch adds an nl80211 CQM command and an event to report
exactly this information and nothing more.  The
NL80211_ATTR_CQM_ROUNDED_RSSI_STEPS attribute is used to set how many
bars are used and assumes -100dbm to be the minimum.  It's not very
flexible.  It may be better, for example to accept a list of thresholds
and a hysteresis value to be used in the same way as with the
NL80211_ATTR_CQM_RSSI_THOLD attribute, or even modify it to allow
multiple thresholds to be given but that would need a problematic
driver api change.  What would be the best way to do that?

The assumption is that you can't simulate the same behavior with just
the current NL80211_ATTR_CQM_RSSI_THOLD attribute.

Best regards
---
 include/net/cfg80211.h       | 21 ++++++++++++++++++
 include/net/mac80211.h       |  4 ++++
 include/uapi/linux/nl80211.h | 10 +++++++++
 net/mac80211/cfg.c           | 18 +++++++++++++++
 net/mac80211/ieee80211_i.h   | 11 ++++++++--
 net/mac80211/mlme.c          | 27 +++++++++++++++++++++++
 net/wireless/nl80211.c       | 52 ++++++++++++++++++++++++++++++++++++++++++++
 net/wireless/rdev-ops.h      | 11 ++++++++++
 net/wireless/trace.h         | 33 ++++++++++++++++++++++++++++
 9 files changed, 185 insertions(+), 2 deletions(-)

Comments

Johannes Berg Oct. 21, 2016, 8:30 a.m. UTC | #1
Hi,

> Userspace tends to display a wifi icon with the rssi shown as a
> number of "signal bars", usually 4, 5, or 6.

It's actually not clear to me that this is really how it should be.
There's a point to be made that taking a more holistic "link quality"
would be a better choice. That's related, but maybe can be a separate
discussion.

> and a hysteresis value to be used in the same way as with the
> NL80211_ATTR_CQM_RSSI_THOLD attribute, or even modify it to allow
> multiple thresholds to be given but that would need a problematic
> driver api change.  What would be the best way to do that?

So ... I don't think there's a good way to do this at all.

The problem is that you really want this to be offloaded to the device,
*especially* if you care about low power usage, because you absolutely
don't want to be processing each beacon (which is typically what we
derive the data from today) in the host CPU - you want those to be
filtered by the device so you don't wake up the host CPU every ~102ms.

Without offloading to the device, your patch is actually fairly much
pointless, because you've already woken up the host CPU, at which point
punting to userspace probably isn't all that much more expensive over
the cost of waking up the CPU in the first place.

Looking at the code, it seems like only a single driver could support
this in a pseudo-offloaded fashion (iwlmvm), where all the others that
offload it today would not be able to support this API at all, unless
they fall back to complete software processing which, as I wrote above,
will have far worse power consumption consequences.


Therefore, adding this API just for mac80211 seems pretty pointless,
especially since you're targeting this for IoT, where I expect people
will be using full-MAC chips rather than soft-MAC, with the consequence
of not even using mac80211-based drivers. Oops.

> The assumption is that you can't simulate the same behavior with just
> the current NL80211_ATTR_CQM_RSSI_THOLD attribute.

Is that true? Technically, it seems that perhaps if you wanted to have
a few ranges, you could always pick one that's currently active, and
program that into the driver/device, with a hysteresis big enough to
extend to the edges of the next range, or something?

Let's say you're currently sitting at -50dBm, and want your ranges to
be -100..-80..-60..-40..-20. Then you could program -50dBm with
hysteresis of 10dBm, and it'd tell you when you move below/above?

This probably won't really work, the hysteresis probably won't be
honoured precisely enough by all drivers. Or we can just fix the
drivers, I guess. However, if you're just at the edge of your range,
e.g. when your current signal strength is -60dBm, you'd still flip-flop 
in and out of that range continuously, although userspace could just
program a different threshold/hysteresis for those cases as well.

Anyway, not sure this can be made to work, and drivers would play ball.

Nevertheless, somewhere along these lines we could probably make things
work better than hard-coding the kind of assumptions you have in your
patch. Perhaps by extending this to know about low/medium/high, which a
number of drivers seem to support, so that you can program -60,-40
(with some appropriate hysteresis) and then get a signal when you move
out of that range towards low or towards high. When that's stable
enough, you can then dynamically reconfigure to the next range.

johannes
Denis Kenzior Oct. 21, 2016, 2:20 p.m. UTC | #2
Hi Johannes,

On 10/21/2016 03:30 AM, Johannes Berg wrote:
> Hi,
>
>> Userspace tends to display a wifi icon with the rssi shown as a
>> number of "signal bars", usually 4, 5, or 6.
>
> It's actually not clear to me that this is really how it should be.
> There's a point to be made that taking a more holistic "link quality"
> would be a better choice. That's related, but maybe can be a separate
> discussion.
>

Can you elaborate on this 'link quality' idea?

>> and a hysteresis value to be used in the same way as with the
>> NL80211_ATTR_CQM_RSSI_THOLD attribute, or even modify it to allow
>> multiple thresholds to be given but that would need a problematic
>> driver api change.  What would be the best way to do that?
>
> So ... I don't think there's a good way to do this at all.
>
> The problem is that you really want this to be offloaded to the device,
> *especially* if you care about low power usage, because you absolutely
> don't want to be processing each beacon (which is typically what we
> derive the data from today) in the host CPU - you want those to be
> filtered by the device so you don't wake up the host CPU every ~102ms.

Yes, this would be ideal.

>
> Without offloading to the device, your patch is actually fairly much
> pointless, because you've already woken up the host CPU, at which point
> punting to userspace probably isn't all that much more expensive over
> the cost of waking up the CPU in the first place.

Requiring user space application to process every beacon seems like a 
cop-out to me.  Kernel should provide a proper API for this.  That way 
if the hardware / drivers add offloading support for this in the future, 
userspace can be blissfully ignorant.

Not to mention that the cost of waking up the process, sending a netlink 
message, having userspace process the beacon, etc is not insignificant.

>
> Looking at the code, it seems like only a single driver could support
> this in a pseudo-offloaded fashion (iwlmvm), where all the others that
> offload it today would not be able to support this API at all, unless
> they fall back to complete software processing which, as I wrote above,
> will have far worse power consumption consequences.
>
>
> Therefore, adding this API just for mac80211 seems pretty pointless,
> especially since you're targeting this for IoT, where I expect people
> will be using full-MAC chips rather than soft-MAC, with the consequence
> of not even using mac80211-based drivers. Oops.
>

The scope is not limited to IoT devices.  This has to work everywhere. 
So we need a proper solution to report signal strength / signal quality 
without resorting to polling or having userspace process every packet on 
the WiFi device.  Suggestions?

>> The assumption is that you can't simulate the same behavior with just
>> the current NL80211_ATTR_CQM_RSSI_THOLD attribute.
>
> Is that true? Technically, it seems that perhaps if you wanted to have
> a few ranges, you could always pick one that's currently active, and
> program that into the driver/device, with a hysteresis big enough to
> extend to the edges of the next range, or something?
>
> Let's say you're currently sitting at -50dBm, and want your ranges to
> be -100..-80..-60..-40..-20. Then you could program -50dBm with
> hysteresis of 10dBm, and it'd tell you when you move below/above?
>
> This probably won't really work, the hysteresis probably won't be
> honoured precisely enough by all drivers. Or we can just fix the
> drivers, I guess. However, if you're just at the edge of your range,
> e.g. when your current signal strength is -60dBm, you'd still flip-flop
> in and out of that range continuously, although userspace could just
> program a different threshold/hysteresis for those cases as well.
>
> Anyway, not sure this can be made to work, and drivers would play ball.

This sounds really brittle.  Furthermore, we also need a facility to 
know when signal strength is getting low to trigger roaming logic.  This 
would mean sharing CQM facility between roaming & signal strength 
notifications.  As you wrote above, things become quite impractical.

Regards,
-Denis
Andrew Zaborowski Oct. 21, 2016, 7:03 p.m. UTC | #3
Hi,

> The problem is that you really want this to be offloaded to the device,
> *especially* if you care about low power usage, because you absolutely
> don't want to be processing each beacon (which is typically what we
> derive the data from today) in the host CPU - you want those to be
> filtered by the device so you don't wake up the host CPU every ~102ms.

Right, that would be a separate optimisation but it probably won't arrive
until there's an API that the drivers can implement, so I think this is a
prerequisite.

> Without offloading to the device, your patch is actually fairly much
> pointless, because you've already woken up the host CPU, at which point
> punting to userspace probably isn't all that much more expensive over
> the cost of waking up the CPU in the first place.

The userspace switches count too and are the main motivation for this
patch.  Eventually, as you say, things will depend on specific drivers
and you will want to optimise whatever you can assuming the hardware
you're constrained to.

> Looking at the code, it seems like only a single driver could support
> this in a pseudo-offloaded fashion (iwlmvm), where all the others that
> offload it today would not be able to support this API at all, unless
> they fall back to complete software processing which, as I wrote above,
> will have far worse power consumption consequences.
>
> Therefore, adding this API just for mac80211 seems pretty pointless,
> especially since you're targeting this for IoT, where I expect people
> will be using full-MAC chips rather than soft-MAC, with the consequence
> of not even using mac80211-based drivers. Oops.

Hence this mail, I want to find out what would be a good API and it
should work with both mac80211 and full MAC chips.  I included a mac80211
implementation because, well, it's in software and thus you can see it
and use it for reference, and it covers a range of hardware.

It seems like any mechanism you choose can be implemented on top of
hardware that supports a low and a high threshold by setting the next
values while handling the event related to the previous threshold crossing
event.  If the thresholds are specified by a middle value and a hysteresis
or distance, that would work equally well.
The API I added in the patch also allows offloading to such hardware.

The remaining drivers might not be able to support any useful mechanism.

>> The assumption is that you can't simulate the same behavior with just
>> the current NL80211_ATTR_CQM_RSSI_THOLD attribute.
>
> Is that true? Technically, it seems that perhaps if you wanted to have
> a few ranges, you could always pick one that's currently active, and
> program that into the driver/device, with a hysteresis big enough to
> extend to the edges of the next range, or something?

Yes, but not with the current netlink API since the cqm_last_rssi_event
(whatever the driver calls it) value is reset when you set new thresholds,
so you won't know what "last" value will be used, and you'll also get a
spurious event on the next beacon.

And then at least two drivers use the hysteresis value differently
(wl1251, cw1200).

In short a nl80211 change would be needed regardless of the mechanism
chosen.

Best regards

-----------------------------------------------------------
Intel Corporation Iberia, S.A. 
Registered Office: Torre Picasso, 25th Floor, 
Plaza Pablo Ruiz Picasso, no. 1, 28020  Madrid

Este mensaje se dirige exclusivamente a su destinatario y puede 
contener informacion privilegiada o confidencial. Si no es vd. 
el destinatario indicado, queda notificado de que la lectura, 
utilizacion, divulgacion y,o copia sin autorizacion esta prohibida 
en virtud de la legislacion vigente. Si ha recibido este mensaje por 
error, le rogamos que nos lo communique inmediatamente por 
esta misma via y proceda a su destruccion.

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
Johannes Berg Oct. 26, 2016, 1:05 p.m. UTC | #4
On Fri, 2016-10-21 at 19:03 +0000, Zaborowski, Andrew wrote:

> > The problem is that you really want this to be offloaded to the
> > device, *especially* if you care about low power usage, because you
> > absolutely don't want to be processing each beacon (which is
> > typically what we derive the data from today) in the host CPU - you
> > want those to be filtered by the device so you don't wake up the
> > host CPU every ~102ms.
> 
> Right, that would be a separate optimisation but it probably won't
> arrive until there's an API that the drivers can implement, so I
> think this is a prerequisite.

Huh? No, beacon filtering is implemented by a lot of drivers today.

> The userspace switches count too and are the main motivation for this
> patch.  Eventually, as you say, things will depend on specific
> drivers and you will want to optimise whatever you can assuming the
> hardware you're constrained to.

Yes and no. I think we can probably define a reasonable subset that
you'd expect more devices to implement. I don't really see the
requirement to do the "banding" that you did here offloaded - doing it
in mac80211 won't help much, and won't work in cases where you have
beacon filtering already anyway.

Drivers like iwlwifi would be able to implement the banding in
software, since they get a notification on every change above a
configurable threshold, but other drivers like one I looked at actually
do have a low and high threshold today, and program them to be the same
value with our current CQM API definitions.

> It seems like any mechanism you choose can be implemented on top of
> hardware that supports a low and a high threshold by setting the next
> values while handling the event related to the previous threshold
> crossing event.  If the thresholds are specified by a middle value
> and a hysteresis or distance, that would work equally well.
> The API I added in the patch also allows offloading to such hardware.

Well, ok, technically the API can be implemented on top of drivers with
low/high thresholds, by doing the configuration according to the
current range you're in.

I would argue though that it makes more sense to expose a simpler
capability (e.g. two instead of the current single threshold) and do
the reprogramming from higher layers. That ends up being more flexible,
since you can then, for example, also have ranges that aren't all
identical - which makes some sense because above a certain level you
don't really care at all.

> In short a nl80211 change would be needed regardless of the mechanism
> chosen.

Agree. I'm just not convinced that the banding mechanism you propose is
the most reasonable choice for new API.

johannes
Johannes Berg Oct. 26, 2016, 1:11 p.m. UTC | #5
On Fri, 2016-10-21 at 09:20 -0500, Denis Kenzior wrote:

> > It's actually not clear to me that this is really how it should be.
> > There's a point to be made that taking a more holistic "link
> > quality" would be a better choice. That's related, but maybe can be
> > a separate discussion.

> Can you elaborate on this 'link quality' idea?

Well, I didn't really want to - getting 3 system folks into a room will
result in 4 different ways of doing it - but you can take into account
not just the RSSI, but also the bitrate you can reasonably use on the
channel/with the AP, the noise you can perhaps detect (if you can), the
amount of packet loss or retransmissions you experience, etc.

I think that some systems (Android, maybe Windows) already do something
more complex than pure RSSI indicators, but I don't really know for
sure.

> > Yes, this would be ideal.
> > 
> > [...]

see my other email

> This sounds really brittle.  Furthermore, we also need a facility to 
> know when signal strength is getting low to trigger roaming
> logic.  This would mean sharing CQM facility between roaming & signal
> strength notifications.  As you wrote above, things become quite
> impractical.

This would likely go through the supplicant anyway, so it could manage
proper range overlaps etc. for this.

It does seem brittle if we just have a single value, but if we add
low/high thresholds (with hysteresis) then I think we can do this, and
gain more flexibility in the process. But let's discuss more details
over in the other email I just sent :)

johannes
Denis Kenzior Oct. 27, 2016, 6:12 p.m. UTC | #6
Hi Johannes,

 > Huh? No, beacon filtering is implemented by a lot of drivers today.

Pardon a dumb question, but can filtering be turned off?  I doubt anyone 
would want to, but just wondering.

>
>> The userspace switches count too and are the main motivation for this
>> patch.  Eventually, as you say, things will depend on specific
>> drivers and you will want to optimise whatever you can assuming the
>> hardware you're constrained to.
>
> Yes and no. I think we can probably define a reasonable subset that
> you'd expect more devices to implement. I don't really see the
> requirement to do the "banding" that you did here offloaded - doing it
> in mac80211 won't help much, and won't work in cases where you have
> beacon filtering already anyway.

Is there anything you have in mind?  Our goal is to minimize 
hardware-kernel-userspace wakeups.  With the nl80211 API as it is today, 
it doesn't seem feasible to do anything besides polling.  Whatever we 
come up with will surely be better than that.

> Well, ok, technically the API can be implemented on top of drivers with
> low/high thresholds, by doing the configuration according to the
> current range you're in.

So you're thinking of having high and low threshold.  So we'd get an 
event when we're higher than the high threshold and lower than the low 
threshold, right?  Then we'd need to bootstrap our current rssi somehow, 
or do we get another event?  I'm guessing we're going to have some race 
condition issues?

>
> I would argue though that it makes more sense to expose a simpler
> capability (e.g. two instead of the current single threshold) and do
> the reprogramming from higher layers. That ends up being more flexible,
> since you can then, for example, also have ranges that aren't all
> identical - which makes some sense because above a certain level you
> don't really care at all.

It seems like we'd be limiting ourselves here.  Couple reasons that come 
to mind:
- This would still require user space to keep re-setting the new 
thresholds.  While the wakeups are much less frequent than with polling 
for example, they still add up.  Scheduling userspace, processing 
nl80211 messages, etc is still a cost.
- It feels like we're exposing a lowest-common-denominator API with no 
possibility of offload / optimization in the future.  E.g. even drivers 
that can support arbitrary number of thresholds will be boxed in.

Would using an n-threshold API be possible?  That way user space can 
program in whatever threholds once, and then the kernel would figure out 
how to support that given the relevant hardware capabilities.

>
>> In short a nl80211 change would be needed regardless of the mechanism
>> chosen.
>
> Agree. I'm just not convinced that the banding mechanism you propose is
> the most reasonable choice for new API.
>

Understood, but it was just a stab in the dark to get this discussion 
started.

Regards,
-Denis
Johannes Berg Oct. 27, 2016, 6:42 p.m. UTC | #7
> Pardon a dumb question, but can filtering be turned off?  I doubt
> anyone would want to, but just wondering.

Generally I assume that a typical device/firmware will be able to turn
it off (I know ours can), but we don't think we even provide a knob for
it for anyone to request it, so you'd have to modify the driver.

> Is there anything you have in mind?  Our goal is to minimize 
> hardware-kernel-userspace wakeups.  With the nl80211 API as it is
> today, it doesn't seem feasible to do anything besides
> polling.  Whatever we come up with will surely be better than that.

I was thinking of just providing two thresholds, since you should be
able to emulate more of them with that, without much cost.

> So you're thinking of having high and low threshold.  So we'd get an 
> event when we're higher than the high threshold and lower than the
> low threshold, right?  

I'm mostly handwaving, but yes.

> Then we'd need to bootstrap our current rssi somehow, or do we get
> another event?  I'm guessing we're going to have some race condition
> issues?

Generally, with CQM, when you initially program it you get an event
telling you where you're at right now - so hopefully you'd get "middle"
(rather than "low"/"high") with the actual signal falling squarely into
your range, and from there on you're pretty much good to go.

> Would using an n-threshold API be possible?  That way user space can 
> program in whatever threholds once, and then the kernel would figure
> out how to support that given the relevant hardware capabilities.

That seems like a reasonable idea. We'd want to have code in cfg80211
that does the emulation as we discussed above, so that from a userspace
POV an "arbitrary" number of thresholds is supported (if the capability
is supported at all, which would depend on the device doing >=2
thresholds).

johannes
diff mbox

Patch

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index bd19faa..3487b3f 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2606,6 +2606,10 @@  struct cfg80211_nan_func {
  *	disabled.)
  * @set_cqm_txe_config: Configure connection quality monitor TX error
  *	thresholds.
+ * @set_cqm_rssi_steps: Configure number of RSSI levels for the connection
+ *	quality monitor to use in deciding when to send rounded RSSI change
+ *	events.  After configuration, the driver should (soon) send an event
+ *	indicating the current level out of the number of levels set.
  * @sched_scan_start: Tell the driver to start a scheduled scan.
  * @sched_scan_stop: Tell the driver to stop an ongoing scheduled scan. This
  *	call must stop the scheduled scan and be ready for starting a new one
@@ -2891,6 +2895,9 @@  struct cfg80211_ops {
 				      struct net_device *dev,
 				      u32 rate, u32 pkts, u32 intvl);
 
+	int	(*set_cqm_rssi_steps)(struct wiphy *wiphy,
+				      struct net_device *dev, u32 steps);
+
 	void	(*mgmt_frame_register)(struct wiphy *wiphy,
 				       struct wireless_dev *wdev,
 				       u16 frame_type, bool reg);
@@ -5213,6 +5220,20 @@  void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer,
 void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp);
 
 /**
+ * cfg80211_cqm_rounded_rssi_notify - connection quality monitoring rssi event
+ * @dev: network device
+ * @signal_level: the signal level in the range of 0 to (steps - 1)
+ * @gfp: context flags
+ *
+ * This function is called when the RSSI level, rounded to the resolution
+ * set by cfg80211_ops::set_cqm_rssi_steps, has changed.  In other words
+ * the RSSI value has gone from one of the equally sized intervals, covering
+ * the range of -100dBm to 0, to another.
+ */
+void cfg80211_cqm_rounded_rssi_notify(struct net_device *dev,
+				      u32 signal_level, gfp_t gfp);
+
+/**
  * cfg80211_radar_event - radar detection event
  * @wiphy: the wiphy
  * @chandef: chandef for the current channel
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index a810dfc..5fbe275 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -503,6 +503,9 @@  struct ieee80211_mu_group_data {
  *	cause an event to be sent indicating where the current value is in
  *	relation to the newly configured threshold.
  * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis
+ * @cqm_rssi_steps: Connection quality monitor's number of RSSI levels to
+ *	use when sending rounded RSSI change events.  A zero value implies this
+ *	event is disabled.
  * @arp_addr_list: List of IPv4 addresses for hardware ARP filtering. The
  *	may filter ARP queries targeted for other addresses than listed here.
  *	The driver must allow ARP queries targeted for all address listed here
@@ -554,6 +557,7 @@  struct ieee80211_bss_conf {
 	u16 ht_operation_mode;
 	s32 cqm_rssi_thold;
 	u32 cqm_rssi_hyst;
+	u32 cqm_rssi_steps;
 	struct cfg80211_chan_def chandef;
 	struct ieee80211_mu_group_data mu_group;
 	__be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN];
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 56368e9..a2c8817 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -3859,6 +3859,14 @@  enum nl80211_ps_state {
  *	%NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting.
  * @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon
  *	loss event
+ * @NL80211_ATTR_CQM_ROUNDED_RSSI_STEPS: number of evenly spaced RSSI ranges
+ *	to be distinguished between -100 dBm and 0. %NL80211_CMD_NOTIFY_CQM
+ *	events will be sent when the RSSI value crosses from one range of
+ *	values to another.  Set to 0 to disable
+ *	%NL80211_ATTR_CQM_ROUNDED_RSSI_EVENT reporting.
+ * @NL80211_ATTR_CQM_ROUNDED_RSSI_EVENT: the RSSI level with range reduced to
+ *	0 to steps - 1 as configured in %NL80211_ATTR_CQM_ROUNDED_RSSI_STEPS
+ *	value.
  * @__NL80211_ATTR_CQM_AFTER_LAST: internal
  * @NL80211_ATTR_CQM_MAX: highest key attribute
  */
@@ -3872,6 +3880,8 @@  enum nl80211_attr_cqm {
 	NL80211_ATTR_CQM_TXE_PKTS,
 	NL80211_ATTR_CQM_TXE_INTVL,
 	NL80211_ATTR_CQM_BEACON_LOSS_EVENT,
+	NL80211_ATTR_CQM_ROUNDED_RSSI_STEPS,
+	NL80211_ATTR_CQM_ROUNDED_RSSI_EVENT,
 
 	/* keep last */
 	__NL80211_ATTR_CQM_AFTER_LAST,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index fd6541f..73550a7 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2657,6 +2657,23 @@  static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy,
 	return 0;
 }
 
+static int ieee80211_set_cqm_rssi_steps(struct wiphy *wiphy,
+					 struct net_device *dev,
+					 u32 steps)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_vif *vif = &sdata->vif;
+	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+
+	if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)
+		return -EOPNOTSUPP;
+
+	bss_conf->cqm_rssi_steps = steps;
+	sdata->u.mgd.last_cqm_event_rounded_rssi = 0;
+
+	return 0;
+}
+
 static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
 				      struct net_device *dev,
 				      const u8 *addr,
@@ -3645,6 +3662,7 @@  const struct cfg80211_ops mac80211_config_ops = {
 	.mgmt_tx = ieee80211_mgmt_tx,
 	.mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait,
 	.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
+	.set_cqm_rssi_steps = ieee80211_set_cqm_rssi_steps,
 	.mgmt_frame_register = ieee80211_mgmt_frame_register,
 	.set_antenna = ieee80211_set_antenna,
 	.get_antenna = ieee80211_get_antenna,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 34c2add..1c04c21 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -507,12 +507,19 @@  struct ieee80211_if_managed {
 
 	/*
 	 * Last Beacon frame signal strength average (ave_beacon_signal / 16)
-	 * that triggered a cqm event. 0 indicates that no event has been
-	 * generated for the current association.
+	 * that triggered a cqm RSSI threshold-crossed event. 0 indicates
+	 * that no event has been generated for the current association.
 	 */
 	int last_cqm_event_signal;
 
 	/*
+	 * Last Beacon frame signal strength average (ave_beacon_signal / 16)
+	 * that triggered a cqm rounded RSSI level event. 0 indicates that no
+	 * event has been generated for the current association.
+	 */
+	int last_cqm_event_rounded_rssi;
+
+	/*
 	 * State variables for keeping track of RSSI of the AP currently
 	 * connected to and informing driver when RSSI has gone
 	 * below/above a certain threshold.
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 7486f2d..a87422b 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -3253,6 +3253,14 @@  static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
 		ieee80211_reset_ap_probe(sdata);
 }
 
+static void ieee80211_cqm_rounded_rssi_notify(struct ieee80211_vif *vif,
+					      u32 signal_level, gfp_t gfp)
+{
+	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+	cfg80211_cqm_rounded_rssi_notify(sdata->dev, signal_level, gfp);
+}
+
 /*
  * This is the canonical list of information elements we care about,
  * the filter code also gives us all changes to the Microsoft OUI
@@ -3416,6 +3424,25 @@  static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 		}
 	}
 
+	if (bss_conf->cqm_rssi_steps &&
+	    ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
+		int sig = -ewma_beacon_signal_read(&ifmgd->ave_beacon_signal);
+		int last_event = ifmgd->last_cqm_event_rounded_rssi;
+		int steps = bss_conf->cqm_rssi_steps;
+		int last_level = (last_event + 100) * steps / 100;
+		int cur_level = (sig + 100) * steps / 100;
+		/* Use a threshold of 1/4th of the step size (100dBm / steps) */
+		int thold = 25 / steps;
+
+		if (last_event == 0 ||
+		    (cur_level < last_level && sig < last_event - thold) ||
+		    (cur_level > last_level && sig > last_event + thold)) {
+			ifmgd->last_cqm_event_rounded_rssi = sig;
+			ieee80211_cqm_rounded_rssi_notify(
+					&sdata->vif, cur_level, GFP_KERNEL);
+		}
+	}
+
 	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) {
 		mlme_dbg_ratelimited(sdata,
 				     "cancelling AP probe due to a received beacon\n");
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index c510810..5bfc853 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -9278,6 +9278,8 @@  nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
 	[NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
 	[NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
 	[NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
+	[NL80211_ATTR_CQM_ROUNDED_RSSI_STEPS] = { .type = NLA_U32 },
+	[NL80211_ATTR_CQM_ROUNDED_RSSI_EVENT] = { .type = NLA_U32 },
 };
 
 static int nl80211_set_cqm_txe(struct genl_info *info,
@@ -9324,6 +9326,25 @@  static int nl80211_set_cqm_rssi(struct genl_info *info,
 	return rdev_set_cqm_rssi_config(rdev, dev, threshold, hysteresis);
 }
 
+static int nl80211_set_cqm_rounded_rssi_steps(struct genl_info *info, u32 steps)
+{
+	struct cfg80211_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+	if (steps == 1)
+		return -EINVAL;
+
+	if (!rdev->ops->set_cqm_rssi_steps)
+		return -EOPNOTSUPP;
+
+	if (wdev->iftype != NL80211_IFTYPE_STATION &&
+	    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
+		return -EOPNOTSUPP;
+
+	return rdev_set_cqm_rssi_steps(rdev, dev, steps);
+}
+
 static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
 {
 	struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
@@ -9357,6 +9378,13 @@  static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
 		return nl80211_set_cqm_txe(info, rate, pkts, intvl);
 	}
 
+	if (attrs[NL80211_ATTR_CQM_ROUNDED_RSSI_STEPS]) {
+		u32 steps = nla_get_u32(
+				attrs[NL80211_ATTR_CQM_ROUNDED_RSSI_STEPS]);
+
+		return nl80211_set_cqm_rounded_rssi_steps(info, steps);
+	}
+
 	return -EINVAL;
 }
 
@@ -13832,6 +13860,30 @@  void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp)
 }
 EXPORT_SYMBOL(cfg80211_cqm_beacon_loss_notify);
 
+void cfg80211_cqm_rounded_rssi_notify(struct net_device *dev,
+				      u32 signal_level, gfp_t gfp)
+{
+	struct sk_buff *msg;
+
+	trace_cfg80211_cqm_rounded_rssi_notify(dev, signal_level);
+
+	msg = cfg80211_prepare_cqm(dev, NULL, gfp);
+	if (!msg)
+		return;
+
+	if (nla_put_u32(msg, NL80211_ATTR_CQM_ROUNDED_RSSI_EVENT,
+			signal_level))
+		goto nla_put_failure;
+
+	cfg80211_send_cqm(msg, gfp);
+
+	return;
+
+ nla_put_failure:
+	nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_rounded_rssi_notify);
+
 static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
 				     struct net_device *netdev, const u8 *bssid,
 				     const u8 *replay_ctr, gfp_t gfp)
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 11cf83c..3521a2f 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -737,6 +737,17 @@  rdev_set_cqm_txe_config(struct cfg80211_registered_device *rdev,
 	return ret;
 }
 
+static inline int
+rdev_set_cqm_rssi_steps(struct cfg80211_registered_device *rdev,
+			struct net_device *dev, u32 steps)
+{
+	int ret;
+	trace_rdev_set_cqm_rssi_steps(&rdev->wiphy, dev, steps);
+	ret = rdev->ops->set_cqm_rssi_steps(&rdev->wiphy, dev, steps);
+	trace_rdev_return_int(&rdev->wiphy, ret);
+	return ret;
+}
+
 static inline void
 rdev_mgmt_frame_register(struct cfg80211_registered_device *rdev,
 			 struct wireless_dev *wdev, u16 frame_type, bool reg)
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index a3d0a91..dbc2450 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1327,6 +1327,24 @@  TRACE_EVENT(rdev_set_cqm_txe_config,
 		  __entry->intvl)
 );
 
+TRACE_EVENT(rdev_set_cqm_rssi_steps,
+	TP_PROTO(struct wiphy *wiphy,
+		 struct net_device *netdev, u32 steps),
+	TP_ARGS(wiphy, netdev, steps),
+	TP_STRUCT__entry(
+		WIPHY_ENTRY
+		NETDEV_ENTRY
+		__field(u32, steps)
+	),
+	TP_fast_assign(
+		WIPHY_ASSIGN;
+		NETDEV_ASSIGN;
+		__entry->steps = steps;
+	),
+	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", steps: %u ",
+		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->steps)
+);
+
 TRACE_EVENT(rdev_disconnect,
 	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
 		 u16 reason_code),
@@ -2486,6 +2504,21 @@  TRACE_EVENT(cfg80211_cqm_rssi_notify,
 		  NETDEV_PR_ARG, __entry->rssi_event)
 );
 
+TRACE_EVENT(cfg80211_cqm_rounded_rssi_notify,
+	TP_PROTO(struct net_device *netdev, u32 signal_level),
+	TP_ARGS(netdev, signal_level),
+	TP_STRUCT__entry(
+		NETDEV_ENTRY
+		__field(u32 signal_level)
+	),
+	TP_fast_assign(
+		NETDEV_ASSIGN;
+		__entry->signal_level = signal_level;
+	),
+	TP_printk(NETDEV_PR_FMT ", signal_level: %d",
+		  NETDEV_PR_ARG, __entry->signal_level)
+);
+
 TRACE_EVENT(cfg80211_reg_can_beacon,
 	TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef,
 		 enum nl80211_iftype iftype, bool check_no_ir),