wifi: ath11k: fix connection failure due to unexpected peer delete
authorBaochen Qiang <quic_bqiang@quicinc.com>
Tue, 23 Jan 2024 02:57:00 +0000 (10:57 +0800)
committerKalle Valo <quic_kvalo@quicinc.com>
Thu, 25 Jan 2024 16:44:02 +0000 (18:44 +0200)
Currently ath11k_mac_op_unassign_vif_chanctx() deletes peer but
ath11k_mac_op_assign_vif_chanctx() doesn't create it. This results in
connection failure if MAC80211 calls drv_unassign_vif_chanctx() and
drv_assign_vif_chanctx() during AUTH and ASSOC, see below log:

[  102.372431] wlan0: authenticated
[  102.372585] ath11k_pci 0000:01:00.0: wlan0: disabling HT/VHT/HE as WMM/QoS is not supported by the AP
[  102.372593] ath11k_pci 0000:01:00.0: mac chanctx unassign ptr ffff895084638598 vdev_id 0
[  102.372808] ath11k_pci 0000:01:00.0: WMI vdev stop id 0x0
[  102.383114] ath11k_pci 0000:01:00.0: vdev stopped for vdev id 0
[  102.384689] ath11k_pci 0000:01:00.0: WMI peer delete vdev_id 0 peer_addr 20:e5:2a:21:c4:51
[  102.396676] ath11k_pci 0000:01:00.0: htt peer unmap vdev 0 peer 20:e5:2a:21:c4:51 id 3
[  102.396711] ath11k_pci 0000:01:00.0: peer delete resp for vdev id 0 addr 20:e5:2a:21:c4:51
[  102.396722] ath11k_pci 0000:01:00.0: mac removed peer 20:e5:2a:21:c4:51  vdev 0 after vdev stop
[  102.396780] ath11k_pci 0000:01:00.0: mac chanctx assign ptr ffff895084639c18 vdev_id 0
[  102.400628] wlan0: associate with 20:e5:2a:21:c4:51 (try 1/3)
[  102.508864] wlan0: associate with 20:e5:2a:21:c4:51 (try 2/3)
[  102.612815] wlan0: associate with 20:e5:2a:21:c4:51 (try 3/3)
[  102.720846] wlan0: association with 20:e5:2a:21:c4:51 timed out

The peer delete logic in ath11k_mac_op_unassign_vif_chanctx() is
introduced by commit b4a0f54156ac ("ath11k: move peer delete after
vdev stop of station for QCA6390 and WCN6855") to fix firmware
crash issue caused by unexpected vdev stop/peer delete sequence.

Actually for a STA interface peer should be deleted in
ath11k_mac_op_sta_state() when STA's state changes from
IEEE80211_STA_NONE to IEEE80211_STA_NOTEXIST, which also coincides
with current peer creation design that peer is created during
IEEE80211_STA_NOTEXIST -> IEEE80211_STA_NONE transition. So move
peer delete back to ath11k_mac_op_sta_state(), also stop vdev before
deleting peer to fix the firmware crash issue mentioned there. In
this way the connection failure mentioned here is also fixed.

Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1
Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23
Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1
Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1

Fixes: b4a0f54156ac ("ath11k: move peer delete after vdev stop of station for QCA6390 and WCN6855")
Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
Acked-by: Jeff Johnson <quic_jjohnson@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://msgid.link/20240123025700.2929-5-quic_bqiang@quicinc.com
drivers/net/wireless/ath/ath11k/mac.c

index 324bbb377eacb960c82a8bdf72e1a256bda8f5bb..4a6ab45e9386ae7041ab83cd2ad377ff41c4bb1c 100644 (file)
@@ -7397,6 +7397,30 @@ static int ath11k_mac_start_vdev_delay(struct ieee80211_hw *hw,
        return 0;
 }
 
+static int ath11k_mac_stop_vdev_early(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif)
+{
+       struct ath11k *ar = hw->priv;
+       struct ath11k_base *ab = ar->ab;
+       struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
+       int ret;
+
+       if (WARN_ON(!arvif->is_started))
+               return -EBUSY;
+
+       ret = ath11k_mac_vdev_stop(arvif);
+       if (ret) {
+               ath11k_warn(ab, "failed to stop vdev %i: %d\n",
+                           arvif->vdev_id, ret);
+               return ret;
+       }
+
+       arvif->is_started = false;
+
+       /* TODO: Setup ps and cts/rts protection */
+       return 0;
+}
+
 static u8 ath11k_mac_get_tpe_count(u8 txpwr_intrprt, u8 txpwr_cnt)
 {
        switch (txpwr_intrprt) {
@@ -7931,15 +7955,17 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
                goto out;
        }
 
-       ret = ath11k_mac_vdev_start(arvif, ctx);
-       if (ret) {
-               ath11k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n",
-                           arvif->vdev_id, vif->addr,
-                           ctx->def.chan->center_freq, ret);
-               goto out;
-       }
+       if (!arvif->is_started) {
+               ret = ath11k_mac_vdev_start(arvif, ctx);
+               if (ret) {
+                       ath11k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n",
+                                   arvif->vdev_id, vif->addr,
+                                   ctx->def.chan->center_freq, ret);
+                       goto out;
+               }
 
-       arvif->is_started = true;
+               arvif->is_started = true;
+       }
 
        if (arvif->vdev_type != WMI_VDEV_TYPE_MONITOR &&
            test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags)) {
@@ -7979,8 +8005,6 @@ ath11k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
                   "chanctx unassign ptr %p vdev_id %i\n",
                   ctx, arvif->vdev_id);
 
