Ver Fonte

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
Dustin Brown há 8 anos atrás
pai
commit
f27bce8764

+ 40 - 0
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);
 	}
 

+ 75 - 60
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;

+ 20 - 0
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,

+ 4 - 2
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);
 	}
 

+ 8 - 0
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]),