diff mbox series

[1/2] wifi: ath11k: Fix DMA buffer allocation to resolve SWIOTLB issues

Message ID 20250119164219.647059-2-quic_ppranees@quicinc.com
State New
Headers show
Series wifi: ath11k: Fix SWIOTLB issues with DMA buffer allocation | expand

Commit Message

P Praneesh Jan. 19, 2025, 4:42 p.m. UTC
Currently, the driver allocates cacheable DMA buffers for rings like
HAL_REO_DST and HAL_WBM2SW_RELEASE. The buffers for HAL_WBM2SW_RELEASE
are large (1024 KiB), exceeding the SWIOTLB slot size of 256 KiB. This
leads to "swiotlb buffer is full" error messages on systems without an
IOMMU that use SWIOTLB, causing driver initialization failures. The driver
calls dma_map_single() with these large buffers obtained from kzalloc(),
resulting in ring initialization errors on systems without an IOMMU that
use SWIOTLB.

To address these issues, replace the flawed buffer allocation mechanism
with the appropriate DMA API. Specifically, use dma_alloc_noncoherent()
for cacheable DMA buffers, ensuring proper freeing of buffers with
dma_free_noncoherent().

Error log:
[   10.194343] ath11k_pci 0000:04:00.0: swiotlb buffer is full (sz:1048583 bytes), total 32768 (slots), used 2529 (slots)
[   10.194406] ath11k_pci 0000:04:00.0: failed to set up tcl_comp ring (0) :-12
[   10.194781] ath11k_pci 0000:04:00.0: failed to init DP: -12

Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3

Reported-by: Tim Harvey <tharvey@gateworks.com>
Closes: https://lore.kernel.org/all/20241210041133.GA17116@lst.de/
Signed-off-by: P Praneesh <quic_ppranees@quicinc.com>
---
 drivers/net/wireless/ath/ath11k/dp.c | 35 +++++++++-------------------
 1 file changed, 11 insertions(+), 24 deletions(-)

Comments

Tim Harvey Jan. 21, 2025, 5:25 p.m. UTC | #1
On Sun, Jan 19, 2025 at 8:42 AM P Praneesh <quic_ppranees@quicinc.com> wrote:
>
> Currently, the driver allocates cacheable DMA buffers for rings like
> HAL_REO_DST and HAL_WBM2SW_RELEASE. The buffers for HAL_WBM2SW_RELEASE
> are large (1024 KiB), exceeding the SWIOTLB slot size of 256 KiB. This
> leads to "swiotlb buffer is full" error messages on systems without an
> IOMMU that use SWIOTLB, causing driver initialization failures. The driver
> calls dma_map_single() with these large buffers obtained from kzalloc(),
> resulting in ring initialization errors on systems without an IOMMU that
> use SWIOTLB.
>
> To address these issues, replace the flawed buffer allocation mechanism
> with the appropriate DMA API. Specifically, use dma_alloc_noncoherent()
> for cacheable DMA buffers, ensuring proper freeing of buffers with
> dma_free_noncoherent().
>
> Error log:
> [   10.194343] ath11k_pci 0000:04:00.0: swiotlb buffer is full (sz:1048583 bytes), total 32768 (slots), used 2529 (slots)
> [   10.194406] ath11k_pci 0000:04:00.0: failed to set up tcl_comp ring (0) :-12
> [   10.194781] ath11k_pci 0000:04:00.0: failed to init DP: -12
>
> Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1
> Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3
>
> Reported-by: Tim Harvey <tharvey@gateworks.com>
> Closes: https://lore.kernel.org/all/20241210041133.GA17116@lst.de/
> Signed-off-by: P Praneesh <quic_ppranees@quicinc.com>
> ---
>  drivers/net/wireless/ath/ath11k/dp.c | 35 +++++++++-------------------
>  1 file changed, 11 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c
> index fbf666d0ecf1..f124b7329e1a 100644
> --- a/drivers/net/wireless/ath/ath11k/dp.c
> +++ b/drivers/net/wireless/ath/ath11k/dp.c
> @@ -1,7 +1,7 @@
>  // SPDX-License-Identifier: BSD-3-Clause-Clear
>  /*
>   * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
> - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
>   */
>
>  #include <crypto/hash.h>
> @@ -104,14 +104,12 @@ void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring)
>         if (!ring->vaddr_unaligned)
>                 return;
>
> -       if (ring->cached) {
> -               dma_unmap_single(ab->dev, ring->paddr_unaligned, ring->size,
> -                                DMA_FROM_DEVICE);
> -               kfree(ring->vaddr_unaligned);
> -       } else {
> +       if (ring->cached)
> +               dma_free_noncoherent(ab->dev, ring->size, ring->vaddr_unaligned,
> +                                    ring->paddr_unaligned, DMA_FROM_DEVICE);
> +       else
>                 dma_free_coherent(ab->dev, ring->size, ring->vaddr_unaligned,
>                                   ring->paddr_unaligned);
> -       }
>
>         ring->vaddr_unaligned = NULL;
>  }
> @@ -249,25 +247,14 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring,
>                 default:
>                         cached = false;
>                 }
> -
> -               if (cached) {
> -                       ring->vaddr_unaligned = kzalloc(ring->size, GFP_KERNEL);
> -                       if (!ring->vaddr_unaligned)
> -                               return -ENOMEM;
> -
> -                       ring->paddr_unaligned = dma_map_single(ab->dev,
> -                                                              ring->vaddr_unaligned,
> -                                                              ring->size,
> -                                                              DMA_FROM_DEVICE);
> -                       if (dma_mapping_error(ab->dev, ring->paddr_unaligned)) {
> -                               kfree(ring->vaddr_unaligned);
> -                               ring->vaddr_unaligned = NULL;
> -                               return -ENOMEM;
> -                       }
> -               }
>         }
>
> -       if (!cached)
> +       if (cached)
> +               ring->vaddr_unaligned = dma_alloc_noncoherent(ab->dev, ring->size,
> +                                                             &ring->paddr_unaligned,
> +                                                             DMA_FROM_DEVICE,
> +                                                             GFP_KERNEL);
> +       else
>                 ring->vaddr_unaligned = dma_alloc_coherent(ab->dev, ring->size,
>                                                            &ring->paddr_unaligned,
>                                                            GFP_KERNEL);
> --
> 2.34.1
>

Thanks, this resolves the issue I reported. I tested this with qcn9074
on an imx8mm (no iommu) with 4GiB DRAM and verified swiotlb was not
being used and infrastructure mode worked fine.

Tested-By: Tim Harvey <tharvey@gateworks.com>

Best regards,

Tim
diff mbox series

Patch

diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c
index fbf666d0ecf1..f124b7329e1a 100644
--- a/drivers/net/wireless/ath/ath11k/dp.c
+++ b/drivers/net/wireless/ath/ath11k/dp.c
@@ -1,7 +1,7 @@ 
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <crypto/hash.h>
@@ -104,14 +104,12 @@  void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring)
 	if (!ring->vaddr_unaligned)
 		return;
 
-	if (ring->cached) {
-		dma_unmap_single(ab->dev, ring->paddr_unaligned, ring->size,
-				 DMA_FROM_DEVICE);
-		kfree(ring->vaddr_unaligned);
-	} else {
+	if (ring->cached)
+		dma_free_noncoherent(ab->dev, ring->size, ring->vaddr_unaligned,
+				     ring->paddr_unaligned, DMA_FROM_DEVICE);
+	else
 		dma_free_coherent(ab->dev, ring->size, ring->vaddr_unaligned,
 				  ring->paddr_unaligned);
-	}
 
 	ring->vaddr_unaligned = NULL;
 }
@@ -249,25 +247,14 @@  int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring,
 		default:
 			cached = false;
 		}
-
-		if (cached) {
-			ring->vaddr_unaligned = kzalloc(ring->size, GFP_KERNEL);
-			if (!ring->vaddr_unaligned)
-				return -ENOMEM;
-
-			ring->paddr_unaligned = dma_map_single(ab->dev,
-							       ring->vaddr_unaligned,
-							       ring->size,
-							       DMA_FROM_DEVICE);
-			if (dma_mapping_error(ab->dev, ring->paddr_unaligned)) {
-				kfree(ring->vaddr_unaligned);
-				ring->vaddr_unaligned = NULL;
-				return -ENOMEM;
-			}
-		}
 	}
 
-	if (!cached)
+	if (cached)
+		ring->vaddr_unaligned = dma_alloc_noncoherent(ab->dev, ring->size,
+							      &ring->paddr_unaligned,
+							      DMA_FROM_DEVICE,
+							      GFP_KERNEL);
+	else
 		ring->vaddr_unaligned = dma_alloc_coherent(ab->dev, ring->size,
 							   &ring->paddr_unaligned,
 							   GFP_KERNEL);