* [PATCH v8 net-next] octeontx2-af: switch qmem from coherent DMA alloc to streaming DMA mapping
@ 2026-09-08 6:34 Ratheesh Kannoth
2026-09-10 15:00 ` Leon Romanovsky
2026-09-11 9:36 ` netdev-bot+sashiko
0 siblings, 2 replies; 3+ messages in thread
From: Ratheesh Kannoth @ 2026-09-08 6:34 UTC (permalink / raw)
To: davem, gakula, linux-kernel, netdev, sgoutham
Cc: andrew+netdev, edumazet, kuba, pabeni, leon, Ratheesh Kannoth
qmem_alloc() uses dma_alloc_attrs() with DMA_ATTR_FORCE_CONTIGUOUS, which
allocates CPU-cache-coherent DMA memory and, with CMA enabled, draws from
the CMA pool. qmem backs NIX/NPA queue contexts, admin queues, and LMTST
regions (including CN10K LMTST areas that span page boundaries), so
consumption grows with enabled interfaces and is hard to provision in CMA.
Switch qmem to a streaming-DMA-style path: allocate memory with kmalloc(),
then map it for device access with dma_map_single(). Add
otx2_dma_alloc_coherent() and otx2_dma_free_coherent() helpers and wire
qmem_alloc()/qmem_free() through them instead of dma_alloc_attrs()/
dma_free_attrs().
This works on Octeon because the octeontx2 driver is written for
DMA-coherent devices: Octeon platforms provide IO coherency (via SMMU), so
the driver already uses streaming DMA APIs for packet data while
deliberately skipping explicit CPU cache sync (DMA_ATTR_SKIP_CPU_SYNC).
The same IO coherency lets qmem use a streaming map of kmalloc-backed
memory instead of a dedicated coherent allocator or CMA reservation. That
is valid because the platform is DMA-coherent, not because omitting
dma_sync_* magically makes memory coherent.
cc: Geetha sowjanya <gakula@marvell.com>
Fixes: 73d33dbc0723 ("octeontx2-af: Use DMA_ATTR_FORCE_CONTIGUOUS attribute in DMA alloc")
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
v7 -> v8: Addressed Leon comments.
- Replace __get_free_pages() and __GFP_COMP with kmalloc()
- Drop GFP_DMA32 retry loop and dma_capable()/phys_to_dma() mask probing
- Use dma_map_single()/dma_unmap_single() instead of dma_map_page_attrs()
with DMA_ATTR_REQUIRE_COHERENT
- Remove defensive parameter checks and dma_max_mapping_size() from the
allocator helper
- Move MAX_PAGE_ORDER validation to qmem_alloc()
- Retain dev_is_dma_coherent() guard in otx2_dma_alloc_coherent()
v6 -> v7: Addressed Sashiko comments.
https://lore.kernel.org/netdev/178863855246.219967.10510865726694393307@kernel.org/
v5 -> v6: Addressed review comments.
https://lore.kernel.org/netdev/20260901015621.2708182-1-rkannoth@marvell.com/
v4 -> v5: Fixed compilation issues.
https://lore.kernel.org/netdev/20260831024210.208447-1-rkannoth@marvell.com/
v3 -> v4: Fixed compilation issues.
https://lore.kernel.org/netdev/apTpKcN_S1xIwRbZ@rkannoth-OptiPlex-7090/
v2 -> v3: Addressed sashiko comments.
https://sashiko.dev/#/patchset/20260825045616.3723078-1-rkannoth%40marvell.com
v1 -> v2: Rewrote patch as per sashiko comment.
---
.../ethernet/marvell/octeontx2/af/common.h | 45 ++++++++++++++++---
1 file changed, 39 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/common.h b/drivers/net/ethernet/marvell/octeontx2/af/common.h
index 779413a383b7..0569b6d9f03b 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/common.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/common.h
@@ -7,6 +7,10 @@
#ifndef COMMON_H
#define COMMON_H
+#include <linux/dma-mapping.h>
+#include <linux/gfp.h>
+#include <linux/mm.h>
+
#include "rvu_struct.h"
#define OTX2_ALIGN 128 /* Align to cacheline */
@@ -44,6 +48,33 @@ struct qmem {
u32 qsize;
};
+static inline void *otx2_dma_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle)
+{
+ dma_addr_t dma_addr;
+ void *vaddr;
+
+ vaddr = kmalloc(size, GFP_KERNEL | __GFP_ZERO);
+ if (!vaddr)
+ return NULL;
+
+ dma_addr = dma_map_single(dev, vaddr, size, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, dma_addr)) {
+ kfree(vaddr);
+ return NULL;
+ }
+
+ *dma_handle = dma_addr;
+ return vaddr;
+}
+
+static inline void otx2_dma_free_coherent(struct device *dev, size_t size,
+ void *vaddr, dma_addr_t dma_handle)
+{
+ dma_unmap_single(dev, dma_handle, size, DMA_BIDIRECTIONAL);
+ kfree(vaddr);
+}
+
static inline int qmem_alloc(struct device *dev, struct qmem **q,
int qsize, int entry_sz)
{
@@ -60,8 +91,11 @@ static inline int qmem_alloc(struct device *dev, struct qmem **q,
qmem->entry_sz = entry_sz;
qmem->alloc_sz = (qsize * entry_sz) + OTX2_ALIGN;
- qmem->base = dma_alloc_attrs(dev, qmem->alloc_sz, &qmem->iova,
- GFP_KERNEL, DMA_ATTR_FORCE_CONTIGUOUS);
+
+ if (get_order(PAGE_ALIGN(qmem->alloc_sz)) > MAX_PAGE_ORDER)
+ return -ENOMEM;
+
+ qmem->base = otx2_dma_alloc_coherent(dev, qmem->alloc_sz, &qmem->iova);
if (!qmem->base)
return -ENOMEM;
@@ -80,10 +114,9 @@ static inline void qmem_free(struct device *dev, struct qmem *qmem)
return;
if (qmem->base)
- dma_free_attrs(dev, qmem->alloc_sz,
- qmem->base - qmem->align,
- qmem->iova - qmem->align,
- DMA_ATTR_FORCE_CONTIGUOUS);
+ otx2_dma_free_coherent(dev, qmem->alloc_sz,
+ qmem->base - qmem->align,
+ qmem->iova - qmem->align);
devm_kfree(dev, qmem);
}
--
2.43.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v8 net-next] octeontx2-af: switch qmem from coherent DMA alloc to streaming DMA mapping
2026-09-08 6:34 [PATCH v8 net-next] octeontx2-af: switch qmem from coherent DMA alloc to streaming DMA mapping Ratheesh Kannoth
@ 2026-09-10 15:00 ` Leon Romanovsky
2026-09-11 9:36 ` netdev-bot+sashiko
1 sibling, 0 replies; 3+ messages in thread
From: Leon Romanovsky @ 2026-09-10 15:00 UTC (permalink / raw)
To: Ratheesh Kannoth
Cc: davem, gakula, linux-kernel, netdev, sgoutham, andrew+netdev,
edumazet, kuba, pabeni
On Tue, Sep 08, 2026 at 12:04:11PM +0530, Ratheesh Kannoth wrote:
> qmem_alloc() uses dma_alloc_attrs() with DMA_ATTR_FORCE_CONTIGUOUS, which
> allocates CPU-cache-coherent DMA memory and, with CMA enabled, draws from
> the CMA pool. qmem backs NIX/NPA queue contexts, admin queues, and LMTST
> regions (including CN10K LMTST areas that span page boundaries), so
> consumption grows with enabled interfaces and is hard to provision in CMA.
>
> Switch qmem to a streaming-DMA-style path: allocate memory with kmalloc(),
> then map it for device access with dma_map_single(). Add
> otx2_dma_alloc_coherent() and otx2_dma_free_coherent() helpers and wire
> qmem_alloc()/qmem_free() through them instead of dma_alloc_attrs()/
> dma_free_attrs().
>
> This works on Octeon because the octeontx2 driver is written for
> DMA-coherent devices: Octeon platforms provide IO coherency (via SMMU), so
> the driver already uses streaming DMA APIs for packet data while
> deliberately skipping explicit CPU cache sync (DMA_ATTR_SKIP_CPU_SYNC).
> The same IO coherency lets qmem use a streaming map of kmalloc-backed
> memory instead of a dedicated coherent allocator or CMA reservation. That
> is valid because the platform is DMA-coherent, not because omitting
> dma_sync_* magically makes memory coherent.
>
> cc: Geetha sowjanya <gakula@marvell.com>
> Fixes: 73d33dbc0723 ("octeontx2-af: Use DMA_ATTR_FORCE_CONTIGUOUS attribute in DMA alloc")
> Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
>
> ---
> v7 -> v8: Addressed Leon comments.
> - Replace __get_free_pages() and __GFP_COMP with kmalloc()
> - Drop GFP_DMA32 retry loop and dma_capable()/phys_to_dma() mask probing
> - Use dma_map_single()/dma_unmap_single() instead of dma_map_page_attrs()
> with DMA_ATTR_REQUIRE_COHERENT
> - Remove defensive parameter checks and dma_max_mapping_size() from the
> allocator helper
> - Move MAX_PAGE_ORDER validation to qmem_alloc()
> - Retain dev_is_dma_coherent() guard in otx2_dma_alloc_coherent()
>
> v6 -> v7: Addressed Sashiko comments.
> https://lore.kernel.org/netdev/178863855246.219967.10510865726694393307@kernel.org/
>
> v5 -> v6: Addressed review comments.
> https://lore.kernel.org/netdev/20260901015621.2708182-1-rkannoth@marvell.com/
>
> v4 -> v5: Fixed compilation issues.
> https://lore.kernel.org/netdev/20260831024210.208447-1-rkannoth@marvell.com/
>
> v3 -> v4: Fixed compilation issues.
> https://lore.kernel.org/netdev/apTpKcN_S1xIwRbZ@rkannoth-OptiPlex-7090/
>
> v2 -> v3: Addressed sashiko comments.
> https://sashiko.dev/#/patchset/20260825045616.3723078-1-rkannoth%40marvell.com
>
> v1 -> v2: Rewrote patch as per sashiko comment.
> ---
> .../ethernet/marvell/octeontx2/af/common.h | 45 ++++++++++++++++---
> 1 file changed, 39 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/net/ethernet/marvell/octeontx2/af/common.h b/drivers/net/ethernet/marvell/octeontx2/af/common.h
> index 779413a383b7..0569b6d9f03b 100644
> --- a/drivers/net/ethernet/marvell/octeontx2/af/common.h
> +++ b/drivers/net/ethernet/marvell/octeontx2/af/common.h
> @@ -7,6 +7,10 @@
> #ifndef COMMON_H
> #define COMMON_H
>
> +#include <linux/dma-mapping.h>
> +#include <linux/gfp.h>
> +#include <linux/mm.h>
> +
> #include "rvu_struct.h"
>
> #define OTX2_ALIGN 128 /* Align to cacheline */
> @@ -44,6 +48,33 @@ struct qmem {
> u32 qsize;
> };
>
> +static inline void *otx2_dma_alloc_coherent(struct device *dev, size_t size,
> + dma_addr_t *dma_handle)
> +{
> + dma_addr_t dma_addr;
> + void *vaddr;
> +
> + vaddr = kmalloc(size, GFP_KERNEL | __GFP_ZERO);
This is kzalloc(size, GFP_KERNEL)
Thanks
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v8 net-next] octeontx2-af: switch qmem from coherent DMA alloc to streaming DMA mapping
2026-09-08 6:34 [PATCH v8 net-next] octeontx2-af: switch qmem from coherent DMA alloc to streaming DMA mapping Ratheesh Kannoth
2026-09-10 15:00 ` Leon Romanovsky
@ 2026-09-11 9:36 ` netdev-bot+sashiko
1 sibling, 0 replies; 3+ messages in thread
From: netdev-bot+sashiko @ 2026-09-11 9:36 UTC (permalink / raw)
To: rkannoth
Cc: davem, gakula, linux-kernel, netdev, sgoutham, andrew+netdev,
edumazet, kuba, pabeni, leon
Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider:
Critical: 0 · High: 3 (1) · Medium: 0 · Low: 0
- [High] otx2_dma_alloc_coherent()
(drivers/net/ethernet/marvell/octeontx2/af/common.h:51-69) creates a…
- [High] dma_map_single() is not guaranteed to return an address that
aliases the caller's buffer, and the new helper accepts any successful…
- [High] The newly added check in qmem_alloc() (af/common.h:95) 'if
(get_order(PAGE_ALIGN(qmem->alloc_sz)) > MAX_PAGE_ORDER) return…
Pre-existing issues:
- [High] qmem_alloc() computes 'qmem->alloc_sz = (qsize * entry_sz) +
OTX2_ALIGN' (af/common.h:93) in int from mailbox-supplied, unbounded…
--
octeontx2-af: switch qmem from coherent DMA alloc to streaming DMA mapping
This replaces the dma_alloc_attrs(DMA_ATTR_FORCE_CONTIGUOUS) backing of
qmem with kmalloc() plus dma_map_single(), wrapped in new
otx2_dma_alloc_coherent()/otx2_dma_free_coherent() helpers. qmem_alloc()
and qmem_free() are wired through the new helpers, and a
get_order()/MAX_PAGE_ORDER size check is added to qmem_alloc().
> diff --git a/drivers/net/ethernet/marvell/octeontx2/af/common.h b/drivers/net/ethernet/marvell/octeontx2/af/common.h
> index 779413a383b74..0569b6d9f03b5 100644
> --- a/drivers/net/ethernet/marvell/octeontx2/af/common.h
> +++ b/drivers/net/ethernet/marvell/octeontx2/af/common.h
[ ... ]
> +static inline void *otx2_dma_alloc_coherent(struct device *dev, size_t size,
> + dma_addr_t *dma_handle)
> +{
> + dma_addr_t dma_addr;
> + void *vaddr;
> +
> + vaddr = kmalloc(size, GFP_KERNEL | __GFP_ZERO);
> + if (!vaddr)
> + return NULL;
> +
> + dma_addr = dma_map_single(dev, vaddr, size, DMA_BIDIRECTIONAL);
> + if (dma_mapping_error(dev, dma_addr)) {
> + kfree(vaddr);
> + return NULL;
> + }
[Severity: High]
The v8 changelog says "Retain dev_is_dma_coherent() guard in
otx2_dma_alloc_coherent()", but is that guard actually present here? A
grep of drivers/net/ethernet/marvell/octeontx2/ for
dev_is_dma_coherent|dma_sync_single|dma_max_mapping_size returns nothing at
this commit, so the coherency premise of the changelog does not appear to
be expressed anywhere in the code.
Does this matter for the qmem consumers? They use the buffer in the
dma_alloc_coherent() model, not the streaming model. For example
nix_aq_enqueue_wait() in af/rvu_nix.c:
memcpy((void *)(aq->inst->base + (head * aq->inst->entry_sz)),
(void *)inst, aq->inst->entry_sz);
memset(result, 0, sizeof(*result));
/* sync into memory */
wmb();
/* Ring the doorbell and wait for result */
rvu_write64(rvu, block->addr, NIX_AF_AQ_DOOR, 1);
while (result->compcode == NIX_AQ_COMP_NOTDONE) {
The CPU writes the instruction, the device DMA-writes result->compcode, and
the only barrier is wmb() with no dma_sync_single_for_device()/for_cpu()
ownership transfer. npa_aq_enqueue_wait() and the NIX CQ rings do the
same.
On a device that is not IO-coherent, can the CPU-written contexts stay in
dirty cache lines while the device fetches stale data, and can the
device-written result words never be invalidated so the poll loop spins on
a stale compcode until it times out?
The kernel already has DMA_ATTR_REQUIRE_COHERENT for exactly this, in
include/linux/dma-mapping.h:
/*
* DMA_ATTR_REQUIRE_COHERENT: Indicates that DMA coherency is required.
* All mappings that carry this attribute can't work with SWIOTLB and cache
* flushing.
*/
The v8 changelog states this attribute was dropped. Would using it (or an
explicit dev_is_dma_coherent() bail-out) make the requirement enforced
rather than assumed?
[Severity: High]
Separately, this helper accepts any non-error dma_addr and returns the
kmalloc pointer to the caller. Is dma_map_single() guaranteed to return an
address that aliases that buffer?
In dma_direct_map_phys() (kernel/dma/direct.c):
if (is_swiotlb_force_bounce(dev)) {
if (attrs & (DMA_ATTR_MMIO | DMA_ATTR_REQUIRE_COHERENT))
return DMA_MAPPING_ERROR;
return swiotlb_map(dev, phys, size, dir, attrs);
}
and later the dma_capable()/dma_kmalloc_needs_bounce() path also falls back
to swiotlb_map() when DMA_ATTR_REQUIRE_COHERENT is absent.
dma_kmalloc_needs_bounce() in include/linux/dma-map-ops.h is:
return !dma_kmalloc_safe(dev, dir) && !dma_kmalloc_size_aligned(size);
which is true for a non-coherent device with DMA_BIDIRECTIONAL when the
kmalloc size is neither >= 2*ARCH_DMA_MINALIGN nor cache-line rounded --
for instance qmem_alloc(dev, &sq->sqe, 1, 64) in nic/otx2_common.c gives
alloc_sz = 192.
If the mapping is bounced, the IOVA programmed into NIX_AF_LFX_*_BASE,
NPA_AF_LFX_*_BASE, RVU_PF_VF_MBOX_ADDR and the LMT map table refers to a
bounce slot that receives one all-zero copy, while the driver keeps reading
and writing the original kmalloc buffer with no dma_sync_* to reconcile
them. Can this leave the device armed with memory the driver never
updates? dma_alloc_attrs()/dma_alloc_coherent() did not have this
property.
Also, under SWIOTLB the largest streaming mapping is
IO_TLB_SEGSIZE * IO_TLB_SIZE (256 KiB). Do the multi-hundred-KiB qmem
regions (the CN10K LMT region sized num_online_cpus() * 32 * 128, CQ rings,
aura/pool context tables) simply fail to map and abort queue init in that
configuration?
> +
> + *dma_handle = dma_addr;
> + return vaddr;
> +}
[ ... ]
> @@ -60,8 +91,11 @@ static inline int qmem_alloc(struct device *dev, struct qmem **q,
>
> qmem->entry_sz = entry_sz;
> qmem->alloc_sz = (qsize * entry_sz) + OTX2_ALIGN;
> - qmem->base = dma_alloc_attrs(dev, qmem->alloc_sz, &qmem->iova,
> - GFP_KERNEL, DMA_ATTR_FORCE_CONTIGUOUS);
> +
> + if (get_order(PAGE_ALIGN(qmem->alloc_sz)) > MAX_PAGE_ORDER)
> + return -ENOMEM;
[Severity: High]
Does this new check, combined with the switch to kmalloc(), introduce a
hard size ceiling that the old CMA-backed path did not have?
KMALLOC_MAX_SIZE in include/linux/slab.h is:
#define KMALLOC_SHIFT_MAX (MAX_PAGE_ORDER + PAGE_SHIFT)
/* Maximum allocatable size */
#define KMALLOC_MAX_SIZE (1UL << KMALLOC_SHIFT_MAX)
i.e. 4 MiB on a 4 KiB-page kernel. Some existing callers ask for more.
cn20k_pfvf_mbox_alloc() in nic/otx2_pf.c does:
err = qmem_alloc(&pf->pdev->dev, &mbox_addr, numvfs, MBOX_SIZE);
with MBOX_SIZE == SZ_64K, so at numvfs == 64 alloc_sz is 4 MiB + 128,
PAGE_ALIGN gives 0x401000 and get_order() returns 11 > MAX_PAGE_ORDER (10).
qmem_alloc() then returns -ENOMEM before attempting any allocation and
otx2_pfvf_mbox_init()/otx2_sriov_enable() fail, even though the same driver
handles numvfs > 64 elsewhere. Does 'echo 64 > .../sriov_numvfs' still
work on CN20K after this change?
rvu_alloc_mbox_memory() in af/cn20k/mbox_init.c requests ndevs * MBOX_SIZE
scaling with the hardware PF/VF count, and rvu_mbox_handler_npa_lf_alloc()
in af/rvu_npa.c requests NPA_AURA_COUNT(aura_sz) * hwctx_size, which for
the mailbox-legal aura_sz == NPA_AURA_SZ_1M is 1M * 128 = 128 MiB. Do
those now fail too?
Two smaller points on the same branch: get_order(PAGE_ALIGN(x)) equals
get_order(x), so the PAGE_ALIGN() adds nothing, and the branch returns
-ENOMEM with no dev_err() where the previous path produced a visible
allocation warning. Could the new size ceiling and the changed failure
characteristics (plain GFP_KERNEL order-9/10 buddy allocations, no
__GFP_NOWARN or __GFP_RETRY_MAYFAIL) be mentioned in the changelog, given
the Fixes: tag makes this a stable-backport candidate?
[Severity: High]
This isn't a bug introduced by this patch, but the value being validated
here has already wrapped. qmem->alloc_sz is an int and the multiplication
'qsize * entry_sz' is done in int on mailbox-supplied counts, so the new
get_order() check bounds the wrapped size rather than the requested one.
rvu_mbox_handler_npa_lf_alloc() in af/rvu_npa.c rejects only !req->nr_pools
(a u32 mailbox field) before calling:
err = qmem_alloc(rvu->dev, &pfvf->pool_ctx, req->nr_pools, hwctx_size);
With nr_pools = 0x2000000 and hwctx_size = 128 the product is 2^32, which
wraps to 0, so alloc_sz = 128, get_order(PAGE_ALIGN(128)) = 0 passes the
new check and kmalloc(128) succeeds -- while pool_ctx->qsize keeps the full
33554432. rvu_npa_aq_enq_inst() then only checks against that qsize:
if (req->aura.pool_addr >= pfvf->pool_ctx->qsize) {
rc = NPA_AF_ERR_AQ_FULL;
break;
}
/* Set pool's context address */
req->aura.pool_addr = pfvf->pool_ctx->iova +
(req->aura.pool_addr * pfvf->pool_ctx->entry_sz);
so the device is handed an IOVA up to 4 GiB past a 128-byte mapping. The
NIX path has the same shape with the u32 req->rq_cnt/sq_cnt/cq_cnt fields
in rvu_mbox_handler_nix_lf_alloc(), whose values are also programmed into
NIX_AF_LFX_*_BASE.
Since this patch adds a size-validation step at exactly this point, would
it make sense to do the arithmetic in size_t with size_mul()-style
overflow-checked helpers before validating, so the check bounds the real
request?
> +
> + qmem->base = otx2_dma_alloc_coherent(dev, qmem->alloc_sz, &qmem->iova);
> if (!qmem->base)
> return -ENOMEM;
>
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260908063411.257228-1-rkannoth%40marvell.com
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-11 9:36 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-08 6:34 [PATCH v8 net-next] octeontx2-af: switch qmem from coherent DMA alloc to streaming DMA mapping Ratheesh Kannoth
2026-09-10 15:00 ` Leon Romanovsky
2026-09-11 9:36 ` netdev-bot+sashiko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®