diff options
| author | Dustin Brown <dustinb@codeaurora.org> | 2016-11-03 12:52:27 -0700 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-11-07 11:26:14 -0800 |
| commit | b35c454626d9e693f74a6fcdd160d68599bdc5d1 (patch) | |
| tree | 39548fc196d02e237633ec0779075814dde4b6bf | |
| parent | 5f49486caeea28f62674c50a83073968fd926d9c (diff) | |
qcacld-3.0: Stop pending PNO scan during unload
The driver unload path does not check for or stop pending PNO scans.
Stop any pending PNO scans during driver unload.
Change-Id: I96866979c1f4368cfbe5345bc208f12cde7f4b10
CRs-Fixed: 1084324
| -rw-r--r-- | core/hdd/src/wlan_hdd_main.c | 40 | ||||
| -rw-r--r-- | core/hdd/src/wlan_hdd_scan.c | 135 | ||||
| -rw-r--r-- | core/hdd/src/wlan_hdd_scan.h | 20 | ||||
| -rw-r--r-- | core/sme/src/common/sme_api.c | 6 | ||||
| -rw-r--r-- | core/sme/src/common/sme_power_save.c | 8 |
5 files changed, 147 insertions, 62 deletions
diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index d237a72923ec..62db0cc911f8 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -4439,6 +4439,45 @@ QDF_STATUS hdd_abort_mac_scan_all_adapters(hdd_context_t *hdd_ctx) return QDF_STATUS_SUCCESS; } +/** + * hdd_abort_sched_scan_all_adapters() - stops scheduled (PNO) scans for all + * adapters + * @hdd_ctx: The HDD context containing the adapters to operate on + * + * return: QDF_STATUS_SUCCESS + */ +static QDF_STATUS hdd_abort_sched_scan_all_adapters(hdd_context_t *hdd_ctx) +{ + hdd_adapter_list_node_t *adapter_node = NULL, *next_node = NULL; + QDF_STATUS status; + hdd_adapter_t *adapter; + int err; + + ENTER(); + + status = hdd_get_front_adapter(hdd_ctx, &adapter_node); + + while (NULL != adapter_node && QDF_STATUS_SUCCESS == status) { + adapter = adapter_node->pAdapter; + if ((adapter->device_mode == QDF_STA_MODE) || + (adapter->device_mode == QDF_P2P_CLIENT_MODE) || + (adapter->device_mode == QDF_IBSS_MODE) || + (adapter->device_mode == QDF_P2P_DEVICE_MODE) || + (adapter->device_mode == QDF_SAP_MODE) || + (adapter->device_mode == QDF_P2P_GO_MODE)) { + err = wlan_hdd_sched_scan_stop(adapter->dev); + if (err) + hdd_err("Unable to stop scheduled scan"); + } + status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next_node); + adapter_node = next_node; + } + + EXIT(); + + return QDF_STATUS_SUCCESS; +} + #ifdef WLAN_NS_OFFLOAD /** * hdd_wlan_unregister_ip6_notifier() - unregister IPv6 change notifier @@ -4876,6 +4915,7 @@ static void hdd_wlan_exit(hdd_context_t *hdd_ctx) * completed, all scans will be cancelled */ hdd_abort_mac_scan_all_adapters(hdd_ctx); + hdd_abort_sched_scan_all_adapters(hdd_ctx); hdd_stop_all_adapters(hdd_ctx); } diff --git a/core/hdd/src/wlan_hdd_scan.c b/core/hdd/src/wlan_hdd_scan.c index 83f1601a87e1..d785de4021c6 100644 --- a/core/hdd/src/wlan_hdd_scan.c +++ b/core/hdd/src/wlan_hdd_scan.c @@ -2583,111 +2583,126 @@ int wlan_hdd_cfg80211_sched_scan_start(struct wiphy *wiphy, return ret; } -/** - * __wlan_hdd_cfg80211_sched_scan_stop() - stop cfg80211 scheduled scan(pno) - * @wiphy: Pointer to wiphy - * @dev: Pointer network device - * - * Return: 0 for success, non zero for failure - */ -static int __wlan_hdd_cfg80211_sched_scan_stop(struct wiphy *wiphy, - struct net_device *dev) +int wlan_hdd_sched_scan_stop(struct net_device *dev) { - QDF_STATUS status = QDF_STATUS_E_FAILURE; - hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); - hdd_context_t *pHddCtx; + QDF_STATUS status; + hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + hdd_context_t *hdd_ctx; tHalHandle hHal; - tpSirPNOScanReq pPnoRequest = NULL; + tSirPNOScanReq *pno_req = NULL; int ret = 0; - ENTER(); + ENTER_DEV(dev); if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { hdd_err("Command not allowed in FTM mode"); - return -EINVAL; + ret = -EINVAL; + goto exit; } - if (wlan_hdd_validate_session_id(pAdapter->sessionId)) { - hdd_err("invalid session id: %d", pAdapter->sessionId); - return -EINVAL; + if (wlan_hdd_validate_session_id(adapter->sessionId)) { + hdd_err("invalid session id: %d", adapter->sessionId); + ret = -EINVAL; + goto exit; } - pHddCtx = WLAN_HDD_GET_CTX(pAdapter); - - if (NULL == pHddCtx) { + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (NULL == hdd_ctx) { hdd_err("HDD context is Null"); - return -ENODEV; - } - - /* The return 0 is intentional when Recovery and Load/Unload in - * progress. We did observe a crash due to a return of - * failure in sched_scan_stop , especially for a case where the unload - * of the happens at the same time. The function __cfg80211_stop_sched_scan - * was clearing rdev->sched_scan_req only when the sched_scan_stop returns - * success. If it returns a failure , then its next invocation due to the - * clean up of the second interface will have the dev pointer corresponding - * to the first one leading to a crash. - */ - if (cds_is_driver_recovering()) { - hdd_err("Recovery in Progress. State: 0x%x Ignore!!!", - cds_get_driver_state()); - return ret; - } - - if (cds_is_load_or_unload_in_progress()) { - hdd_err("Unload/Load in Progress, state: 0x%x. Ignore!!!", - cds_get_driver_state()); - return ret; + ret = -ENODEV; + goto exit; } - hHal = WLAN_HDD_GET_HAL_CTX(pAdapter); + hHal = WLAN_HDD_GET_HAL_CTX(adapter); if (NULL == hHal) { hdd_err(" HAL context is Null!!!"); - return -EINVAL; + ret = -EINVAL; + goto exit; } - pPnoRequest = (tpSirPNOScanReq) qdf_mem_malloc(sizeof(tSirPNOScanReq)); - if (NULL == pPnoRequest) { + pno_req = (tpSirPNOScanReq) qdf_mem_malloc(sizeof(tSirPNOScanReq)); + if (NULL == pno_req) { hdd_err("qdf_mem_malloc failed"); - return -ENOMEM; + ret = -ENOMEM; + goto exit; } - pPnoRequest->enable = 0; /* Disable PNO */ - pPnoRequest->ucNetworksCount = 0; - MTRACE(qdf_trace(QDF_MODULE_ID_HDD, TRACE_CODE_HDD_CFG80211_SCHED_SCAN_STOP, - pAdapter->sessionId, pAdapter->device_mode)); - status = sme_set_preferred_network_list(hHal, pPnoRequest, - pAdapter->sessionId, - NULL, pAdapter); + adapter->sessionId, adapter->device_mode)); + + /* Disable PNO */ + pno_req->enable = 0; + pno_req->ucNetworksCount = 0; + status = sme_set_preferred_network_list(hHal, pno_req, + adapter->sessionId, + NULL, adapter); + qdf_mem_free(pno_req); + if (QDF_STATUS_SUCCESS != status) { hdd_err("Failed to disabled PNO"); ret = -EINVAL; + goto exit; } hdd_notice("PNO scan disabled"); - qdf_mem_free(pPnoRequest); - +exit: EXIT(); return ret; } /** - * wlan_hdd_cfg80211_sched_scan_stop() - stop cfg80211 scheduled scan(pno) - * @wiphy: Pointer to wiphy + * __wlan_hdd_cfg80211_sched_scan_stop() - stop cfg80211 scheduled scan(pno) * @dev: Pointer network device * + * This is a wrapper around wlan_hdd_sched_scan_stop() that returns success + * in the event that the driver is currently recovering or unloading. This + * prevents a race condition where we get a scan stop from kernel during + * a driver unload from PLD. + * * Return: 0 for success, non zero for failure */ +static int __wlan_hdd_cfg80211_sched_scan_stop(struct net_device *dev) +{ + int err; + + ENTER_DEV(dev); + + /* The return 0 is intentional when Recovery and Load/Unload in + * progress. We did observe a crash due to a return of + * failure in sched_scan_stop , especially for a case where the unload + * of the happens at the same time. The function __cfg80211_stop_sched_scan + * was clearing rdev->sched_scan_req only when the sched_scan_stop returns + * success. If it returns a failure , then its next invocation due to the + * clean up of the second interface will have the dev pointer corresponding + * to the first one leading to a crash. + */ + if (cds_is_driver_recovering()) { + hdd_err("Recovery in Progress. State: 0x%x Ignore!!!", + cds_get_driver_state()); + return 0; + } + + if (cds_is_load_or_unload_in_progress()) { + hdd_err("Unload/Load in Progress, state: 0x%x. Ignore!!!", + cds_get_driver_state()); + return 0; + } + + err = wlan_hdd_sched_scan_stop(dev); + + EXIT(); + return err; +} + int wlan_hdd_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) { int ret; cds_ssr_protect(__func__); - ret = __wlan_hdd_cfg80211_sched_scan_stop(wiphy, dev); + ret = __wlan_hdd_cfg80211_sched_scan_stop(dev); cds_ssr_unprotect(__func__); return ret; diff --git a/core/hdd/src/wlan_hdd_scan.h b/core/hdd/src/wlan_hdd_scan.h index 2c549859f2d9..249449594272 100644 --- a/core/hdd/src/wlan_hdd_scan.h +++ b/core/hdd/src/wlan_hdd_scan.h @@ -65,8 +65,28 @@ int wlan_hdd_cfg80211_sched_scan_start(struct wiphy *wiphy, struct cfg80211_sched_scan_request *request); +/** + * wlan_hdd_cfg80211_sched_scan_stop() - stop cfg80211 scheduled (PNO) scan + * @wiphy: Pointer to wiphy + * @dev: Pointer network device + * + * Note, this returns success if the driver is recovering or unloading to + * prevent race conditions between PLD initiating an unload and kernel + * initiating a scheduled scan stop via cfg80211. Unload is expected to stop + * any pending scheduled scans in this case. + * + * Return: 0 for success, non zero for failure + */ int wlan_hdd_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev); + +/** + * wlan_hdd_sched_scan_stop() - stop scheduled (PNO) scans + * @dev: Pointer network device + * + * Return: 0 for success, non zero for failure + */ +int wlan_hdd_sched_scan_stop(struct net_device *dev); #endif /* End of FEATURE_WLAN_SCAN_PNO */ int wlan_hdd_cfg80211_vendor_scan(struct wiphy *wiphy, diff --git a/core/sme/src/common/sme_api.c b/core/sme/src/common/sme_api.c index 3c756e7278fa..7721789c87ba 100644 --- a/core/sme/src/common/sme_api.c +++ b/core/sme/src/common/sme_api.c @@ -6572,8 +6572,10 @@ QDF_STATUS sme_set_preferred_network_list(tHalHandle hHal, sessionId, request->ucNetworksCount)); status = sme_acquire_global_lock(&pMac->sme); if (QDF_IS_STATUS_SUCCESS(status)) { - sme_set_ps_preferred_network_list(hHal, request, sessionId, - callback_routine, callback_context); + status = sme_set_ps_preferred_network_list(hHal, request, + sessionId, + callback_routine, + callback_context); sme_release_global_lock(&pMac->sme); } diff --git a/core/sme/src/common/sme_power_save.c b/core/sme/src/common/sme_power_save.c index 915af516c2ea..f364be3c102d 100644 --- a/core/sme/src/common/sme_power_save.c +++ b/core/sme/src/common/sme_power_save.c @@ -736,6 +736,14 @@ QDF_STATUS sme_set_ps_preferred_network_list(tHalHandle hal_ctx, "%s: session is NULL", __func__); return QDF_STATUS_E_FAILURE; } + + /* save some work if PNO is already disabled */ + if (!session->pnoStarted && !request->enable) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_INFO, + "%s: PNO already disabled", __func__); + return QDF_STATUS_SUCCESS; + } + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_INFO, "%s: SSID = 0x%08x%08x%08x%08x%08x%08x%08x%08x, 0x%08x%08x%08x%08x%08x%08x%08x%08x", __func__, *((uint32_t *) &request->aNetworks[0].ssId.ssId[0]), |
