1
0

qcacmn: Allow disconnect req in INIT state for link switch

VDEV is moved to INIT state as part of link switch disconnect,
before set MAC address response is received, any disconnect
request on this VDEV will not be handled as VDEV is in INIT
state, if link switch is in progress then it will abort link
switch and starts link switch dequeue process.

If the new disconnect request is from userspace it will
increment the OSIF ops, but if link switch is on assoc VDEV
OSIF is notified to restore the adapter deflink as part of
link switch complete where it wait for all OSIF ops to
complete. This is a deadlock case where driver is waiting
for ops completion on same thread where ops is initiated.

To fix this issue, do not handle link switch dequeue on the
same thread, instead move the link switch state to abort
and when actual link switch thread comes it will flush from
serialization.

If userspace disconnect is not queued as VDEV is in INIT
state due to link switch, kernel won't be notified about
the disconnect as this notification is only done on assoc
VDEV and any further connect requests from supplicant gets
dropped in kernel saying already connected and supplicant
will immediately try disconnect which driver will again
drop as VDEV is in INIT state. To avoid this kernel-driver
out of sync, forcefully move VDEV to disconnecting state
and queue the disconnect request.

Change-Id: I116859601ebba21d44797e74e160b56532ef833c
CRs-Fixed: 3588936
Este cometimento está contido em:
Vinod Kumar Pirla
2023-08-17 08:36:38 -07:00
cometido por Rahul Choudhary
ascendente 78d988e666
cometimento 9e3a7ecb78
8 ficheiros modificados com 126 adições e 30 eliminações

Ver ficheiro

@@ -1682,7 +1682,7 @@ cm_handle_connect_req_in_non_init_state(struct cnx_mgr *cm_ctx,
/* Reject any link switch connect request while in non-init state */
if (cm_req->req.source == CM_MLO_LINK_SWITCH_CONNECT) {
mlme_info(CM_PREFIX_FMT "Ignore disconnect req from source %d state %d",
mlme_info(CM_PREFIX_FMT "Ignore connect req from source %d state %d",
CM_PREFIX_REF(vdev_id, cm_req->cm_id),
cm_req->req.source, cm_state_substate);
return QDF_STATUS_E_INVAL;

Ver ficheiro

@@ -639,7 +639,8 @@ QDF_STATUS cm_disconnect_complete(struct cnx_mgr *cm_ctx,
if (is_link_switch_cmd) {
cm_reset_active_cm_id(cm_ctx->vdev, resp->req.cm_id);
mlo_mgr_link_switch_disconnect_done(cm_ctx->vdev,
link_switch_status);
link_switch_status,
is_link_switch_cmd);
}
return QDF_STATUS_SUCCESS;
@@ -760,13 +761,27 @@ cm_handle_discon_req_in_non_connected_state(struct cnx_mgr *cm_ctx,
*
* So no need to do anything here, just return failure and drop
* disconnect.
*
* Notification to userspace is done on non-LINK VDEV in case of
* MLO connection, and if assoc VDEV is in INIT state due to
* link switch disconnect and dropping userspace disconnect here
* might lead to not notifying kernel and any further connect
* requests from supplicant will be dropped by kernel saying
* already connected and supplicant will immediately attempt
* disconnect which will again gets dropped.
* Notify MLO manager to terminate link switch operation and
* instead of dropping the disconnect forcefully move VDEV state
* to disconnecting and add disconnect request to queue so that
* kernel and driver will be in sync.
*/
if (cm_req->req.source != CM_MLO_LINK_SWITCH_DISCONNECT &&
wlan_vdev_mlme_is_mlo_link_switch_in_progress(cm_ctx->vdev)) {
mlme_info(CM_PREFIX_FMT "Notfiy MLO MGR to abort link switch",
CM_PREFIX_REF(vdev_id, cm_req->cm_id));
mlo_mgr_link_switch_disconnect_done(cm_ctx->vdev,
QDF_STATUS_E_ABORTED);
QDF_STATUS_E_ABORTED,
false);
break;
} else {
mlme_info(CM_PREFIX_FMT "dropping disconnect req from source %d in INIT state",

Ver ficheiro

@@ -116,13 +116,24 @@ static bool cm_state_init_event(void *ctx, uint16_t event,
cm_disconnect_complete(cm_ctx, data);
break;
case WLAN_CM_SM_EV_DISCONNECT_REQ:
cm_handle_discon_req_in_non_connected_state(cm_ctx, data,
WLAN_CM_S_INIT);
status = cm_handle_discon_req_in_non_connected_state(cm_ctx, data,
WLAN_CM_S_INIT);
if (QDF_IS_STATUS_ERROR(status)) {
/*
* Return not handled as this req need to be
* dropped and return failure to the requester
*/
event_handled = false;
break;
}
/*
* Return not handled as this req need to be dropped and return
* failure to the requester
* If status is success, then forcefully queue
* disconnect req and move VDEV state to disconnecting.
*/
event_handled = false;
cm_sm_transition_to(cm_ctx, WLAN_CM_S_DISCONNECTING);
cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_DISCONNECT_START,
data_len, data);
break;
case WLAN_CM_SM_EV_ROAM_SYNC:
/**

Ver ficheiro

@@ -640,7 +640,8 @@ cm_handle_disconnect_flush(struct cnx_mgr *cm_ctx, struct cm_req *cm_req)
if (resp.req.cm_id & CM_ID_LSWITCH_BIT) {
cm_reset_active_cm_id(cm_ctx->vdev, resp.req.cm_id);
mlo_mgr_link_switch_disconnect_done(cm_ctx->vdev,
QDF_STATUS_E_ABORTED);
QDF_STATUS_E_ABORTED,
true);
}
}