* [PATCH 01/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node()
2026-11-06 18:44 [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
@ 2026-11-06 18:44 ` Jakub Kicinski
2026-11-06 23:41 ` Marek Szyprowski
2026-11-06 18:44 ` [PATCH 02/11] dma-direct: add dma_direct_alloc_node() Jakub Kicinski
` (11 subsequent siblings)
12 siblings, 1 reply; 22+ messages in thread
From: Jakub Kicinski @ 2026-11-06 18:44 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: netdev, iommu, joro, will, Jakub Kicinski, Björn Töpel
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().
Nothing takes it into account yet, following patches wire it up
in dma-direct and iommu-dma. Allocations from fixed pools and from
dma_map_ops->alloc implementations will keep ignoring it.
Add an explicit new export instead of using static inline wrappers
because we want the new API to be GPL-only.
Reviewed-by: Björn Töpel <bjorn@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v1:
- move before the patches wiring up the node in dma-direct, iommu-dma
and CMA (Robin)
- dma-api.rst: drop the NUMA_NO_NODE suggestion (Björn)
- docs: describe how the node is handled in the patches wiring it up,
drop the mention of dma_free_coherent()
- explain the separate GPL-only export in the commit message (Marek)
rfc: https://lore.kernel.org/20261027022528.633359-5-kuba@kernel.org
---
Documentation/core-api/dma-api.rst | 13 +++++++++++++
include/linux/dma-mapping.h | 14 ++++++++++++++
kernel/dma/mapping.c | 26 ++++++++++++++++++++++++--
3 files changed, 51 insertions(+), 2 deletions(-)
diff --git a/Documentation/core-api/dma-api.rst b/Documentation/core-api/dma-api.rst
index 69d4ebe4f96d..5d079507ffa1 100644
--- a/Documentation/core-api/dma-api.rst
+++ b/Documentation/core-api/dma-api.rst
@@ -57,6 +57,19 @@ 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.
+
+Like with kmalloc_node() the node is only a preference.
+
::
void
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index efde0e5a58dc..f8529f761823 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 82e701648d60..1b1cd3bc4a40 100644
--- a/kernel/dma/mapping.c
+++ b/kernel/dma/mapping.c
@@ -630,8 +630,22 @@ 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.
+ *
+ * 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;
@@ -681,6 +695,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* Re: [PATCH 01/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node()
2026-11-06 18:44 ` [PATCH 01/11] " Jakub Kicinski
@ 2026-11-06 23:41 ` Marek Szyprowski
0 siblings, 0 replies; 22+ messages in thread
From: Marek Szyprowski @ 2026-11-06 23:41 UTC (permalink / raw)
To: Jakub Kicinski, robin.murphy
Cc: netdev, iommu, joro, will, Björn Töpel
On 06.11.2026 19:44, Jakub Kicinski wrote:
> 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().
> Nothing takes it into account yet, following patches wire it up
> in dma-direct and iommu-dma. Allocations from fixed pools and from
> dma_map_ops->alloc implementations will keep ignoring it.
>
> Add an explicit new export instead of using static inline wrappers
> because we want the new API to be GPL-only.
>
> Reviewed-by: Björn Töpel <bjorn@kernel.org>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Acked-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
> v1:
> - move before the patches wiring up the node in dma-direct, iommu-dma
> and CMA (Robin)
> - dma-api.rst: drop the NUMA_NO_NODE suggestion (Björn)
> - docs: describe how the node is handled in the patches wiring it up,
> drop the mention of dma_free_coherent()
> - explain the separate GPL-only export in the commit message (Marek)
> rfc: https://lore.kernel.org/20261027022528.633359-5-kuba@kernel.org
> ---
> Documentation/core-api/dma-api.rst | 13 +++++++++++++
> include/linux/dma-mapping.h | 14 ++++++++++++++
> kernel/dma/mapping.c | 26 ++++++++++++++++++++++++--
> 3 files changed, 51 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/core-api/dma-api.rst b/Documentation/core-api/dma-api.rst
> index 69d4ebe4f96d..5d079507ffa1 100644
> --- a/Documentation/core-api/dma-api.rst
> +++ b/Documentation/core-api/dma-api.rst
> @@ -57,6 +57,19 @@ 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.
> +
> +Like with kmalloc_node() the node is only a preference.
> +
> ::
>
> void
> diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
> index efde0e5a58dc..f8529f761823 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 82e701648d60..1b1cd3bc4a40 100644
> --- a/kernel/dma/mapping.c
> +++ b/kernel/dma/mapping.c
> @@ -630,8 +630,22 @@ 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.
> + *
> + * 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;
> @@ -681,6 +695,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,
Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 02/11] dma-direct: add dma_direct_alloc_node()
2026-11-06 18:44 [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
2026-11-06 18:44 ` [PATCH 01/11] " Jakub Kicinski
@ 2026-11-06 18:44 ` Jakub Kicinski
2026-11-06 18:44 ` [PATCH 03/11] iommu/dma: pass the NUMA node to iommu_dma_alloc() Jakub Kicinski
` (10 subsequent siblings)
12 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-11-06 18:44 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: netdev, iommu, joro, will, Jakub Kicinski, Björn Töpel
dma_direct_alloc() allocates the pages on the node of the device.
Add dma_direct_alloc_node() which takes the node as an argument,
and pass it the node requested via dma_alloc_attrs_node().
dma_direct_alloc() stays as a wrapper passing dev_to_node(),
swiotlb-xen uses it as its dma_map_ops->alloc on non-x86 platforms.
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. The per-numa CMA area is still picked
based on the node of the device, a following patch takes care of that.
Acked-by: Marek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: Björn Töpel <bjorn@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v1:
- add dma_direct_alloc_node() and keep dma_direct_alloc() as a wrapper,
swiotlb-xen uses it as its ->alloc (fixes the build on arm/arm64)
- pass the node from dma_alloc_attrs_node() directly, the API patch now
comes first
- docs: note that the page allocator may fall back to other nodes
rfc: https://lore.kernel.org/20261027022528.633359-3-kuba@kernel.org
---
Documentation/core-api/dma-api.rst | 3 ++-
include/linux/dma-direct.h | 11 +++++++++--
kernel/dma/direct.c | 20 +++++++++++---------
kernel/dma/mapping.c | 6 ++++--
4 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/Documentation/core-api/dma-api.rst b/Documentation/core-api/dma-api.rst
index 5d079507ffa1..f44cc770a472 100644
--- a/Documentation/core-api/dma-api.rst
+++ b/Documentation/core-api/dma-api.rst
@@ -68,7 +68,8 @@ 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.
-Like with kmalloc_node() the node is only a preference.
+Like with kmalloc_node() the node is only a preference, the page allocator
+may fall back to other nodes if the given node is out of memory.
::
diff --git a/include/linux/dma-direct.h b/include/linux/dma-direct.h
index daa31a1adf7b..b822e96c351e 100644
--- a/include/linux/dma-direct.h
+++ b/include/linux/dma-direct.h
@@ -157,8 +157,8 @@ 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);
+void *dma_direct_alloc_node(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, 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,
@@ -168,4 +168,11 @@ void dma_direct_free_pages(struct device *dev, size_t size,
enum dma_data_direction dir);
int dma_direct_supported(struct device *dev, u64 mask);
+static inline void *dma_direct_alloc(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
+{
+ return dma_direct_alloc_node(dev, size, dma_handle, gfp, attrs,
+ dev_to_node(dev));
+}
+
#endif /* _LINUX_DMA_DIRECT_H */
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 25e9a86835fd..a9d24d8bc77c 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;
void *va;
@@ -198,11 +197,12 @@ static bool dma_direct_use_no_mapping(struct device *dev, unsigned long attrs)
}
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;
@@ -217,8 +217,8 @@ static void *dma_direct_alloc_no_mapping(struct device *dev, size_t size,
return page;
}
-void *dma_direct_alloc(struct device *dev, size_t size,
- dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
+void *dma_direct_alloc_node(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs, int node)
{
bool remap = false, set_uncached = false;
bool mark_mem_decrypt = false;
@@ -245,7 +245,8 @@ void *dma_direct_alloc(struct device *dev, size_t size,
gfp |= __GFP_NOWARN;
if (dma_direct_use_no_mapping(dev, attrs))
- 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) &&
@@ -303,7 +304,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;
@@ -468,7 +470,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 1b1cd3bc4a40..6e2847f55622 100644
--- a/kernel/dma/mapping.c
+++ b/kernel/dma/mapping.c
@@ -640,7 +640,8 @@ EXPORT_SYMBOL_GPL(dma_get_required_mask);
* @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.
+ * 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.
*
* Return: the CPU address of the allocation, or NULL on failure.
*/
@@ -679,7 +680,8 @@ void *dma_alloc_attrs_node(struct device *dev, size_t size,
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_node(dev, size, dma_handle, flag,
+ attrs, node);
} 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* [PATCH 03/11] iommu/dma: pass the NUMA node to iommu_dma_alloc()
2026-11-06 18:44 [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
2026-11-06 18:44 ` [PATCH 01/11] " Jakub Kicinski
2026-11-06 18:44 ` [PATCH 02/11] dma-direct: add dma_direct_alloc_node() Jakub Kicinski
@ 2026-11-06 18:44 ` Jakub Kicinski
2026-11-06 18:44 ` [PATCH 04/11] dma-contiguous: pass the NUMA node to dma_alloc_contiguous() Jakub Kicinski
` (9 subsequent siblings)
12 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-11-06 18:44 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: netdev, iommu, joro, will, Jakub Kicinski, Björn Töpel
iommu_dma_alloc() allocates the pages on the node of the device.
Pass in the node requested via dma_alloc_attrs_node() instead.
Memory from the atomic pools is not node-aware. Like in dma-direct
the per-numa CMA area is still picked based on the node of the device.
iommu_dma_alloc_noncontiguous() keeps using the device's node.
Acked-by: Marek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: Björn Töpel <bjorn@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v1:
- pass the node from dma_alloc_attrs_node() rather than dev_to_node(),
the API patch now comes first
rfc: https://lore.kernel.org/20261027022528.633359-4-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 fae5d50e4f27..29a690b1a593 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 78742aeba932..3e3a1a987a23 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -883,10 +883,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)
@@ -939,7 +939,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;
@@ -967,7 +968,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;
@@ -1013,14 +1014,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;
@@ -1061,7 +1063,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;
@@ -1638,11 +1641,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;
@@ -1677,7 +1679,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);
@@ -1693,7 +1695,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) &&
@@ -1703,7 +1706,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 6e2847f55622..f98d4dec8649 100644
--- a/kernel/dma/mapping.c
+++ b/kernel/dma/mapping.c
@@ -683,7 +683,8 @@ void *dma_alloc_attrs_node(struct device *dev, size_t size,
cpu_addr = dma_direct_alloc_node(dev, size, dma_handle, flag,
attrs, node);
} 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,
+ node);
} 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* [PATCH 04/11] dma-contiguous: pass the NUMA node to dma_alloc_contiguous()
2026-11-06 18:44 [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (2 preceding siblings ...)
2026-11-06 18:44 ` [PATCH 03/11] iommu/dma: pass the NUMA node to iommu_dma_alloc() Jakub Kicinski
@ 2026-11-06 18:44 ` Jakub Kicinski
2026-11-12 9:53 ` Paolo Abeni
2026-11-06 18:44 ` [PATCH 05/11] eth: libwx: restore the device's NUMA node after ring allocation Jakub Kicinski
` (8 subsequent siblings)
12 siblings, 1 reply; 22+ messages in thread
From: Jakub Kicinski @ 2026-11-06 18:44 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: netdev, iommu, joro, will, Jakub Kicinski, Björn Töpel
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 dma-direct and
iommu-dma honor the node requested via dma_alloc_attrs_node() for CMA
allocations, too. dma_common_alloc_pages() keeps using the device's node.
Unlike the page allocator, CMA does not fall back to the areas of other
nodes, only to the global CMA area. The allocation may fail even though
the CMA area of the device's node still has free space. Document this.
This completes the wiring of the node, so also document which
allocations don't take it into account.
Acked-by: Marek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: Björn Töpel <bjorn@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v1:
- move after the dma-direct and iommu-dma patches (Robin)
- docs: document that CMA does not fall back to other nodes' areas (Björn)
- docs: document which allocations ignore the node
rfc: https://lore.kernel.org/20261027022528.633359-2-kuba@kernel.org
---
Documentation/core-api/dma-api.rst | 5 ++++-
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/mapping.c | 7 +++++++
kernel/dma/ops_helpers.c | 5 +++--
7 files changed, 24 insertions(+), 14 deletions(-)
diff --git a/Documentation/core-api/dma-api.rst b/Documentation/core-api/dma-api.rst
index f44cc770a472..e6fe24e936ee 100644
--- a/Documentation/core-api/dma-api.rst
+++ b/Documentation/core-api/dma-api.rst
@@ -69,7 +69,10 @@ frequently by both the device and a CPU on a different node, for example
descriptor rings serviced by that CPU.
Like with kmalloc_node() the node is only a preference, the page allocator
-may fall back to other nodes if the given node is out of memory.
+may fall back to other nodes if the given node is out of memory. Note that
+contiguous memory allocator (CMA) does not: only the CMA area of the given
+node and the global CMA area are tried. The allocation may fail even though
+the CMA area of another node, e.g. that of the device, still has free space.
::
diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h
index afaf2c12189b..6014ec433b03 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 3e3a1a987a23..b66e83cfbc5f 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -1648,7 +1648,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) {
cpu_addr = alloc_pages_exact_nid(node, alloc_size, gfp);
if (!cpu_addr)
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c
index 90937dc7c627..e30ec6a08af0 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 a9d24d8bc77c..37c49e84dea6 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/mapping.c b/kernel/dma/mapping.c
index f98d4dec8649..b3d0811b1d82 100644
--- a/kernel/dma/mapping.c
+++ b/kernel/dma/mapping.c
@@ -642,6 +642,13 @@ EXPORT_SYMBOL_GPL(dma_get_required_mask);
* 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 or global coherent pools, per-device CMA
+ * areas, restricted or atomic pools, arch_dma_alloc() or dma_map_ops
+ * implementations do not take @node into account.
*
* Return: the CPU address of the allocation, or NULL on failure.
*/
diff --git a/kernel/dma/ops_helpers.c b/kernel/dma/ops_helpers.c
index 5cdf3f60cf47..2bff02c53fb6 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) {
- void *va = alloc_pages_exact_nid(dev_to_node(dev), size, gfp);
+ void *va = alloc_pages_exact_nid(nid, size, gfp);
if (!va)
return NULL;
--
2.56.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH 04/11] dma-contiguous: pass the NUMA node to dma_alloc_contiguous()
2026-11-06 18:44 ` [PATCH 04/11] dma-contiguous: pass the NUMA node to dma_alloc_contiguous() Jakub Kicinski
@ 2026-11-12 9:53 ` Paolo Abeni
2026-11-12 13:28 ` Robin Murphy
0 siblings, 1 reply; 22+ messages in thread
From: Paolo Abeni @ 2026-11-12 9:53 UTC (permalink / raw)
To: Jakub Kicinski, robin.murphy, m.szyprowski
Cc: netdev, iommu, joro, will, Björn Töpel
On 11/6/26 19:44, Jakub Kicinski wrote:
> --- a/kernel/dma/mapping.c
> +++ b/kernel/dma/mapping.c
> @@ -642,6 +642,13 @@ EXPORT_SYMBOL_GPL(dma_get_required_mask);
> * 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 or global coherent pools, per-device CMA
> + * areas, restricted or atomic pools, arch_dma_alloc() or dma_map_ops
> + * implementations do not take @node into account.
I'm possibly over-thinking here, but AFAICS there are a bunch of dma_map_ops that
are using dev_to_node():
dma_iommu_alloc_coherent() in arch/powerpc/kernel/dma-iommu.c
vio_dma_iommu_alloc_coherent() in arch/powerpc/platforms/pseries/vio.c
gart_alloc_coherent() in arch/x86/kernel/amd_gart_64.c
With this series they will stop using the driver provided numa node.
Probably not a blocker but possibly worth to call out?
/P
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 04/11] dma-contiguous: pass the NUMA node to dma_alloc_contiguous()
2026-11-12 9:53 ` Paolo Abeni
@ 2026-11-12 13:28 ` Robin Murphy
2026-11-12 15:43 ` Paolo Abeni
0 siblings, 1 reply; 22+ messages in thread
From: Robin Murphy @ 2026-11-12 13:28 UTC (permalink / raw)
To: Paolo Abeni, Jakub Kicinski, m.szyprowski
Cc: netdev, iommu, joro, will, Björn Töpel
On 2026-11-12 9:53 am, Paolo Abeni wrote:
> On 11/6/26 19:44, Jakub Kicinski wrote:
>> --- a/kernel/dma/mapping.c
>> +++ b/kernel/dma/mapping.c
>> @@ -642,6 +642,13 @@ EXPORT_SYMBOL_GPL(dma_get_required_mask);
>> * 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 or global coherent pools, per-
>> device CMA
>> + * areas, restricted or atomic pools, arch_dma_alloc() or dma_map_ops
>> + * implementations do not take @node into account.
> I'm possibly over-thinking here, but AFAICS there are a bunch of
> dma_map_ops that
> are using dev_to_node():
>
> dma_iommu_alloc_coherent() in arch/powerpc/kernel/dma-iommu.c
> vio_dma_iommu_alloc_coherent() in arch/powerpc/platforms/pseries/vio.c
> gart_alloc_coherent() in arch/x86/kernel/amd_gart_64.c
>
> With this series they will stop using the driver provided numa node.
> Probably not a blocker but possibly worth to call out?
Nothing "stops" - this series adds a *new* interface by which drivers
can now indicate a specific preferred node, but DMA API backends are
still free to ignore the caller-provided hint and continue doing
whatever they want. This is already called out all over the series in
commit messages, documentation and comments including the one above.
If anyone does want to do the additional work to wire this up through
dma_map_ops for powerpc's benefit (I doubt the rest of the
legacy/embedded architectures will ever care), they will be welcome to
do so. But as-is, all that happens is that the driver changes here have
no effect on those systems and will keep working just as they did before.
Thanks,
Robin.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 04/11] dma-contiguous: pass the NUMA node to dma_alloc_contiguous()
2026-11-12 13:28 ` Robin Murphy
@ 2026-11-12 15:43 ` Paolo Abeni
2026-11-12 16:17 ` Jakub Kicinski
2026-11-12 16:21 ` Robin Murphy
0 siblings, 2 replies; 22+ messages in thread
From: Paolo Abeni @ 2026-11-12 15:43 UTC (permalink / raw)
To: Robin Murphy, Jakub Kicinski, m.szyprowski
Cc: netdev, iommu, joro, will, Björn Töpel
On 11/12/26 14:28, Robin Murphy wrote:
> On 2026-11-12 9:53 am, Paolo Abeni wrote:
>> On 11/6/26 19:44, Jakub Kicinski wrote:
>>> --- a/kernel/dma/mapping.c
>>> +++ b/kernel/dma/mapping.c
>>> @@ -642,6 +642,13 @@ EXPORT_SYMBOL_GPL(dma_get_required_mask);
>>> * 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 or global coherent pools, per- device CMA
>>> + * areas, restricted or atomic pools, arch_dma_alloc() or dma_map_ops
>>> + * implementations do not take @node into account.
>> I'm possibly over-thinking here, but AFAICS there are a bunch of dma_map_ops that
>> are using dev_to_node():
>>
>> dma_iommu_alloc_coherent() in arch/powerpc/kernel/dma-iommu.c
>> vio_dma_iommu_alloc_coherent() in arch/powerpc/platforms/pseries/vio.c
>> gart_alloc_coherent() in arch/x86/kernel/amd_gart_64.c
>>
>> With this series they will stop using the driver provided numa node.
>> Probably not a blocker but possibly worth to call out?
>
> Nothing "stops" - this series adds a *new* interface by which drivers can now indicate a specific preferred node, but DMA API backends are still free to ignore the caller-provided hint and continue doing whatever they want. This is already called out all over the series in commit messages, documentation and comments including the one above.
I mean: the series also replaces the old hacks with the new APIs usage
in a few drivers, and in the above cases there could be actual regressions.
But I guess we could handle them if and when they will surface.
/P
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 04/11] dma-contiguous: pass the NUMA node to dma_alloc_contiguous()
2026-11-12 15:43 ` Paolo Abeni
@ 2026-11-12 16:17 ` Jakub Kicinski
2026-11-12 16:21 ` Robin Murphy
1 sibling, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-11-12 16:17 UTC (permalink / raw)
To: Paolo Abeni
Cc: Robin Murphy, m.szyprowski, netdev, iommu, joro, will,
Björn Töpel
On Thu, 12 Nov 2026 16:43:51 +0100 Paolo Abeni wrote:
> >> I'm possibly over-thinking here, but AFAICS there are a bunch of dma_map_ops that
> >> are using dev_to_node():
> >>
> >> dma_iommu_alloc_coherent() in arch/powerpc/kernel/dma-iommu.c
> >> vio_dma_iommu_alloc_coherent() in arch/powerpc/platforms/pseries/vio.c
> >> gart_alloc_coherent() in arch/x86/kernel/amd_gart_64.c
> >>
> >> With this series they will stop using the driver provided numa node.
> >> Probably not a blocker but possibly worth to call out?
> >
> > Nothing "stops" - this series adds a *new* interface by which
> > drivers can now indicate a specific preferred node, but DMA API
> > backends are still free to ignore the caller-provided hint and
> > continue doing whatever they want. This is already called out all
> > over the series in commit messages, documentation and comments
> > including the one above.
>
> I mean: the series also replaces the old hacks with the new APIs usage
> in a few drivers, and in the above cases there could be actual
> regressions.
>
> But I guess we could handle them if and when they will surface.
I know AI helps people surface unlikely cases but we need to draw
the line somewhere.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 04/11] dma-contiguous: pass the NUMA node to dma_alloc_contiguous()
2026-11-12 15:43 ` Paolo Abeni
2026-11-12 16:17 ` Jakub Kicinski
@ 2026-11-12 16:21 ` Robin Murphy
1 sibling, 0 replies; 22+ messages in thread
From: Robin Murphy @ 2026-11-12 16:21 UTC (permalink / raw)
To: Paolo Abeni, Jakub Kicinski, m.szyprowski
Cc: netdev, iommu, joro, will, Björn Töpel
On 2026-11-12 3:43 pm, Paolo Abeni wrote:
> On 11/12/26 14:28, Robin Murphy wrote:
>> On 2026-11-12 9:53 am, Paolo Abeni wrote:
>>> On 11/6/26 19:44, Jakub Kicinski wrote:
>>>> --- a/kernel/dma/mapping.c
>>>> +++ b/kernel/dma/mapping.c
>>>> @@ -642,6 +642,13 @@ EXPORT_SYMBOL_GPL(dma_get_required_mask);
>>>> * 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 or global coherent pools,
>>>> per- device CMA
>>>> + * areas, restricted or atomic pools, arch_dma_alloc() or dma_map_ops
>>>> + * implementations do not take @node into account.
>>> I'm possibly over-thinking here, but AFAICS there are a bunch of
>>> dma_map_ops that
>>> are using dev_to_node():
>>>
>>> dma_iommu_alloc_coherent() in arch/powerpc/kernel/dma-iommu.c
>>> vio_dma_iommu_alloc_coherent() in arch/powerpc/platforms/pseries/vio.c
>>> gart_alloc_coherent() in arch/x86/kernel/amd_gart_64.c
>>>
>>> With this series they will stop using the driver provided numa node.
>>> Probably not a blocker but possibly worth to call out?
>>
>> Nothing "stops" - this series adds a *new* interface by which drivers
>> can now indicate a specific preferred node, but DMA API backends are
>> still free to ignore the caller-provided hint and continue doing
>> whatever they want. This is already called out all over the series in
>> commit messages, documentation and comments including the one above.
> I mean: the series also replaces the old hacks with the new APIs usage
> in a few drivers, and in the above cases there could be actual regressions.
>
> But I guess we could handle them if and when they will surface.
Ah, sorry, I was confused by the comment being made in the context of
this patch - what you mean is that in patches 6-11, the new API isn't
guaranteed to give the same result as the set_dev_node() hacks being
removed? To be fair, the new API itself still does not change any
behaviour, removing the hacks changes the behaviour ;)
I guess it could be worth a note in those commit messages if we think
there's a chance that anyone is using these NICs on POWER systems that
might conceivably observe a performance blip.
Cheers,
Robin.
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 05/11] eth: libwx: restore the device's NUMA node after ring allocation
2026-11-06 18:44 [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (3 preceding siblings ...)
2026-11-06 18:44 ` [PATCH 04/11] dma-contiguous: pass the NUMA node to dma_alloc_contiguous() Jakub Kicinski
@ 2026-11-06 18:44 ` Jakub Kicinski
2026-11-06 18:44 ` [PATCH 06/11] eth: libwx: use dma_alloc_coherent_node() Jakub Kicinski
` (7 subsequent siblings)
12 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-11-06 18:44 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: netdev, iommu, joro, will, Jakub Kicinski, Björn Töpel
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")
Reviewed-by: Björn Töpel <bjorn@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v1: no change
rfc: https://lore.kernel.org/20261027022528.633359-6-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* [PATCH 06/11] eth: libwx: use dma_alloc_coherent_node()
2026-11-06 18:44 [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (4 preceding siblings ...)
2026-11-06 18:44 ` [PATCH 05/11] eth: libwx: restore the device's NUMA node after ring allocation Jakub Kicinski
@ 2026-11-06 18:44 ` Jakub Kicinski
2026-11-06 18:44 ` [PATCH 07/11] eth: ixgbe: " Jakub Kicinski
` (6 subsequent siblings)
12 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-11-06 18:44 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: netdev, iommu, joro, will, Jakub Kicinski, Björn Töpel
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.
Reviewed-by: Björn Töpel <bjorn@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v1: no change
rfc: https://lore.kernel.org/20261027022528.633359-7-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* [PATCH 07/11] eth: ixgbe: use dma_alloc_coherent_node()
2026-11-06 18:44 [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (5 preceding siblings ...)
2026-11-06 18:44 ` [PATCH 06/11] eth: libwx: use dma_alloc_coherent_node() Jakub Kicinski
@ 2026-11-06 18:44 ` Jakub Kicinski
2026-11-06 18:44 ` [PATCH 08/11] eth: xgbe: " Jakub Kicinski
` (5 subsequent siblings)
12 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-11-06 18:44 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: netdev, iommu, joro, will, Jakub Kicinski, Björn Töpel
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.
Reviewed-by: Björn Töpel <bjorn@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v1: no change
rfc: https://lore.kernel.org/20261027022528.633359-8-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 b9496f0bee13..6458cb2e511e 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* [PATCH 08/11] eth: xgbe: use dma_alloc_coherent_node()
2026-11-06 18:44 [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (6 preceding siblings ...)
2026-11-06 18:44 ` [PATCH 07/11] eth: ixgbe: " Jakub Kicinski
@ 2026-11-06 18:44 ` Jakub Kicinski
2026-11-06 18:44 ` [PATCH 09/11] eth: fungible: " Jakub Kicinski
` (4 subsequent siblings)
12 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-11-06 18:44 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: netdev, iommu, joro, will, Jakub Kicinski, Björn Töpel
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.
Reviewed-by: Björn Töpel <bjorn@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v1: no change
rfc: https://lore.kernel.org/20261027022528.633359-9-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* [PATCH 09/11] eth: fungible: use dma_alloc_coherent_node()
2026-11-06 18:44 [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (7 preceding siblings ...)
2026-11-06 18:44 ` [PATCH 08/11] eth: xgbe: " Jakub Kicinski
@ 2026-11-06 18:44 ` Jakub Kicinski
2026-11-06 18:44 ` [PATCH 10/11] eth: mlx5: " Jakub Kicinski
` (3 subsequent siblings)
12 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-11-06 18:44 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: netdev, iommu, joro, will, Jakub Kicinski, Björn Töpel
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.
Reviewed-by: Björn Töpel <bjorn@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v1: no change
rfc: https://lore.kernel.org/20261027022528.633359-10-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* [PATCH 10/11] eth: mlx5: use dma_alloc_coherent_node()
2026-11-06 18:44 [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (8 preceding siblings ...)
2026-11-06 18:44 ` [PATCH 09/11] eth: fungible: " Jakub Kicinski
@ 2026-11-06 18:44 ` Jakub Kicinski
2026-11-12 12:31 ` Tariq Toukan
2026-11-06 18:44 ` [PATCH 11/11] eth: mlx4: " Jakub Kicinski
` (2 subsequent siblings)
12 siblings, 1 reply; 22+ messages in thread
From: Jakub Kicinski @ 2026-11-06 18:44 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: netdev, iommu, joro, will, Jakub Kicinski, Björn Töpel
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.
Reviewed-by: Björn Töpel <bjorn@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v1: no change
rfc: https://lore.kernel.org/20261027022528.633359-11-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* Re: [PATCH 10/11] eth: mlx5: use dma_alloc_coherent_node()
2026-11-06 18:44 ` [PATCH 10/11] eth: mlx5: " Jakub Kicinski
@ 2026-11-12 12:31 ` Tariq Toukan
0 siblings, 0 replies; 22+ messages in thread
From: Tariq Toukan @ 2026-11-12 12:31 UTC (permalink / raw)
To: Jakub Kicinski, robin.murphy, m.szyprowski
Cc: netdev, iommu, joro, will, Björn Töpel
On 06/11/2026 20:44, Jakub Kicinski wrote:
> 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.
>
> Reviewed-by: Björn Töpel <bjorn@kernel.org>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Acked-by: Tariq Toukan <tariqt@nvidia.com>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 11/11] eth: mlx4: use dma_alloc_coherent_node()
2026-11-06 18:44 [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (9 preceding siblings ...)
2026-11-06 18:44 ` [PATCH 10/11] eth: mlx5: " Jakub Kicinski
@ 2026-11-06 18:44 ` Jakub Kicinski
2026-11-12 12:32 ` Tariq Toukan
2026-11-12 16:16 ` [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
2026-11-12 16:51 ` Robin Murphy
12 siblings, 1 reply; 22+ messages in thread
From: Jakub Kicinski @ 2026-11-06 18:44 UTC (permalink / raw)
To: robin.murphy, m.szyprowski
Cc: netdev, iommu, joro, will, Jakub Kicinski, Björn Töpel
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.
Reviewed-by: Björn Töpel <bjorn@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v1: no change
rfc: https://lore.kernel.org/20261027022528.633359-12-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: [PATCH 11/11] eth: mlx4: use dma_alloc_coherent_node()
2026-11-06 18:44 ` [PATCH 11/11] eth: mlx4: " Jakub Kicinski
@ 2026-11-12 12:32 ` Tariq Toukan
0 siblings, 0 replies; 22+ messages in thread
From: Tariq Toukan @ 2026-11-12 12:32 UTC (permalink / raw)
To: Jakub Kicinski, robin.murphy, m.szyprowski
Cc: netdev, iommu, joro, will, Björn Töpel
On 06/11/2026 20:44, Jakub Kicinski wrote:
> 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.
>
> Reviewed-by: Björn Töpel <bjorn@kernel.org>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Acked-by: Tariq Toukan <tariqt@nvidia.com>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node()
2026-11-06 18:44 [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (10 preceding siblings ...)
2026-11-06 18:44 ` [PATCH 11/11] eth: mlx4: " Jakub Kicinski
@ 2026-11-12 16:16 ` Jakub Kicinski
2026-11-12 16:51 ` Robin Murphy
12 siblings, 0 replies; 22+ messages in thread
From: Jakub Kicinski @ 2026-11-12 16:16 UTC (permalink / raw)
To: robin.murphy, m.szyprowski; +Cc: netdev, iommu, joro, will
On Fri, 6 Nov 2026 10:44:22 -0800 Jakub Kicinski wrote:
> Stable tag on top of v7.4-rc1, so that we can also pull this into
> the networking tree:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/kuba/linux.git tags/dma_alloc_coherent_node
Marek, Robin, would you be willing to pull?
Or should I respin the tag to include the latest Ack/Review tags from
the list?
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node()
2026-11-06 18:44 [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
` (11 preceding siblings ...)
2026-11-12 16:16 ` [PATCH 00/11] dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node() Jakub Kicinski
@ 2026-11-12 16:51 ` Robin Murphy
12 siblings, 0 replies; 22+ messages in thread
From: Robin Murphy @ 2026-11-12 16:51 UTC (permalink / raw)
To: Jakub Kicinski, m.szyprowski; +Cc: netdev, iommu, joro, will
On 2026-11-06 6:44 pm, Jakub Kicinski wrote:
> Hi!
>
> 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);
>
> Introduce dma_alloc_coherent_node() so that such racy
> hacks are no longer necessary.
>
> Note that making dma_map_ops->alloc() callbacks explicit-node-aware
> is left for future work.
>
> Stable tag on top of v7.4-rc1, so that we can also pull this into
> the networking tree:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/kuba/linux.git tags/dma_alloc_coherent_node
>
> v1:
> - add the API first, then wire up the node in dma-direct, iommu-dma
> and CMA (Robin)
> - add dma_direct_alloc_node() and keep dma_direct_alloc() as a wrapper,
> swiotlb-xen uses it as its ->alloc (fixes the build on arm/arm64),
> arch/x86 no longer needs changes
> - docs: drop the NUMA_NO_NODE suggestion from dma-api.rst (Björn)
> - docs: document that CMA does not fall back to other nodes' areas (Björn)
> - docs: document which allocations ignore the node
> - explain the separate GPL-only export in the commit message (Marek)
> rfc: https://lore.kernel.org/20261027022528.633359-1-kuba@kernel.org
>
> Jakub Kicinski (11):
> dma-mapping: add dma_alloc_attrs_node() and dma_alloc_coherent_node()
> dma-direct: add dma_direct_alloc_node()
> iommu/dma: pass the NUMA node to iommu_dma_alloc()
> dma-contiguous: pass the NUMA node to dma_alloc_contiguous()
The API side of things looks OK to me. For the DMA patches at least,
Reviewed-by: Robin Murphy <robin.murphy@arm.com>
> 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 | 17 ++++++++
> include/linux/dma-direct.h | 11 ++++-
> 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 -
> 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 | 22 +++++-----
> kernel/dma/mapping.c | 40 +++++++++++++++++--
> kernel/dma/ops_helpers.c | 5 ++-
> 23 files changed, 152 insertions(+), 127 deletions(-)
>
^ permalink raw reply [flat|nested] 22+ messages in thread