Ver Fonte

qcacmn: Avoid moving the SM to INIT state if disconnect is pending

If a connect req fails before serialization, with a disconnect req
Already queued. Indicating the failed connect req and moving
the SM to INIT state will lead to a invalid disconnected state
Even when, disconnect is in progress.

Thus in this case wait for disconnect to complete before indicating
connect failure and moving SM to INIT state.

Change-Id: Ib25ed43276410cd3e1bb717f64742d691bb17568
CRs-Fixed: 2802137
gaurank kathpalia há 4 anos atrás
pai
commit
55929f5239

+ 8 - 1
umac/mlme/connection_mgr/core/src/wlan_cm_connect.c

@@ -920,9 +920,16 @@ cm_handle_connect_req_in_non_init_state(struct cnx_mgr *cm_ctx,
 		 *    cleaning up the active connect request and thus only
 		 *    remove the pending connect.
 		 */
-		cm_flush_pending_request(cm_ctx, CONNECT_REQ_PREFIX);
+		cm_flush_pending_request(cm_ctx, CONNECT_REQ_PREFIX, false);
 		break;
 	case WLAN_CM_S_DISCONNECTING:
+		/*
+		 * Flush failed pending connect req as new req is received
+		 * and its no longer the latest one.
+		 */
+		if (cm_ctx->connect_count)
+			cm_flush_pending_request(cm_ctx, CONNECT_REQ_PREFIX,
+						 true);
 		/*
 		 * In case of disconnecting state, there could be 2 scenarios:-
 		 * In both case no state specific action is required.

+ 18 - 4
umac/mlme/connection_mgr/core/src/wlan_cm_disconnect.c

@@ -389,9 +389,16 @@ QDF_STATUS cm_disconnect_complete(struct cnx_mgr *cm_ctx,
 	 * complete.
 	 */
 	if (resp->req.cm_id == cm_ctx->active_cm_id)
-		cm_flush_pending_request(cm_ctx, DISCONNECT_REQ_PREFIX);
+		cm_flush_pending_request(cm_ctx, DISCONNECT_REQ_PREFIX, false);
 
 	cm_remove_cmd(cm_ctx, resp->req.cm_id);
+	mlme_debug(CM_PREFIX_FMT "disconnect count %d connect count %d",
+		   CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev),
+				 resp->req.cm_id),
+		   cm_ctx->disconnect_count, cm_ctx->connect_count);
+	/* Flush failed connect req as pending disconnect is completed */
+	if (!cm_ctx->disconnect_count && cm_ctx->connect_count)
+		cm_flush_pending_request(cm_ctx, CONNECT_REQ_PREFIX, true);
 
 	return QDF_STATUS_SUCCESS;
 }
@@ -429,7 +436,14 @@ cm_handle_discon_req_in_non_connected_state(struct cnx_mgr *cm_ctx,
 		mlme_cm_osif_update_id_and_src(cm_ctx->vdev,
 					       CM_SOURCE_INVALID,
 					       CM_ID_INVALID);
-		cm_flush_pending_request(cm_ctx, DISCONNECT_REQ_PREFIX);
+		cm_flush_pending_request(cm_ctx, DISCONNECT_REQ_PREFIX, false);
+		/*
+		 * Flush failed pending connect req as new req is received
+		 * and its no longer the latest one.
+		 */
+		if (cm_ctx->connect_count)
+			cm_flush_pending_request(cm_ctx, CONNECT_REQ_PREFIX,
+						 true);
 		break;
 	case WLAN_CM_SS_JOIN_ACTIVE:
 		/*
@@ -462,8 +476,8 @@ cm_handle_discon_req_in_non_connected_state(struct cnx_mgr *cm_ctx,
 		 * disconnect requests pending, so flush all the requests except
 		 * the activated request.
 		 */
-		cm_flush_pending_request(cm_ctx, CONNECT_REQ_PREFIX);
-		cm_flush_pending_request(cm_ctx, DISCONNECT_REQ_PREFIX);
+		cm_flush_pending_request(cm_ctx, CONNECT_REQ_PREFIX, false);
+		cm_flush_pending_request(cm_ctx, DISCONNECT_REQ_PREFIX, false);
 		break;
 	default:
 		mlme_err("Vdev %d disconnect req in invalid state %d",

+ 4 - 0
umac/mlme/connection_mgr/core/src/wlan_cm_main.h

@@ -131,12 +131,16 @@ struct cm_disconnect_req {
  * struct cm_req - connect manager req
  * @node: connection manager req node
  * @cm_id: cm id
+ * @failed_req: set if req failed before serialization,
+ * with a commands pending before it, ie this is the latest command which failed
+ * but still some operation(req) is pending.
  * @connect_req: connect req
  * @disconnect_req: disconnect req
  */
 struct cm_req {
 	qdf_list_node_t node;
 	wlan_cm_id cm_id;
+	bool failed_req;
 	union {
 		struct cm_connect_req connect_req;
 		struct cm_disconnect_req discon_req;

+ 4 - 2
umac/mlme/connection_mgr/core/src/wlan_cm_main_api.h

@@ -617,13 +617,15 @@ bool cm_is_cm_id_current_candidate_single_pmk(struct cnx_mgr *cm_ctx,
 /**
  * cm_flush_pending_request() - Flush all pending requests matching flush prefix
  * @cm_ctx: connection manager context
- * @flush_prefix: prefix for the type of command to flush
+ * @prefix: prefix for the type of command to flush
+ * @only_failed_req: flush only the failed pending req
  *
  * Context: Can be called from any context.
  *
  * Return: void
  */
-void cm_flush_pending_request(struct cnx_mgr *cm_ctx, uint32_t flush_prefix);
+void cm_flush_pending_request(struct cnx_mgr *cm_ctx, uint32_t prefix,
+			      bool only_failed_req);
 
 /**
  * cm_remove_cmd() - Remove cmd from req list and serialization

+ 26 - 0
umac/mlme/connection_mgr/core/src/wlan_cm_sm.c

@@ -422,6 +422,8 @@ static bool cm_subst_join_pending_event(void *ctx, uint16_t event,
 	struct cnx_mgr *cm_ctx = ctx;
 	bool event_handled;
 	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	struct wlan_cm_connect_rsp *resp;
+	struct cm_req *cm_req;
 
 	switch (event) {
 	case WLAN_CM_SM_EV_CONNECT_REQ:
@@ -480,6 +482,30 @@ static bool cm_subst_join_pending_event(void *ctx, uint16_t event,
 			event_handled = false;
 			break;
 		}
+		/*
+		 * On connect req failure (before serialization), if there is a
+		 * pending disconnect req then move to disconnecting state and
+		 * wait for disconnect to complete before moving to INIT state.
+		 * Else directly transition to INIT state.
+		 *
+		 * On disconnect completion or a new connect/disconnect req in
+		 * disconnnecting state, the failed connect req will be flushed.
+		 * This will ensure SM moves to INIT state after completion of
+		 * all operation.
+		 */
+		if (cm_ctx->disconnect_count) {
+			resp = data;
+
+			mlme_debug(CM_PREFIX_FMT "disconnect_count %d",
+				   CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev),
+						 resp->cm_id),
+				   cm_ctx->disconnect_count);
+			cm_req = cm_get_req_by_cm_id(cm_ctx, resp->cm_id);
+			cm_req->failed_req = true;
+			cm_sm_transition_to(cm_ctx, WLAN_CM_S_DISCONNECTING);
+			event_handled = true;
+			break;
+		}
 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_INIT);
 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
 		event_handled = true;

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

@@ -462,11 +462,12 @@ static void cm_remove_cmd_from_serialization(struct cnx_mgr *cm_ctx,
 }
 
 void
-cm_flush_pending_request(struct cnx_mgr *cm_ctx, uint32_t flush_prefix)
+cm_flush_pending_request(struct cnx_mgr *cm_ctx, uint32_t prefix,
+			 bool only_failed_req)
 {
 	qdf_list_node_t *cur_node = NULL, *next_node = NULL;
 	struct cm_req *cm_req;
-	uint32_t prefix;
+	uint32_t req_prefix;
 
 	cm_req_lock_acquire(cm_ctx);
 	qdf_list_peek_front(&cm_ctx->req_list, &cur_node);
@@ -474,17 +475,18 @@ cm_flush_pending_request(struct cnx_mgr *cm_ctx, uint32_t flush_prefix)
 		qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node);
 		cm_req = qdf_container_of(cur_node, struct cm_req, node);
 
-		prefix = CM_ID_GET_PREFIX(cm_req->cm_id);
+		req_prefix = CM_ID_GET_PREFIX(cm_req->cm_id);
 
 		/* Only remove the pending requests matching the flush prefix */
-		if (prefix != flush_prefix ||
-		    cm_req->cm_id == cm_ctx->active_cm_id) {
-			cur_node = next_node;
-			next_node = NULL;
-			continue;
-		}
+		if (req_prefix != prefix ||
+		    cm_req->cm_id == cm_ctx->active_cm_id)
+			goto next;
 
-		if (prefix == CONNECT_REQ_PREFIX) {
+		/* If only_failed_req is set flush only failed req */
+		if (only_failed_req && !cm_req->failed_req)
+			goto next;
+
+		if (req_prefix == CONNECT_REQ_PREFIX) {
 			cm_handle_connect_flush(cm_ctx, cm_req);
 			cm_ctx->connect_count--;
 			cm_free_connect_req_mem(&cm_req->connect_req);
@@ -498,7 +500,7 @@ cm_flush_pending_request(struct cnx_mgr *cm_ctx, uint32_t flush_prefix)
 		cm_remove_cmd_from_serialization(cm_ctx, cm_req->cm_id);
 		qdf_list_remove_node(&cm_ctx->req_list, &cm_req->node);
 		qdf_mem_free(cm_req);
-
+next:
 		cur_node = next_node;
 		next_node = NULL;
 	}