Commit 15d6030b authored by Sam Leffler's avatar Sam Leffler Committed by Johannes Berg

cfg80211: add support for flushing old scan results

Add an NL80211_SCAN_FLAG_FLUSH flag that causes old bss cache
entries to be flushed on scan completion. This is useful for
collecting guaranteed fresh scan/survey result (e.g. on resume).

For normal scan, flushing only happens on successful completion
of a scan; i.e. it does not happen if the scan is aborted.
For scheduled scan, previous scan results are flushed everytime
when we get new scan results.

This feature is enabled by default. Drivers can disable it by
unsetting the NL80211_FEATURE_SCAN_FLUSH flag.
Signed-off-by: default avatarSam Leffler <sleffler@chromium.org>
Tested-by: default avatarAmitkumar Karwar <akarwar@marvell.com>
Signed-off-by: default avatarAmitkumar Karwar <akarwar@marvell.com>
Signed-off-by: default avatarBing Zhao <bzhao@marvell.com>
[invert polarity of feature flag to account for old kernels]
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent b292219f
...@@ -1005,6 +1005,7 @@ struct cfg80211_ssid { ...@@ -1005,6 +1005,7 @@ struct cfg80211_ssid {
* @flags: bit field of flags controlling operation * @flags: bit field of flags controlling operation
* @rates: bitmap of rates to advertise for each band * @rates: bitmap of rates to advertise for each band
* @wiphy: the wiphy this was for * @wiphy: the wiphy this was for
* @scan_start: time (in jiffies) when the scan started
* @wdev: the wireless device to scan for * @wdev: the wireless device to scan for
* @aborted: (internal) scan request was notified as aborted * @aborted: (internal) scan request was notified as aborted
* @no_cck: used to send probe requests at non CCK rate in 2GHz band * @no_cck: used to send probe requests at non CCK rate in 2GHz band
...@@ -1023,6 +1024,7 @@ struct cfg80211_scan_request { ...@@ -1023,6 +1024,7 @@ struct cfg80211_scan_request {
/* internal */ /* internal */
struct wiphy *wiphy; struct wiphy *wiphy;
unsigned long scan_start;
bool aborted; bool aborted;
bool no_cck; bool no_cck;
...@@ -1074,6 +1076,7 @@ struct cfg80211_sched_scan_request { ...@@ -1074,6 +1076,7 @@ struct cfg80211_sched_scan_request {
/* internal */ /* internal */
struct wiphy *wiphy; struct wiphy *wiphy;
struct net_device *dev; struct net_device *dev;
unsigned long scan_start;
/* keep last */ /* keep last */
struct ieee80211_channel *channels[0]; struct ieee80211_channel *channels[0];
......
...@@ -3049,6 +3049,7 @@ enum nl80211_ap_sme_features { ...@@ -3049,6 +3049,7 @@ enum nl80211_ap_sme_features {
* equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station * equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station
* mode * mode
* @NL80211_FEATURE_LOW_PRIORITY_SCAN: This driver supports low priority scan * @NL80211_FEATURE_LOW_PRIORITY_SCAN: This driver supports low priority scan
* @NL80211_FEATURE_SCAN_FLUSH: Scan flush is supported
*/ */
enum nl80211_feature_flags { enum nl80211_feature_flags {
NL80211_FEATURE_SK_TX_STATUS = 1 << 0, NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
...@@ -3058,6 +3059,7 @@ enum nl80211_feature_flags { ...@@ -3058,6 +3059,7 @@ enum nl80211_feature_flags {
NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4, NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4,
NL80211_FEATURE_SAE = 1 << 5, NL80211_FEATURE_SAE = 1 << 5,
NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6, NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6,
NL80211_FEATURE_SCAN_FLUSH = 1 << 7,
}; };
/** /**
...@@ -3100,9 +3102,11 @@ enum nl80211_connect_failed_reason { ...@@ -3100,9 +3102,11 @@ enum nl80211_connect_failed_reason {
* requests. * requests.
* *
* @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority * @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority
* @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning
*/ */
enum nl80211_scan_flags { enum nl80211_scan_flags {
NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0, NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
NL80211_SCAN_FLAG_FLUSH = 1<<1,
}; };
#endif /* __LINUX_NL80211_H */ #endif /* __LINUX_NL80211_H */
...@@ -370,6 +370,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) ...@@ -370,6 +370,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
rdev->wiphy.rts_threshold = (u32) -1; rdev->wiphy.rts_threshold = (u32) -1;
rdev->wiphy.coverage_class = 0; rdev->wiphy.coverage_class = 0;
rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH;
return &rdev->wiphy; return &rdev->wiphy;
} }
EXPORT_SYMBOL(wiphy_new); EXPORT_SYMBOL(wiphy_new);
......
...@@ -4371,8 +4371,10 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) ...@@ -4371,8 +4371,10 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) { if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
request->flags = nla_get_u32( request->flags = nla_get_u32(
info->attrs[NL80211_ATTR_SCAN_FLAGS]); info->attrs[NL80211_ATTR_SCAN_FLAGS]);
if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
!(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) { !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
((request->flags & NL80211_SCAN_FLAG_FLUSH) &&
!(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) {
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto out_free; goto out_free;
} }
...@@ -4383,6 +4385,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) ...@@ -4383,6 +4385,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
request->wdev = wdev; request->wdev = wdev;
request->wiphy = &rdev->wiphy; request->wiphy = &rdev->wiphy;
request->scan_start = jiffies;
rdev->scan_req = request; rdev->scan_req = request;
err = rdev->ops->scan(&rdev->wiphy, request); err = rdev->ops->scan(&rdev->wiphy, request);
...@@ -4612,8 +4615,10 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, ...@@ -4612,8 +4615,10 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) { if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
request->flags = nla_get_u32( request->flags = nla_get_u32(
info->attrs[NL80211_ATTR_SCAN_FLAGS]); info->attrs[NL80211_ATTR_SCAN_FLAGS]);
if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
!(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) { !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
((request->flags & NL80211_SCAN_FLAG_FLUSH) &&
!(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) {
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto out_free; goto out_free;
} }
...@@ -4622,6 +4627,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, ...@@ -4622,6 +4627,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
request->dev = dev; request->dev = dev;
request->wiphy = &rdev->wiphy; request->wiphy = &rdev->wiphy;
request->interval = interval; request->interval = interval;
request->scan_start = jiffies;
err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
if (!err) { if (!err) {
......
...@@ -47,6 +47,27 @@ static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, ...@@ -47,6 +47,27 @@ static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,
kref_put(&bss->ref, bss_release); kref_put(&bss->ref, bss_release);
} }
/* must hold dev->bss_lock! */
static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev,
unsigned long expire_time)
{
struct cfg80211_internal_bss *bss, *tmp;
bool expired = false;
list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
if (atomic_read(&bss->hold))
continue;
if (!time_after(expire_time, bss->ts))
continue;
__cfg80211_unlink_bss(dev, bss);
expired = true;
}
if (expired)
dev->bss_generation++;
}
void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)
{ {
struct cfg80211_scan_request *request; struct cfg80211_scan_request *request;
...@@ -72,10 +93,17 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) ...@@ -72,10 +93,17 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)
if (wdev->netdev) if (wdev->netdev)
cfg80211_sme_scan_done(wdev->netdev); cfg80211_sme_scan_done(wdev->netdev);
if (request->aborted) if (request->aborted) {
nl80211_send_scan_aborted(rdev, wdev); nl80211_send_scan_aborted(rdev, wdev);
else } else {
if (request->flags & NL80211_SCAN_FLAG_FLUSH) {
/* flush entries from previous scans */
spin_lock_bh(&rdev->bss_lock);
__cfg80211_bss_expire(rdev, request->scan_start);
spin_unlock_bh(&rdev->bss_lock);
}
nl80211_send_scan_done(rdev, wdev); nl80211_send_scan_done(rdev, wdev);
}
#ifdef CONFIG_CFG80211_WEXT #ifdef CONFIG_CFG80211_WEXT
if (wdev->netdev && !request->aborted) { if (wdev->netdev && !request->aborted) {
...@@ -126,16 +154,27 @@ EXPORT_SYMBOL(cfg80211_scan_done); ...@@ -126,16 +154,27 @@ EXPORT_SYMBOL(cfg80211_scan_done);
void __cfg80211_sched_scan_results(struct work_struct *wk) void __cfg80211_sched_scan_results(struct work_struct *wk)
{ {
struct cfg80211_registered_device *rdev; struct cfg80211_registered_device *rdev;
struct cfg80211_sched_scan_request *request;
rdev = container_of(wk, struct cfg80211_registered_device, rdev = container_of(wk, struct cfg80211_registered_device,
sched_scan_results_wk); sched_scan_results_wk);
request = rdev->sched_scan_req;
mutex_lock(&rdev->sched_scan_mtx); mutex_lock(&rdev->sched_scan_mtx);
/* we don't have sched_scan_req anymore if the scan is stopping */ /* we don't have sched_scan_req anymore if the scan is stopping */
if (rdev->sched_scan_req) if (request) {
nl80211_send_sched_scan_results(rdev, if (request->flags & NL80211_SCAN_FLAG_FLUSH) {
rdev->sched_scan_req->dev); /* flush entries from previous scans */
spin_lock_bh(&rdev->bss_lock);
__cfg80211_bss_expire(rdev, request->scan_start);
spin_unlock_bh(&rdev->bss_lock);
request->scan_start =
jiffies + msecs_to_jiffies(request->interval);
}
nl80211_send_sched_scan_results(rdev, request->dev);
}
mutex_unlock(&rdev->sched_scan_mtx); mutex_unlock(&rdev->sched_scan_mtx);
} }
...@@ -197,23 +236,9 @@ void cfg80211_bss_age(struct cfg80211_registered_device *dev, ...@@ -197,23 +236,9 @@ void cfg80211_bss_age(struct cfg80211_registered_device *dev,
} }
} }
/* must hold dev->bss_lock! */
void cfg80211_bss_expire(struct cfg80211_registered_device *dev) void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
{ {
struct cfg80211_internal_bss *bss, *tmp; __cfg80211_bss_expire(dev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE);
bool expired = false;
list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
if (atomic_read(&bss->hold))
continue;
if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE))
continue;
__cfg80211_unlink_bss(dev, bss);
expired = true;
}
if (expired)
dev->bss_generation++;
} }
const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len) const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len)
...@@ -962,6 +987,7 @@ int cfg80211_wext_siwscan(struct net_device *dev, ...@@ -962,6 +987,7 @@ int cfg80211_wext_siwscan(struct net_device *dev,
creq->ssids = (void *)&creq->channels[n_channels]; creq->ssids = (void *)&creq->channels[n_channels];
creq->n_channels = n_channels; creq->n_channels = n_channels;
creq->n_ssids = 1; creq->n_ssids = 1;
creq->scan_start = jiffies;
/* translate "Scan on frequencies" request */ /* translate "Scan on frequencies" request */
i = 0; i = 0;
......
...@@ -138,6 +138,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) ...@@ -138,6 +138,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
request->wdev = wdev; request->wdev = wdev;
request->wiphy = &rdev->wiphy; request->wiphy = &rdev->wiphy;
request->scan_start = jiffies;
rdev->scan_req = request; rdev->scan_req = request;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment