wifi: ath12k: Fix memory leak during extended skb allocation
authorP Praneesh <praneesh.p@oss.qualcomm.com>
Fri, 11 Apr 2025 06:01:53 +0000 (11:31 +0530)
committerJeff Johnson <jeff.johnson@oss.qualcomm.com>
Thu, 17 Apr 2025 22:49:33 +0000 (15:49 -0700)
In ath12k_dp_tx(), memory allocated for extended skb is not freed
properly, causing a memory leak even when the host receives tx
completion for those skbs. Fix this issue by storing skb_ext_desc
in the host tx descriptor and using this skb_ext_desc field during
completion or during ath12k_dp_cc_cleanup().

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3

Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices")
Signed-off-by: P Praneesh <praneesh.p@oss.qualcomm.com>
Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com>
Link: https://patch.msgid.link/20250411060154.1388159-4-praneesh.p@oss.qualcomm.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
drivers/net/wireless/ath/ath12k/dp.c
drivers/net/wireless/ath/ath12k/dp.h
drivers/net/wireless/ath/ath12k/dp_tx.c

index 59f61341383a7a4a3a0693ecb75e943f8a2e3264..e04415517860b7629707656a51542081243821d2 100644 (file)
@@ -1206,11 +1206,19 @@ static void ath12k_dp_cc_cleanup(struct ath12k_base *ab)
                        if (!skb)
                                continue;
 
+                       skb_cb = ATH12K_SKB_CB(skb);
+                       if (skb_cb->paddr_ext_desc) {
+                               dma_unmap_single(ab->dev,
+                                                skb_cb->paddr_ext_desc,
+                                                tx_desc_info->skb_ext_desc->len,
+                                                DMA_TO_DEVICE);
+                               dev_kfree_skb_any(tx_desc_info->skb_ext_desc);
+                       }
+
                        /* if we are unregistering, hw would've been destroyed and
                         * ar is no longer valid.
                         */
                        if (!(test_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags))) {
-                               skb_cb = ATH12K_SKB_CB(skb);
                                ar = skb_cb->ar;
 
                                if (atomic_dec_and_test(&ar->dp.num_tx_pending))
index 241da2a3bd3429fce4f342a56ec19d0c84c47d4b..706d766d8c81a59df3e650f65e386094aa6af9ee 100644 (file)
@@ -296,6 +296,7 @@ struct ath12k_rx_desc_info {
 struct ath12k_tx_desc_info {
        struct list_head list;
        struct sk_buff *skb;
+       struct sk_buff *skb_ext_desc;
        u32 desc_id; /* Cookie */
        u8 mac_id;
        u8 pool_id;
@@ -303,6 +304,7 @@ struct ath12k_tx_desc_info {
 
 struct ath12k_tx_desc_params {
        struct sk_buff *skb;
+       struct sk_buff *skb_ext_desc;
        u8 mac_id;
 };
 
index a272d5b8a51c447ccace78a91363892c08bda63b..9bcd20712888d867bc50676177db2455ec37719e 100644 (file)
@@ -84,6 +84,7 @@ static void ath12k_dp_tx_release_txbuf(struct ath12k_dp *dp,
                                       u8 pool_id)
 {
        spin_lock_bh(&dp->tx_desc_lock[pool_id]);
+       tx_desc->skb_ext_desc = NULL;
        list_move_tail(&tx_desc->list, &dp->tx_desc_free_list[pool_id]);
        spin_unlock_bh(&dp->tx_desc_lock[pool_id]);
 }
@@ -431,6 +432,7 @@ map:
                ti.type = HAL_TCL_DESC_TYPE_EXT_DESC;
 
                skb_cb->paddr_ext_desc = ti.paddr;
+               tx_desc->skb_ext_desc = skb_ext_desc;
        }
 
        hal_ring_id = tx_ring->tcl_data_ring.ring_id;
@@ -525,9 +527,11 @@ static void ath12k_dp_tx_free_txbuf(struct ath12k_base *ab,
        ar = ab->pdevs[pdev_id].ar;
 
        dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
-       if (skb_cb->paddr_ext_desc)
+       if (skb_cb->paddr_ext_desc) {
                dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc,
                                 sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE);
+               dev_kfree_skb_any(desc_params->skb_ext_desc);
+       }
 
        ieee80211_free_txskb(ar->ah->hw, msdu);
 
@@ -558,9 +562,11 @@ ath12k_dp_tx_htt_tx_complete_buf(struct ath12k_base *ab,
                wake_up(&ar->dp.tx_empty_waitq);
 
        dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
-       if (skb_cb->paddr_ext_desc)
+       if (skb_cb->paddr_ext_desc) {
                dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc,
                                 sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE);
+               dev_kfree_skb_any(desc_params->skb_ext_desc);
+       }
 
        vif = skb_cb->vif;
        if (vif) {
@@ -773,9 +779,11 @@ static void ath12k_dp_tx_complete_msdu(struct ath12k *ar,
        skb_cb = ATH12K_SKB_CB(msdu);
 
        dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
-       if (skb_cb->paddr_ext_desc)
+       if (skb_cb->paddr_ext_desc) {
                dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc,
                                 sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE);
+               dev_kfree_skb_any(desc_params->skb_ext_desc);
+       }
 
        rcu_read_lock();
 
@@ -953,6 +961,7 @@ void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id)
 
                desc_params.mac_id = tx_desc->mac_id;
                desc_params.skb = tx_desc->skb;
+               desc_params.skb_ext_desc = tx_desc->skb_ext_desc;
 
                /* Release descriptor as soon as extracting necessary info
                 * to reduce contention