Преглед изворни кода

qcacld-3.0: Stop connection in progress STA when SAP comes up

For STA+SAP concurrency support from GUI, first STA connection gets
triggered and while it is in progress, SAP start also comes up. Once
STA association is successful, STA connect event is sent to kernel which
gets queued in kernel workqueue and supplicant won't process M1 received
from AP and send M2 until this NL80211_CONNECT event is received. Workqueue
is not scheduled as RTNL lock is already taken by hostapd thread which has
issued start_bss command to driver. Driver cannot complete start_bss as the
pending command at the head of the SME command pending list is
hw_mode_update for STA session which cannot be processed as SME is in
WAITforKey state for STA interface. The start_bss command for SAP interface
is queued behind the hw_mode_update command and so it cannot be processed
until hw_mode_update command is processed. This causes a deadlock.

Disconnect STA interface first if connection or key exchange is in progress
and then start SAP interface to prevent this deadlock condition.

Change-Id: I2ef5fe0e3bc84e721e6a2baa0dae81c6106c5a8f
CRs-Fixed: 2171912
Himanshu Agarwal пре 7 година
родитељ
комит
6c3607ac01

+ 11 - 0
core/hdd/inc/wlan_hdd_assoc.h

@@ -213,6 +213,7 @@ struct hdd_connection_info {
 /* Forward declarations */
 struct hdd_adapter;
 struct hdd_station_ctx;
+struct hdd_context;
 
 /**
  * hdd_is_connecting() - Function to check connection progress
@@ -239,6 +240,16 @@ bool hdd_conn_is_connected(struct hdd_station_ctx *sta_ctx);
  */
 enum band_info hdd_conn_get_connected_band(struct hdd_station_ctx *sta_ctx);
 
+/**
+ * hdd_get_sta_connection_in_progress() - get STA for which connection
+ *                                        is in progress
+ * @hdd_ctx: hdd context
+ *
+ * Return: hdd adpater for which connection is in progress
+ */
+struct hdd_adapter *hdd_get_sta_connection_in_progress(
+			struct hdd_context *hdd_ctx);
+
 /**
  * hdd_sme_roam_callback() - hdd sme roam callback
  * @pContext: pointer to adapter context

+ 33 - 0
core/hdd/src/wlan_hdd_assoc.c

@@ -304,6 +304,39 @@ hdd_conn_get_connected_cipher_algo(struct hdd_station_ctx *sta_ctx,
 	return fConnected;
 }
 
+struct hdd_adapter *hdd_get_sta_connection_in_progress(
+			struct hdd_context *hdd_ctx)
+{
+	struct hdd_adapter *adapter = NULL;
+	struct hdd_station_ctx *hdd_sta_ctx;
+
+	if (!hdd_ctx) {
+		hdd_err("HDD context is NULL");
+		return NULL;
+	}
+
+	hdd_for_each_adapter(hdd_ctx, adapter) {
+		hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+		if ((QDF_STA_MODE == adapter->device_mode) ||
+		    (QDF_P2P_CLIENT_MODE == adapter->device_mode) ||
+		    (QDF_P2P_DEVICE_MODE == adapter->device_mode)) {
+			if (eConnectionState_Connecting ==
+			    hdd_sta_ctx->conn_info.connState) {
+				hdd_debug("session_id %d: Connection is in progress",
+					  adapter->session_id);
+				return adapter;
+			} else if ((eConnectionState_Associated ==
+				   hdd_sta_ctx->conn_info.connState) &&
+				   !hdd_sta_ctx->conn_info.uIsAuthenticated) {
+				hdd_debug("session_id %d: Key exchange is in progress",
+					  adapter->session_id);
+				return adapter;
+			}
+		}
+	}
+	return NULL;
+}
+
 /**
  * hdd_remove_beacon_filter() - remove beacon filter
  * @adapter: Pointer to the hdd adapter

+ 1 - 10
core/hdd/src/wlan_hdd_cfg80211.c

@@ -18284,16 +18284,7 @@ static int wlan_hdd_cfg80211_connect(struct wiphy *wiphy,
 	return ret;
 }
 
-/**
- * wlan_hdd_disconnect() - hdd disconnect api
- * @adapter: Pointer to adapter
- * @reason: Disconnect reason code
- *
- * This function is used to issue a disconnect request to SME
- *
- * Return: 0 for success, non-zero for failure
- */
-static int wlan_hdd_disconnect(struct hdd_adapter *adapter, u16 reason)
+int wlan_hdd_disconnect(struct hdd_adapter *adapter, u16 reason)
 {
 	int status, result = 0;
 	unsigned long rc;

+ 11 - 0
core/hdd/src/wlan_hdd_cfg80211.h

@@ -509,6 +509,17 @@ uint8_t hdd_get_sap_operating_band(struct hdd_context *hdd_ctx);
  */
 int wlan_hdd_try_disconnect(struct hdd_adapter *adapter);
 
+/**
+ * wlan_hdd_disconnect() - hdd disconnect api
+ * @adapter: Pointer to adapter
+ * @reason: Disconnect reason code
+ *
+ * This function is used to issue a disconnect request to SME
+ *
+ * Return: 0 for success, non-zero for failure
+ */
+int wlan_hdd_disconnect(struct hdd_adapter *adapter, u16 reason);
+
 /**
  * hdd_update_cca_info_cb() - stores congestion value in station context
  * @context : HDD context

+ 25 - 0
core/hdd/src/wlan_hdd_hostapd.c

@@ -7650,6 +7650,7 @@ int wlan_hdd_cfg80211_start_bss(struct hdd_adapter *adapter,
 	bool MFPRequired = false;
 	uint16_t prev_rsn_length = 0;
 	enum dfs_mode mode;
+	struct hdd_adapter *sta_adapter;
 
 	ENTER();
 
@@ -7665,6 +7666,30 @@ int wlan_hdd_cfg80211_start_bss(struct hdd_adapter *adapter,
 		}
 	}
 
+	/*
+	 * For STA+SAP concurrency support from GUI, first STA connection gets
+	 * triggered and while it is in progress, SAP start also comes up.
+	 * Once STA association is successful, STA connect event is sent to
+	 * kernel which gets queued in kernel workqueue and supplicant won't
+	 * process M1 received from AP and send M2 until this NL80211_CONNECT
+	 * event is received. Workqueue is not scheduled as RTNL lock is already
+	 * taken by hostapd thread which has issued start_bss command to driver.
+	 * Driver cannot complete start_bss as the pending command at the head
+	 * of the SME command pending list is hw_mode_update for STA session
+	 * which cannot be processed as SME is in WAITforKey state for STA
+	 * interface. The start_bss command for SAP interface is queued behind
+	 * the hw_mode_update command and so it cannot be processed until
+	 * hw_mode_update command is processed. This is causing a deadlock so
+	 * disconnect the STA interface first if connection or key exchange is
+	 * in progress and then start SAP interface.
+	 */
+	sta_adapter = hdd_get_sta_connection_in_progress(hdd_ctx);
+	if (sta_adapter) {
+		hdd_debug("Disconnecting STA with session id: %d",
+			  sta_adapter->session_id);
+		wlan_hdd_disconnect(sta_adapter, eCSR_DISCONNECT_REASON_DEAUTH);
+	}
+
 	sme_config = qdf_mem_malloc(sizeof(*sme_config));
 	if (!sme_config) {
 		hdd_err("failed to allocate memory");