summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArend Van Spriel <arend.vanspriel@broadcom.com>2017-04-21 08:05:00 -0400
committerJohannes Berg <johannes.berg@intel.com>2017-04-26 17:17:38 -0400
commitca986ad9bcd3893c8b0b4cc2cafcc8cf1554409c (patch)
tree307efb9026256dada2b4bce49cfa087018e640b1
parentab81007a7b519d72f3c26d753a9fe1ffd27edc20 (diff)
nl80211: allow multiple active scheduled scan requests
This patch implements the idea to have multiple scheduled scan requests running concurrently. It mainly illustrates how to deal with the incoming request from user-space in terms of backward compatibility. In order to use multiple scheduled scans user-space needs to provide a flag attribute NL80211_ATTR_SCHED_SCAN_MULTI to indicate support. If not the request is treated as a legacy scan. Drivers currently supporting scheduled scan are now indicating they support a single scheduled scan request. This obsoletes WIPHY_FLAG_SUPPORTS_SCHED_SCAN. Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com> Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com> Reviewed-by: Franky Lin <franky.lin@broadcom.com> Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com> [clean up netlink destroy path to avoid allocations, code cleanups] Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c2
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cfg80211.c2
-rw-r--r--drivers/net/wireless/ti/wlcore/main.c2
-rw-r--r--include/net/cfg80211.h9
-rw-r--r--include/uapi/linux/nl80211.h12
-rw-r--r--net/wireless/core.c29
-rw-r--r--net/wireless/core.h11
-rw-r--r--net/wireless/nl80211.c63
-rw-r--r--net/wireless/rdev-ops.h2
-rw-r--r--net/wireless/scan.c115
-rw-r--r--net/wireless/trace.h18
13 files changed, 205 insertions, 64 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 0c118b7c362c..1906412afa70 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -3973,7 +3973,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
3973 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; 3973 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
3974 3974
3975 if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, ar->fw_capabilities)) 3975 if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, ar->fw_capabilities))
3976 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; 3976 ar->wiphy->max_sched_scan_reqs = 1;
3977 3977
3978 if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT, 3978 if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT,
3979 ar->fw_capabilities)) 3979 ar->fw_capabilities))
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 8c7f1ef288c6..c71173dc9965 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -6374,11 +6374,11 @@ err:
6374static void brcmf_wiphy_pno_params(struct wiphy *wiphy) 6374static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
6375{ 6375{
6376 /* scheduled scan settings */ 6376 /* scheduled scan settings */
6377 wiphy->max_sched_scan_reqs = 1;
6377 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT; 6378 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
6378 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT; 6379 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
6379 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; 6380 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
6380 wiphy->max_sched_scan_plan_interval = BRCMF_PNO_SCHED_SCAN_MAX_PERIOD; 6381 wiphy->max_sched_scan_plan_interval = BRCMF_PNO_SCHED_SCAN_MAX_PERIOD;
6381 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
6382} 6382}
6383 6383
6384#ifdef CONFIG_PM 6384#ifdef CONFIG_PM
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 5cdd95775ba6..8c58d47100a0 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -620,7 +620,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
620 else 620 else
621 hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; 621 hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
622 622
623 hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; 623 hw->wiphy->max_sched_scan_reqs = 1;
624 hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX; 624 hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
625 hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES; 625 hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES;
626 /* we create the 802.11 header and zero length SSID IE. */ 626 /* we create the 802.11 header and zero length SSID IE. */
diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
index 49b4c805b7d5..9927bd5aac56 100644
--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -4297,7 +4297,6 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
4297 wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | 4297 wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
4298 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | 4298 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
4299 WIPHY_FLAG_AP_UAPSD | 4299 WIPHY_FLAG_AP_UAPSD |
4300 WIPHY_FLAG_SUPPORTS_SCHED_SCAN |
4301 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | 4300 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
4302 WIPHY_FLAG_HAS_CHANNEL_SWITCH | 4301 WIPHY_FLAG_HAS_CHANNEL_SWITCH |
4303 WIPHY_FLAG_PS_ON_BY_DEFAULT; 4302 WIPHY_FLAG_PS_ON_BY_DEFAULT;
@@ -4316,6 +4315,7 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
4316 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | 4315 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
4317 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; 4316 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
4318 4317
4318 wiphy->max_sched_scan_reqs = 1;
4319 wiphy->max_sched_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH; 4319 wiphy->max_sched_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH;
4320 wiphy->max_sched_scan_ie_len = MWIFIEX_MAX_VSIE_LEN; 4320 wiphy->max_sched_scan_ie_len = MWIFIEX_MAX_VSIE_LEN;
4321 wiphy->max_match_sets = MWIFIEX_MAX_SSID_LIST_LENGTH; 4321 wiphy->max_match_sets = MWIFIEX_MAX_SSID_LIST_LENGTH;
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index a21fda910529..382ec15ec1af 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -6128,6 +6128,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
6128 wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - 6128 wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
6129 sizeof(struct ieee80211_header); 6129 sizeof(struct ieee80211_header);
6130 6130
6131 wl->hw->wiphy->max_sched_scan_reqs = 1;
6131 wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - 6132 wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
6132 sizeof(struct ieee80211_header); 6133 sizeof(struct ieee80211_header);
6133 6134
@@ -6135,7 +6136,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
6135 6136
6136 wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD | 6137 wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |
6137 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | 6138 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
6138 WIPHY_FLAG_SUPPORTS_SCHED_SCAN |
6139 WIPHY_FLAG_HAS_CHANNEL_SWITCH; 6139 WIPHY_FLAG_HAS_CHANNEL_SWITCH;
6140 6140
6141 wl->hw->wiphy->features |= NL80211_FEATURE_AP_SCAN; 6141 wl->hw->wiphy->features |= NL80211_FEATURE_AP_SCAN;
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index af958938b3b1..43c0f389c273 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1678,6 +1678,8 @@ struct cfg80211_bss_select_adjust {
1678 * @rcu_head: RCU callback used to free the struct 1678 * @rcu_head: RCU callback used to free the struct
1679 * @owner_nlportid: netlink portid of owner (if this should is a request 1679 * @owner_nlportid: netlink portid of owner (if this should is a request
1680 * owned by a particular socket) 1680 * owned by a particular socket)
1681 * @nl_owner_dead: netlink owner socket was closed - this request be freed
1682 * @list: for keeping list of requests.
1681 * @delay: delay in seconds to use before starting the first scan 1683 * @delay: delay in seconds to use before starting the first scan
1682 * cycle. The driver may ignore this parameter and start 1684 * cycle. The driver may ignore this parameter and start
1683 * immediately (or at any other time), if this feature is not 1685 * immediately (or at any other time), if this feature is not
@@ -1722,6 +1724,8 @@ struct cfg80211_sched_scan_request {
1722 unsigned long scan_start; 1724 unsigned long scan_start;
1723 struct rcu_head rcu_head; 1725 struct rcu_head rcu_head;
1724 u32 owner_nlportid; 1726 u32 owner_nlportid;
1727 bool nl_owner_dead;
1728 struct list_head list;
1725 1729
1726 /* keep last */ 1730 /* keep last */
1727 struct ieee80211_channel *channels[0]; 1731 struct ieee80211_channel *channels[0];
@@ -3213,7 +3217,7 @@ enum wiphy_flags {
3213 WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7), 3217 WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7),
3214 WIPHY_FLAG_IBSS_RSN = BIT(8), 3218 WIPHY_FLAG_IBSS_RSN = BIT(8),
3215 WIPHY_FLAG_MESH_AUTH = BIT(10), 3219 WIPHY_FLAG_MESH_AUTH = BIT(10),
3216 WIPHY_FLAG_SUPPORTS_SCHED_SCAN = BIT(11), 3220 /* use hole at 11 */
3217 /* use hole at 12 */ 3221 /* use hole at 12 */
3218 WIPHY_FLAG_SUPPORTS_FW_ROAM = BIT(13), 3222 WIPHY_FLAG_SUPPORTS_FW_ROAM = BIT(13),
3219 WIPHY_FLAG_AP_UAPSD = BIT(14), 3223 WIPHY_FLAG_AP_UAPSD = BIT(14),
@@ -3551,6 +3555,8 @@ struct wiphy_iftype_ext_capab {
3551 * this variable determines its size 3555 * this variable determines its size
3552 * @max_scan_ssids: maximum number of SSIDs the device can scan for in 3556 * @max_scan_ssids: maximum number of SSIDs the device can scan for in
3553 * any given scan 3557 * any given scan
3558 * @max_sched_scan_reqs: maximum number of scheduled scan requests that
3559 * the device can run concurrently.
3554 * @max_sched_scan_ssids: maximum number of SSIDs the device can scan 3560 * @max_sched_scan_ssids: maximum number of SSIDs the device can scan
3555 * for in any given scheduled scan 3561 * for in any given scheduled scan
3556 * @max_match_sets: maximum number of match sets the device can handle 3562 * @max_match_sets: maximum number of match sets the device can handle
@@ -3687,6 +3693,7 @@ struct wiphy {
3687 3693
3688 int bss_priv_size; 3694 int bss_priv_size;
3689 u8 max_scan_ssids; 3695 u8 max_scan_ssids;
3696 u8 max_sched_scan_reqs;
3690 u8 max_sched_scan_ssids; 3697 u8 max_sched_scan_ssids;
3691 u8 max_match_sets; 3698 u8 max_match_sets;
3692 u16 max_scan_ie_len; 3699 u16 max_scan_ie_len;
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 6095a6c4c412..f34127d241e5 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -387,7 +387,9 @@
387 * are used. Extra IEs can also be passed from the userspace by 387 * are used. Extra IEs can also be passed from the userspace by
388 * using the %NL80211_ATTR_IE attribute. The first cycle of the 388 * using the %NL80211_ATTR_IE attribute. The first cycle of the
389 * scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY 389 * scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY
390 * is supplied. 390 * is supplied. If the device supports multiple concurrent scheduled
391 * scans, it will allow such when the caller provides the flag attribute
392 * %NL80211_ATTR_SCHED_SCAN_MULTI to indicate user-space support for it.
391 * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if 393 * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if
392 * scheduled scan is not running. The caller may assume that as soon 394 * scheduled scan is not running. The caller may assume that as soon
393 * as the call returns, it is safe to start a new scheduled scan again. 395 * as the call returns, it is safe to start a new scheduled scan again.
@@ -2081,6 +2083,11 @@ enum nl80211_commands {
2081 * @NL80211_ATTR_PMK: PMK for the PMKSA identified by %NL80211_ATTR_PMKID. 2083 * @NL80211_ATTR_PMK: PMK for the PMKSA identified by %NL80211_ATTR_PMKID.
2082 * This is used with @NL80211_CMD_SET_PMKSA. 2084 * This is used with @NL80211_CMD_SET_PMKSA.
2083 * 2085 *
2086 * @NL80211_ATTR_SCHED_SCAN_MULTI: flag attribute which user-space shall use to
2087 * indicate that it supports multiple active scheduled scan requests.
2088 * @NL80211_ATTR_SCHED_SCAN_MAX_REQS: indicates maximum number of scheduled
2089 * scan request that may be active for the device (u32).
2090 *
2084 * @NUM_NL80211_ATTR: total number of nl80211_attrs available 2091 * @NUM_NL80211_ATTR: total number of nl80211_attrs available
2085 * @NL80211_ATTR_MAX: highest attribute number currently defined 2092 * @NL80211_ATTR_MAX: highest attribute number currently defined
2086 * @__NL80211_ATTR_AFTER_LAST: internal use 2093 * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2500,6 +2507,9 @@ enum nl80211_attrs {
2500 2507
2501 NL80211_ATTR_PMK, 2508 NL80211_ATTR_PMK,
2502 2509
2510 NL80211_ATTR_SCHED_SCAN_MULTI,
2511 NL80211_ATTR_SCHED_SCAN_MAX_REQS,
2512
2503 /* add attributes here, update the policy in nl80211.c */ 2513 /* add attributes here, update the policy in nl80211.c */
2504 2514
2505 __NL80211_ATTR_AFTER_LAST, 2515 __NL80211_ATTR_AFTER_LAST,
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 4ea28de3a636..a3c0c48afb85 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -330,14 +330,16 @@ static void cfg80211_destroy_iface_wk(struct work_struct *work)
330static void cfg80211_sched_scan_stop_wk(struct work_struct *work) 330static void cfg80211_sched_scan_stop_wk(struct work_struct *work)
331{ 331{
332 struct cfg80211_registered_device *rdev; 332 struct cfg80211_registered_device *rdev;
333 struct cfg80211_sched_scan_request *req, *tmp;
333 334
334 rdev = container_of(work, struct cfg80211_registered_device, 335 rdev = container_of(work, struct cfg80211_registered_device,
335 sched_scan_stop_wk); 336 sched_scan_stop_wk);
336 337
337 rtnl_lock(); 338 rtnl_lock();
338 339 list_for_each_entry_safe(req, tmp, &rdev->sched_scan_req_list, list) {
339 __cfg80211_stop_sched_scan(rdev, false); 340 if (req->nl_owner_dead)
340 341 cfg80211_stop_sched_scan_req(rdev, req, false);
342 }
341 rtnl_unlock(); 343 rtnl_unlock();
342} 344}
343 345
@@ -452,6 +454,7 @@ use_default_name:
452 spin_lock_init(&rdev->beacon_registrations_lock); 454 spin_lock_init(&rdev->beacon_registrations_lock);
453 spin_lock_init(&rdev->bss_lock); 455 spin_lock_init(&rdev->bss_lock);
454 INIT_LIST_HEAD(&rdev->bss_list); 456 INIT_LIST_HEAD(&rdev->bss_list);
457 INIT_LIST_HEAD(&rdev->sched_scan_req_list);
455 INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); 458 INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
456 INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); 459 INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results);
457 INIT_LIST_HEAD(&rdev->mlme_unreg); 460 INIT_LIST_HEAD(&rdev->mlme_unreg);
@@ -1028,7 +1031,7 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
1028 struct wireless_dev *wdev) 1031 struct wireless_dev *wdev)
1029{ 1032{
1030 struct net_device *dev = wdev->netdev; 1033 struct net_device *dev = wdev->netdev;
1031 struct cfg80211_sched_scan_request *sched_scan_req; 1034 struct cfg80211_sched_scan_request *pos, *tmp;
1032 1035
1033 ASSERT_RTNL(); 1036 ASSERT_RTNL();
1034 ASSERT_WDEV_LOCK(wdev); 1037 ASSERT_WDEV_LOCK(wdev);
@@ -1039,9 +1042,11 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
1039 break; 1042 break;
1040 case NL80211_IFTYPE_P2P_CLIENT: 1043 case NL80211_IFTYPE_P2P_CLIENT:
1041 case NL80211_IFTYPE_STATION: 1044 case NL80211_IFTYPE_STATION:
1042 sched_scan_req = rtnl_dereference(rdev->sched_scan_req); 1045 list_for_each_entry_safe(pos, tmp, &rdev->sched_scan_req_list,
1043 if (sched_scan_req && dev == sched_scan_req->dev) 1046 list) {
1044 __cfg80211_stop_sched_scan(rdev, false); 1047 if (dev == pos->dev)
1048 cfg80211_stop_sched_scan_req(rdev, pos, false);
1049 }
1045 1050
1046#ifdef CONFIG_CFG80211_WEXT 1051#ifdef CONFIG_CFG80211_WEXT
1047 kfree(wdev->wext.ie); 1052 kfree(wdev->wext.ie);
@@ -1116,7 +1121,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
1116 struct net_device *dev = netdev_notifier_info_to_dev(ptr); 1121 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1117 struct wireless_dev *wdev = dev->ieee80211_ptr; 1122 struct wireless_dev *wdev = dev->ieee80211_ptr;
1118 struct cfg80211_registered_device *rdev; 1123 struct cfg80211_registered_device *rdev;
1119 struct cfg80211_sched_scan_request *sched_scan_req; 1124 struct cfg80211_sched_scan_request *pos, *tmp;
1120 1125
1121 if (!wdev) 1126 if (!wdev)
1122 return NOTIFY_DONE; 1127 return NOTIFY_DONE;
@@ -1193,10 +1198,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
1193 ___cfg80211_scan_done(rdev, false); 1198 ___cfg80211_scan_done(rdev, false);
1194 } 1199 }
1195 1200
1196 sched_scan_req = rtnl_dereference(rdev->sched_scan_req); 1201 list_for_each_entry_safe(pos, tmp,
1197 if (WARN_ON(sched_scan_req && 1202 &rdev->sched_scan_req_list, list) {
1198 sched_scan_req->dev == wdev->netdev)) { 1203 if (WARN_ON(pos && pos->dev == wdev->netdev))
1199 __cfg80211_stop_sched_scan(rdev, false); 1204 cfg80211_stop_sched_scan_req(rdev, pos, false);
1200 } 1205 }
1201 1206
1202 rdev->opencount--; 1207 rdev->opencount--;
diff --git a/net/wireless/core.h b/net/wireless/core.h
index f9b748e3425a..06eaf96053a8 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -74,7 +74,7 @@ struct cfg80211_registered_device {
74 u32 bss_entries; 74 u32 bss_entries;
75 struct cfg80211_scan_request *scan_req; /* protected by RTNL */ 75 struct cfg80211_scan_request *scan_req; /* protected by RTNL */
76 struct sk_buff *scan_msg; 76 struct sk_buff *scan_msg;
77 struct cfg80211_sched_scan_request __rcu *sched_scan_req; 77 struct list_head sched_scan_req_list;
78 unsigned long suspend_at; 78 unsigned long suspend_at;
79 struct work_struct scan_done_wk; 79 struct work_struct scan_done_wk;
80 struct work_struct sched_scan_results_wk; 80 struct work_struct sched_scan_results_wk;
@@ -416,9 +416,16 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
416void __cfg80211_scan_done(struct work_struct *wk); 416void __cfg80211_scan_done(struct work_struct *wk);
417void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, 417void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
418 bool send_message); 418 bool send_message);
419void cfg80211_add_sched_scan_req(struct cfg80211_registered_device *rdev,
420 struct cfg80211_sched_scan_request *req);
421int cfg80211_sched_scan_req_possible(struct cfg80211_registered_device *rdev,
422 bool want_multi);
419void __cfg80211_sched_scan_results(struct work_struct *wk); 423void __cfg80211_sched_scan_results(struct work_struct *wk);
424int cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev,
425 struct cfg80211_sched_scan_request *req,
426 bool driver_initiated);
420int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, 427int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
421 bool driver_initiated); 428 u64 reqid, bool driver_initiated);
422void cfg80211_upload_connect_keys(struct wireless_dev *wdev); 429void cfg80211_upload_connect_keys(struct wireless_dev *wdev);
423int cfg80211_change_iface(struct cfg80211_registered_device *rdev, 430int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
424 struct net_device *dev, enum nl80211_iftype ntype, 431 struct net_device *dev, enum nl80211_iftype ntype,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 45f5f418e562..ac7e2314f9ec 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -419,6 +419,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
419 .len = FILS_ERP_MAX_RRK_LEN }, 419 .len = FILS_ERP_MAX_RRK_LEN },
420 [NL80211_ATTR_FILS_CACHE_ID] = { .len = 2 }, 420 [NL80211_ATTR_FILS_CACHE_ID] = { .len = 2 },
421 [NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN }, 421 [NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN },
422 [NL80211_ATTR_SCHED_SCAN_MULTI] = { .type = NLA_FLAG },
422}; 423};
423 424
424/* policy for the key attributes */ 425/* policy for the key attributes */
@@ -1376,7 +1377,7 @@ static int nl80211_add_commands_unsplit(struct cfg80211_registered_device *rdev,
1376 CMD(tdls_mgmt, TDLS_MGMT); 1377 CMD(tdls_mgmt, TDLS_MGMT);
1377 CMD(tdls_oper, TDLS_OPER); 1378 CMD(tdls_oper, TDLS_OPER);
1378 } 1379 }
1379 if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) 1380 if (rdev->wiphy.max_sched_scan_reqs)
1380 CMD(sched_scan_start, START_SCHED_SCAN); 1381 CMD(sched_scan_start, START_SCHED_SCAN);
1381 CMD(probe_client, PROBE_CLIENT); 1382 CMD(probe_client, PROBE_CLIENT);
1382 CMD(set_noack_map, SET_NOACK_MAP); 1383 CMD(set_noack_map, SET_NOACK_MAP);
@@ -1815,6 +1816,11 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
1815 nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG)) 1816 nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
1816 goto nla_put_failure; 1817 goto nla_put_failure;
1817 1818
1819 if (rdev->wiphy.max_sched_scan_reqs &&
1820 nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_MAX_REQS,
1821 rdev->wiphy.max_sched_scan_reqs))
1822 goto nla_put_failure;
1823
1818 if (nla_put(msg, NL80211_ATTR_EXT_FEATURES, 1824 if (nla_put(msg, NL80211_ATTR_EXT_FEATURES,
1819 sizeof(rdev->wiphy.ext_features), 1825 sizeof(rdev->wiphy.ext_features),
1820 rdev->wiphy.ext_features)) 1826 rdev->wiphy.ext_features))
@@ -7336,14 +7342,16 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
7336 struct net_device *dev = info->user_ptr[1]; 7342 struct net_device *dev = info->user_ptr[1];
7337 struct wireless_dev *wdev = dev->ieee80211_ptr; 7343 struct wireless_dev *wdev = dev->ieee80211_ptr;
7338 struct cfg80211_sched_scan_request *sched_scan_req; 7344 struct cfg80211_sched_scan_request *sched_scan_req;
7345 bool want_multi;
7339 int err; 7346 int err;
7340 7347
7341 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || 7348 if (!rdev->wiphy.max_sched_scan_reqs || !rdev->ops->sched_scan_start)
7342 !rdev->ops->sched_scan_start)
7343 return -EOPNOTSUPP; 7349 return -EOPNOTSUPP;
7344 7350
7345 if (rdev->sched_scan_req) 7351 want_multi = info->attrs[NL80211_ATTR_SCHED_SCAN_MULTI];
7346 return -EINPROGRESS; 7352 err = cfg80211_sched_scan_req_possible(rdev, want_multi);
7353 if (err)
7354 return err;
7347 7355
7348 sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev, 7356 sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
7349 info->attrs, 7357 info->attrs,
@@ -7353,6 +7361,14 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
7353 if (err) 7361 if (err)
7354 goto out_err; 7362 goto out_err;
7355 7363
7364 /* leave request id zero for legacy request
7365 * or if driver does not support multi-scheduled scan
7366 */
7367 if (want_multi && rdev->wiphy.max_sched_scan_reqs > 1) {
7368 while (!sched_scan_req->reqid)
7369 sched_scan_req->reqid = rdev->wiphy.cookie_counter++;
7370 }
7371
7356 err = rdev_sched_scan_start(rdev, dev, sched_scan_req); 7372 err = rdev_sched_scan_start(rdev, dev, sched_scan_req);
7357 if (err) 7373 if (err)
7358 goto out_free; 7374 goto out_free;
@@ -7363,7 +7379,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
7363 if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) 7379 if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
7364 sched_scan_req->owner_nlportid = info->snd_portid; 7380 sched_scan_req->owner_nlportid = info->snd_portid;
7365 7381
7366 rcu_assign_pointer(rdev->sched_scan_req, sched_scan_req); 7382 cfg80211_add_sched_scan_req(rdev, sched_scan_req);
7367 7383
7368 nl80211_send_sched_scan(sched_scan_req, NL80211_CMD_START_SCHED_SCAN); 7384 nl80211_send_sched_scan(sched_scan_req, NL80211_CMD_START_SCHED_SCAN);
7369 return 0; 7385 return 0;
@@ -7377,13 +7393,27 @@ out_err:
7377static int nl80211_stop_sched_scan(struct sk_buff *skb, 7393static int nl80211_stop_sched_scan(struct sk_buff *skb,
7378 struct genl_info *info) 7394 struct genl_info *info)
7379{ 7395{
7396 struct cfg80211_sched_scan_request *req;
7380 struct cfg80211_registered_device *rdev = info->user_ptr[0]; 7397 struct cfg80211_registered_device *rdev = info->user_ptr[0];
7398 u64 cookie;
7381 7399
7382 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || 7400 if (!rdev->wiphy.max_sched_scan_reqs || !rdev->ops->sched_scan_stop)
7383 !rdev->ops->sched_scan_stop)
7384 return -EOPNOTSUPP; 7401 return -EOPNOTSUPP;
7385 7402
7386 return __cfg80211_stop_sched_scan(rdev, false); 7403 if (info->attrs[NL80211_ATTR_COOKIE]) {
7404 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
7405 return __cfg80211_stop_sched_scan(rdev, cookie, false);
7406 }
7407
7408 req = list_first_or_null_rcu(&rdev->sched_scan_req_list,
7409 struct cfg80211_sched_scan_request,
7410 list);
7411 if (!req || req->reqid ||
7412 (req->owner_nlportid &&
7413 req->owner_nlportid != info->snd_portid))
7414 return -ENOENT;
7415
7416 return cfg80211_stop_sched_scan_req(rdev, req, false);
7387} 7417}
7388 7418
7389static int nl80211_start_radar_detection(struct sk_buff *skb, 7419static int nl80211_start_radar_detection(struct sk_buff *skb,
@@ -14883,16 +14913,15 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
14883 rcu_read_lock(); 14913 rcu_read_lock();
14884 14914
14885 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { 14915 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
14886 struct cfg80211_sched_scan_request *sched_scan_req = 14916 struct cfg80211_sched_scan_request *sched_scan_req;
14887 rcu_dereference(rdev->sched_scan_req);
14888
14889 if (sched_scan_req && notify->portid &&
14890 sched_scan_req->owner_nlportid == notify->portid) {
14891 sched_scan_req->owner_nlportid = 0;
14892 14917
14893 if (rdev->ops->sched_scan_stop && 14918 list_for_each_entry_rcu(sched_scan_req,
14894 rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) 14919 &rdev->sched_scan_req_list,
14920 list) {
14921 if (sched_scan_req->owner_nlportid == notify->portid) {
14922 sched_scan_req->nl_owner_dead = true;
14895 schedule_work(&rdev->sched_scan_stop_wk); 14923 schedule_work(&rdev->sched_scan_stop_wk);
14924 }
14896 } 14925 }
14897 14926
14898 list_for_each_entry_rcu(wdev, &rdev->wiphy.wdev_list, list) { 14927 list_for_each_entry_rcu(wdev, &rdev->wiphy.wdev_list, list) {
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index e4a99989dd06..783f89c3e504 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -813,7 +813,7 @@ rdev_sched_scan_start(struct cfg80211_registered_device *rdev,
813 struct cfg80211_sched_scan_request *request) 813 struct cfg80211_sched_scan_request *request)
814{ 814{
815 int ret; 815 int ret;
816 trace_rdev_sched_scan_start(&rdev->wiphy, dev, request); 816 trace_rdev_sched_scan_start(&rdev->wiphy, dev, request->reqid);
817 ret = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); 817 ret = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
818 trace_rdev_return_int(&rdev->wiphy, ret); 818 trace_rdev_return_int(&rdev->wiphy, ret);
819 return ret; 819 return ret;
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 6f4996c0f4df..bd9feed95c1e 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -300,6 +300,70 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request,
300} 300}
301EXPORT_SYMBOL(cfg80211_scan_done); 301EXPORT_SYMBOL(cfg80211_scan_done);
302 302
303void cfg80211_add_sched_scan_req(struct cfg80211_registered_device *rdev,
304 struct cfg80211_sched_scan_request *req)
305{
306 ASSERT_RTNL();
307
308 list_add_rcu(&req->list, &rdev->sched_scan_req_list);
309}
310
311static void cfg80211_del_sched_scan_req(struct cfg80211_registered_device *rdev,
312 struct cfg80211_sched_scan_request *req)
313{
314 ASSERT_RTNL();
315
316 list_del_rcu(&req->list);
317 kfree_rcu(req, rcu_head);
318}
319
320static struct cfg80211_sched_scan_request *
321cfg80211_find_sched_scan_req(struct cfg80211_registered_device *rdev, u64 reqid)
322{
323 struct cfg80211_sched_scan_request *pos;
324
325 ASSERT_RTNL();
326
327 list_for_each_entry(pos, &rdev->sched_scan_req_list, list) {
328 if (pos->reqid == reqid)
329 return pos;
330 }
331 return ERR_PTR(-ENOENT);
332}
333
334/*
335 * Determines if a scheduled scan request can be handled. When a legacy
336 * scheduled scan is running no other scheduled scan is allowed regardless
337 * whether the request is for legacy or multi-support scan. When a multi-support
338 * scheduled scan is running a request for legacy scan is not allowed. In this
339 * case a request for multi-support scan can be handled if resources are
340 * available, ie. struct wiphy::max_sched_scan_reqs limit is not yet reached.
341 */
342int cfg80211_sched_scan_req_possible(struct cfg80211_registered_device *rdev,
343 bool want_multi)
344{
345 struct cfg80211_sched_scan_request *pos;
346 int i = 0;
347
348 list_for_each_entry(pos, &rdev->sched_scan_req_list, list) {
349 /* request id zero means legacy in progress */
350 if (!i && !pos->reqid)
351 return -EINPROGRESS;
352 i++;
353 }
354
355 if (i) {
356 /* no legacy allowed when multi request(s) are active */
357 if (!want_multi)
358 return -EINPROGRESS;
359
360 /* resource limit reached */
361 if (i == rdev->wiphy.max_sched_scan_reqs)
362 return -ENOSPC;
363 }
364 return 0;
365}
366
303void __cfg80211_sched_scan_results(struct work_struct *wk) 367void __cfg80211_sched_scan_results(struct work_struct *wk)
304{ 368{
305 struct cfg80211_registered_device *rdev; 369 struct cfg80211_registered_device *rdev;
@@ -310,10 +374,10 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)
310 374
311 rtnl_lock(); 375 rtnl_lock();
312 376
313 request = rtnl_dereference(rdev->sched_scan_req); 377 request = cfg80211_find_sched_scan_req(rdev, 0);
314 378
315 /* we don't have sched_scan_req anymore if the scan is stopping */ 379 /* we don't have sched_scan_req anymore if the scan is stopping */
316 if (request) { 380 if (!IS_ERR(request)) {
317 if (request->flags & NL80211_SCAN_FLAG_FLUSH) { 381 if (request->flags & NL80211_SCAN_FLAG_FLUSH) {
318 /* flush entries from previous scans */ 382 /* flush entries from previous scans */
319 spin_lock_bh(&rdev->bss_lock); 383 spin_lock_bh(&rdev->bss_lock);
@@ -329,10 +393,17 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)
329 393
330void cfg80211_sched_scan_results(struct wiphy *wiphy) 394void cfg80211_sched_scan_results(struct wiphy *wiphy)
331{ 395{
396 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
397 struct cfg80211_sched_scan_request *request;
398
332 trace_cfg80211_sched_scan_results(wiphy); 399 trace_cfg80211_sched_scan_results(wiphy);
333 /* ignore if we're not scanning */ 400 /* ignore if we're not scanning */
334 401
335 if (rcu_access_pointer(wiphy_to_rdev(wiphy)->sched_scan_req)) 402 rtnl_lock();
403 request = cfg80211_find_sched_scan_req(rdev, 0);
404 rtnl_unlock();
405
406 if (!IS_ERR(request))
336 queue_work(cfg80211_wq, 407 queue_work(cfg80211_wq,
337 &wiphy_to_rdev(wiphy)->sched_scan_results_wk); 408 &wiphy_to_rdev(wiphy)->sched_scan_results_wk);
338} 409}
@@ -346,7 +417,7 @@ void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy)
346 417
347 trace_cfg80211_sched_scan_stopped(wiphy); 418 trace_cfg80211_sched_scan_stopped(wiphy);
348 419
349 __cfg80211_stop_sched_scan(rdev, true); 420 __cfg80211_stop_sched_scan(rdev, 0, true);
350} 421}
351EXPORT_SYMBOL(cfg80211_sched_scan_stopped_rtnl); 422EXPORT_SYMBOL(cfg80211_sched_scan_stopped_rtnl);
352 423
@@ -358,34 +429,40 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy)
358} 429}
359EXPORT_SYMBOL(cfg80211_sched_scan_stopped); 430EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
360 431
361int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, 432int cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev,
362 bool driver_initiated) 433 struct cfg80211_sched_scan_request *req,
434 bool driver_initiated)
363{ 435{
364 struct cfg80211_sched_scan_request *sched_scan_req;
365 struct net_device *dev;
366
367 ASSERT_RTNL(); 436 ASSERT_RTNL();
368 437
369 if (!rdev->sched_scan_req)
370 return -ENOENT;
371
372 sched_scan_req = rtnl_dereference(rdev->sched_scan_req);
373 dev = sched_scan_req->dev;
374
375 if (!driver_initiated) { 438 if (!driver_initiated) {
376 int err = rdev_sched_scan_stop(rdev, dev); 439 int err = rdev_sched_scan_stop(rdev, req->dev);
377 if (err) 440 if (err)
378 return err; 441 return err;
379 } 442 }
380 443
381 nl80211_send_sched_scan(sched_scan_req, NL80211_CMD_SCHED_SCAN_STOPPED); 444 nl80211_send_sched_scan(req, NL80211_CMD_SCHED_SCAN_STOPPED);
382 445
383 RCU_INIT_POINTER(rdev->sched_scan_req, NULL); 446 cfg80211_del_sched_scan_req(rdev, req);
384 kfree_rcu(sched_scan_req, rcu_head);
385 447
386 return 0; 448 return 0;
387} 449}
388 450
451int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
452 u64 reqid, bool driver_initiated)
453{
454 struct cfg80211_sched_scan_request *sched_scan_req;
455
456 ASSERT_RTNL();
457
458 sched_scan_req = cfg80211_find_sched_scan_req(rdev, reqid);
459 if (IS_ERR(sched_scan_req))
460 return PTR_ERR(sched_scan_req);
461
462 return cfg80211_stop_sched_scan_req(rdev, sched_scan_req,
463 driver_initiated);
464}
465
389void cfg80211_bss_age(struct cfg80211_registered_device *rdev, 466void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
390 unsigned long age_secs) 467 unsigned long age_secs)
391{ 468{
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index fd55786f0462..52935c48b342 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1610,20 +1610,26 @@ DEFINE_EVENT(tx_rx_evt, rdev_set_antenna,
1610 TP_ARGS(wiphy, rx, tx) 1610 TP_ARGS(wiphy, rx, tx)
1611); 1611);
1612 1612
1613TRACE_EVENT(rdev_sched_scan_start, 1613DECLARE_EVENT_CLASS(wiphy_netdev_id_evt,
1614 TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, 1614 TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u64 id),
1615 struct cfg80211_sched_scan_request *request), 1615 TP_ARGS(wiphy, netdev, id),
1616 TP_ARGS(wiphy, netdev, request),
1617 TP_STRUCT__entry( 1616 TP_STRUCT__entry(
1618 WIPHY_ENTRY 1617 WIPHY_ENTRY
1619 NETDEV_ENTRY 1618 NETDEV_ENTRY
1619 __field(u64, id)
1620 ), 1620 ),
1621 TP_fast_assign( 1621 TP_fast_assign(
1622 WIPHY_ASSIGN; 1622 WIPHY_ASSIGN;
1623 NETDEV_ASSIGN; 1623 NETDEV_ASSIGN;
1624 __entry->id = id;
1624 ), 1625 ),
1625 TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, 1626 TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", id: %llu",
1626 WIPHY_PR_ARG, NETDEV_PR_ARG) 1627 WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->id)
1628);
1629
1630DEFINE_EVENT(wiphy_netdev_id_evt, rdev_sched_scan_start,
1631 TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u64 id),
1632 TP_ARGS(wiphy, netdev, id)
1627); 1633);
1628 1634
1629TRACE_EVENT(rdev_tdls_mgmt, 1635TRACE_EVENT(rdev_tdls_mgmt,