2016-11-06 02:08:49

by Andrew Zaborowski

[permalink] [raw]
Subject: [PATCH 0/3][RFC] CQM RSSI event with many thresholds

From: Andrew Zaborowski <[email protected]>

This is a first stab at a nl80211 API using multiple RSSI thresholds
discussed previously. It is similar to the current CQM RSSI event with
one threshold and a hysteresis parameter. I tried to avoid creating
new nl80211 commands and events so I extended the existing command.
There's still a NL80211_ATTR_CQM_RSSI_THOLD attribute and a hysteresis
attribute but multiple thresholds can be supplied as an array.

All this is a proposal and open to changes.

There are two different mechanisms for compatibility with the current
behavior in which events are generated every time the RSSI value is
more than $hysteresis dBm away from the last event's level, even if
$threshold wasn't crossed. For example with the rssi going up from 20dBm
and the threshold set at 40 and hysteresis at 5, there'll be events at
40, 45, 50 and so on. I assume this is intentional because most
drivers replicate this behavior. If 2 or more thresholds are supplied
in NL80211_ATTR_CQM_RSSI_THOLD (i.e. it is not 4 byte long) this will
enable the new mechanism and events are only generated when a threshold
is crossed.

My opinion is that the new mechanism is similar enough to the current
one that it would be best if each driver didn't have to implement both
mechanisms, and one could be a special case of the other. However I
can't update all drivers because I can't test them. So at the driver
level there have to be two methods until all drivers implement the
new method (set_cqm_rssi_range_config, which uses a low and a high
threshold) and the current behavior can be implemented on to of it.
So the next best thing is that at least the netlink API can present it
as a single interface. Also both mechanisms never have to be enabled
simultaneously, the driver doesn't have to support them both at the
same time.

Also it seems that the wl1271 (wlcore) driver can offload RSSI
monitoring to hardware using exactly the two parameters used today:
one threshold and a hysteresis value, and no other mechanism is
supported by the hardware, so it wouldn't make sense for it to drop
the current method.

Andrew Zaborowski (3):
mac80211: Pass new RSSI level in CQM RSSI notification
cfg80211: Pass new RSSI level in CQM RSSI notification
nl80211/mac80211: Accept multiple RSSI thresholds for CQM

drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 2 +
drivers/net/wireless/marvell/mwifiex/sta_event.c | 4 +-
drivers/net/wireless/rndis_wlan.c | 2 +-
drivers/net/wireless/rsi/rsi_91x_mac80211.c | 2 +-
drivers/net/wireless/st/cw1200/sta.c | 2 +-
drivers/net/wireless/ti/wl1251/event.c | 4 +-
drivers/net/wireless/ti/wlcore/event.c | 3 +-
include/net/cfg80211.h | 15 ++-
include/net/mac80211.h | 8 ++
include/uapi/linux/nl80211.h | 7 +-
net/mac80211/cfg.c | 28 +++++
net/mac80211/mlme.c | 33 ++++-
net/mac80211/trace.h | 11 +-
net/wireless/core.c | 13 ++
net/wireless/core.h | 9 ++
net/wireless/nl80211.c | 146 +++++++++++++++++++++--
net/wireless/rdev-ops.h | 12 ++
net/wireless/trace.h | 33 ++++-
18 files changed, 299 insertions(+), 35 deletions(-)

--
2.7.4


2016-11-28 15:36:02

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 3/3][RFC] nl80211/mac80211: Accept multiple RSSI thresholds for CQM

On Mon, 2016-11-28 at 16:29 +0100, Andrew Zaborowski wrote:

> In order to keep the hardware offload feature when working with
> hardware that can only offload the old single-thresholds method, but
> where the kernel also wants to support the new method by looking at
> all the beacons in software.  IIRC I identified just one driver that
> would be in this situation: wl1271.

IMHO it would be better to not allow that. I'd think that wl1271 is
only used in devices where power consumption will be far more
interesting than providing this kind of API.

> This is a specific case and the semantics implemented by the wl1271
> may be a little different from those in the rest of the drivers so
> this may be worth very little.  I can change the comment to imply
> only one method should be implemented.

We might still have to allow both to be present for mac80211 though.

> > Seems there still should be a hysteresis? Or am I misunderstanding
> > the
> > intent here? I.e. isn't it meant to report low/medium/high later?
>
> This isn't exposed directly to users, instead it's used by the code
> in
> nl80211.c which will always reset the thresholds when either
> threshold
> is crossed.  The hysteresis can then be either handled in nl80211.c
> (factored into the threshold values) or in the firmware/driver, this
> won't change the number of wakeups.

That's only if you assume you can actually react to this fast enough
though, no? If I offload this, I'd want to also offload a hysteresis to
firmware, I'd think.

johannes

2016-11-06 02:08:55

by Andrew Zaborowski

[permalink] [raw]
Subject: [PATCH 3/3][RFC] nl80211/mac80211: Accept multiple RSSI thresholds for CQM

Change the SET CQM command RSSI threshold attribute semantic to accept
any number of thresholds as a sorted array. The API should be backwards
compatible so that if one s32 threshold value is passed, the old
mechanism is enabled. The netlink event generated is the same too.