-       WARN_ON(!arvif->is_started);
-
        if (ab->hw_params.vdev_start_delay &&
            arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) {
                spin_lock_bh(&ab->base_lock);
@@ -8004,24 +8028,13 @@ ath11k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
                return;
        }
 
-       ret = ath11k_mac_vdev_stop(arvif);
-       if (ret)
-               ath11k_warn(ab, "failed to stop vdev %i: %d\n",
-                           arvif->vdev_id, ret);
-
-       arvif->is_started = false;
-
-       if (ab->hw_params.vdev_start_delay &&
-           arvif->vdev_type == WMI_VDEV_TYPE_STA) {
-               ret = ath11k_peer_delete(ar, arvif->vdev_id, arvif->bssid);
+       if (arvif->is_started) {
+               ret = ath11k_mac_vdev_stop(arvif);
                if (ret)
-                       ath11k_warn(ar->ab,
-                                   "failed to delete peer %pM for vdev %d: %d\n",
-                                   arvif->bssid, arvif->vdev_id, ret);
-               else
-                       ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
-                                  "removed peer %pM  vdev %d after vdev stop\n",
-                                  arvif->bssid, arvif->vdev_id);
+                       ath11k_warn(ab, "failed to stop vdev %i: %d\n",
+                                   arvif->vdev_id, ret);
+
+               arvif->is_started = false;
        }
 
        if (ab->hw_params.vdev_start_delay &&
@@ -9473,6 +9486,46 @@ exit:
        return ret;
 }
 
+static int ath11k_mac_station_remove(struct ath11k *ar,
+                                    struct ieee80211_vif *vif,
+                                    struct ieee80211_sta *sta)
+{
+       struct ath11k_base *ab = ar->ab;
+       struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
+       struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta);
+       int ret;
+
+       if (ab->hw_params.vdev_start_delay &&
+           arvif->is_started &&
+           arvif->vdev_type != WMI_VDEV_TYPE_AP) {
+               ret = ath11k_mac_stop_vdev_early(ar->hw, vif);
+               if (ret) {
+                       ath11k_warn(ab, "failed to do early vdev stop: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr);
+
+       ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr);
+       if (ret)
+               ath11k_warn(ab, "Failed to delete peer: %pM for VDEV: %d\n",
+                           sta->addr, arvif->vdev_id);
+       else
+               ath11k_dbg(ab, ATH11K_DBG_MAC, "Removed peer: %pM for VDEV: %d\n",
+                          sta->addr, arvif->vdev_id);
+
+       ath11k_mac_dec_num_stations(arvif, sta);
+
+       kfree(arsta->tx_stats);
+       arsta->tx_stats = NULL;
+
+       kfree(arsta->rx_stats);
+       arsta->rx_stats = NULL;
+
+       return ret;
+}
+
 static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
                                   struct ieee80211_vif *vif,
                                   struct ieee80211_sta *sta,
@@ -9508,31 +9561,15 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
                                    sta->addr, arvif->vdev_id);
        } else if ((old_state == IEEE80211_STA_NONE &&
                    new_state == IEEE80211_STA_NOTEXIST)) {
-               bool skip_peer_delete = ar->ab->hw_params.vdev_start_delay &&
-                       vif->type == NL80211_IFTYPE_STATION;
-
-               ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr);
-
-               if (!skip_peer_delete) {
-                       ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr);
-                       if (ret)
-                               ath11k_warn(ar->ab,
-                                           "Failed to delete peer: %pM for VDEV: %d\n",
-                                           sta->addr, arvif->vdev_id);
-                       else
-                               ath11k_dbg(ar->ab,
-                                          ATH11K_DBG_MAC,
-                                          "Removed peer: %pM for VDEV: %d\n",
-                                          sta->addr, arvif->vdev_id);
-               }
+               ret = ath11k_mac_station_remove(ar, vif, sta);
+               if (ret)
+                       ath11k_warn(ar->ab, "Failed to remove station: %pM for VDEV: %d\n",
+                                   sta->addr, arvif->vdev_id);
 
-               ath11k_mac_dec_num_stations(arvif, sta);
                mutex_lock(&ar->ab->tbl_mtx_lock);
                spin_lock_bh(&ar->ab->base_lock);
                peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr);
-               if (skip_peer_delete && peer) {
-                       peer->sta = NULL;
-               } else if (peer && peer->sta == sta) {
+               if (peer && peer->sta == sta) {
                        ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n",
                                    vif->addr, arvif->vdev_id);
                        ath11k_peer_rhash_delete(ar->ab, peer);
@@ -9543,12 +9580,6 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
                }
                spin_unlock_bh(&ar->ab->base_lock);
                mutex_unlock(&ar->ab->tbl_mtx_lock);
-
-               kfree(arsta->tx_stats);
-               arsta->tx_stats = NULL;
-
-               kfree(arsta->rx_stats);
-               arsta->rx_stats = NULL;
        } else if (old_state == IEEE80211_STA_AUTH &&
                   new_state == IEEE80211_STA_ASSOC &&
                   (vif->type == NL80211_IFTYPE_AP ||