فهرست منبع

qcacmn: Fix scan with random address failed since wdev is connected

Reproduce steps:
1.	Connect from OSIF success, wdev->connected = true;
2.	Disconnect from target if and reassoc from OSIF happens back to
	back.
3.	Disconnect event is not sent to kernel, wdev->connected keeps
	true, isn't cleared.
4.	Connect from OSIF failed too, wdev->connected keeps true,  isn't
	cleared.
5.	Scan with random address failed since wdev->connected is true.

To fix it, if connect req was a reassoc req and received in not connected
state for race between disconnect from target if and reassoc connect from
OSIF, set reassoc_in_non_connected to send disconnect instead of
connect rsp to kernel to cleanup kernel flags like: wdev->connected.

change-Id: Ibbe38da14e9339b49589216250453b76c7387b57
CRs-Fixed: 3290496
Jianmin Zhu 2 سال پیش
والد
کامیت
fb43cc12b8

+ 29 - 2
os_if/linux/mlme/src/osif_cm_connect_rsp.c

@@ -1080,13 +1080,14 @@ QDF_STATUS osif_connect_handler(struct wlan_objmgr_vdev *vdev,
 	struct vdev_osif_priv *osif_priv  = wlan_vdev_get_ospriv(vdev);
 	QDF_STATUS status;
 
-	osif_nofl_info("%s(vdevid-%d): " QDF_MAC_ADDR_FMT " Connect with " QDF_MAC_ADDR_FMT " SSID \"%.*s\" is %s cm_id 0x%x cm_reason %d status_code %d is_reassoc %d",
+	osif_nofl_info("%s(vdevid-%d): " QDF_MAC_ADDR_FMT " Connect with " QDF_MAC_ADDR_FMT " SSID \"%.*s\" is %s cm_id 0x%x cm_reason %d status_code %d is_reassoc %d send discon %d",
 		       osif_priv->wdev->netdev->name, rsp->vdev_id,
 		       QDF_MAC_ADDR_REF(wlan_vdev_mlme_get_macaddr(vdev)),
 		       QDF_MAC_ADDR_REF(rsp->bssid.bytes),
 		       rsp->ssid.length, rsp->ssid.ssid,
 		       rsp->connect_status ? "FAILURE" : "SUCCESS", rsp->cm_id,
-		       rsp->reason, rsp->status_code, rsp->is_reassoc);
+		       rsp->reason, rsp->status_code, rsp->is_reassoc,
+		       rsp->send_disconnect);
 
 	osif_check_and_unlink_bss(vdev, osif_priv, rsp);
 
@@ -1097,8 +1098,34 @@ QDF_STATUS osif_connect_handler(struct wlan_objmgr_vdev *vdev,
 	}
 
 	osif_cm_connect_comp_ind(vdev, rsp, OSIF_PRE_USERSPACE_UPDATE);
+
+	/*
+	 * To fix issue that scan with random address failed since wdev keeps
+	 * connected, rsp->send_disconnect is added.
+	 * Reproduce steps:
+	 *  1.  Connect from OSIF success, wdev->connected = true;
+	 *  2.  Disconnect from target if and reassoc from OSIF happens back to
+	 *	back.
+	 *  3.  Disconnect event is not sent to kernel, wdev->connected keeps
+	 *	true, isn't cleared.
+	 *  4.  Connect from OSIF failed too, wdev->connected keeps true,  isn't
+	 *	cleared.
+	 *  5.  Scan with random address failed since wdev->connected is true.
+	 *
+	 * To fix it, if connect req was a reassoc req and received in not
+	 * connected state for race between disconnect from target if and
+	 * reassoc connect from OSIF, set reassoc_in_non_connected to send
+	 * disconnect instead of connect rsp to kernel to cleanup kernel flags
+	 * like: wdev->connected.
+	 */
 	if (rsp->is_reassoc)
 		osif_indicate_reassoc_results(vdev, osif_priv, rsp);
+	else if (rsp->send_disconnect &&
+		 QDF_IS_STATUS_ERROR(rsp->connect_status))
+		osif_cm_indicate_disconnect(vdev, osif_priv->wdev->netdev,
+					    WLAN_REASON_UNSPECIFIED,
+					    false, NULL, 0,
+					    qdf_mem_malloc_flags());
 	else
 		osif_indcate_connect_results(vdev, osif_priv, rsp);
 	osif_cm_connect_comp_ind(vdev, rsp, OSIF_POST_USERSPACE_UPDATE);

+ 5 - 4
os_if/linux/mlme/src/osif_cm_disconnect_rsp.c

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2012-2015, 2020-2021 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -81,7 +82,7 @@ rel_lock:
 (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0))
 #ifdef WLAN_FEATURE_11BE_MLO
 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
-static void
+void
 osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
 			    struct net_device *dev,
 			    enum ieee80211_reasoncode reason,