cfg80211 handles an arbitrary number of RSSI thresholds but drivers have
to provide a method (set_cqm_rssi_range_config) that configures a range
set by a high and a low value. Drivers have to call back when the RSSI
goes out of that range and there's no additional event every time the
range is reconfigured. There's no reason that the whole mechanism with
more than 2 thresholds couldn't be offloaded if the was hardware
available.

I added only a mac80211 implementation of the set_cqm_rssi_range_config
method as an illustration and because I can't run-time test any other
driver. In this patch there's also no API for mac80211-based drivers
that do beacon filtering to offload this but that would be the intended
use.
---
include/net/cfg80211.h | 12 ++++
include/net/mac80211.h | 6 ++
include/uapi/linux/nl80211.h | 4 +-
net/mac80211/cfg.c | 28 +++++++++
net/mac80211/mlme.c | 24 ++++++++
net/wireless/core.c | 13 ++++
net/wireless/core.h | 9 +++
net/wireless/nl80211.c | 137 +++++++++++++++++++++++++++++++++++++++----
net/wireless/rdev-ops.h | 12 ++++
net/wireless/trace.h | 22 +++++++
10 files changed, 255 insertions(+), 12 deletions(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 632dce1..0105e86 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2604,6 +2604,10 @@ struct cfg80211_nan_func {
* the current level is above/below the configured threshold; this may
* need some care when the configuration is changed (without first being
* disabled.)
+ * @set_cqm_rssi_range_config: Configure two RSSI thresholds in the
+ * connection quality monitor. Even if the driver implements both the
+ * single threshold and low/high thresholds mechanisms, it should assume
+ * only one is active at any time.
* @set_cqm_txe_config: Configure connection quality monitor TX error
* thresholds.
* @sched_scan_start: Tell the driver to start a scheduled scan.
@@ -2887,6 +2891,10 @@ struct cfg80211_ops {
struct net_device *dev,
s32 rssi_thold, u32 rssi_hyst);

+ int (*set_cqm_rssi_range_config)(struct wiphy *wiphy,
+ struct net_device *dev,
+ s32 rssi_low, s32 rssi_high);
+
int (*set_cqm_txe_config)(struct wiphy *wiphy,
struct net_device *dev,
u32 rate, u32 pkts, u32 intvl);
@@ -3709,6 +3717,7 @@ void wiphy_free(struct wiphy *wiphy);
struct cfg80211_conn;
struct cfg80211_internal_bss;
struct cfg80211_cached_keys;
+struct cfg80211_cqm_config;

/**
* struct wireless_dev - wireless device state
@@ -3769,6 +3778,7 @@ struct cfg80211_cached_keys;
* @event_list: (private) list for internal event processing
* @event_lock: (private) lock for event list
* @owner_nlportid: (private) owner socket port ID
+ * @cqm_config: (private) nl80211 RSSI monitor state
*/
struct wireless_dev {
struct wiphy *wiphy;
@@ -3833,6 +3843,8 @@ struct wireless_dev {
bool prev_bssid_valid;
} wext;
#endif
+
+ struct cfg80211_cqm_config *cqm_config;
};

static inline u8 *wdev_address(struct wireless_dev *wdev)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 33026e1..7da1056 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -502,6 +502,10 @@ struct ieee80211_mu_group_data {
* implies disabled. As with the cfg80211 callback, a change here should
* cause an event to be sent indicating where the current value is in
* relation to the newly configured threshold.
+ * @cqm_rssi_low: Connection quality monitor RSSI lower threshold, a zero value
+ * implies disabled. This is an alternative mechanism to the single
+ * threshold event and can't be enabled simultaneously.
+ * @cqm_rssi_low: Connection quality monitor RSSI upper threshold.
* @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis
* @arp_addr_list: List of IPv4 addresses for hardware ARP filtering. The
* may filter ARP queries targeted for other addresses than listed here.
@@ -554,6 +558,8 @@ struct ieee80211_bss_conf {
u16 ht_operation_mode;
s32 cqm_rssi_thold;
u32 cqm_rssi_hyst;
+ s32 cqm_rssi_low;
+ s32 cqm_rssi_high;
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 48108fd..d632c6a 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -3839,7 +3839,9 @@ enum nl80211_ps_state {
* @__NL80211_ATTR_CQM_INVALID: invalid
* @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies
* the threshold for the RSSI level at which an event will be sent. Zero
- * to disable.
+ * to disable. Alternatively multiple values can be supplied as a
+ * low-to-high sorted array of thresholds in dBm. Events will be sent
+ * when the RSSI value crosses any of the thresholds.
* @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies
* the minimum amount the RSSI level must change after an event before a
* new event may be issued (to reduce effects of RSSI oscillation).
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index fd6541f..6ac0523 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2647,6 +2647,33 @@ static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy,

bss_conf->cqm_rssi_thold = rssi_thold;
bss_conf->cqm_rssi_hyst = rssi_hyst;
+ bss_conf->cqm_rssi_low = 0;
+ bss_conf->cqm_rssi_high = 0;
+ sdata->u.mgd.last_cqm_event_signal = 0;
+
+ /* tell the driver upon association, unless already associated */
+ if (sdata->u.mgd.associated &&
+ sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM);
+
+ return 0;
+}
+
+static int ieee80211_set_cqm_rssi_range_config(struct wiphy *wiphy,
+ struct net_device *dev,
+ s32 rssi_low, s32 rssi_high)
+{
+ 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_low = rssi_low;
+ bss_conf->cqm_rssi_high = rssi_high;
+ bss_conf->cqm_rssi_thold = 0;
+ bss_conf->cqm_rssi_hyst = 0;
sdata->u.mgd.last_cqm_event_signal = 0;

/* tell the driver upon association, unless already associated */
@@ -3645,6 +3672,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_range_config = ieee80211_set_cqm_rssi_range_config,
.mgmt_frame_register = ieee80211_mgmt_frame_register,
.set_antenna = ieee80211_set_antenna,
.get_antenna = ieee80211_get_antenna,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 6898ecb..7f99918 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -3416,6 +3416,30 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
}

+ if (bss_conf->cqm_rssi_low &&
+ 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_signal;
+ int low = bss_conf->cqm_rssi_low;
+ int high = bss_conf->cqm_rssi_high;
+
+ if (sig < low &&
+ (last_event == 0 || last_event >= low)) {
+ ifmgd->last_cqm_event_signal = sig;
+ ieee80211_cqm_rssi_notify(
+ &sdata->vif,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+ sig, GFP_KERNEL);
+ } else if (sig > high &&
+ (last_event == 0 || last_event <= high)) {
+ ifmgd->last_cqm_event_signal = sig;
+ ieee80211_cqm_rssi_notify(
+ &sdata->vif,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+ sig, 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/core.c b/net/wireless/core.c
index 8201e6d..f6c1bf2 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -928,6 +928,16 @@ void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked)
}
EXPORT_SYMBOL(wiphy_rfkill_set_hw_state);

+void cfg80211_cqm_config_free(struct wireless_dev *wdev)
+{
+ if (!wdev->cqm_config)
+ return;
+
+ kfree(wdev->cqm_config->rssi_thresholds);
+ kfree(wdev->cqm_config);
+ wdev->cqm_config = NULL;
+}
+
void cfg80211_unregister_wdev(struct wireless_dev *wdev)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
@@ -954,6 +964,8 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)
WARN_ON_ONCE(1);
break;
}
+
+ cfg80211_cqm_config_free(wdev);
}
EXPORT_SYMBOL(cfg80211_unregister_wdev);

@@ -1205,6 +1217,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
#ifdef CONFIG_CFG80211_WEXT
kzfree(wdev->wext.keys);
#endif
+ cfg80211_cqm_config_free(wdev);
}
/*
* synchronise (so that we won't find this netdev
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 08d2e94..75fc334 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -270,6 +270,13 @@ struct cfg80211_iface_destroy {
u32 nlportid;
};

+struct cfg80211_cqm_config {
+ s32 *rssi_thresholds;
+ int n_rssi_thresholds;
+ u32 rssi_hyst;
+ s32 last_rssi_event_value;
+};
+
void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev);

/* free object */
@@ -504,4 +511,6 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
#define CFG80211_DEV_WARN_ON(cond) ({bool __r = (cond); __r; })
#endif

+void cfg80211_cqm_config_free(struct wireless_dev *wdev);
+
#endif /* __NET_WIRELESS_CORE_H */
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 5d10774..907bd2a 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -9272,7 +9272,7 @@ static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)

static const struct nla_policy
nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
- [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_BINARY },
[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
[NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
@@ -9301,28 +9301,127 @@ static int nl80211_set_cqm_txe(struct genl_info *info,
return rdev_set_cqm_txe_config(rdev, dev, rate, pkts, intvl);
}

+static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ s32 last, low, high;
+ u32 hyst;
+ int i, n;
+ int err;
+
+ /* RSSI reporting disabled? */
+ if (!wdev->cqm_config)
+ return rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0);
+
+ /*
+ * Obtain current RSSI value if possible, if not and no RSSI threshold
+ * event has been received yet, we should receive an event after a
+ * connection is established and enough beacons received to calculate
+ * the average.
+ */
+ if (!wdev->cqm_config->last_rssi_event_value && wdev->current_bss) {
+ struct station_info sinfo;
+ u8 *mac_addr;
+
+ mac_addr = wdev->current_bss->pub.bssid;
+
+ err = rdev_get_station(rdev, dev, mac_addr, &sinfo);
+ if (err)
+ return err;
+
+ if (sinfo.filled & BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG))
+ wdev->cqm_config->last_rssi_event_value =
+ (s8) sinfo.rx_beacon_signal_avg;
+ }
+
+ last = wdev->cqm_config->last_rssi_event_value;
+ hyst = wdev->cqm_config->rssi_hyst;
+ n = wdev->cqm_config->n_rssi_thresholds;
+
+ for (i = 0; i < n; i++)
+ if (last < wdev->cqm_config->rssi_thresholds[i])
+ break;
+
+ low = i > 0 ? wdev->cqm_config->rssi_thresholds[i - 1] : S32_MIN;
+ high = i < n ? wdev->cqm_config->rssi_thresholds[i] : S32_MAX;
+
+ if (low > (s32) (last - hyst))
+ low = last - hyst;
+ if (high < (s32) (last + hyst))
+ high = last + hyst;
+
+ return rdev_set_cqm_rssi_range_config(rdev, dev, low, high - 1);
+}
+
static int nl80211_set_cqm_rssi(struct genl_info *info,
- s32 threshold, u32 hysteresis)
+ const s32 *thresholds, int n_thresholds,
+ u32 hysteresis)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int i, err;
+ s32 prev = S32_MIN;

- if (threshold > 0)
- return -EINVAL;
+ /* Check all values negative and sorted */
+ for (i = 0; i < n_thresholds; i++) {
+ if (thresholds[i] > 0 || thresholds[i] <= prev)
+ return -EINVAL;
+
+ prev = thresholds[i];
+ }
+
+ if (n_thresholds == 1 && thresholds[0] == 0)
+ n_thresholds = 0;

/* disabling - hysteresis should also be zero then */
- if (threshold == 0)
+ if (n_thresholds == 0)
hysteresis = 0;

- if (!rdev->ops->set_cqm_rssi_config)
- return -EOPNOTSUPP;
-
if (wdev->iftype != NL80211_IFTYPE_STATION &&
wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
return -EOPNOTSUPP;

- return rdev_set_cqm_rssi_config(rdev, dev, threshold, hysteresis);
+ wdev_lock(wdev);
+ cfg80211_cqm_config_free(wdev);
+ wdev_unlock(wdev);
+
+ if (n_thresholds <= 1 && rdev->ops->set_cqm_rssi_config) {
+ const s32 disable = 0;
+
+ if (n_thresholds == 0) {
+ n_thresholds = 1;
+ thresholds = &disable;
+ }
+
+ return rdev_set_cqm_rssi_config(rdev, dev,
+ thresholds[0], hysteresis);
+ }
+
+ if (!rdev->ops->set_cqm_rssi_range_config || !rdev->ops->get_station)
+ return -EOPNOTSUPP;
+
+ wdev_lock(wdev);
+ if (n_thresholds) {
+ struct cfg80211_cqm_config *cqm_config;
+
+ cqm_config = kzalloc(sizeof(struct cfg80211_cqm_config),
+ GFP_KERNEL);
+ cqm_config->rssi_thresholds =
+ kmemdup(thresholds, n_thresholds * sizeof(s32),
+ GFP_KERNEL);
+ cqm_config->n_rssi_thresholds = n_thresholds;
+ cqm_config->rssi_hyst = hysteresis;
+
+ wdev->cqm_config = cqm_config;
+ }
+
+ err = cfg80211_cqm_rssi_update(rdev, dev);
+
+ wdev_unlock(wdev);
+
+ return err;
}

static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
@@ -9342,10 +9441,15 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)

if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
- s32 threshold = nla_get_s32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
+ s32 *thresholds = nla_data(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
+ int len = nla_len(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
u32 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);

- return nl80211_set_cqm_rssi(info, threshold, hysteresis);
+ if (len % 4)
+ return -EINVAL;
+
+ return nl80211_set_cqm_rssi(info, thresholds, len / 4,
+ hysteresis);
}

if (attrs[NL80211_ATTR_CQM_TXE_RATE] &&
@@ -13741,6 +13845,8 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
s32 rssi_level, gfp_t gfp)
{
struct sk_buff *msg;
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);

trace_cfg80211_cqm_rssi_notify(dev, rssi_event, rssi_level);

@@ -13748,6 +13854,15 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH))
return;

+ if (wdev->cqm_config) {
+ wdev->cqm_config->last_rssi_event_value = rssi_level;
+
+ cfg80211_cqm_rssi_update(rdev, dev);
+
+ if (rssi_level == 0)
+ rssi_level = wdev->cqm_config->last_rssi_event_value;
+ }
+
msg = cfg80211_prepare_cqm(dev, NULL, gfp);
if (!msg)
return;
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 11cf83c..ae180f3 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -726,6 +726,18 @@ rdev_set_cqm_rssi_config(struct cfg80211_registered_device *rdev,
}

static inline int
+rdev_set_cqm_rssi_range_config(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, s32 low, s32 high)
+{
+ int ret;
+ trace_rdev_set_cqm_rssi_range_config(&rdev->wiphy, dev, low, high);
+ ret = rdev->ops->set_cqm_rssi_range_config(&rdev->wiphy, dev,
+ low, high);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
rdev_set_cqm_txe_config(struct cfg80211_registered_device *rdev,
struct net_device *dev, u32 rate, u32 pkts, u32 intvl)
{
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 6ac46a0..993f661 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1304,6 +1304,28 @@ TRACE_EVENT(rdev_set_cqm_rssi_config,
__entry->rssi_thold, __entry->rssi_hyst)
);

+TRACE_EVENT(rdev_set_cqm_rssi_range_config,
+ TP_PROTO(struct wiphy *wiphy,
+ struct net_device *netdev, s32 low, s32 high),
+ TP_ARGS(wiphy, netdev, low, high),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(s32, rssi_low)
+ __field(s32, rssi_high)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->rssi_low = low;
+ __entry->rssi_high = high;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT
+ ", range: %d - %d ",
+ WIPHY_PR_ARG, NETDEV_PR_ARG,
+ __entry->rssi_low, __entry->rssi_high)
+);
+
TRACE_EVENT(rdev_set_cqm_txe_config,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 rate,
u32 pkts, u32 intvl),
--
2.7.4

2016-11-06 02:08:53

by Andrew Zaborowski

[permalink] [raw]
Subject: [PATCH 2/3][RFC] cfg80211: Pass new RSSI level in CQM RSSI notification

Update the drivers to pass the RSSI level as a cfg80211_cqm_rssi_notify
parameter and pass this value to userspace in a new nl80211 attribute.
This helps both userspace and also helps in the implementation of the
multiple RSSI thresholds CQM mechanism.

Note for marvell/mwifiexe I pass 0 for the RSSI value because the new
RSSI value is not available to the driver at the time of the
cfg80211_cqm_rssi_notify call, but the driver queries the new value
immediately after that, so it is actually available just a moment later
if we wanted to defer caling cfg80211_cqm_rssi_notify until that moment.
Without this, the new cfg80211 code (patch 3) will call .get_station
which will send a duplicate HostCmd_CMD_RSSI_INFO command to the hardware.
---
drivers/net/wireless/marvell/mwifiex/sta_event.c | 4 ++--
drivers/net/wireless/rndis_wlan.c | 2 +-
include/net/cfg80211.h | 3 ++-
include/uapi/linux/nl80211.h | 3 +++
net/mac80211/mlme.c | 2 +-
net/wireless/nl80211.c | 9 +++++++--
net/wireless/trace.h | 11 +++++++----
7 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/drivers/net/wireless/marvell/mwifiex/sta_event.c b/drivers/net/wireless/marvell/mwifiex/sta_event.c
index 9df0c4d..5cc3aa7 100644
--- a/drivers/net/wireless/marvell/mwifiex/sta_event.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_event.c
@@ -824,7 +824,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
case EVENT_RSSI_LOW:
cfg80211_cqm_rssi_notify(priv->netdev,
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
- GFP_KERNEL);
+ 0, GFP_KERNEL);
mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO,
HostCmd_ACT_GEN_GET, 0, NULL, false);
priv->subsc_evt_rssi_state = RSSI_LOW_RECVD;
@@ -839,7 +839,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
case EVENT_RSSI_HIGH:
cfg80211_cqm_rssi_notify(priv->netdev,
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
- GFP_KERNEL);
+ 0, GFP_KERNEL);
mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO,
HostCmd_ACT_GEN_GET, 0, NULL, false);
priv->subsc_evt_rssi_state = RSSI_HIGH_RECVD;
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 603c904..785334f 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -3187,7 +3187,7 @@ static void rndis_do_cqm(struct usbnet *usbdev, s32 rssi)
return;

priv->last_cqm_event_rssi = rssi;
- cfg80211_cqm_rssi_notify(usbdev->net, event, GFP_KERNEL);
+ cfg80211_cqm_rssi_notify(usbdev->net, event, rssi, GFP_KERNEL);
}

#define DEVICE_POLLER_JIFFIES (HZ)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index bd19faa..632dce1 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -5167,6 +5167,7 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
* cfg80211_cqm_rssi_notify - connection quality monitoring rssi event
* @dev: network device
* @rssi_event: the triggered RSSI event
+ * @rssi_level: new RSSI level value or 0 if not available
* @gfp: context flags
*
* This function is called when a configured connection quality monitoring
@@ -5174,7 +5175,7 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
*/
void cfg80211_cqm_rssi_notify(struct net_device *dev,
enum nl80211_cqm_rssi_threshold_event rssi_event,
- gfp_t gfp);
+ s32 rssi_level, gfp_t gfp);

/**
* cfg80211_cqm_pktloss_notify - notify userspace about packetloss to peer
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 56368e9..48108fd 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -3859,6 +3859,8 @@ 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_RSSI_LEVEL: the RSSI value in dBm that triggered the
+ * RSSI threshold event.
* @__NL80211_ATTR_CQM_AFTER_LAST: internal
* @NL80211_ATTR_CQM_MAX: highest key attribute
*/
@@ -3872,6 +3874,7 @@ enum nl80211_attr_cqm {
NL80211_ATTR_CQM_TXE_PKTS,
NL80211_ATTR_CQM_TXE_INTVL,
NL80211_ATTR_CQM_BEACON_LOSS_EVENT,
+ NL80211_ATTR_CQM_RSSI_LEVEL,

/* keep last */
__NL80211_ATTR_CQM_AFTER_LAST,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index a55cdd7..6898ecb 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -5007,7 +5007,7 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,

trace_api_cqm_rssi_notify(sdata, rssi_event, rssi_level);

- cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
+ cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, rssi_level, gfp);
}
EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index c510810..5d10774 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -9278,6 +9278,7 @@ 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_RSSI_LEVEL] = { .type = NLA_S32 },
};

static int nl80211_set_cqm_txe(struct genl_info *info,
@@ -13737,11 +13738,11 @@ static void cfg80211_send_cqm(struct sk_buff *msg, gfp_t gfp)

void cfg80211_cqm_rssi_notify(struct net_device *dev,
enum nl80211_cqm_rssi_threshold_event rssi_event,
- gfp_t gfp)
+ s32 rssi_level, gfp_t gfp)
{
struct sk_buff *msg;

- trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
+ trace_cfg80211_cqm_rssi_notify(dev, rssi_event, rssi_level);

if (WARN_ON(rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW &&
rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH))
@@ -13755,6 +13756,10 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
rssi_event))
goto nla_put_failure;

+ if (rssi_level && nla_put_s32(msg, NL80211_ATTR_CQM_RSSI_LEVEL,
+ rssi_level))
+ goto nla_put_failure;
+
cfg80211_send_cqm(msg, gfp);

return;
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index a3d0a91..6ac46a0 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -2472,18 +2472,21 @@ TRACE_EVENT(cfg80211_mgmt_tx_status,

TRACE_EVENT(cfg80211_cqm_rssi_notify,
TP_PROTO(struct net_device *netdev,
- enum nl80211_cqm_rssi_threshold_event rssi_event),
- TP_ARGS(netdev, rssi_event),
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ s32 rssi_level),
+ TP_ARGS(netdev, rssi_event, rssi_level),
TP_STRUCT__entry(
NETDEV_ENTRY
__field(enum nl80211_cqm_rssi_threshold_event, rssi_event)
+ __field(s32, rssi_level)
),
TP_fast_assign(
NETDEV_ASSIGN;
__entry->rssi_event = rssi_event;
+ __entry->rssi_level = rssi_level;
),
- TP_printk(NETDEV_PR_FMT ", rssi event: %d",
- NETDEV_PR_ARG, __entry->rssi_event)
+ TP_printk(NETDEV_PR_FMT ", rssi event: %d, level: %d",
+ NETDEV_PR_ARG, __entry->rssi_event, __entry->rssi_level)
);

TRACE_EVENT(cfg80211_reg_can_beacon,
--
2.7.4

2016-11-28 18:06:07

by Andrew Zaborowski

[permalink] [raw]
Subject: Re: [PATCH 3/3][RFC] nl80211/mac80211: Accept multiple RSSI thresholds for CQM

On 28 November 2016 at 16:35, Johannes Berg <[email protected]> wrote:
> On Mon, 2016-11-28 at 16:29 +0100, Andrew Zaborowski wrote:
>> In order to keep the hardware offload feature when working with
>> hardware that can only offload the old single-thresholds method, but
>> where the kernel also wants to support the new method by looking at
>> all the beacons in software. IIRC I identified just one driver that
>> would be in this situation: wl1271.
>
> IMHO it would be better to not allow that. I'd think that wl1271 is
> only used in devices where power consumption will be far more
> interesting than providing this kind of API.

Ok.

>
>> This is a specific case and the semantics implemented by the wl1271
>> may be a little different from those in the rest of the drivers so
>> this may be worth very little. I can change the comment to imply
>> only one method should be implemented.
>
> We might still have to allow both to be present for mac80211 though.
>
>> > Seems there still should be a hysteresis? Or am I misunderstanding
>> > the
>> > intent here? I.e. isn't it meant to report low/medium/high later?
>>
>> This isn't exposed directly to users, instead it's used by the code
>> in
>> nl80211.c which will always reset the thresholds when either
>> threshold
>> is crossed. The hysteresis can then be either handled in nl80211.c
>> (factored into the threshold values) or in the firmware/driver, this
>> won't change the number of wakeups.
>
> That's only if you assume you can actually react to this fast enough
> though, no? If I offload this, I'd want to also offload a hysteresis to
> firmware, I'd think.

I wasn't clear: nl80211 sets the thresholds so that "high" is higher
than last known value and "low" is lower than last known value, also
the distance is at least 2 x hysteresis. There's no purpose for
reporting "middle" rssi events because we have to set a new range as
soon as we receive a high or a low event. I realize I need to
document better.

So I don't think this can result in additional events that wouldn't
occur if the firmware handled rssi hysteresis. I think this is
generic enough that you can implement any monitoring logic on top of
it and squeeze the same number of wakeup in all scenarios as if the
driver handled it. But it shouldn't discriminate hardware that
doesn't have the hysteresis parameter from offloading this.

BTW I fear if you wanted to have the hysteresis parameter handled by
firmware you'd end up with slightly differing semantics depending of
what the firmware does the moment you change you threshold values,
whether the rssi tracking is reset.

Best regards

2016-11-28 15:29:54

by Andrew Zaborowski

[permalink] [raw]
Subject: Re: [PATCH 3/3][RFC] nl80211/mac80211: Accept multiple RSSI thresholds for CQM

Hi Johannes,

On 28 November 2016 at 15:47, Johannes Berg <[email protected]> wrote:
>
>> + * @set_cqm_rssi_range_config: Configure two RSSI thresholds in the
>> > + * connection quality monitor. Even if the driver implements both the
>> > + * single threshold and low/high thresholds mechanisms, it should assume
>> + * only one is active at any time.
>
> Why would a driver still (be allowed to!) implement both?

In order to keep the hardware offload feature when working with
hardware that can only offload the old single-thresholds method, but
where the kernel also wants to support the new method by looking at
all the beacons in software. IIRC I identified just one driver that
would be in this situation: wl1271.

This is a specific case and the semantics implemented by the wl1271
may be a little different from those in the rest of the drivers so
this may be worth very little. I can change the comment to imply only
one method should be implemented.

>
>> + int (*set_cqm_rssi_range_config)(struct wiphy *wiphy,
>> + struct net_device *dev,
>> + s32 rssi_low, s32 rssi_high);
>
> Seems there still should be a hysteresis? Or am I misunderstanding the
> intent here? I.e. isn't it meant to report low/medium/high later?

This isn't exposed directly to users, instead it's used by the code in
nl80211.c which will always reset the thresholds when either threshold
is crossed. The hysteresis can then be either handled in nl80211.c
(factored into the threshold values) or in the firmware/driver, this
won't change the number of wakeups.

It's probably easier to do it in one place and keep it simple on the drivers?

>
>> diff --git a/include/net/mac80211.h b/include/net/mac80211.h
>> index 33026e1..7da1056 100644
>> --- a/include/net/mac80211.h
>> +++ b/include/net/mac80211.h
>
> I'd prefer you split cfg80211 and mac80211 patches, i.e. provide the
> new API first and then implement it in mac80211 separately.

Yes, will do, only as this is purely an RFC I preferred to keep the
conext together.

>
>> +void cfg80211_cqm_config_free(struct wireless_dev *wdev)
>> +{
>> + if (!wdev->cqm_config)
>> + return;
>> +
>> + kfree(wdev->cqm_config->rssi_thresholds);
>> + kfree(wdev->cqm_config);
>> + wdev->cqm_config = NULL;
>> +}
>
> You can save this complexity by just making the cqm_config struct have
> all the thresholds inside itself - pretty easy to allocate by just
> counting them first.

Ok, guess I can do that and let last_rssi_event_value get reset when
the thresholds are reconfigured.

Best regards

2016-11-28 14:48:02

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 3/3][RFC] nl80211/mac80211: Accept multiple RSSI thresholds for CQM


> + * @set_cqm_rssi_range_config: Configure two RSSI thresholds in the
> > + * connection quality monitor.  Even if the driver implements both the
> > + * single threshold and low/high thresholds mechanisms, it should assume
> + * only one is active at any time.

Why would a driver still (be allowed to!) implement both?

> + int (*set_cqm_rssi_range_config)(struct wiphy *wiphy,
> +      struct net_device *dev,
> +      s32 rssi_low, s32 rssi_high);

Seems there still should be a hysteresis? Or am I misunderstanding the
intent here? I.e. isn't it meant to report low/medium/high later?

> diff --git a/include/net/mac80211.h b/include/net/mac80211.h
> index 33026e1..7da1056 100644
> --- a/include/net/mac80211.h
> +++ b/include/net/mac80211.h

I'd prefer you split cfg80211 and mac80211 patches, i.e. provide the
new API first and then implement it in mac80211 separately.

> +void cfg80211_cqm_config_free(struct wireless_dev *wdev)
> +{
> + if (!wdev->cqm_config)
> + return;
> +
> + kfree(wdev->cqm_config->rssi_thresholds);
> + kfree(wdev->cqm_config);
> + wdev->cqm_config = NULL;
> +}

You can save this complexity by just making the cqm_config struct have
all the thresholds inside itself - pretty easy to allocate by just
counting them first.

johannes

2016-11-06 02:08:51

by Andrew Zaborowski

[permalink] [raw]
Subject: [PATCH 1/3][RFC] mac80211: Pass new RSSI level in CQM RSSI notification

---
drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 2 ++
drivers/net/wireless/rsi/rsi_91x_mac80211.c | 2 +-
drivers/net/wireless/st/cw1200/sta.c | 2 +-
drivers/net/wireless/ti/wl1251/event.c | 4 ++--
drivers/net/wireless/ti/wlcore/event.c | 3 ++-
include/net/mac80211.h | 2 ++
net/mac80211/mlme.c | 7 ++++---
net/mac80211/trace.h | 11 +++++++----
8 files changed, 21 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
index 0e60e38..e06a2e3 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
@@ -571,6 +571,7 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
ieee80211_cqm_rssi_notify(
vif,
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+ sig,
GFP_KERNEL);
} else if (sig > thold &&
(last_event == 0 || sig > last_event + hyst)) {
@@ -580,6 +581,7 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
ieee80211_cqm_rssi_notify(
vif,
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+ sig,
GFP_KERNEL);
}
}
diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
index dbb2389..3e260b4 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
@@ -818,7 +818,7 @@ static void rsi_perform_cqm(struct rsi_common *common,

common->cqm_info.last_cqm_event_rssi = rssi;
rsi_dbg(INFO_ZONE, "CQM: Notifying event: %d\n", event);
- ieee80211_cqm_rssi_notify(adapter->vifs[0], event, GFP_KERNEL);
+ ieee80211_cqm_rssi_notify(adapter->vifs[0], event, rssi, GFP_KERNEL);

return;
}
diff --git a/drivers/net/wireless/st/cw1200/sta.c b/drivers/net/wireless/st/cw1200/sta.c
index daf06a4..a522248 100644
--- a/drivers/net/wireless/st/cw1200/sta.c
+++ b/drivers/net/wireless/st/cw1200/sta.c
@@ -1019,7 +1019,7 @@ void cw1200_event_handler(struct work_struct *work)
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
pr_debug("[CQM] RSSI event: %d.\n", rcpi_rssi);
- ieee80211_cqm_rssi_notify(priv->vif, cqm_evt,
+ ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, rcpi_rssi,
GFP_KERNEL);
break;
}
diff --git a/drivers/net/wireless/ti/wl1251/event.c b/drivers/net/wireless/ti/wl1251/event.c
index d0593bc..f5acd24 100644
--- a/drivers/net/wireless/ti/wl1251/event.c
+++ b/drivers/net/wireless/ti/wl1251/event.c
@@ -150,7 +150,7 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox)
"ROAMING_TRIGGER_LOW_RSSI_EVENT");
ieee80211_cqm_rssi_notify(wl->vif,
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
- GFP_KERNEL);
+ 0, GFP_KERNEL);
}

if (vector & ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID) {
@@ -158,7 +158,7 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox)
"ROAMING_TRIGGER_REGAINED_RSSI_EVENT");
ieee80211_cqm_rssi_notify(wl->vif,
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
- GFP_KERNEL);
+ 0, GFP_KERNEL);
}
}

diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c
index 4b59f67..f2e90d2 100644
--- a/drivers/net/wireless/ti/wlcore/event.c
+++ b/drivers/net/wireless/ti/wlcore/event.c
@@ -129,7 +129,8 @@ void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr)

vif = wl12xx_wlvif_to_vif(wlvif);
if (event != wlvif->last_rssi_event)
- ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL);
+ ieee80211_cqm_rssi_notify(vif, event, metric,
+ GFP_KERNEL);
wlvif->last_rssi_event = event;
}
}
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index a810dfc..33026e1 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -5244,6 +5244,7 @@ void ieee80211_resume_disconnect(struct ieee80211_vif *vif);
*
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
* @rssi_event: the RSSI trigger event type
+ * @rssi_level: new RSSI level value or 0 if not available
* @gfp: context flags
*
* When the %IEEE80211_VIF_SUPPORTS_CQM_RSSI is set, and a connection quality
@@ -5252,6 +5253,7 @@ void ieee80211_resume_disconnect(struct ieee80211_vif *vif);
*/
void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
enum nl80211_cqm_rssi_threshold_event rssi_event,
+ s32 rssi_level,
gfp_t gfp);

/**
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 7486f2d..a55cdd7 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -3405,14 +3405,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ieee80211_cqm_rssi_notify(
&sdata->vif,
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
- GFP_KERNEL);
+ sig, GFP_KERNEL);
} else if (sig > thold &&
(last_event == 0 || sig > last_event + hyst)) {
ifmgd->last_cqm_event_signal = sig;
ieee80211_cqm_rssi_notify(
&sdata->vif,
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
- GFP_KERNEL);
+ sig, GFP_KERNEL);
}
}

@@ -5000,11 +5000,12 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)

void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
enum nl80211_cqm_rssi_threshold_event rssi_event,
+ s32 rssi_level,
gfp_t gfp)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);

- trace_api_cqm_rssi_notify(sdata, rssi_event);
+ trace_api_cqm_rssi_notify(sdata, rssi_event, rssi_level);

cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
}
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 92a47af..f78d9f4 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -1996,23 +1996,26 @@ TRACE_EVENT(api_connection_loss,

TRACE_EVENT(api_cqm_rssi_notify,
TP_PROTO(struct ieee80211_sub_if_data *sdata,
- enum nl80211_cqm_rssi_threshold_event rssi_event),
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ s32 rssi_level),

- TP_ARGS(sdata, rssi_event),
+ TP_ARGS(sdata, rssi_event, rssi_level),

TP_STRUCT__entry(
VIF_ENTRY
__field(u32, rssi_event)
+ __field(s32, rssi_level)
),

TP_fast_assign(
VIF_ASSIGN;
__entry->rssi_event = rssi_event;
+ __entry->rssi_level = rssi_level;
),

TP_printk(
- VIF_PR_FMT " event:%d",
- VIF_PR_ARG, __entry->rssi_event
+ VIF_PR_FMT " event:%d rssi:%d",
+ VIF_PR_ARG, __entry->rssi_event, __entry->rssi_level
)
);

--
2.7.4

2016-12-13 16:12:01

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 3/3][RFC] nl80211/mac80211: Accept multiple RSSI thresholds for CQM


> I wasn't clear: nl80211 sets the thresholds so that "high" is higher
> than last known value and "low" is lower than last known value, also
> the distance is at least 2 x hysteresis.  There's no purpose for
> reporting "middle" rssi events because we have to set a new range as
> soon as we receive a high or a low event.  I realize I need to
> document better.

But there can be a delay between reporting and reprogramming, and if
during that time a new event could be reported? I guess it doesn't
matter much if we assume that upon reprogramming the driver will always
report a new event if the current value falls outside the new range
(either high or low)... it just seemed a little bit more consistent to
unconditionally report a new event at the beginning, even if that new
event is "yup - falling into the middle of your range now".

johannes

2016-12-18 00:18:18

by Andrew Zaborowski

[permalink] [raw]
Subject: Re: [PATCH 3/3][RFC] nl80211/mac80211: Accept multiple RSSI thresholds for CQM

On 13 December 2016 at 11:11, Johannes Berg <[email protected]> wrote:
>
>> I wasn't clear: nl80211 sets the thresholds so that "high" is higher
>> than last known value and "low" is lower than last known value, also
>> the distance is at least 2 x hysteresis. There's no purpose for
>> reporting "middle" rssi events because we have to set a new range as
>> soon as we receive a high or a low event. I realize I need to
>> document better.
>
> But there can be a delay between reporting and reprogramming, and if
> during that time a new event could be reported? I guess it doesn't
> matter much if we assume that upon reprogramming the driver will always
> report a new event if the current value falls outside the new range

That's the intention, I'm not sure if the comments in the code are
enough to make it clear.

> (either high or low)... it just seemed a little bit more consistent to
> unconditionally report a new event at the beginning, even if that new
> event is "yup - falling into the middle of your range now".

I can add a new value to the enum for "middle". I think we'd
definitely want it if it was a userspace API but this is the internal
api with just this one use. Also some drivers will have to query the
firmware for the new value and may need an additional wake-up for
every RSSI wake-up, doubling the amount of work.

Best regards