* [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node()
@ 2026-10-27 2:25 Jakub Kicinski
2026-10-27 2:25 ` [RFC 01/11] dma-contiguous: pass the NUMA node to dma_alloc_contiguous() Jakub Kicinski
` (11 more replies)
0 siblings, 12 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-10-27 2:25 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: tglx, dave.hansen, x86, joro, will, leon, mbloch, iommu, netdev,
Jakub Kicinski
Hi!
I want to add NUMA-awareness to a networking driver (bnxt) and
I run into the inexistence of dma_alloc_coherent_node().
On NUMA systems networking drivers want to allocate rings
and buffers close to the NAPI-handling core rather than
allocating all from where the device is connected.
Looks like we have accumulated 6 drivers which do:
set_dev_node(dma_dev, numa_node);
va = dma_alloc_coherent(...);
set_dev_node(dma_dev, old_node);
IMHO this looks like an ugly hack. Can we add an API
with the Node ID explicitly passed in?
In the unlikely case anyone feels the urgent need to merge
this please pull (so that we can also pull this into net-next):
git://git.kernel.org/pub/scm/linux/kernel/git/kuba/linux.git tags/dma_alloc_coherent_node
Jakub Kicinski (11):
dma-contiguous: pass the NUMA node to dma_alloc_contiguous()
dma-direct: pass the NUMA node to dma_direct_alloc()
iommu/dma: pass the NUMA node to iommu_dma_alloc()
dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node()
eth: libwx: restore the device's NUMA node after ring allocation
eth: libwx: use dma_alloc_coherent_node()
eth: ixgbe: use dma_alloc_coherent_node()
eth: xgbe: use dma_alloc_coherent_node()
eth: fungible: use dma_alloc_coherent_node()
eth: mlx5: use dma_alloc_coherent_node()
eth: mlx4: use dma_alloc_coherent_node()
Documentation/core-api/dma-api.rst | 15 ++++++++
include/linux/dma-direct.h | 2 +-
include/linux/dma-map-ops.h | 5 +--
include/linux/dma-mapping.h | 14 ++++++++
include/linux/iommu-dma.h | 2 +-
include/linux/mlx4/device.h | 2 +-
include/linux/mlx5/driver.h | 2 --
arch/x86/kernel/amd_gart_64.c | 3 +-
drivers/iommu/dma-iommu.c | 30 +++++++++-------
drivers/net/ethernet/amd/xgbe/xgbe-desc.c | 6 +---
.../net/ethernet/fungible/funcore/fun_queue.c | 8 ++---
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 20 ++++-------
drivers/net/ethernet/mellanox/mlx4/alloc.c | 12 +++----
drivers/net/ethernet/mellanox/mlx4/en_cq.c | 9 ++---
.../net/ethernet/mellanox/mlx4/en_netdev.c | 2 +-
drivers/net/ethernet/mellanox/mlx4/en_rx.c | 5 ++-
drivers/net/ethernet/mellanox/mlx4/en_tx.c | 5 ++-
.../net/ethernet/mellanox/mlx5/core/alloc.c | 24 ++-----------
.../net/ethernet/mellanox/mlx5/core/main.c | 4 ---
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 22 +++++-------
kernel/dma/contiguous.c | 12 +++----
kernel/dma/direct.c | 20 ++++++-----
kernel/dma/mapping.c | 34 ++++++++++++++++---
kernel/dma/ops_helpers.c | 5 +--
24 files changed, 137 insertions(+), 126 deletions(-)
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [RFC 01/11] dma-contiguous: pass the NUMA node to dma_alloc_contiguous()
2026-10-27 2:25 [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
@ 2026-10-27 2:25 ` Jakub Kicinski
2026-10-27 2:25 ` [RFC 02/11] dma-direct: pass the NUMA node to dma_direct_alloc() Jakub Kicinski
` (10 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-10-27 2:25 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: tglx, dave.hansen, x86, joro, will, leon, mbloch, iommu, netdev,
Jakub Kicinski
dma_alloc_contiguous() picks the per-numa CMA area based on the node of
the device. Let the callers pass the node in, so that the DMA allocators
can be extended to place memory on a node other than the device's.
All callers pass dev_to_node() for now, no functional change.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
include/linux/dma-map-ops.h | 5 +++--
drivers/iommu/dma-iommu.c | 2 +-
kernel/dma/contiguous.c | 12 +++++-------
kernel/dma/direct.c | 2 +-
kernel/dma/ops_helpers.c | 5 +++--
5 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h
index 8fae2b7deb20..3f1e4cd3392e 100644
--- a/include/linux/dma-map-ops.h
+++ b/include/linux/dma-map-ops.h
@@ -102,7 +102,8 @@ struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,
unsigned int order, bool no_warn);
bool dma_release_from_contiguous(struct device *dev, struct page *pages,
int count);
-struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp);
+struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp,
+ int nid);
void dma_free_contiguous(struct device *dev, struct page *page, size_t size);
void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size);
@@ -136,7 +137,7 @@ static inline bool dma_release_from_contiguous(struct device *dev,
}
/* Use fallback alloc() and free() when CONFIG_DMA_CMA=n */
static inline struct page *dma_alloc_contiguous(struct device *dev, size_t size,
- gfp_t gfp)
+ gfp_t gfp, int nid)
{
return NULL;
}
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 68ee57800fef..bb82ba3be440 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -1627,7 +1627,7 @@ static void *iommu_dma_alloc_pages(struct device *dev, size_t size,
struct page *page = NULL;
void *cpu_addr;
- page = dma_alloc_contiguous(dev, alloc_size, gfp);
+ page = dma_alloc_contiguous(dev, alloc_size, gfp, node);
if (!page)
page = alloc_pages_node(node, gfp, get_order(alloc_size));
if (!page)
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c
index 66093460584e..b5944ffefca7 100644
--- a/kernel/dma/contiguous.c
+++ b/kernel/dma/contiguous.c
@@ -402,22 +402,20 @@ static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp)
* @dev: Pointer to device for which the allocation is performed.
* @size: Requested allocation size.
* @gfp: Allocation flags.
+ * @nid: NUMA node to allocate the memory on.
*
* tries to use device specific contiguous memory area if available, or it
- * tries to use per-numa cma, if the allocation fails, it will fallback to
- * try default global one.
+ * tries to use per-numa cma of @nid, if the allocation fails, it will fallback
+ * to try default global one.
*
* Note that it bypass one-page size of allocations from the per-numa and
* global area as the addresses within one page are always contiguous, so
* there is no need to waste CMA pages for that kind; it also helps reduce
* fragmentations.
*/
-struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
+struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp,
+ int nid)
{
-#ifdef CONFIG_DMA_NUMA_CMA
- int nid = dev_to_node(dev);
-#endif
-
/* CMA can be used only in the context which permits sleeping */
if (!gfpflags_allow_blocking(gfp))
return NULL;
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index da665ca22d5c..9787a45f46f1 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -124,7 +124,7 @@ static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
WARN_ON_ONCE(!PAGE_ALIGNED(size));
gfp |= dma_direct_optimal_gfp_mask(dev, &phys_limit);
- page = dma_alloc_contiguous(dev, size, gfp);
+ page = dma_alloc_contiguous(dev, size, gfp, node);
if (page) {
if (dma_coherent_ok(dev, page_to_phys(page), size) &&
(allow_highmem || !PageHighMem(page)))
diff --git a/kernel/dma/ops_helpers.c b/kernel/dma/ops_helpers.c
index 6b5f9208d31c..28a3c958158b 100644
--- a/kernel/dma/ops_helpers.c
+++ b/kernel/dma/ops_helpers.c
@@ -63,12 +63,13 @@ struct page *dma_common_alloc_pages(struct device *dev, size_t size,
dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp)
{
const struct dma_map_ops *ops = get_dma_ops(dev);
+ int nid = dev_to_node(dev);
struct page *page;
phys_addr_t phys;
- page = dma_alloc_contiguous(dev, size, gfp);
+ page = dma_alloc_contiguous(dev, size, gfp, nid);
if (!page)
- page = alloc_pages_node(dev_to_node(dev), gfp, get_order(size));
+ page = alloc_pages_node(nid, gfp, get_order(size));
if (!page)
return NULL;
--
2.56.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC 02/11] dma-direct: pass the NUMA node to dma_direct_alloc()
2026-10-27 2:25 [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
2026-10-27 2:25 ` [RFC 01/11] dma-contiguous: pass the NUMA node to dma_alloc_contiguous() Jakub Kicinski
@ 2026-10-27 2:25 ` Jakub Kicinski
2026-10-27 2:25 ` [RFC 03/11] iommu/dma: pass the NUMA node to iommu_dma_alloc() Jakub Kicinski
` (9 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-10-27 2:25 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: tglx, dave.hansen, x86, joro, will, leon, mbloch, iommu, netdev,
Jakub Kicinski
dma_direct_alloc() allocates the pages on the node of the device.
Pass the node in explicitly, so that a node-aware coherent allocation
API can be layered on top.
Memory from the atomic pools, the global coherent pool, restricted
swiotlb pools and arch_dma_alloc() is not node-aware, the node is only
used for the page allocator and the per-numa CMA areas.
Callers pass dev_to_node() for now, no functional change.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
include/linux/dma-direct.h | 2 +-
arch/x86/kernel/amd_gart_64.c | 3 ++-
kernel/dma/direct.c | 18 ++++++++++--------
kernel/dma/mapping.c | 3 ++-
4 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/include/linux/dma-direct.h b/include/linux/dma-direct.h
index daa31a1adf7b..7e864b75be6b 100644
--- a/include/linux/dma-direct.h
+++ b/include/linux/dma-direct.h
@@ -158,7 +158,7 @@ static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size,
u64 dma_direct_get_required_mask(struct device *dev);
void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
- gfp_t gfp, unsigned long attrs);
+ gfp_t gfp, unsigned long attrs, int node);
void dma_direct_free(struct device *dev, size_t size, void *cpu_addr,
dma_addr_t dma_addr, unsigned long attrs);
struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
diff --git a/arch/x86/kernel/amd_gart_64.c b/arch/x86/kernel/amd_gart_64.c
index b5f1f031d45b..05f3d7da6df8 100644
--- a/arch/x86/kernel/amd_gart_64.c
+++ b/arch/x86/kernel/amd_gart_64.c
@@ -468,7 +468,8 @@ gart_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_addr,
{
void *vaddr;
- vaddr = dma_direct_alloc(dev, size, dma_addr, flag, attrs);
+ vaddr = dma_direct_alloc(dev, size, dma_addr, flag, attrs,
+ dev_to_node(dev));
if (!vaddr ||
!force_iommu || dev->coherent_dma_mask <= DMA_BIT_MASK(24))
return vaddr;
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 9787a45f46f1..81953adf872c 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -115,9 +115,8 @@ static struct page *dma_direct_alloc_swiotlb(struct device *dev, size_t size,
}
static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
- gfp_t gfp, bool allow_highmem)
+ gfp_t gfp, bool allow_highmem, int node)
{
- int node = dev_to_node(dev);
struct page *page;
u64 phys_limit;
@@ -180,11 +179,12 @@ static struct page *dma_direct_alloc_from_pool(struct device *dev, size_t size,
}
static void *dma_direct_alloc_no_mapping(struct device *dev, size_t size,
- dma_addr_t *dma_handle, gfp_t gfp)
+ dma_addr_t *dma_handle, gfp_t gfp, int node)
{
struct page *page;
- page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, true);
+ page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, true,
+ node);
if (!page)
return NULL;
@@ -200,7 +200,7 @@ static void *dma_direct_alloc_no_mapping(struct device *dev, size_t size,
}
void *dma_direct_alloc(struct device *dev, size_t size,
- dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
+ dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs, int node)
{
bool remap = false, set_uncached = false;
bool mark_mem_decrypt = false;
@@ -228,7 +228,8 @@ void *dma_direct_alloc(struct device *dev, size_t size,
if (((attrs & (DMA_ATTR_NO_KERNEL_MAPPING | __DMA_ATTR_ALLOC_CC_SHARED)) ==
DMA_ATTR_NO_KERNEL_MAPPING) && !is_swiotlb_for_alloc(dev))
- return dma_direct_alloc_no_mapping(dev, size, dma_handle, gfp);
+ return dma_direct_alloc_no_mapping(dev, size, dma_handle, gfp,
+ node);
if (!dev_is_dma_coherent(dev)) {
if (IS_ENABLED(CONFIG_ARCH_HAS_DMA_ALLOC) &&
@@ -286,7 +287,8 @@ void *dma_direct_alloc(struct device *dev, size_t size,
}
/* we always manually zero the memory once we are done */
- page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, allow_highmem);
+ page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, allow_highmem,
+ node);
if (!page)
return NULL;
@@ -452,7 +454,7 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
goto setup_page;
}
- page = __dma_direct_alloc_pages(dev, size, gfp, false);
+ page = __dma_direct_alloc_pages(dev, size, gfp, false, dev_to_node(dev));
if (!page)
return NULL;
diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c
index bf2651a70b7c..1c3a70025195 100644
--- a/kernel/dma/mapping.c
+++ b/kernel/dma/mapping.c
@@ -665,7 +665,8 @@ void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
flag &= ~(__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM);
if (dma_alloc_direct(dev, ops) || arch_dma_alloc_direct(dev)) {
- cpu_addr = dma_direct_alloc(dev, size, dma_handle, flag, attrs);
+ cpu_addr = dma_direct_alloc(dev, size, dma_handle, flag, attrs,
+ dev_to_node(dev));
} else if (use_dma_iommu(dev)) {
cpu_addr = iommu_dma_alloc(dev, size, dma_handle, flag, attrs);
} else if (ops->alloc) {
--
2.56.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC 03/11] iommu/dma: pass the NUMA node to iommu_dma_alloc()
2026-10-27 2:25 [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
2026-10-27 2:25 ` [RFC 01/11] dma-contiguous: pass the NUMA node to dma_alloc_contiguous() Jakub Kicinski
2026-10-27 2:25 ` [RFC 02/11] dma-direct: pass the NUMA node to dma_direct_alloc() Jakub Kicinski
@ 2026-10-27 2:25 ` Jakub Kicinski
2026-10-27 2:25 ` [RFC 04/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (8 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-10-27 2:25 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: tglx, dave.hansen, x86, joro, will, leon, mbloch, iommu, netdev,
Jakub Kicinski
iommu_dma_alloc() allocates the pages on the node of the device.
Pass the node in explicitly, so that a node-aware coherent allocation
API can be layered on top.
Memory from the atomic pools is not node-aware.
iommu_dma_alloc_noncontiguous() keeps using the device's node.
Callers pass dev_to_node() for now, no functional change.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
include/linux/iommu-dma.h | 2 +-
drivers/iommu/dma-iommu.c | 28 ++++++++++++++++------------
kernel/dma/mapping.c | 3 ++-
3 files changed, 19 insertions(+), 14 deletions(-)
diff --git a/include/linux/iommu-dma.h b/include/linux/iommu-dma.h
index 060f6e23ab3c..067872f14629 100644
--- a/include/linux/iommu-dma.h
+++ b/include/linux/iommu-dma.h
@@ -31,7 +31,7 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
enum dma_data_direction dir, unsigned long attrs);
void *iommu_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
- gfp_t gfp, unsigned long attrs);
+ gfp_t gfp, unsigned long attrs, int node);
int iommu_dma_mmap(struct device *dev, struct vm_area_struct *vma,
void *cpu_addr, dma_addr_t dma_addr, size_t size,
unsigned long attrs);
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index bb82ba3be440..1dc2e235380a 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -882,10 +882,10 @@ static void __iommu_dma_free_pages(struct page **pages, int count)
}
static struct page **__iommu_dma_alloc_pages(struct device *dev,
- unsigned int count, unsigned long order_mask, gfp_t gfp)
+ unsigned int count, unsigned long order_mask, gfp_t gfp, int nid)
{
struct page **pages;
- unsigned int i = 0, nid = dev_to_node(dev);
+ unsigned int i = 0;
order_mask &= GENMASK(MAX_PAGE_ORDER, 0);
if (!order_mask)
@@ -938,7 +938,8 @@ static struct page **__iommu_dma_alloc_pages(struct device *dev,
* but an IOMMU which supports smaller pages might not map the whole thing.
*/
static struct page **__iommu_dma_alloc_noncontiguous(struct device *dev,
- size_t size, struct sg_table *sgt, gfp_t gfp, unsigned long attrs)
+ size_t size, struct sg_table *sgt, gfp_t gfp, unsigned long attrs,
+ int node)
{
struct iommu_domain *domain = iommu_get_dma_domain(dev);
struct iommu_dma_cookie *cookie = domain->iova_cookie;
@@ -966,7 +967,7 @@ static struct page **__iommu_dma_alloc_noncontiguous(struct device *dev,
count = PAGE_ALIGN(size) >> PAGE_SHIFT;
pages = __iommu_dma_alloc_pages(dev, count, alloc_sizes >> PAGE_SHIFT,
- gfp);
+ gfp, node);
if (!pages)
return NULL;
@@ -1012,14 +1013,15 @@ static struct page **__iommu_dma_alloc_noncontiguous(struct device *dev,
}
static void *iommu_dma_alloc_remap(struct device *dev, size_t size,
- dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
+ dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs, int node)
{
struct page **pages;
struct sg_table sgt;
void *vaddr;
pgprot_t prot = dma_pgprot(dev, PAGE_KERNEL, attrs);
- pages = __iommu_dma_alloc_noncontiguous(dev, size, &sgt, gfp, attrs);
+ pages = __iommu_dma_alloc_noncontiguous(dev, size, &sgt, gfp, attrs,
+ node);
if (!pages)
return NULL;
*dma_handle = sgt.sgl->dma_address;
@@ -1060,7 +1062,8 @@ struct sg_table *iommu_dma_alloc_noncontiguous(struct device *dev, size_t size,
if (!sh)
return NULL;
- sh->pages = __iommu_dma_alloc_noncontiguous(dev, size, &sh->sgt, gfp, attrs);
+ sh->pages = __iommu_dma_alloc_noncontiguous(dev, size, &sh->sgt, gfp,
+ attrs, dev_to_node(dev));
if (!sh->pages) {
kfree(sh);
return NULL;
@@ -1619,11 +1622,10 @@ void iommu_dma_free(struct device *dev, size_t size, void *cpu_addr,
}
static void *iommu_dma_alloc_pages(struct device *dev, size_t size,
- struct page **pagep, gfp_t gfp, unsigned long attrs)
+ struct page **pagep, gfp_t gfp, unsigned long attrs, int node)
{
bool coherent = dev_is_dma_coherent(dev);
size_t alloc_size = PAGE_ALIGN(size);
- int node = dev_to_node(dev);
struct page *page = NULL;
void *cpu_addr;
@@ -1656,7 +1658,7 @@ static void *iommu_dma_alloc_pages(struct device *dev, size_t size,
}
void *iommu_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
- gfp_t gfp, unsigned long attrs)
+ gfp_t gfp, unsigned long attrs, int node)
{
bool coherent = dev_is_dma_coherent(dev);
int ioprot = dma_info_to_prot(DMA_BIDIRECTIONAL, coherent, attrs);
@@ -1672,7 +1674,8 @@ void *iommu_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
if (gfpflags_allow_blocking(gfp) &&
!(attrs & DMA_ATTR_FORCE_CONTIGUOUS)) {
- return iommu_dma_alloc_remap(dev, size, handle, gfp, attrs);
+ return iommu_dma_alloc_remap(dev, size, handle, gfp, attrs,
+ node);
}
if (IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
@@ -1682,7 +1685,8 @@ void *iommu_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
if (!page)
return NULL;
} else {
- cpu_addr = iommu_dma_alloc_pages(dev, size, &page, gfp, attrs);
+ cpu_addr = iommu_dma_alloc_pages(dev, size, &page, gfp, attrs,
+ node);
if (!cpu_addr)
return NULL;
}
diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c
index 1c3a70025195..4ace4b68c9c2 100644
--- a/kernel/dma/mapping.c
+++ b/kernel/dma/mapping.c
@@ -668,7 +668,8 @@ void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
cpu_addr = dma_direct_alloc(dev, size, dma_handle, flag, attrs,
dev_to_node(dev));
} else if (use_dma_iommu(dev)) {
- cpu_addr = iommu_dma_alloc(dev, size, dma_handle, flag, attrs);
+ cpu_addr = iommu_dma_alloc(dev, size, dma_handle, flag, attrs,
+ dev_to_node(dev));
} else if (ops->alloc) {
cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs);
} else {
--
2.56.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC 04/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node()
2026-10-27 2:25 [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (2 preceding siblings ...)
2026-10-27 2:25 ` [RFC 03/11] iommu/dma: pass the NUMA node to iommu_dma_alloc() Jakub Kicinski
@ 2026-10-27 2:25 ` Jakub Kicinski
2026-10-27 9:03 ` Björn Töpel
2026-10-27 15:24 ` Marek Szyprowski
2026-10-27 2:25 ` [RFC 05/11] eth: libwx: restore the device's NUMA node after ring allocation Jakub Kicinski
` (7 subsequent siblings)
11 siblings, 2 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-10-27 2:25 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: tglx, dave.hansen, x86, joro, will, leon, mbloch, iommu, netdev,
Jakub Kicinski
Coherent DMA memory is always allocated on the NUMA node of the device.
That's the right choice for memory mostly accessed by the device, but
some coherent allocations are accessed by the CPU on every operation.
For example a NIC's descriptor and completion rings are written and
read by the CPU servicing the queue, which may well be on a different
node than the device. Placing such rings on the device's node means
the CPU accesses remote memory in the fast path.
Drivers work around this today by temporarily changing the node
of the device with set_dev_node() around dma_alloc_coherent()
(mlx4, mlx5, ixgbe, xgbe). This is racy against anything else
looking at the device's node at the same time.
Add dma_alloc_attrs_node() which takes the node explicitly, and
a dma_alloc_coherent_node() wrapper. dma_alloc_attrs() now passes
dev_to_node(). The node is only a preference, the same as for
kmalloc_node(). It is honored by dma-direct and iommu-dma, for both
the page allocator and the per-numa CMA areas. Allocations from fixed
pools and from dma_map_ops->alloc implementations ignore it.
The memory is freed with the existing dma_free_attrs() /
dma_free_coherent(), freeing does not depend on the node.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
Documentation/core-api/dma-api.rst | 15 ++++++++++++++
include/linux/dma-mapping.h | 14 +++++++++++++
kernel/dma/mapping.c | 32 ++++++++++++++++++++++++++----
3 files changed, 57 insertions(+), 4 deletions(-)
diff --git a/Documentation/core-api/dma-api.rst b/Documentation/core-api/dma-api.rst
index ba23a472f794..f9a32b8c338f 100644
--- a/Documentation/core-api/dma-api.rst
+++ b/Documentation/core-api/dma-api.rst
@@ -57,6 +57,21 @@ The flag parameter allows the caller to specify the ``GFP_`` flags (see
kmalloc()) for the allocation (the implementation may ignore flags that affect
the location of the returned memory, like GFP_DMA).
+::
+
+ void *
+ dma_alloc_coherent_node(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flag, int node)
+
+Same as dma_alloc_coherent(), but places the memory on the given NUMA node
+rather than on the node of the device. This is useful for memory accessed
+frequently by both the device and a CPU on a different node, for example
+descriptor rings serviced by that CPU. Pass NUMA_NO_NODE for no preference.
+
+Like with kmalloc_node() the node is only a preference. Memory allocated from
+per-device or restricted pools, or by platforms with custom DMA ops may come
+from a different node. The memory is freed with dma_free_coherent().
+
::
void
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index a3e880649fa4..71fbfc101ab9 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -188,6 +188,8 @@ void dma_unmap_resource(struct device *dev, dma_addr_t addr, size_t size,
enum dma_data_direction dir, unsigned long attrs);
void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
gfp_t flag, unsigned long attrs);
+void *dma_alloc_attrs_node(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flag, unsigned long attrs, int node);
void dma_free_attrs(struct device *dev, size_t size, void *cpu_addr,
dma_addr_t dma_handle, unsigned long attrs);
void *dmam_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
@@ -273,6 +275,11 @@ static inline void *dma_alloc_attrs(struct device *dev, size_t size,
{
return NULL;
}
+static inline void *dma_alloc_attrs_node(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flag, unsigned long attrs, int node)
+{
+ return NULL;
+}
static inline void dma_free_attrs(struct device *dev, size_t size,
void *cpu_addr, dma_addr_t dma_handle, unsigned long attrs)
{
@@ -626,6 +633,13 @@ static inline void *dma_alloc_coherent(struct device *dev, size_t size,
(gfp & __GFP_NOWARN) ? DMA_ATTR_NO_WARN : 0);
}
+static inline void *dma_alloc_coherent_node(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t gfp, int node)
+{
+ return dma_alloc_attrs_node(dev, size, dma_handle, gfp,
+ (gfp & __GFP_NOWARN) ? DMA_ATTR_NO_WARN : 0, node);
+}
+
static inline void dma_free_coherent(struct device *dev, size_t size,
void *cpu_addr, dma_addr_t dma_handle)
{
diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c
index 4ace4b68c9c2..9a90790d5c66 100644
--- a/kernel/dma/mapping.c
+++ b/kernel/dma/mapping.c
@@ -630,8 +630,24 @@ u64 dma_get_required_mask(struct device *dev)
}
EXPORT_SYMBOL_GPL(dma_get_required_mask);
-void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
- gfp_t flag, unsigned long attrs)
+/**
+ * dma_alloc_attrs_node - allocate coherent memory on a given NUMA node
+ * @dev: device to allocate the memory for
+ * @size: size of the allocation
+ * @dma_handle: returns the DMA address of the allocation
+ * @flag: GFP flags for the allocation
+ * @attrs: DMA_ATTR_* flags for the allocation
+ * @node: NUMA node to allocate the memory on, NUMA_NO_NODE for no preference
+ *
+ * Same as dma_alloc_attrs() but places the memory on @node rather than on the
+ * node of @dev. Like with kmalloc_node() the node is only a preference.
+ * Allocations served from per-device or restricted pools, atomic pools,
+ * or by dma_map_ops implementations do not take @node into account.
+ *
+ * Return: the CPU address of the allocation, or NULL on failure.
+ */
+void *dma_alloc_attrs_node(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flag, unsigned long attrs, int node)
{
const struct dma_map_ops *ops = get_dma_ops(dev);
void *cpu_addr;
@@ -666,10 +682,10 @@ void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
if (dma_alloc_direct(dev, ops) || arch_dma_alloc_direct(dev)) {
cpu_addr = dma_direct_alloc(dev, size, dma_handle, flag, attrs,
- dev_to_node(dev));
+ node);
} else if (use_dma_iommu(dev)) {
cpu_addr = iommu_dma_alloc(dev, size, dma_handle, flag, attrs,
- dev_to_node(dev));
+ node);
} else if (ops->alloc) {
cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs);
} else {
@@ -683,6 +699,14 @@ void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
debug_dma_alloc_coherent(dev, size, *dma_handle, cpu_addr, attrs);
return cpu_addr;
}
+EXPORT_SYMBOL_GPL(dma_alloc_attrs_node);
+
+void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
+ gfp_t flag, unsigned long attrs)
+{
+ return dma_alloc_attrs_node(dev, size, dma_handle, flag, attrs,
+ dev_to_node(dev));
+}
EXPORT_SYMBOL(dma_alloc_attrs);
void dma_free_attrs(struct device *dev, size_t size, void *cpu_addr,
--
2.56.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC 05/11] eth: libwx: restore the device's NUMA node after ring allocation
2026-10-27 2:25 [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (3 preceding siblings ...)
2026-10-27 2:25 ` [RFC 04/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
@ 2026-10-27 2:25 ` Jakub Kicinski
2026-10-27 2:25 ` [RFC 06/11] eth: libwx: use dma_alloc_coherent_node() Jakub Kicinski
` (6 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-10-27 2:25 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: tglx, dave.hansen, x86, joro, will, leon, mbloch, iommu, netdev,
Jakub Kicinski
wx_setup_{rx,tx}_resources() temporarily switch the device to the node
of the ring's q_vector to allocate the descriptor ring there. The
original node is only restored if the allocation fails. When it
succeeds the device is left on the node of the last ring allocated,
or on NUMA_NO_NODE for rings without a q_vector. Everything allocated
for the device afterwards, and anything else consulting the device's
node, sees the wrong node.
Restore the node right after the allocation, regardless of the result.
Fixes: 850b971110b2 ("net: libwx: Allocate Rx and Tx resources")
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index dcbf5811046e..dc31e3bc15aa 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -2910,11 +2910,10 @@ static int wx_setup_rx_resources(struct wx_ring *rx_ring)
set_dev_node(dev, numa_node);
rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size,
&rx_ring->dma, GFP_KERNEL);
- if (!rx_ring->desc) {
- set_dev_node(dev, orig_node);
+ set_dev_node(dev, orig_node);
+ if (!rx_ring->desc)
rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size,
&rx_ring->dma, GFP_KERNEL);
- }
if (!rx_ring->desc)
goto err;
@@ -3019,11 +3018,10 @@ static int wx_setup_tx_resources(struct wx_ring *tx_ring)
set_dev_node(dev, numa_node);
tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size,
&tx_ring->dma, GFP_KERNEL);
- if (!tx_ring->desc) {
- set_dev_node(dev, orig_node);
+ set_dev_node(dev, orig_node);
+ if (!tx_ring->desc)
tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size,
&tx_ring->dma, GFP_KERNEL);
- }
if (!tx_ring->desc)
goto err;
--
2.56.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC 06/11] eth: libwx: use dma_alloc_coherent_node()
2026-10-27 2:25 [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (4 preceding siblings ...)
2026-10-27 2:25 ` [RFC 05/11] eth: libwx: restore the device's NUMA node after ring allocation Jakub Kicinski
@ 2026-10-27 2:25 ` Jakub Kicinski
2026-10-27 2:25 ` [RFC 07/11] eth: ixgbe: " Jakub Kicinski
` (5 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-10-27 2:25 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: tglx, dave.hansen, x86, joro, will, leon, mbloch, iommu, netdev,
Jakub Kicinski
Use dma_alloc_coherent_node() to place the descriptor rings on the
node of the ring's q_vector, instead of temporarily changing the node
of the device. NUMA_NO_NODE (ring without a q_vector) means no
preference, same as what set_dev_node(dev, NUMA_NO_NODE) resulted in.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index dc31e3bc15aa..e6ef7b9f2358 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -2888,7 +2888,6 @@ static int wx_alloc_page_pool(struct wx_ring *rx_ring)
static int wx_setup_rx_resources(struct wx_ring *rx_ring)
{
struct device *dev = rx_ring->dev;
- int orig_node = dev_to_node(dev);
int numa_node = NUMA_NO_NODE;
int size, ret;
@@ -2907,10 +2906,9 @@ static int wx_setup_rx_resources(struct wx_ring *rx_ring)
rx_ring->size = rx_ring->count * sizeof(union wx_rx_desc);
rx_ring->size = ALIGN(rx_ring->size, 4096);
- set_dev_node(dev, numa_node);
- rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size,
- &rx_ring->dma, GFP_KERNEL);
- set_dev_node(dev, orig_node);
+ rx_ring->desc = dma_alloc_coherent_node(dev, rx_ring->size,
+ &rx_ring->dma, GFP_KERNEL,
+ numa_node);
if (!rx_ring->desc)
rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size,
&rx_ring->dma, GFP_KERNEL);
@@ -2996,7 +2994,6 @@ static void wx_setup_headwb_resources(struct wx_ring *tx_ring)
static int wx_setup_tx_resources(struct wx_ring *tx_ring)
{
struct device *dev = tx_ring->dev;
- int orig_node = dev_to_node(dev);
int numa_node = NUMA_NO_NODE;
int size;
@@ -3015,10 +3012,9 @@ static int wx_setup_tx_resources(struct wx_ring *tx_ring)
tx_ring->size = tx_ring->count * sizeof(union wx_tx_desc);
tx_ring->size = ALIGN(tx_ring->size, 4096);
- set_dev_node(dev, numa_node);
- tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size,
- &tx_ring->dma, GFP_KERNEL);
- set_dev_node(dev, orig_node);
+ tx_ring->desc = dma_alloc_coherent_node(dev, tx_ring->size,
+ &tx_ring->dma, GFP_KERNEL,
+ numa_node);
if (!tx_ring->desc)
tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size,
&tx_ring->dma, GFP_KERNEL);
--
2.56.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC 07/11] eth: ixgbe: use dma_alloc_coherent_node()
2026-10-27 2:25 [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (5 preceding siblings ...)
2026-10-27 2:25 ` [RFC 06/11] eth: libwx: use dma_alloc_coherent_node() Jakub Kicinski
@ 2026-10-27 2:25 ` Jakub Kicinski
2026-10-27 9:06 ` Björn Töpel
2026-10-27 2:25 ` [RFC 08/11] eth: xgbe: " Jakub Kicinski
` (4 subsequent siblings)
11 siblings, 1 reply; 22+ messages in thread
From: Jakub Kicinski @ 2026-10-27 2:25 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: tglx, dave.hansen, x86, joro, will, leon, mbloch, iommu, netdev,
Jakub Kicinski
Use dma_alloc_coherent_node() to place the descriptor rings on the
node of the ring's q_vector, instead of temporarily changing the node
of the device. NUMA_NO_NODE (ring without a q_vector) means no
preference, same as what set_dev_node(dev, NUMA_NO_NODE) resulted in.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 20 ++++++-------------
1 file changed, 6 insertions(+), 14 deletions(-)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index f91856498eb2..770648f8ab89 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -7083,7 +7083,6 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter,
int ixgbe_setup_tx_resources(struct ixgbe_ring *tx_ring)
{
struct device *dev = tx_ring->dev;
- int orig_node = dev_to_node(dev);
int ring_node = NUMA_NO_NODE;
int size;
@@ -7102,12 +7101,9 @@ int ixgbe_setup_tx_resources(struct ixgbe_ring *tx_ring)
tx_ring->size = tx_ring->count * sizeof(union ixgbe_adv_tx_desc);
tx_ring->size = ALIGN(tx_ring->size, 4096);
- set_dev_node(dev, ring_node);
- tx_ring->desc = dma_alloc_coherent(dev,
- tx_ring->size,
- &tx_ring->dma,
- GFP_KERNEL);
- set_dev_node(dev, orig_node);
+ tx_ring->desc = dma_alloc_coherent_node(dev, tx_ring->size,
+ &tx_ring->dma, GFP_KERNEL,
+ ring_node);
if (!tx_ring->desc)
tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size,
&tx_ring->dma, GFP_KERNEL);
@@ -7184,7 +7180,6 @@ int ixgbe_setup_rx_resources(struct ixgbe_adapter *adapter,
struct ixgbe_ring *rx_ring)
{
struct device *dev = rx_ring->dev;
- int orig_node = dev_to_node(dev);
int ring_node = NUMA_NO_NODE;
int size;
@@ -7203,12 +7198,9 @@ int ixgbe_setup_rx_resources(struct ixgbe_adapter *adapter,
rx_ring->size = rx_ring->count * sizeof(union ixgbe_adv_rx_desc);
rx_ring->size = ALIGN(rx_ring->size, 4096);
- set_dev_node(dev, ring_node);
- rx_ring->desc = dma_alloc_coherent(dev,
- rx_ring->size,
- &rx_ring->dma,
- GFP_KERNEL);
- set_dev_node(dev, orig_node);
+ rx_ring->desc = dma_alloc_coherent_node(dev, rx_ring->size,
+ &rx_ring->dma, GFP_KERNEL,
+ ring_node);
if (!rx_ring->desc)
rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size,
&rx_ring->dma, GFP_KERNEL);
--
2.56.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC 08/11] eth: xgbe: use dma_alloc_coherent_node()
2026-10-27 2:25 [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (6 preceding siblings ...)
2026-10-27 2:25 ` [RFC 07/11] eth: ixgbe: " Jakub Kicinski
@ 2026-10-27 2:25 ` Jakub Kicinski
2026-10-27 2:25 ` [RFC 09/11] eth: fungible: " Jakub Kicinski
` (3 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-10-27 2:25 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: tglx, dave.hansen, x86, joro, will, leon, mbloch, iommu, netdev,
Jakub Kicinski
Use dma_alloc_coherent_node() in xgbe_dma_alloc_node() to place
the descriptor rings on the channel's node, instead of temporarily
changing the node of the device.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/amd/xgbe/xgbe-desc.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-desc.c b/drivers/net/ethernet/amd/xgbe/xgbe-desc.c
index 7c8a19988a52..369cd00fb426 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-desc.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-desc.c
@@ -91,12 +91,8 @@ static void *xgbe_dma_alloc_node(struct device *dev, size_t size,
dma_addr_t *dma, int node)
{
void *mem;
- int cur_node = dev_to_node(dev);
-
- set_dev_node(dev, node);
- mem = dma_alloc_coherent(dev, size, dma, GFP_KERNEL);
- set_dev_node(dev, cur_node);
+ mem = dma_alloc_coherent_node(dev, size, dma, GFP_KERNEL, node);
if (!mem)
mem = dma_alloc_coherent(dev, size, dma, GFP_KERNEL);
--
2.56.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC 09/11] eth: fungible: use dma_alloc_coherent_node()
2026-10-27 2:25 [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (7 preceding siblings ...)
2026-10-27 2:25 ` [RFC 08/11] eth: xgbe: " Jakub Kicinski
@ 2026-10-27 2:25 ` Jakub Kicinski
2026-10-27 2:25 ` [RFC 10/11] eth: mlx5: " Jakub Kicinski
` (2 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-10-27 2:25 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: tglx, dave.hansen, x86, joro, will, leon, mbloch, iommu, netdev,
Jakub Kicinski
Use dma_alloc_coherent_node() in fun_alloc_ring_mem() to place the
rings on the requested node, instead of temporarily changing the node
of the device. NUMA_NO_NODE keeps meaning the device's node.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/fungible/funcore/fun_queue.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/fungible/funcore/fun_queue.c b/drivers/net/ethernet/fungible/funcore/fun_queue.c
index 0eca751a9534..17c8eba1bc34 100644
--- a/drivers/net/ethernet/fungible/funcore/fun_queue.c
+++ b/drivers/net/ethernet/fungible/funcore/fun_queue.c
@@ -21,21 +21,19 @@ void *fun_alloc_ring_mem(struct device *dma_dev, size_t depth,
int numa_node, dma_addr_t *dma_addr, void **sw_va,
volatile __be64 **wb_va)
{
- int dev_node = dev_to_node(dma_dev);
size_t dma_sz;
void *va;
if (numa_node == NUMA_NO_NODE)
- numa_node = dev_node;
+ numa_node = dev_to_node(dma_dev);
/* Place optional write-back area at end of descriptor ring. */
dma_sz = hw_desc_sz * depth;
if (wb)
dma_sz += sizeof(u64);
- set_dev_node(dma_dev, numa_node);
- va = dma_alloc_coherent(dma_dev, dma_sz, dma_addr, GFP_KERNEL);
- set_dev_node(dma_dev, dev_node);
+ va = dma_alloc_coherent_node(dma_dev, dma_sz, dma_addr, GFP_KERNEL,
+ numa_node);
if (!va)
return NULL;
--
2.56.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC 10/11] eth: mlx5: use dma_alloc_coherent_node()
2026-10-27 2:25 [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (8 preceding siblings ...)
2026-10-27 2:25 ` [RFC 09/11] eth: fungible: " Jakub Kicinski
@ 2026-10-27 2:25 ` Jakub Kicinski
2026-10-27 2:25 ` [RFC 11/11] eth: mlx4: " Jakub Kicinski
2026-10-27 9:03 ` [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Björn Töpel
11 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-10-27 2:25 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: tglx, dave.hansen, x86, joro, will, leon, mbloch, iommu, netdev,
Jakub Kicinski
Use dma_alloc_coherent_node() to allocate the DMA pool pages on the
pool's node, instead of temporarily changing the node of the device.
alloc_mutex only serialized those temporary changes, remove it.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
include/linux/mlx5/driver.h | 2 --
.../net/ethernet/mellanox/mlx5/core/alloc.c | 24 +++----------------
.../net/ethernet/mellanox/mlx5/core/main.c | 4 ----
3 files changed, 3 insertions(+), 27 deletions(-)
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index abb99371ff13..c58cc5a8f8df 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -596,8 +596,6 @@ struct mlx5_priv {
struct mlx5_debugfs_entries dbg;
/* start: alloc stuff */
- /* protect buffer allocation according to numa node */
- struct mutex alloc_mutex;
int numa_node;
struct mlx5_frag_buf_node_pools **frag_buf_node_pools;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c
index dcd281c4691d..936ce07aaefb 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c
@@ -80,25 +80,6 @@ struct mlx5_dma_pool_stats {
* register it in a memory region at HCA virtual address 0.
*/
-static void *mlx5_dma_zalloc_coherent_node(struct mlx5_core_dev *dev,
- size_t size, dma_addr_t *dma_handle,
- int node)
-{
- struct device *device = mlx5_core_dma_dev(dev);
- struct mlx5_priv *priv = &dev->priv;
- int original_node;
- void *cpu_handle;
-
- mutex_lock(&priv->alloc_mutex);
- original_node = dev_to_node(device);
- set_dev_node(device, node);
- cpu_handle = dma_alloc_coherent(device, size, dma_handle,
- GFP_KERNEL);
- set_dev_node(device, original_node);
- mutex_unlock(&priv->alloc_mutex);
- return cpu_handle;
-}
-
static void mlx5_dma_pool_destroy(struct mlx5_dma_pool *pool)
{
mutex_destroy(&pool->lock);
@@ -139,8 +120,9 @@ mlx5_dma_pool_page_alloc(struct mlx5_dma_pool *pool)
goto err_free_page;
bitmap_fill(page->bitmap, blocks_per_page);
- page->buf = mlx5_dma_zalloc_coherent_node(pool->dev, PAGE_SIZE,
- &page->dma, node);
+ page->buf = dma_alloc_coherent_node(mlx5_core_dma_dev(pool->dev),
+ PAGE_SIZE, &page->dma, GFP_KERNEL,
+ node);
if (!page->buf)
goto err_free_bitmap;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 1a45474ce03d..b06e7c5c6110 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -1822,8 +1822,6 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
INIT_LIST_HEAD(&priv->bfregs.reg_head.list);
INIT_LIST_HEAD(&priv->bfregs.wc_head.list);
- mutex_init(&priv->alloc_mutex);
-
priv->numa_node = dev_to_node(mlx5_core_dma_dev(dev));
priv->dbg.dbg_root = debugfs_create_dir(dev_name(dev->device),
mlx5_debugfs_root);
@@ -1902,7 +1900,6 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
mlx5_frag_buf_pools_cleanup(dev);
err_frag_buf_pools_init:
debugfs_remove(dev->priv.dbg.dbg_root);
- mutex_destroy(&priv->alloc_mutex);
mutex_destroy(&priv->bfregs.wc_head.lock);
mutex_destroy(&priv->bfregs.reg_head.lock);
mutex_destroy(&dev->intf_state_mutex);
@@ -1927,7 +1924,6 @@ void mlx5_mdev_uninit(struct mlx5_core_dev *dev)
mlx5_db_pools_cleanup(dev);
mlx5_frag_buf_pools_cleanup(dev);
debugfs_remove_recursive(dev->priv.dbg.dbg_root);
- mutex_destroy(&priv->alloc_mutex);
mutex_destroy(&priv->bfregs.wc_head.lock);
mutex_destroy(&priv->bfregs.reg_head.lock);
mutex_destroy(&dev->wc_state_lock);
--
2.56.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC 11/11] eth: mlx4: use dma_alloc_coherent_node()
2026-10-27 2:25 [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (9 preceding siblings ...)
2026-10-27 2:25 ` [RFC 10/11] eth: mlx5: " Jakub Kicinski
@ 2026-10-27 2:25 ` Jakub Kicinski
2026-10-27 9:03 ` [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Björn Töpel
11 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-10-27 2:25 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: tglx, dave.hansen, x86, joro, will, leon, mbloch, iommu, netdev,
Jakub Kicinski
mlx4_en temporarily changes the node of the device around
mlx4_alloc_hwq_res() to place the queue buffers on the queue's node.
This is racy against other users of the device's node, e.g. mlx4_ib
allocating its own buffers at the same time.
Add a node argument to mlx4_alloc_hwq_res() and use
dma_alloc_coherent_node() for the queue buffer. mlx4_buf_alloc()
and the en_netdev page keep using the device's node.
The comment about the MTT allocation was stale, the MTT ICM is not
coherent and uses mlx4_dev->numa_node, which set_dev_node() did not
affect.
The doorbell record pages, which mlx4_db_alloc() allocates on demand
and shares between all queues, now always come from the device's node.
So far they ended up on the node of whichever queue happened to need
a new page.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
include/linux/mlx4/device.h | 2 +-
drivers/net/ethernet/mellanox/mlx4/alloc.c | 12 ++++++------
drivers/net/ethernet/mellanox/mlx4/en_cq.c | 9 ++-------
drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 2 +-
drivers/net/ethernet/mellanox/mlx4/en_rx.c | 5 ++---
drivers/net/ethernet/mellanox/mlx4/en_tx.c | 5 ++---
6 files changed, 14 insertions(+), 21 deletions(-)
diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h
index f016263e1fcf..0d34e0e18580 100644
--- a/include/linux/mlx4/device.h
+++ b/include/linux/mlx4/device.h
@@ -1139,7 +1139,7 @@ int mlx4_db_alloc(struct mlx4_dev *dev, struct mlx4_db *db, unsigned int order);
void mlx4_db_free(struct mlx4_dev *dev, struct mlx4_db *db);
int mlx4_alloc_hwq_res(struct mlx4_dev *dev, struct mlx4_hwq_resources *wqres,
- int size);
+ int size, int node);
void mlx4_free_hwq_res(struct mlx4_dev *mdev, struct mlx4_hwq_resources *wqres,
int size);
diff --git a/drivers/net/ethernet/mellanox/mlx4/alloc.c b/drivers/net/ethernet/mellanox/mlx4/alloc.c
index 7ed4452e20a9..0621347112df 100644
--- a/drivers/net/ethernet/mellanox/mlx4/alloc.c
+++ b/drivers/net/ethernet/mellanox/mlx4/alloc.c
@@ -553,7 +553,7 @@ u32 mlx4_zone_free_entries_unique(struct mlx4_zone_allocator *zones, u32 obj, u3
}
static int mlx4_buf_direct_alloc(struct mlx4_dev *dev, int size,
- struct mlx4_buf *buf)
+ struct mlx4_buf *buf, int node)
{
dma_addr_t t;
@@ -561,8 +561,8 @@ static int mlx4_buf_direct_alloc(struct mlx4_dev *dev, int size,
buf->npages = 1;
buf->page_shift = get_order(size) + PAGE_SHIFT;
buf->direct.buf =
- dma_alloc_coherent(&dev->persist->pdev->dev, size, &t,
- GFP_KERNEL);
+ dma_alloc_coherent_node(&dev->persist->pdev->dev, size, &t,
+ GFP_KERNEL, node);
if (!buf->direct.buf)
return -ENOMEM;
@@ -585,7 +585,7 @@ int mlx4_buf_alloc(struct mlx4_dev *dev, int size, int max_direct,
struct mlx4_buf *buf)
{
if (size <= max_direct) {
- return mlx4_buf_direct_alloc(dev, size, buf);
+ return mlx4_buf_direct_alloc(dev, size, buf, dev->numa_node);
} else {
dma_addr_t t;
int i;
@@ -749,7 +749,7 @@ void mlx4_db_free(struct mlx4_dev *dev, struct mlx4_db *db)
EXPORT_SYMBOL_GPL(mlx4_db_free);
int mlx4_alloc_hwq_res(struct mlx4_dev *dev, struct mlx4_hwq_resources *wqres,
- int size)
+ int size, int node)
{
int err;
@@ -759,7 +759,7 @@ int mlx4_alloc_hwq_res(struct mlx4_dev *dev, struct mlx4_hwq_resources *wqres,
*wqres->db.db = 0;
- err = mlx4_buf_direct_alloc(dev, size, &wqres->buf);
+ err = mlx4_buf_direct_alloc(dev, size, &wqres->buf, node);
if (err)
goto err_db;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c
index 0e92956e84cf..6c47509422f6 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c
@@ -65,13 +65,8 @@ int mlx4_en_create_cq(struct mlx4_en_priv *priv,
cq->type = mode;
cq->vector = mdev->dev->caps.num_comp_vectors;
- /* Allocate HW buffers on provided NUMA node.
- * dev->numa_node is used in mtt range allocation flow.
- */
- set_dev_node(&mdev->dev->persist->pdev->dev, node);
- err = mlx4_alloc_hwq_res(mdev->dev, &cq->wqres,
- cq->buf_size);
- set_dev_node(&mdev->dev->persist->pdev->dev, mdev->dev->numa_node);
+ /* Allocate HW buffers on provided NUMA node */
+ err = mlx4_alloc_hwq_res(mdev->dev, &cq->wqres, cq->buf_size, node);
if (err)
goto err_cq;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 071564c54522..68f1eb57c32a 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -3299,7 +3299,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
/* Allocate page for receive rings */
err = mlx4_alloc_hwq_res(mdev->dev, &priv->res,
- MLX4_EN_PAGE_SIZE);
+ MLX4_EN_PAGE_SIZE, mdev->dev->numa_node);
if (err) {
en_err(priv, "Failed to allocate page for rx qps\n");
goto out;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index fe18606c2ab5..e2d97ecbafd2 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -292,9 +292,8 @@ int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv,
ring->rx_info, tmp);
/* Allocate HW buffers on provided NUMA node */
- set_dev_node(&mdev->dev->persist->pdev->dev, node);
- err = mlx4_alloc_hwq_res(mdev->dev, &ring->wqres, ring->buf_size);
- set_dev_node(&mdev->dev->persist->pdev->dev, mdev->dev->numa_node);
+ err = mlx4_alloc_hwq_res(mdev->dev, &ring->wqres, ring->buf_size,
+ node);
if (err)
goto err_info;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
index c5d564e5a581..9865ccd8d6e9 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
@@ -91,9 +91,8 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv,
ring->buf_size = ALIGN(size * ring->sp_stride, MLX4_EN_PAGE_SIZE);
/* Allocate HW buffers on provided NUMA node */
- set_dev_node(&mdev->dev->persist->pdev->dev, node);
- err = mlx4_alloc_hwq_res(mdev->dev, &ring->sp_wqres, ring->buf_size);
- set_dev_node(&mdev->dev->persist->pdev->dev, mdev->dev->numa_node);
+ err = mlx4_alloc_hwq_res(mdev->dev, &ring->sp_wqres, ring->buf_size,
+ node);
if (err) {
en_err(priv, "Failed allocating hwq resources\n");
goto err_bounce;
--
2.56.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node()
2026-10-27 2:25 [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (10 preceding siblings ...)
2026-10-27 2:25 ` [RFC 11/11] eth: mlx4: " Jakub Kicinski
@ 2026-10-27 9:03 ` Björn Töpel
11 siblings, 0 replies; 22+ messages in thread
From: Björn Töpel @ 2026-10-27 9:03 UTC (permalink / raw)
To: Jakub Kicinski, robin.murphy, m.szyprowski
Cc: tglx, dave.hansen, x86, joro, will, leon, mbloch, iommu, netdev,
Jakub Kicinski
Jakub Kicinski <kuba@kernel.org> writes:
> Hi!
>
> I want to add NUMA-awareness to a networking driver (bnxt) and
> I run into the inexistence of dma_alloc_coherent_node().
>
> On NUMA systems networking drivers want to allocate rings
> and buffers close to the NAPI-handling core rather than
> allocating all from where the device is connected.
>
> Looks like we have accumulated 6 drivers which do:
>
> set_dev_node(dma_dev, numa_node);
> va = dma_alloc_coherent(...);
> set_dev_node(dma_dev, old_node);
>
> IMHO this looks like an ugly hack. Can we add an API
> with the Node ID explicitly passed in?
>
>
> In the unlikely case anyone feels the urgent need to merge
> this please pull (so that we can also pull this into net-next):
>
> git://git.kernel.org/pub/scm/linux/kernel/git/kuba/linux.git tags/dma_alloc_coherent_node
>
> Jakub Kicinski (11):
> dma-contiguous: pass the NUMA node to dma_alloc_contiguous()
> dma-direct: pass the NUMA node to dma_direct_alloc()
> iommu/dma: pass the NUMA node to iommu_dma_alloc()
> dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node()
> eth: libwx: restore the device's NUMA node after ring allocation
> eth: libwx: use dma_alloc_coherent_node()
> eth: ixgbe: use dma_alloc_coherent_node()
> eth: xgbe: use dma_alloc_coherent_node()
> eth: fungible: use dma_alloc_coherent_node()
> eth: mlx5: use dma_alloc_coherent_node()
> eth: mlx4: use dma_alloc_coherent_node()
Nice clean up on the driver side! I have some questions/nits for some
patches. Regardless, for the series:
Reviewed-by: Björn Töpel <bjorn@kernel.org>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC 04/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node()
2026-10-27 2:25 ` [RFC 04/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
@ 2026-10-27 9:03 ` Björn Töpel
2026-10-27 15:53 ` Jakub Kicinski
2026-10-27 15:24 ` Marek Szyprowski
1 sibling, 1 reply; 22+ messages in thread
From: Björn Töpel @ 2026-10-27 9:03 UTC (permalink / raw)
To: Jakub Kicinski, robin.murphy, m.szyprowski
Cc: tglx, dave.hansen, x86, joro, will, leon, mbloch, iommu, netdev,
Jakub Kicinski
Jakub Kicinski <kuba@kernel.org> writes:
> Coherent DMA memory is always allocated on the NUMA node of the device.
> That's the right choice for memory mostly accessed by the device, but
> some coherent allocations are accessed by the CPU on every operation.
> For example a NIC's descriptor and completion rings are written and
> read by the CPU servicing the queue, which may well be on a different
> node than the device. Placing such rings on the device's node means
> the CPU accesses remote memory in the fast path.
>
> Drivers work around this today by temporarily changing the node
> of the device with set_dev_node() around dma_alloc_coherent()
> (mlx4, mlx5, ixgbe, xgbe). This is racy against anything else
> looking at the device's node at the same time.
>
> Add dma_alloc_attrs_node() which takes the node explicitly, and
> a dma_alloc_coherent_node() wrapper. dma_alloc_attrs() now passes
> dev_to_node(). The node is only a preference, the same as for
> kmalloc_node(). It is honored by dma-direct and iommu-dma, for both
> the page allocator and the per-numa CMA areas. Allocations from fixed
> pools and from dma_map_ops->alloc implementations ignore it.
>
> The memory is freed with the existing dma_free_attrs() /
> dma_free_coherent(), freeing does not depend on the node.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
> ---
> Documentation/core-api/dma-api.rst | 15 ++++++++++++++
> include/linux/dma-mapping.h | 14 +++++++++++++
> kernel/dma/mapping.c | 32 ++++++++++++++++++++++++++----
> 3 files changed, 57 insertions(+), 4 deletions(-)
>
> diff --git a/Documentation/core-api/dma-api.rst b/Documentation/core-api/dma-api.rst
> index ba23a472f794..f9a32b8c338f 100644
> --- a/Documentation/core-api/dma-api.rst
> +++ b/Documentation/core-api/dma-api.rst
> @@ -57,6 +57,21 @@ The flag parameter allows the caller to specify the ``GFP_`` flags (see
> kmalloc()) for the allocation (the implementation may ignore flags that affect
> the location of the returned memory, like GFP_DMA).
>
> +::
> +
> + void *
> + dma_alloc_coherent_node(struct device *dev, size_t size,
> + dma_addr_t *dma_handle, gfp_t flag, int node)
> +
> +Same as dma_alloc_coherent(), but places the memory on the given NUMA node
> +rather than on the node of the device. This is useful for memory accessed
> +frequently by both the device and a CPU on a different node, for example
> +descriptor rings serviced by that CPU. Pass NUMA_NO_NODE for no preference.
nit: maybe mention that callers that want the device-node semantics need
to pass dev_to_node(dev) themselves?
Björn
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC 07/11] eth: ixgbe: use dma_alloc_coherent_node()
2026-10-27 2:25 ` [RFC 07/11] eth: ixgbe: " Jakub Kicinski
@ 2026-10-27 9:06 ` Björn Töpel
2026-10-27 15:51 ` Jakub Kicinski
0 siblings, 1 reply; 22+ messages in thread
From: Björn Töpel @ 2026-10-27 9:06 UTC (permalink / raw)
To: Jakub Kicinski, robin.murphy, m.szyprowski
Cc: tglx, dave.hansen, x86, joro, will, leon, mbloch, iommu, netdev,
Jakub Kicinski
Jakub Kicinski <kuba@kernel.org> writes:
> Use dma_alloc_coherent_node() to place the descriptor rings on the
> node of the ring's q_vector, instead of temporarily changing the node
> of the device. NUMA_NO_NODE (ring without a q_vector) means no
> preference, same as what set_dev_node(dev, NUMA_NO_NODE) resulted in.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
> ---
> drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 20 ++++++-------------
> 1 file changed, 6 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
> index f91856498eb2..770648f8ab89 100644
> --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
> +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
> @@ -7083,7 +7083,6 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter,
> int ixgbe_setup_tx_resources(struct ixgbe_ring *tx_ring)
> {
> struct device *dev = tx_ring->dev;
> - int orig_node = dev_to_node(dev);
> int ring_node = NUMA_NO_NODE;
> int size;
>
> @@ -7102,12 +7101,9 @@ int ixgbe_setup_tx_resources(struct ixgbe_ring *tx_ring)
> tx_ring->size = tx_ring->count * sizeof(union ixgbe_adv_tx_desc);
> tx_ring->size = ALIGN(tx_ring->size, 4096);
>
> - set_dev_node(dev, ring_node);
> - tx_ring->desc = dma_alloc_coherent(dev,
> - tx_ring->size,
> - &tx_ring->dma,
> - GFP_KERNEL);
> - set_dev_node(dev, orig_node);
> + tx_ring->desc = dma_alloc_coherent_node(dev, tx_ring->size,
> + &tx_ring->dma, GFP_KERNEL,
> + ring_node);
> if (!tx_ring->desc)
> tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size,
> &tx_ring->dma, GFP_KERNEL);
Not really this series, but the fallback here is odd, no? I thought the
allocator already did cross-zone fallback?
Björn
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC 04/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node()
2026-10-27 2:25 ` [RFC 04/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
2026-10-27 9:03 ` Björn Töpel
@ 2026-10-27 15:24 ` Marek Szyprowski
2026-10-27 17:52 ` Jakub Kicinski
1 sibling, 1 reply; 22+ messages in thread
From: Marek Szyprowski @ 2026-10-27 15:24 UTC (permalink / raw)
To: Jakub Kicinski, robin.murphy
Cc: tglx, dave.hansen, x86, joro, will, leon, mbloch, iommu, netdev
On 27.10.2026 03:25, Jakub Kicinski wrote:
> @@ -683,6 +699,14 @@ void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
> debug_dma_alloc_coherent(dev, size, *dma_handle, cpu_addr, attrs);
> return cpu_addr;
> }
> +EXPORT_SYMBOL_GPL(dma_alloc_attrs_node);
> +
> +void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
> + gfp_t flag, unsigned long attrs)
> +{
> + return dma_alloc_attrs_node(dev, size, dma_handle, flag, attrs,
> + dev_to_node(dev));
> +}
> EXPORT_SYMBOL(dma_alloc_attrs);
Do we really need two exported symbols for dma_alloc_attrs()? I would prefer
to make dma_alloc_attrs() a static inline calling dma_alloc_attrs_node().
Did I missed something obvious against that?
Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC 07/11] eth: ixgbe: use dma_alloc_coherent_node()
2026-10-27 9:06 ` Björn Töpel
@ 2026-10-27 15:51 ` Jakub Kicinski
2026-10-27 16:06 ` Björn Töpel
0 siblings, 1 reply; 22+ messages in thread
From: Jakub Kicinski @ 2026-10-27 15:51 UTC (permalink / raw)
To: Björn Töpel
Cc: robin.murphy, m.szyprowski, tglx, dave.hansen, x86, joro, will,
leon, mbloch, iommu, netdev
On Tue, 27 Oct 2026 10:06:03 +0100 Björn Töpel wrote:
> > - set_dev_node(dev, orig_node);
> > + tx_ring->desc = dma_alloc_coherent_node(dev, tx_ring->size,
> > + &tx_ring->dma, GFP_KERNEL,
> > + ring_node);
> > if (!tx_ring->desc)
> > tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size,
> > &tx_ring->dma, GFP_KERNEL);
>
> Not really this series, but the fallback here is odd, no? I thought the
> allocator already did cross-zone fallback?
Fair, I was too lazy to check. I'll remove the fallbacks in the same
patch. Seems obvious enough. LMK if you prefer separate.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC 04/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node()
2026-10-27 9:03 ` Björn Töpel
@ 2026-10-27 15:53 ` Jakub Kicinski
0 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-10-27 15:53 UTC (permalink / raw)
To: Björn Töpel
Cc: robin.murphy, m.szyprowski, tglx, dave.hansen, x86, joro, will,
leon, mbloch, iommu, netdev
On Tue, 27 Oct 2026 10:03:43 +0100 Björn Töpel wrote:
> > +::
> > +
> > + void *
> > + dma_alloc_coherent_node(struct device *dev, size_t size,
> > + dma_addr_t *dma_handle, gfp_t flag, int node)
> > +
> > +Same as dma_alloc_coherent(), but places the memory on the given NUMA node
> > +rather than on the node of the device. This is useful for memory accessed
> > +frequently by both the device and a CPU on a different node, for example
> > +descriptor rings serviced by that CPU. Pass NUMA_NO_NODE for no preference.
>
> nit: maybe mention that callers that want the device-node semantics need
> to pass dev_to_node(dev) themselves?
Let me just remove the NUMA_NO_NODE suggestion.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC 07/11] eth: ixgbe: use dma_alloc_coherent_node()
2026-10-27 15:51 ` Jakub Kicinski
@ 2026-10-27 16:06 ` Björn Töpel
2026-10-27 18:20 ` Jakub Kicinski
0 siblings, 1 reply; 22+ messages in thread
From: Björn Töpel @ 2026-10-27 16:06 UTC (permalink / raw)
To: Jakub Kicinski
Cc: robin.murphy, m.szyprowski, tglx, dave.hansen, x86, joro, will,
leon, mbloch, iommu, netdev
Jakub Kicinski <kuba@kernel.org> writes:
> On Tue, 27 Oct 2026 10:06:03 +0100 Björn Töpel wrote:
>> > - set_dev_node(dev, orig_node);
>> > + tx_ring->desc = dma_alloc_coherent_node(dev, tx_ring->size,
>> > + &tx_ring->dma, GFP_KERNEL,
>> > + ring_node);
>> > if (!tx_ring->desc)
>> > tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size,
>> > &tx_ring->dma, GFP_KERNEL);
>>
>> Not really this series, but the fallback here is odd, no? I thought the
>> allocator already did cross-zone fallback?
>
> Fair, I was too lazy to check. I'll remove the fallbacks in the same
> patch. Seems obvious enough. LMK if you prefer separate.
Hmm. Seems like dma_alloc_contiguous() doesn't spill over to other
nodes. It only tries the per-NUMA CMA... so keeping it is bit more
robust. (Then again, I dunno...)
I'd say keep it.
Björn
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC 04/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node()
2026-10-27 15:24 ` Marek Szyprowski
@ 2026-10-27 17:52 ` Jakub Kicinski
2026-10-28 9:00 ` Marek Szyprowski
0 siblings, 1 reply; 22+ messages in thread
From: Jakub Kicinski @ 2026-10-27 17:52 UTC (permalink / raw)
To: Marek Szyprowski
Cc: robin.murphy, tglx, dave.hansen, x86, joro, will, leon, mbloch,
iommu, netdev
On Tue, 27 Oct 2026 16:24:44 +0100 Marek Szyprowski wrote:
> > +EXPORT_SYMBOL_GPL(dma_alloc_attrs_node);
> > +
> > +void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
> > + gfp_t flag, unsigned long attrs)
> > +{
> > + return dma_alloc_attrs_node(dev, size, dma_handle, flag, attrs,
> > + dev_to_node(dev));
> > +}
> > EXPORT_SYMBOL(dma_alloc_attrs);
>
> Do we really need two exported symbols for dma_alloc_attrs()? I would prefer
> to make dma_alloc_attrs() a static inline calling dma_alloc_attrs_node().
>
> Did I missed something obvious against that?
License, I associate DMA API with hch so I thought we'd want to make
the new symbol GPL-only? LMK if I'm overthinking this.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC 07/11] eth: ixgbe: use dma_alloc_coherent_node()
2026-10-27 16:06 ` Björn Töpel
@ 2026-10-27 18:20 ` Jakub Kicinski
0 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-10-27 18:20 UTC (permalink / raw)
To: Björn Töpel
Cc: robin.murphy, m.szyprowski, tglx, dave.hansen, x86, joro, will,
leon, mbloch, iommu, netdev
On Tue, 27 Oct 2026 17:06:44 +0100 Björn Töpel wrote:
> Jakub Kicinski <kuba@kernel.org> writes:
> > On Tue, 27 Oct 2026 10:06:03 +0100 Björn Töpel wrote:
> >> Not really this series, but the fallback here is odd, no? I thought the
> >> allocator already did cross-zone fallback?
> >
> > Fair, I was too lazy to check. I'll remove the fallbacks in the same
> > patch. Seems obvious enough. LMK if you prefer separate.
>
> Hmm. Seems like dma_alloc_contiguous() doesn't spill over to other
> nodes. It only tries the per-NUMA CMA... so keeping it is bit more
> robust. (Then again, I dunno...)
>
> I'd say keep it.
Or we fold the retry into the helper? I tried to write the kdoc:
/**
* dma_alloc_attrs_node - allocate coherent memory on a given NUMA node
* @dev: device to allocate the memory for
* @size: size of the allocation
* @dma_handle: returns the DMA address of the allocation
* @flag: GFP flags for the allocation
* @attrs: DMA_ATTR_* flags for the allocation
* @node: NUMA node to allocate the memory on, NUMA_NO_NODE for no preference
*
* Same as dma_alloc_attrs() but places the memory on @node rather than on the
* node of @dev. Like with kmalloc_node() the node is only a preference,
* the page allocator may fall back to other nodes if @node is out of memory.
* The contiguous memory allocator (CMA) does not: only the CMA area of @node
* and the global CMA area are tried. The allocation may fail even though the
* CMA area of another node, e.g. that of @dev, still has free space.
*
* Allocations served from per-device coherent pools or CMA areas, restricted
* or atomic pools, or by dma_map_ops implementations do not take @node into
* account.
*
* Return: the CPU address of the allocation, or NULL on failure.
*/
This feels like too much explaining of the internals for a commonly
used APIs. If we retry in the helper all we have to say is basically
"acts like kmalloc_node()".
WDYT?
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC 04/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node()
2026-10-27 17:52 ` Jakub Kicinski
@ 2026-10-28 9:00 ` Marek Szyprowski
0 siblings, 0 replies; 22+ messages in thread
From: Marek Szyprowski @ 2026-10-28 9:00 UTC (permalink / raw)
To: Jakub Kicinski
Cc: robin.murphy, tglx, dave.hansen, x86, joro, will, leon, mbloch,
iommu, netdev
On 27.10.2026 18:52, Jakub Kicinski wrote:
> On Tue, 27 Oct 2026 16:24:44 +0100 Marek Szyprowski wrote:
>>> +EXPORT_SYMBOL_GPL(dma_alloc_attrs_node);
>>> +
>>> +void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
>>> + gfp_t flag, unsigned long attrs)
>>> +{
>>> + return dma_alloc_attrs_node(dev, size, dma_handle, flag, attrs,
>>> + dev_to_node(dev));
>>> +}
>>> EXPORT_SYMBOL(dma_alloc_attrs);
>> Do we really need two exported symbols for dma_alloc_attrs()? I would prefer
>> to make dma_alloc_attrs() a static inline calling dma_alloc_attrs_node().
>>
>> Did I missed something obvious against that?
> License, I associate DMA API with hch so I thought we'd want to make
> the new symbol GPL-only? LMK if I'm overthinking this.
Right, I missed that GPL suffix in the new API.
Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2026-10-28 9:00 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-27 2:25 [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
2026-10-27 2:25 ` [RFC 01/11] dma-contiguous: pass the NUMA node to dma_alloc_contiguous() Jakub Kicinski
2026-10-27 2:25 ` [RFC 02/11] dma-direct: pass the NUMA node to dma_direct_alloc() Jakub Kicinski
2026-10-27 2:25 ` [RFC 03/11] iommu/dma: pass the NUMA node to iommu_dma_alloc() Jakub Kicinski
2026-10-27 2:25 ` [RFC 04/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
2026-10-27 9:03 ` Björn Töpel
2026-10-27 15:53 ` Jakub Kicinski
2026-10-27 15:24 ` Marek Szyprowski
2026-10-27 17:52 ` Jakub Kicinski
2026-10-28 9:00 ` Marek Szyprowski
2026-10-27 2:25 ` [RFC 05/11] eth: libwx: restore the device's NUMA node after ring allocation Jakub Kicinski
2026-10-27 2:25 ` [RFC 06/11] eth: libwx: use dma_alloc_coherent_node() Jakub Kicinski
2026-10-27 2:25 ` [RFC 07/11] eth: ixgbe: " Jakub Kicinski
2026-10-27 9:06 ` Björn Töpel
2026-10-27 15:51 ` Jakub Kicinski
2026-10-27 16:06 ` Björn Töpel
2026-10-27 18:20 ` Jakub Kicinski
2026-10-27 2:25 ` [RFC 08/11] eth: xgbe: " Jakub Kicinski
2026-10-27 2:25 ` [RFC 09/11] eth: fungible: " Jakub Kicinski
2026-10-27 2:25 ` [RFC 10/11] eth: mlx5: " Jakub Kicinski
2026-10-27 2:25 ` [RFC 11/11] eth: mlx4: " Jakub Kicinski
2026-10-27 9:03 ` [RFC 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Björn Töpel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox