From: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
To: Jeff Johnson <jjohnson@kernel.org>
Cc: linux-wireless@vger.kernel.org, ath12k@lists.infradead.org,
Baochen Qiang <baochen.qiang@oss.qualcomm.com>
Subject: [PATCH ath-next 3/3] wifi: ath12k: align QMI target memory to 64 KB
Date: Thu, 30 Jul 2026 14:17:42 +0800 [thread overview]
Message-ID: <20260730-ath12k-align-qmi-memory-v1-3-0fef98dda614@oss.qualcomm.com> (raw)
In-Reply-To: <20260730-ath12k-align-qmi-memory-v1-0-0fef98dda614@oss.qualcomm.com>
ath12k_qmi_alloc_chunk() allocates the target memory chunks the firmware
requests via dma_alloc_coherent() and hands the resulting physical
addresses to the firmware. The firmware does not require every chunk to
be aligned, but each unaligned chunk consumes extra TLB entries when the
firmware maps it, and too many unaligned chunks exhaust the TLB and make
the firmware crash. The firmware team recommends aligning the chunks to
64 KB so that the TLB stays within budget. dma_alloc_coherent() only
guarantees page alignment, so a chunk can end up unaligned.
Align the physical address handed to the firmware to 64 KB. A natural
allocation is often already 64 KB aligned, so try the exact size first
and keep it when it happens to be aligned; only when it is not, free it
and over-allocate by SZ_64K - PAGE_SIZE and round the base up. Since the
DMA allocator already guarantees page alignment, that padding is enough
to reach the next 64 KB boundary. Record the raw allocation in
paddr_unaligned/vaddr_unaligned/total_size so the chunk can still be
freed correctly.
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0.c2-00074-QCACOLSWPL_V1_TO_SILICONZ-1
Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/qmi.c | 79 +++++++++++++++++++++++++++--------
drivers/net/wireless/ath/ath12k/qmi.h | 4 ++
2 files changed, 65 insertions(+), 18 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c
index 03ec2057ca27..bfb5b6659f21 100644
--- a/drivers/net/wireless/ath/ath12k/qmi.c
+++ b/drivers/net/wireless/ath/ath12k/qmi.c
@@ -14,6 +14,7 @@
#include <linux/of_address.h>
#include <linux/ioport.h>
#include <linux/of_reserved_mem.h>
+#include <linux/sizes.h>
#define SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02
#define HOST_CSTATE_BIT 0x04
@@ -2583,20 +2584,28 @@ static void ath12k_qmi_free_mlo_mem_chunk(struct ath12k_base *ab,
mlo_chunk->v.ioaddr = NULL;
} else if (mlo_chunk->v.addr) {
dma_free_coherent(ab->dev,
- mlo_chunk->size,
- mlo_chunk->v.addr,
- mlo_chunk->paddr);
+ mlo_chunk->total_size,
+ mlo_chunk->vaddr_unaligned,
+ mlo_chunk->paddr_unaligned);
+ mlo_chunk->vaddr_unaligned = NULL;
mlo_chunk->v.addr = NULL;
}
mlo_chunk->paddr = 0;
+ mlo_chunk->paddr_unaligned = 0;
mlo_chunk->size = 0;
- if (fixed_mem)
+ mlo_chunk->total_size = 0;
+
+ if (fixed_mem) {
chunk->v.ioaddr = NULL;
- else
+ } else {
chunk->v.addr = NULL;
+ chunk->vaddr_unaligned = NULL;
+ }
chunk->paddr = 0;
+ chunk->paddr_unaligned = 0;
chunk->size = 0;
+ chunk->total_size = 0;
}
static void ath12k_qmi_free_target_mem_chunk(struct ath12k_base *ab)
@@ -2621,14 +2630,17 @@ static void ath12k_qmi_free_target_mem_chunk(struct ath12k_base *ab)
if (!chunk->v.addr)
continue;
dma_free_coherent(ab->dev,
- chunk->prev_size,
- chunk->v.addr,
- chunk->paddr);
+ chunk->total_size,
+ chunk->vaddr_unaligned,
+ chunk->paddr_unaligned);
+ chunk->vaddr_unaligned = NULL;
chunk->v.addr = NULL;
}
chunk->paddr = 0;
+ chunk->paddr_unaligned = 0;
chunk->size = 0;
+ chunk->total_size = 0;
}
}
@@ -2641,6 +2653,10 @@ static void ath12k_qmi_free_target_mem_chunk(struct ath12k_base *ab)
static int ath12k_qmi_alloc_chunk(struct ath12k_base *ab,
struct target_mem_chunk *chunk)
{
+ dma_addr_t paddr;
+ size_t size;
+ void *vaddr;
+
/* Firmware reloads in recovery/resume.
* In such cases, no need to allocate memory for FW again.
*/
@@ -2650,29 +2666,56 @@ static int ath12k_qmi_alloc_chunk(struct ath12k_base *ab,
goto this_chunk_done;
/* cannot reuse the existing chunk */
- dma_free_coherent(ab->dev, chunk->prev_size,
- chunk->v.addr, chunk->paddr);
+ dma_free_coherent(ab->dev, chunk->total_size,
+ chunk->vaddr_unaligned, chunk->paddr_unaligned);
chunk->v.addr = NULL;
+ chunk->vaddr_unaligned = NULL;
+ chunk->paddr_unaligned = 0;
+ chunk->total_size = 0;
}
- chunk->v.addr = dma_alloc_coherent(ab->dev,
- chunk->size,
- &chunk->paddr,
+ /*
+ * Each unaligned chunk costs the firmware extra TLB entries when
+ * mapping it, and too many unaligned chunks exhaust the TLB and
+ * crash the firmware, so align the base to 64 KB. The DMA
+ * allocator only guarantees page alignment, but a natural
+ * allocation is often already 64 KB aligned. Try the exact size
+ * first and keep it when it happens to be aligned; only fall back
+ * to over-allocating and rounding up when it is not.
+ */
+ size = chunk->size;
+ vaddr = dma_alloc_coherent(ab->dev, size, &paddr,
+ GFP_KERNEL | __GFP_NOWARN);
+ if (vaddr && !IS_ALIGNED(paddr, SZ_64K)) {
+ dma_free_coherent(ab->dev, size, vaddr, paddr);
+
+ size = chunk->size + SZ_64K - PAGE_SIZE;
+ vaddr = dma_alloc_coherent(ab->dev, size, &paddr,
GFP_KERNEL | __GFP_NOWARN);
- if (!chunk->v.addr) {
+ }
+
+ if (!vaddr) {
if (chunk->size > ab->hw_params->qmi_max_chunk_size) {
ab->qmi.target_mem_delayed = true;
ath12k_warn(ab,
- "qmi dma allocation failed (%u B type %u), will try later with small size\n",
- chunk->size,
+ "qmi dma allocation failed (%zu B type %u), will try later with small size\n",
+ size,
chunk->type);
ath12k_qmi_free_target_mem_chunk(ab);
return -EAGAIN;
}
- ath12k_warn(ab, "memory allocation failure for %u size: %u\n",
- chunk->type, chunk->size);
+ ath12k_warn(ab, "memory allocation failure for %u size: %zu\n",
+ chunk->type, size);
return -ENOMEM;
}
+
+ chunk->vaddr_unaligned = vaddr;
+ chunk->paddr_unaligned = paddr;
+ chunk->total_size = size;
+
+ chunk->paddr = ALIGN(paddr, SZ_64K);
+ chunk->v.addr = (u8 *)vaddr + (chunk->paddr - paddr);
+
chunk->prev_type = chunk->type;
chunk->prev_size = chunk->size;
this_chunk_done:
diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h
index cbe5be30053a..afa380f5e934 100644
--- a/drivers/net/wireless/ath/ath12k/qmi.h
+++ b/drivers/net/wireless/ath/ath12k/qmi.h
@@ -101,6 +101,10 @@ struct target_mem_chunk {
void __iomem *ioaddr;
void *addr;
} v;
+
+ dma_addr_t paddr_unaligned;
+ void *vaddr_unaligned;
+ u32 total_size;
};
struct target_info {
--
2.25.1
next prev parent reply other threads:[~2026-07-30 6:18 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-30 6:17 [PATCH ath-next 0/3] wifi: ath12k: align QMI memory to 64 KB boundary Baochen Qiang
2026-07-30 6:17 ` [PATCH ath-next 1/3] wifi: ath12k: use per-device max QMI chunk size Baochen Qiang
2026-07-30 6:17 ` [PATCH ath-next 2/3] wifi: ath12k: clear chunk paddr and size in ath12k_qmi_free_target_mem_chunk() Baochen Qiang
2026-07-30 6:17 ` Baochen Qiang [this message]
2026-08-19 7:09 ` [PATCH ath-next 0/3] wifi: ath12k: align QMI memory to 64 KB boundary Rameshkumar Sundaram
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260730-ath12k-align-qmi-memory-v1-3-0fef98dda614@oss.qualcomm.com \
--to=baochen.qiang@oss.qualcomm.com \
--cc=ath12k@lists.infradead.org \
--cc=jjohnson@kernel.org \
--cc=linux-wireless@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox