wifi: ath12k: add wait operation for tx management packets for flush from mac80211
authorKarthik M <quic_karm@quicinc.com>
Fri, 28 Apr 2023 17:01:37 +0000 (20:01 +0300)
committerKalle Valo <quic_kvalo@quicinc.com>
Fri, 5 May 2023 12:56:02 +0000 (15:56 +0300)
Transmission of management packets are done in a work queue. Sometimes
the workqueue does not finish Tx immediately, then it lead after the next
step of vdev delete finished, it start to send the management packet to
firmware and lead firmware crash.

ieee80211_set_disassoc() have logic of ieee80211_flush_queues() after
it send_deauth_disassoc() to ath12k, its purpose is make sure the
deauth was actually sent, so it need to change ath12k to match the
purpose of mac80211.

To address these issues wait for Tx management as well as Tx data packets.

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1

Signed-off-by: Karthik M <quic_karm@quicinc.com>
Signed-off-by: Ramya Gnanasekar <quic_rgnanase@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20230419095738.19859-1-quic_rgnanase@quicinc.com
drivers/net/wireless/ath/ath12k/core.c
drivers/net/wireless/ath/ath12k/core.h
drivers/net/wireless/ath/ath12k/mac.c
drivers/net/wireless/ath/ath12k/wmi.c

index a89e66653f044ffc25ad720265da416a65e75afe..499b81cd938ea6ab680ff696e2edc5d9ac3f6db1 100644 (file)
@@ -706,6 +706,7 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
                idr_for_each(&ar->txmgmt_idr,
                             ath12k_mac_tx_mgmt_pending_free, ar);
                idr_destroy(&ar->txmgmt_idr);
+               wake_up(&ar->txmgmt_empty_waitq);
        }
 
        wake_up(&ab->wmi_ab.tx_credits_wq);
index 9439052a652e818e520181fae54167ce4254f172..2f93296db792a501d775e98f17af6151c20b7a87 100644 (file)
@@ -533,6 +533,7 @@ struct ath12k {
        /* protects txmgmt_idr data */
        spinlock_t txmgmt_idr_lock;
        atomic_t num_pending_mgmt_tx;
+       wait_queue_head_t txmgmt_empty_waitq;
 
        /* cycle count is reported twice for each visited channel during scan.
         * access protected by data_lock
index ee792822b41132ebb3711e1a7e645d2c9477437a..b52675a113f44d0ffad6c319de6448bfb4c1cee2 100644 (file)
@@ -4375,6 +4375,21 @@ static int __ath12k_set_antenna(struct ath12k *ar, u32 tx_ant, u32 rx_ant)
        return 0;
 }
 
+static void ath12k_mgmt_over_wmi_tx_drop(struct ath12k *ar, struct sk_buff *skb)
+{
+       int num_mgmt;
+
+       ieee80211_free_txskb(ar->hw, skb);
+
+       num_mgmt = atomic_dec_if_positive(&ar->num_pending_mgmt_tx);
+
+       if (num_mgmt < 0)
+               WARN_ON_ONCE(1);
+
+       if (!num_mgmt)
+               wake_up(&ar->txmgmt_empty_waitq);
+}
+
 int ath12k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx)
 {
        struct sk_buff *msdu = skb;
@@ -4391,7 +4406,7 @@ int ath12k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx)
        info = IEEE80211_SKB_CB(msdu);
        memset(&info->status, 0, sizeof(info->status));
 
-       ieee80211_free_txskb(ar->hw, msdu);
+       ath12k_mgmt_over_wmi_tx_drop(ar, skb);
 
        return 0;
 }
@@ -4475,7 +4490,7 @@ static void ath12k_mgmt_over_wmi_tx_purge(struct ath12k *ar)
        struct sk_buff *skb;
 
        while ((skb = skb_dequeue(&ar->wmi_mgmt_tx_queue)) != NULL)
-               ieee80211_free_txskb(ar->hw, skb);
+               ath12k_mgmt_over_wmi_tx_drop(ar, skb);
 }
 
 static void ath12k_mgmt_over_wmi_tx_work(struct work_struct *work)
@@ -4490,7 +4505,7 @@ static void ath12k_mgmt_over_wmi_tx_work(struct work_struct *work)
                skb_cb = ATH12K_SKB_CB(skb);
                if (!skb_cb->vif) {
                        ath12k_warn(ar->ab, "no vif found for mgmt frame\n");
-                       ieee80211_free_txskb(ar->hw, skb);
+                       ath12k_mgmt_over_wmi_tx_drop(ar, skb);
                        continue;
                }
 
@@ -4501,16 +4516,14 @@ static void ath12k_mgmt_over_wmi_tx_work(struct work_struct *work)
                        if (ret) {
                                ath12k_warn(ar->ab, "failed to tx mgmt frame, vdev_id %d :%d\n",
                                            arvif->vdev_id, ret);
-                               ieee80211_free_txskb(ar->hw, skb);
-                       } else {
-                               atomic_inc(&ar->num_pending_mgmt_tx);
+                               ath12k_mgmt_over_wmi_tx_drop(ar, skb);
                        }
                } else {
                        ath12k_warn(ar->ab,
                                    "dropping mgmt frame for vdev %d, is_started %d\n",
                                    arvif->vdev_id,
                                    arvif->is_started);
-                       ieee80211_free_txskb(ar->hw, skb);
+                       ath12k_mgmt_over_wmi_tx_drop(ar, skb);
                }
        }
 }
@@ -4541,6 +4554,7 @@ static int ath12k_mac_mgmt_tx(struct ath12k *ar, struct sk_buff *skb,
        }
 
        skb_queue_tail(q, skb);
+       atomic_inc(&ar->num_pending_mgmt_tx);
        ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work);
 
        return 0;
@@ -6014,6 +6028,13 @@ static void ath12k_mac_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *v
                                       ATH12K_FLUSH_TIMEOUT);
        if (time_left == 0)
                ath12k_warn(ar->ab, "failed to flush transmit queue %ld\n", time_left);
+
+       time_left = wait_event_timeout(ar->txmgmt_empty_waitq,
+                                      (atomic_read(&ar->num_pending_mgmt_tx) == 0),
+                                      ATH12K_FLUSH_TIMEOUT);
+       if (time_left == 0)
+               ath12k_warn(ar->ab, "failed to flush mgmt transmit queue %ld\n",
+                           time_left);
 }
 
 static int
@@ -6991,6 +7012,7 @@ int ath12k_mac_register(struct ath12k_base *ab)
                if (ret)
                        goto err_cleanup;
 
+               init_waitqueue_head(&ar->txmgmt_empty_waitq);
                idr_init(&ar->txmgmt_idr);
                spin_lock_init(&ar->txmgmt_idr_lock);
        }
index 7ae0bb78b2b538924ddd8086304679ace7ab3b10..f1d0743eb3498f715226e3930330604d82e470bb 100644 (file)
@@ -4640,6 +4640,7 @@ static int wmi_process_mgmt_tx_comp(struct ath12k *ar, u32 desc_id,
        struct sk_buff *msdu;
        struct ieee80211_tx_info *info;
        struct ath12k_skb_cb *skb_cb;
+       int num_mgmt;
 
        spin_lock_bh(&ar->txmgmt_idr_lock);
        msdu = idr_find(&ar->txmgmt_idr, desc_id);
@@ -4663,10 +4664,15 @@ static int wmi_process_mgmt_tx_comp(struct ath12k *ar, u32 desc_id,
 
        ieee80211_tx_status_irqsafe(ar->hw, msdu);
 
+       num_mgmt = atomic_dec_if_positive(&ar->num_pending_mgmt_tx);
+
        /* WARN when we received this event without doing any mgmt tx */
-       if (atomic_dec_if_positive(&ar->num_pending_mgmt_tx) < 0)
+       if (num_mgmt < 0)
                WARN_ON_ONCE(1);
 
+       if (!num_mgmt)
+               wake_up(&ar->txmgmt_empty_waitq);
+
        return 0;
 }