summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDustin Brown <dustinb@codeaurora.org>2016-11-03 12:52:27 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-11-07 11:26:14 -0800
commitb35c454626d9e693f74a6fcdd160d68599bdc5d1 (patch)
tree39548fc196d02e237633ec0779075814dde4b6bf
parent5f49486caeea28f62674c50a83073968fd926d9c (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.c40
-rw-r--r--core/hdd/src/wlan_hdd_scan.c135
-rw-r--r--core/hdd/src/wlan_hdd_scan.h20
-rw-r--r--core/sme/src/common/sme_api.c6
-rw-r--r--core/sme/src/common/sme_power_save.c8
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]),