@@ -98,7 +99,7 @@ osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
 	}
 }
 #else /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
-static void
+void
 osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
 			    struct net_device *dev,
 			    enum ieee80211_reasoncode reason,
@@ -128,7 +129,7 @@ osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
 }
 #endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
 #else /* WLAN_FEATURE_11BE_MLO */
-static void
+void
 osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
 			    struct net_device *dev,
 			    enum ieee80211_reasoncode reason,
@@ -139,7 +140,7 @@ osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
 }
 #endif /* WLAN_FEATURE_11BE_MLO */
 #else
-static void
+void
 osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
 			    struct net_device *dev,
 			    enum ieee80211_reasoncode reason,

+ 22 - 0
os_if/linux/mlme/src/osif_cm_rsp.h

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2012-2015,2020-2021 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -134,4 +135,25 @@ osif_indicate_reassoc_results(struct wlan_objmgr_vdev *vdev,
 QDF_STATUS osif_failed_candidate_handler(struct wlan_objmgr_vdev *vdev,
 					 struct wlan_cm_connect_resp *rsp);
 
+/**
+ * osif_cm_indicate_disconnect - notify osif that connection was dropped
+ *
+ * @vdev: pointer to vdev
+ * @dev: network device
+ * @reason: reason code for the disconnection, set it to 0 if unknown
+ * @locally_generated: disconnection was requested locally
+ * @ie: information elements of the deauth/disassoc frame (may be %NULL)
+ * @ie_len: length of IEs
+ * @gfp: allocation flags
+ *
+ * After it calls this function, the driver should enter an idle state
+ * and not try to connect to any AP any more.
+ */
+void
+osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
+			    struct net_device *dev,
+			    enum ieee80211_reasoncode reason,
+			    bool locally_generated, const u8 *ie,
+			    size_t ie_len, gfp_t gfp);
+
 #endif /* __OSIF_CM_RSP_H */

+ 67 - 0
umac/mlme/connection_mgr/core/src/wlan_cm_connect.c

@@ -1340,6 +1340,15 @@ cm_handle_connect_req_in_non_init_state(struct cnx_mgr *cm_ctx,
 					struct cm_connect_req *cm_req,
 					enum wlan_cm_sm_state cm_state_substate)
 {
+	if (cm_state_substate != WLAN_CM_S_CONNECTED &&
+	    cm_is_connect_req_reassoc(&cm_req->req)) {
+		cm_req->req.reassoc_in_non_connected = true;
+		mlme_debug(CM_PREFIX_FMT "Reassoc received in %d state",
+			   CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev),
+					 cm_req->cm_id),
+			   cm_state_substate);
+	}
+
 	switch (cm_state_substate) {
 	case WLAN_CM_S_ROAMING:
 		/* for FW roam/LFR3 remove the req from the list */
@@ -2236,11 +2245,69 @@ cm_clear_vdev_mlo_cap(struct wlan_objmgr_vdev *vdev)
 { }
 #endif /*WLAN_FEATURE_11BE_MLO*/
 
+/**
+ * cm_is_connect_id_reassoc_in_non_connected()
+ * @cm_ctx: connection manager context
+ * @cm_id: cm id
+ *
+ * If connect req is a reassoc req and received in not connected state
+ *
+ * Return: bool
+ */
+static bool cm_is_connect_id_reassoc_in_non_connected(struct cnx_mgr *cm_ctx,
+						      wlan_cm_id cm_id)
+{
+	qdf_list_node_t *cur_node = NULL, *next_node = NULL;
+	struct cm_req *cm_req;
+	uint32_t prefix = CM_ID_GET_PREFIX(cm_id);
+	bool is_reassoc = false;
+
+	if (prefix != CONNECT_REQ_PREFIX)
+		return is_reassoc;
+
+	cm_req_lock_acquire(cm_ctx);
+	qdf_list_peek_front(&cm_ctx->req_list, &cur_node);
+	while (cur_node) {
+		qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node);
+		cm_req = qdf_container_of(cur_node, struct cm_req, node);
+
+		if (cm_req->cm_id == cm_id) {
+			if (cm_req->connect_req.req.reassoc_in_non_connected)
+				is_reassoc = true;
+			cm_req_lock_release(cm_ctx);
+			return is_reassoc;
+		}
+
+		cur_node = next_node;
+		next_node = NULL;
+	}
+	cm_req_lock_release(cm_ctx);
+
+	return is_reassoc;
+}
+
 QDF_STATUS cm_notify_connect_complete(struct cnx_mgr *cm_ctx,
 				      struct wlan_cm_connect_resp *resp)
 {
+	enum wlan_cm_sm_state sm_state;
+
+	sm_state = cm_get_state(cm_ctx);
+
 	mlme_cm_connect_complete_ind(cm_ctx->vdev, resp);
 	mlo_sta_link_connect_notify(cm_ctx->vdev, resp);
+	/*
+	 * If connect req was a reassoc req and was received in not connected
+	 * state send disconnect instead of connect resp to kernel to cleanup
+	 * kernel flags
+	 */
+	if (QDF_IS_STATUS_ERROR(resp->connect_status) &&
+	    sm_state == WLAN_CM_S_INIT &&
+	    cm_is_connect_id_reassoc_in_non_connected(cm_ctx, resp->cm_id)) {
+		resp->send_disconnect = true;
+		mlme_debug(CM_PREFIX_FMT "Set send disconnect to true to indicate disconnect instaed of connect resp",
+			   CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev),
+					 resp->cm_id));
+	}
 	mlme_cm_osif_connect_complete(cm_ctx->vdev, resp);
 	cm_if_mgr_inform_connect_complete(cm_ctx->vdev,
 					  resp->connect_status);

+ 8 - 0
umac/mlme/connection_mgr/core/src/wlan_cm_main_api.h

@@ -1193,4 +1193,12 @@ void cm_set_candidate_custom_sort_cb(
 				 qdf_list_t *list));
 
 #endif
+
+/**
+ * cm_is_connect_req_reassoc() - Is connect req for reassoc
+ * @req: connect req
+ *
+ * Return: void
+ */
+bool cm_is_connect_req_reassoc(struct wlan_cm_connect_req *req);
 #endif /* __WLAN_CM_MAIN_API_H__ */

+ 1 - 3
umac/mlme/connection_mgr/core/src/wlan_cm_roam_util.c

@@ -72,9 +72,7 @@ QDF_STATUS cm_check_and_prepare_roam_req(struct cnx_mgr *cm_ctx,
 	 * Reject re-assoc unless freq along with prev bssid and one
 	 * of bssid or bssid hint is present.
 	 */
-	if (!freq || qdf_is_macaddr_zero(&req->prev_bssid) ||
-	    (qdf_is_macaddr_zero(&req->bssid) &&
-	     qdf_is_macaddr_zero(&req->bssid_hint)))
+	if (!cm_is_connect_req_reassoc(req))
 		return QDF_STATUS_E_FAILURE;
 
 	wlan_vdev_get_bss_peer_mac(cm_ctx->vdev, &bssid);

+ 11 - 0
umac/mlme/connection_mgr/core/src/wlan_cm_util.c

@@ -1256,6 +1256,17 @@ void cm_fill_ml_partner_info(struct wlan_cm_connect_req *req,
 }
 #endif
 
+bool cm_is_connect_req_reassoc(struct wlan_cm_connect_req *req)
+{
+	if (!qdf_is_macaddr_zero(&req->prev_bssid) &&
+	    (!qdf_is_macaddr_zero(&req->bssid) ||
+	     !qdf_is_macaddr_zero(&req->bssid_hint)) &&
+	    (req->chan_freq || req->chan_freq_hint))
+		return true;
+
+	return false;
+}
+
 bool cm_get_active_connect_req(struct wlan_objmgr_vdev *vdev,
 			       struct wlan_cm_vdev_connect_req *req)
 {

+ 7 - 2
umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_public_struct.h

@@ -188,6 +188,7 @@ enum wlan_cm_source {
  * for production.
  * @is_wps_connection: if its wps connection
  * @is_osen_connection: if its osen connection
+ * @reassoc_in_non_connected: if reassoc received in non connected
  * @dot11mode_filter: dot11mode filter used to restrict connection to
  * 11n/11ac/11ax.
  * @sae_pwe: SAE mechanism for PWE derivation
@@ -216,7 +217,8 @@ struct wlan_cm_connect_req {
 	struct element_info scan_ie;
 	uint8_t force_rsne_override:1,
 		is_wps_connection:1,
-		is_osen_connection:1;
+		is_osen_connection:1,
+		reassoc_in_non_connected:1;
 	enum dot11_mode_filter dot11mode_filter;
 	uint8_t sae_pwe;
 	uint16_t ht_caps;
@@ -483,6 +485,8 @@ struct wlan_roam_sync_info {
  * @is_reassoc: if response is for reassoc/roam
  * @is_ft: is FT reassoc
  * @is_assoc: if response is for assoc
+ * @send_disconnect: if disconnect needed to sent to kernel, for reassoc
+ * received in non connected state, this is to cleanup kernel
  * @cm_id: Connect manager id
  * @bssid: BSSID of the ap
  * @ssid: SSID of the connection
@@ -502,7 +506,8 @@ struct wlan_cm_connect_resp {
 		is_osen_connection:1,
 		is_reassoc:1,
 		is_ft:1,
-		is_assoc:1;
+		is_assoc:1,
+		send_disconnect:1;
 	wlan_cm_id cm_id;
 	struct qdf_mac_addr bssid;
 	struct wlan_ssid ssid;