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.
Unified · Split
| @@ -3973,7 +3973,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) · 3972 unmodified lines | ||
| 3973 | 3973 | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; |
| 3974 | 3974 | |
| 3975 | 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 | 3978 | if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT, |
| 3979 | 3979 | ar->fw_capabilities)) |
| @@ -6374,11 +6374,11 @@ err: · 6373 unmodified lines | ||
| 6374 | 6374 | static void brcmf_wiphy_pno_params(struct wiphy *wiphy) |
| 6375 | 6375 | { |
| 6376 | 6376 | /* scheduled scan settings */ |
| 6377 | + wiphy->max_sched_scan_reqs = 1; | |
| 6377 | 6378 | wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT; |
| 6378 | 6379 | wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT; |
| 6379 | 6380 | wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; |
| 6380 | 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 | 6384 | #ifdef CONFIG_PM |
| @@ -620,7 +620,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) · 619 unmodified lines | ||
| 620 | 620 | else |
| 621 | 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 | 624 | hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX; |
| 625 | 625 | hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES; |
| 626 | 626 | /* we create the 802.11 header and zero length SSID IE. */ |
| @@ -4297,7 +4297,6 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) · 4296 unmodified lines | ||
| 4297 | 4297 | wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | |
| 4298 | 4298 | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | |
| 4299 | 4299 | WIPHY_FLAG_AP_UAPSD | |
| 4300 | − WIPHY_FLAG_SUPPORTS_SCHED_SCAN | | |
| 4301 | 4300 | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | |
| 4302 | 4301 | WIPHY_FLAG_HAS_CHANNEL_SWITCH | |
| 4303 | 4302 | WIPHY_FLAG_PS_ON_BY_DEFAULT; |
| @@ -4316,6 +4315,7 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) · 12 unmodified lines | ||
| 4316 | 4315 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | |
| 4317 | 4316 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; |
| 4318 | 4317 | |
| 4318 | + wiphy->max_sched_scan_reqs = 1; | |
| 4319 | 4319 | wiphy->max_sched_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH; |
| 4320 | 4320 | wiphy->max_sched_scan_ie_len = MWIFIEX_MAX_VSIE_LEN; |
| 4321 | 4321 | wiphy->max_match_sets = MWIFIEX_MAX_SSID_LIST_LENGTH; |
| @@ -6128,6 +6128,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) · 6127 unmodified lines | ||
| 6128 | 6128 | wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - |
| 6129 | 6129 | sizeof(struct ieee80211_header); |
| 6130 | 6130 | |
| 6131 | + wl->hw->wiphy->max_sched_scan_reqs = 1; | |
| 6131 | 6132 | wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - |
| 6132 | 6133 | sizeof(struct ieee80211_header); |
| 6133 | 6134 | |
| @@ -6135,7 +6136,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) · 1 unmodified line | ||
| 6135 | 6136 | |
| 6136 | 6137 | wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD | |
| 6137 | 6138 | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | |
| 6138 | − WIPHY_FLAG_SUPPORTS_SCHED_SCAN | | |
| 6139 | 6139 | WIPHY_FLAG_HAS_CHANNEL_SWITCH; |
| 6140 | 6140 | |
| 6141 | 6141 | wl->hw->wiphy->features |= NL80211_FEATURE_AP_SCAN; |
| @@ -1678,6 +1678,8 @@ struct cfg80211_bss_select_adjust { · 1677 unmodified lines | ||
| 1678 | 1678 | * @rcu_head: RCU callback used to free the struct |
| 1679 | 1679 | * @owner_nlportid: netlink portid of owner (if this should is a request |
| 1680 | 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 | 1683 | * @delay: delay in seconds to use before starting the first scan |
| 1682 | 1684 | * cycle. The driver may ignore this parameter and start |
| 1683 | 1685 | * immediately (or at any other time), if this feature is not |
| @@ -1722,6 +1724,8 @@ struct cfg80211_sched_scan_request { · 38 unmodified lines | ||
| 1722 | 1724 | unsigned long scan_start; |
| 1723 | 1725 | struct rcu_head rcu_head; |
| 1724 | 1726 | u32 owner_nlportid; |
| 1727 | + bool nl_owner_dead; | |
| 1728 | + struct list_head list; | |
| 1725 | 1729 | |
| 1726 | 1730 | /* keep last */ |
| 1727 | 1731 | struct ieee80211_channel *channels[0]; |
| @@ -3213,7 +3217,7 @@ enum wiphy_flags { · 1485 unmodified lines | ||
| 3213 | 3217 | WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7), |
| 3214 | 3218 | WIPHY_FLAG_IBSS_RSN = BIT(8), |
| 3215 | 3219 | WIPHY_FLAG_MESH_AUTH = BIT(10), |
| 3216 | − WIPHY_FLAG_SUPPORTS_SCHED_SCAN = BIT(11), | |
| 3220 | + /* use hole at 11 */ | |
| 3217 | 3221 | /* use hole at 12 */ |
| 3218 | 3222 | WIPHY_FLAG_SUPPORTS_FW_ROAM = BIT(13), |
| 3219 | 3223 | WIPHY_FLAG_AP_UAPSD = BIT(14), |
| @@ -3551,6 +3555,8 @@ struct wiphy_iftype_ext_capab { · 331 unmodified lines | ||
| 3551 | 3555 | * this variable determines its size |
| 3552 | 3556 | * @max_scan_ssids: maximum number of SSIDs the device can scan for in |
| 3553 | 3557 | * any given scan |
| 3558 | + * @max_sched_scan_reqs: maximum number of scheduled scan requests that | |
| 3559 | + * the device can run concurrently. | |
| 3554 | 3560 | * @max_sched_scan_ssids: maximum number of SSIDs the device can scan |
| 3555 | 3561 | * for in any given scheduled scan |
| 3556 | 3562 | * @max_match_sets: maximum number of match sets the device can handle |
| @@ -3687,6 +3693,7 @@ struct wiphy { · 130 unmodified lines | ||
| 3687 | 3693 | |
| 3688 | 3694 | int bss_priv_size; |
| 3689 | 3695 | u8 max_scan_ssids; |
| 3696 | + u8 max_sched_scan_reqs; | |
| 3690 | 3697 | u8 max_sched_scan_ssids; |
| 3691 | 3698 | u8 max_match_sets; |
| 3692 | 3699 | u16 max_scan_ie_len; |
| @@ -387,7 +387,9 @@ · 386 unmodified lines | ||
| 387 | 387 | * are used. Extra IEs can also be passed from the userspace by |
| 388 | 388 | * using the %NL80211_ATTR_IE attribute. The first cycle of the |
| 389 | 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 | 393 | * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if |
| 392 | 394 | * scheduled scan is not running. The caller may assume that as soon |
| 393 | 395 | * as the call returns, it is safe to start a new scheduled scan again. |
| @@ -2081,6 +2083,11 @@ enum nl80211_commands { · 1687 unmodified lines | ||
| 2081 | 2083 | * @NL80211_ATTR_PMK: PMK for the PMKSA identified by %NL80211_ATTR_PMKID. |
| 2082 | 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 | 2091 | * @NUM_NL80211_ATTR: total number of nl80211_attrs available |
| 2085 | 2092 | * @NL80211_ATTR_MAX: highest attribute number currently defined |
| 2086 | 2093 | * @__NL80211_ATTR_AFTER_LAST: internal use |
| @@ -2500,6 +2507,9 @@ enum nl80211_attrs { · 413 unmodified lines | ||
| 2500 | 2507 | |
| 2501 | 2508 | NL80211_ATTR_PMK, |
| 2502 | 2509 | |
| 2510 | + NL80211_ATTR_SCHED_SCAN_MULTI, | |
| 2511 | + NL80211_ATTR_SCHED_SCAN_MAX_REQS, | |
| 2512 | + | |
| 2503 | 2513 | /* add attributes here, update the policy in nl80211.c */ |
| 2504 | 2514 | |
| 2505 | 2515 | __NL80211_ATTR_AFTER_LAST, |
| @@ -330,14 +330,16 @@ static void cfg80211_destroy_iface_wk(struct work_struct *work) · 329 unmodified lines | ||
| 330 | 330 | static void cfg80211_sched_scan_stop_wk(struct work_struct *work) |
| 331 | 331 | { |
| 332 | 332 | struct cfg80211_registered_device *rdev; |
| 333 | + struct cfg80211_sched_scan_request *req, *tmp; | |
| 333 | 334 | |
| 334 | 335 | rdev = container_of(work, struct cfg80211_registered_device, |
| 335 | 336 | sched_scan_stop_wk); |
| 336 | 337 | |
| 337 | 338 | rtnl_lock(); |
| 338 | − | |
| 339 | − __cfg80211_stop_sched_scan(rdev, false); | |
| 340 | − | |
| 339 | + list_for_each_entry_safe(req, tmp, &rdev->sched_scan_req_list, list) { | |
| 340 | + if (req->nl_owner_dead) | |
| 341 | + cfg80211_stop_sched_scan_req(rdev, req, false); | |
| 342 | + } | |
| 341 | 343 | rtnl_unlock(); |
| 342 | 344 | } |
| 343 | 345 | |
| @@ -452,6 +454,7 @@ use_default_name: · 108 unmodified lines | ||
| 452 | 454 | spin_lock_init(&rdev->beacon_registrations_lock); |
| 453 | 455 | spin_lock_init(&rdev->bss_lock); |
| 454 | 456 | INIT_LIST_HEAD(&rdev->bss_list); |
| 457 | + INIT_LIST_HEAD(&rdev->sched_scan_req_list); | |
| 455 | 458 | INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); |
| 456 | 459 | INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); |
| 457 | 460 | INIT_LIST_HEAD(&rdev->mlme_unreg); |
| @@ -1028,7 +1031,7 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, · 570 unmodified lines | ||
| 1028 | 1031 | struct wireless_dev *wdev) |
| 1029 | 1032 | { |
| 1030 | 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 | 1036 | ASSERT_RTNL(); |
| 1034 | 1037 | ASSERT_WDEV_LOCK(wdev); |
| @@ -1039,9 +1042,11 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, · 4 unmodified lines | ||
| 1039 | 1042 | break; |
| 1040 | 1043 | case NL80211_IFTYPE_P2P_CLIENT: |
| 1041 | 1044 | case NL80211_IFTYPE_STATION: |
| 1042 | − sched_scan_req = rtnl_dereference(rdev->sched_scan_req); | |
| 1043 | − if (sched_scan_req && dev == sched_scan_req->dev) | |
| 1044 | − __cfg80211_stop_sched_scan(rdev, false); | |
| 1045 | + list_for_each_entry_safe(pos, tmp, &rdev->sched_scan_req_list, | |
| 1046 | + list) { | |
| 1047 | + if (dev == pos->dev) | |
| 1048 | + cfg80211_stop_sched_scan_req(rdev, pos, false); | |
| 1049 | + } | |
| 1045 | 1050 | |
| 1046 | 1051 | #ifdef CONFIG_CFG80211_WEXT |
| 1047 | 1052 | kfree(wdev->wext.ie); |
| @@ -1116,7 +1121,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, · 68 unmodified lines | ||
| 1116 | 1121 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
| 1117 | 1122 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
| 1118 | 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 | 1126 | if (!wdev) |
| 1122 | 1127 | return NOTIFY_DONE; |
| @@ -1193,10 +1198,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, · 70 unmodified lines | ||
| 1193 | 1198 | ___cfg80211_scan_done(rdev, false); |
| 1194 | 1199 | } |
| 1195 | 1200 | |
| 1196 | − sched_scan_req = rtnl_dereference(rdev->sched_scan_req); | |
| 1197 | − if (WARN_ON(sched_scan_req && | |
| 1198 | − sched_scan_req->dev == wdev->netdev)) { | |
| 1199 | − __cfg80211_stop_sched_scan(rdev, false); | |
| 1201 | + list_for_each_entry_safe(pos, tmp, | |
| 1202 | + &rdev->sched_scan_req_list, list) { | |
| 1203 | + if (WARN_ON(pos && pos->dev == wdev->netdev)) | |
| 1204 | + cfg80211_stop_sched_scan_req(rdev, pos, false); | |
| 1200 | 1205 | } |
| 1201 | 1206 | |
| 1202 | 1207 | rdev->opencount--; |
| @@ -74,7 +74,7 @@ struct cfg80211_registered_device { · 73 unmodified lines | ||
| 74 | 74 | u32 bss_entries; |
| 75 | 75 | struct cfg80211_scan_request *scan_req; /* protected by RTNL */ |
| 76 | 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 | 78 | unsigned long suspend_at; |
| 79 | 79 | struct work_struct scan_done_wk; |
| 80 | 80 | struct work_struct sched_scan_results_wk; |
| @@ -416,9 +416,16 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, · 335 unmodified lines | ||
| 416 | 416 | void __cfg80211_scan_done(struct work_struct *wk); |
| 417 | 417 | void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, |
| 418 | 418 | bool send_message); |
| 419 | +void cfg80211_add_sched_scan_req(struct cfg80211_registered_device *rdev, | |
| 420 | + struct cfg80211_sched_scan_request *req); | |
| 421 | +int cfg80211_sched_scan_req_possible(struct cfg80211_registered_device *rdev, | |
| 422 | + bool want_multi); | |
| 419 | 423 | void __cfg80211_sched_scan_results(struct work_struct *wk); |
| 424 | +int cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev, | |
| 425 | + struct cfg80211_sched_scan_request *req, | |
| 426 | + bool driver_initiated); | |
| 420 | 427 | int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, |
| 421 | − bool driver_initiated); | |
| 428 | + u64 reqid, bool driver_initiated); | |
| 422 | 429 | void cfg80211_upload_connect_keys(struct wireless_dev *wdev); |
| 423 | 430 | int cfg80211_change_iface(struct cfg80211_registered_device *rdev, |
| 424 | 431 | struct net_device *dev, enum nl80211_iftype ntype, |
| @@ -419,6 +419,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { · 418 unmodified lines | ||
| 419 | 419 | .len = FILS_ERP_MAX_RRK_LEN }, |
| 420 | 420 | [NL80211_ATTR_FILS_CACHE_ID] = { .len = 2 }, |
| 421 | 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 | 425 | /* policy for the key attributes */ |
| @@ -1376,7 +1377,7 @@ static int nl80211_add_commands_unsplit(struct cfg80211_registered_device *rdev, · 951 unmodified lines | ||
| 1376 | 1377 | CMD(tdls_mgmt, TDLS_MGMT); |
| 1377 | 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 | 1381 | CMD(sched_scan_start, START_SCHED_SCAN); |
| 1381 | 1382 | CMD(probe_client, PROBE_CLIENT); |
| 1382 | 1383 | CMD(set_noack_map, SET_NOACK_MAP); |
| @@ -1815,6 +1816,11 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, · 432 unmodified lines | ||
| 1815 | 1816 | nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG)) |
| 1816 | 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 | 1824 | if (nla_put(msg, NL80211_ATTR_EXT_FEATURES, |
| 1819 | 1825 | sizeof(rdev->wiphy.ext_features), |
| 1820 | 1826 | rdev->wiphy.ext_features)) |
| @@ -7336,14 +7342,16 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, · 5515 unmodified lines | ||
| 7336 | 7342 | struct net_device *dev = info->user_ptr[1]; |
| 7337 | 7343 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
| 7338 | 7344 | struct cfg80211_sched_scan_request *sched_scan_req; |
| 7345 | + bool want_multi; | |
| 7339 | 7346 | int err; |
| 7340 | 7347 | |
| 7341 | − if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || | |
| 7342 | − !rdev->ops->sched_scan_start) | |
| 7348 | + if (!rdev->wiphy.max_sched_scan_reqs || !rdev->ops->sched_scan_start) | |
| 7343 | 7349 | return -EOPNOTSUPP; |
| 7344 | 7350 | |
| 7345 | − if (rdev->sched_scan_req) | |
| 7346 | − return -EINPROGRESS; | |
| 7351 | + want_multi = info->attrs[NL80211_ATTR_SCHED_SCAN_MULTI]; | |
| 7352 | + err = cfg80211_sched_scan_req_possible(rdev, want_multi); | |
| 7353 | + if (err) | |
| 7354 | + return err; | |
| 7347 | 7355 | |
| 7348 | 7356 | sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev, |
| 7349 | 7357 | info->attrs, |
| @@ -7353,6 +7361,14 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, · 3 unmodified lines | ||
| 7353 | 7361 | if (err) |
| 7354 | 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 | 7372 | err = rdev_sched_scan_start(rdev, dev, sched_scan_req); |
| 7357 | 7373 | if (err) |
| 7358 | 7374 | goto out_free; |
| @@ -7363,7 +7379,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, · 4 unmodified lines | ||
| 7363 | 7379 | if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) |
| 7364 | 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 | 7384 | nl80211_send_sched_scan(sched_scan_req, NL80211_CMD_START_SCHED_SCAN); |
| 7369 | 7385 | return 0; |
| @@ -7377,13 +7393,27 @@ out_err: · 7 unmodified lines | ||
| 7377 | 7393 | static int nl80211_stop_sched_scan(struct sk_buff *skb, |
| 7378 | 7394 | struct genl_info *info) |
| 7379 | 7395 | { |
| 7396 | + struct cfg80211_sched_scan_request *req; | |
| 7380 | 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) || | |
| 7383 | − !rdev->ops->sched_scan_stop) | |
| 7400 | + if (!rdev->wiphy.max_sched_scan_reqs || !rdev->ops->sched_scan_stop) | |
| 7384 | 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 | |
| 7389 | 7419 | static int nl80211_start_radar_detection(struct sk_buff *skb, |
| @@ -14883,16 +14913,15 @@ static int nl80211_netlink_notify(struct notifier_block * nb, · 7493 unmodified lines | ||
| 14883 | 14913 | rcu_read_lock(); |
| 14884 | 14914 | |
| 14885 | 14915 | list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { |
| 14886 | − 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; | |
| 14916 | + struct cfg80211_sched_scan_request *sched_scan_req; | |
| 14892 | 14917 | |
| 14893 | − if (rdev->ops->sched_scan_stop && | |
| 14894 | − rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) | |
| 14918 | + list_for_each_entry_rcu(sched_scan_req, | |
| 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 | 14923 | schedule_work(&rdev->sched_scan_stop_wk); |
| 14924 | + } | |
| 14896 | 14925 | } |
| 14897 | 14926 | |
| 14898 | 14927 | list_for_each_entry_rcu(wdev, &rdev->wiphy.wdev_list, list) { |
| @@ -813,7 +813,7 @@ rdev_sched_scan_start(struct cfg80211_registered_device *rdev, · 812 unmodified lines | ||
| 813 | 813 | struct cfg80211_sched_scan_request *request) |
| 814 | 814 | { |
| 815 | 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 | 817 | ret = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); |
| 818 | 818 | trace_rdev_return_int(&rdev->wiphy, ret); |
| 819 | 819 | return ret; |
| @@ -300,6 +300,70 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, · 299 unmodified lines | ||
| 300 | 300 | } |
| 301 | 301 | EXPORT_SYMBOL(cfg80211_scan_done); |
| 302 | 302 | |
| 303 | +void 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 | + | |
| 311 | +static 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 | + | |
| 320 | +static struct cfg80211_sched_scan_request * | |
| 321 | +cfg80211_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 | + */ | |
| 342 | +int 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 | + | |
| 303 | 367 | void __cfg80211_sched_scan_results(struct work_struct *wk) |
| 304 | 368 | { |
| 305 | 369 | struct cfg80211_registered_device *rdev; |
| @@ -310,10 +374,10 @@ void __cfg80211_sched_scan_results(struct work_struct *wk) · 4 unmodified lines | ||
| 310 | 374 | |
| 311 | 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 | 379 | /* we don't have sched_scan_req anymore if the scan is stopping */ |
| 316 | − if (request) { | |
| 380 | + if (!IS_ERR(request)) { | |
| 317 | 381 | if (request->flags & NL80211_SCAN_FLAG_FLUSH) { |
| 318 | 382 | /* flush entries from previous scans */ |
| 319 | 383 | spin_lock_bh(&rdev->bss_lock); |
| @@ -329,10 +393,17 @@ void __cfg80211_sched_scan_results(struct work_struct *wk) · 9 unmodified lines | ||
| 329 | 393 | |
| 330 | 394 | void 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 | 399 | trace_cfg80211_sched_scan_results(wiphy); |
| 333 | 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 | 407 | queue_work(cfg80211_wq, |
| 337 | 408 | &wiphy_to_rdev(wiphy)->sched_scan_results_wk); |
| 338 | 409 | } |
| @@ -346,7 +417,7 @@ void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy) · 7 unmodified lines | ||
| 346 | 417 | |
| 347 | 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 | } |
| 351 | 422 | EXPORT_SYMBOL(cfg80211_sched_scan_stopped_rtnl); |
| 352 | 423 | |
| @@ -358,34 +429,40 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy) · 5 unmodified lines | ||
| 358 | 429 | } |
| 359 | 430 | EXPORT_SYMBOL(cfg80211_sched_scan_stopped); |
| 360 | 431 | |
| 361 | −int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, | |
| 362 | − bool driver_initiated) | |
| 432 | +int cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev, | |
| 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 | 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 | 438 | if (!driver_initiated) { |
| 376 | − int err = rdev_sched_scan_stop(rdev, dev); | |
| 439 | + int err = rdev_sched_scan_stop(rdev, req->dev); | |
| 377 | 440 | if (err) |
| 378 | 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); | |
| 384 | − kfree_rcu(sched_scan_req, rcu_head); | |
| 446 | + cfg80211_del_sched_scan_req(rdev, req); | |
| 385 | 447 | |
| 386 | 448 | return 0; |
| 387 | 449 | } |
| 388 | 450 | |
| 451 | +int __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 | + | |
| 389 | 466 | void cfg80211_bss_age(struct cfg80211_registered_device *rdev, |
| 390 | 467 | unsigned long age_secs) |
| 391 | 468 | { |
| @@ -1610,20 +1610,26 @@ DEFINE_EVENT(tx_rx_evt, rdev_set_antenna, · 1609 unmodified lines | ||
| 1610 | 1610 | TP_ARGS(wiphy, rx, tx) |
| 1611 | 1611 | ); |
| 1612 | 1612 | |
| 1613 | −TRACE_EVENT(rdev_sched_scan_start, | |
| 1614 | − TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, | |
| 1615 | − struct cfg80211_sched_scan_request *request), | |
| 1616 | − TP_ARGS(wiphy, netdev, request), | |
| 1613 | +DECLARE_EVENT_CLASS(wiphy_netdev_id_evt, | |
| 1614 | + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u64 id), | |
| 1615 | + TP_ARGS(wiphy, netdev, id), | |
| 1617 | 1616 | TP_STRUCT__entry( |
| 1618 | 1617 | WIPHY_ENTRY |
| 1619 | 1618 | NETDEV_ENTRY |
| 1619 | + __field(u64, id) | |
| 1620 | 1620 | ), |
| 1621 | 1621 | TP_fast_assign( |
| 1622 | 1622 | WIPHY_ASSIGN; |
| 1623 | 1623 | NETDEV_ASSIGN; |
| 1624 | + __entry->id = id; | |
| 1624 | 1625 | ), |
| 1625 | − TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, | |
| 1626 | − WIPHY_PR_ARG, NETDEV_PR_ARG) | |
| 1626 | + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", id: %llu", | |
| 1627 | + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->id) | |
| 1628 | +); | |
| 1629 | + | |
| 1630 | +DEFINE_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 | |
| 1629 | 1635 | TRACE_EVENT(rdev_tdls_mgmt, |