Merge branch 'net-consolidate-devmem-net_iov-freelist-and-dma-handling'

Stanislav Fomichev says:

====================
net: consolidate devmem net_iov freelist and DMA handling

Follow up on the recently merged devmem gen_pool removal by consolidating
devmem's net_iov freelist and DMA-address handling.

- move devmem freelist to index-based approach with push/pop helpers
- switch devmem to public memory-provider helpers and reject DMA
  addresses that cannot be represented
- decode stored DMA addresses for TX on 32-bit systems with 64-bit
  dma_addr_t
====================

Link: https://patch.msgid.link/20260925201522.254717-1-sdf@fomichev.me
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-09-29 15:41:34 +02:00
commit 6e23f90f2b
6 changed files with 87 additions and 56 deletions

View File

@ -3816,9 +3816,12 @@ static inline dma_addr_t __skb_frag_dma_map(struct device *dev,
size_t offset, size_t size,
enum dma_data_direction dir)
{
dma_addr_t addr;
if (skb_frag_is_net_iov(frag)) {
return netmem_to_net_iov(frag->netmem)->desc.dma_addr +
offset + frag->offset;
addr = netmem_dma_addr_decode(
netmem_get_dma_addr(frag->netmem));
return addr + offset + frag->offset;
}
return dma_map_page(dev, skb_frag_page(frag),
skb_frag_off(frag) + offset, size, dir);

View File

@ -358,6 +358,33 @@ static inline bool netmem_is_pfmemalloc(netmem_ref netmem)
return page_is_pfmemalloc(netmem_to_page(netmem));
}
#define NETMEM_32BIT_ARCH_WITH_64BIT_DMA \
(sizeof(dma_addr_t) > sizeof(unsigned long))
static inline unsigned long netmem_dma_addr_encode(dma_addr_t addr)
{
if (NETMEM_32BIT_ARCH_WITH_64BIT_DMA)
addr >>= PAGE_SHIFT;
return addr;
}
static inline dma_addr_t netmem_dma_addr_decode(unsigned long addr)
{
if (NETMEM_32BIT_ARCH_WITH_64BIT_DMA)
return (dma_addr_t)addr << PAGE_SHIFT;
return addr;
}
static inline bool netmem_dma_addr_fits(dma_addr_t addr)
{
/* We assume page alignment to shave off bottom bits,
* if this "compression" doesn't work we need to drop.
*/
return addr == netmem_dma_addr_decode(netmem_dma_addr_encode(addr));
}
static inline unsigned long netmem_get_dma_addr(netmem_ref netmem)
{
return netmem_to_nmdesc(netmem)->dma_addr;

View File

@ -408,9 +408,6 @@ static inline void page_pool_recycle_direct_netmem(struct page_pool *pool,
page_pool_put_full_netmem(pool, netmem, true);
}
#define PAGE_POOL_32BIT_ARCH_WITH_64BIT_DMA \
(sizeof(dma_addr_t) > sizeof(unsigned long))
/**
* page_pool_free_va() - free a va into the page_pool
* @pool: pool from which va was allocated
@ -427,12 +424,7 @@ static inline void page_pool_free_va(struct page_pool *pool, void *va,
static inline dma_addr_t page_pool_get_dma_addr_netmem(netmem_ref netmem)
{
dma_addr_t ret = netmem_get_dma_addr(netmem);
if (PAGE_POOL_32BIT_ARCH_WITH_64BIT_DMA)
ret <<= PAGE_SHIFT;
return ret;
return netmem_dma_addr_decode(netmem_get_dma_addr(netmem));
}
/**

View File

@ -17,11 +17,9 @@
#include <net/page_pool/memory_provider.h>
#include <net/sock.h>
#include <net/tcp.h>
#include <trace/events/page_pool.h>
#include "devmem.h"
#include "mp_dmabuf_devmem.h"
#include "page_pool_priv.h"
/* Device memory support */
@ -29,6 +27,29 @@ static DEFINE_XARRAY_FLAGS(net_devmem_dmabuf_bindings, XA_FLAGS_ALLOC1);
static const struct memory_provider_ops dmabuf_devmem_ops;
static struct net_iov *net_iov_free_pop(struct net_iov_area *area,
u32 *freelist, u32 *free_count)
{
u32 idx;
if (unlikely(!*free_count))
return NULL;
idx = freelist[--(*free_count)];
return &area->niovs[idx];
}
static void net_iov_free_push(struct net_iov_area *area,
u32 *freelist, u32 *free_count,
struct net_iov *niov)
{
if (WARN_ON_ONCE(net_iov_owner(niov) != area ||
*free_count >= area->num_niovs))
return;
freelist[(*free_count)++] = net_iov_idx(niov);
}
static void net_devmem_dmabuf_binding_release(struct percpu_ref *ref)
{
struct net_devmem_dmabuf_binding *binding =
@ -44,7 +65,7 @@ void __net_devmem_dmabuf_binding_free(struct work_struct *wq)
if (binding->freelist)
WARN(binding->free_count != binding->area.num_niovs,
"destroying dmabuf binding with outstanding net_iovs: total=%zu, free=%zu",
"destroying dmabuf binding with outstanding net_iovs: total=%zu, free=%u",
binding->area.num_niovs, binding->free_count);
kvfree(binding->area.niovs);
@ -63,14 +84,15 @@ static unsigned int
net_devmem_alloc_dmabuf_bulk(struct net_devmem_dmabuf_binding *binding,
netmem_ref *netmems, unsigned int count)
{
struct net_iov *niov;
unsigned int i;
spin_lock_bh(&binding->freelist_lock);
count = min_t(size_t, count, binding->free_count);
count = min(count, binding->free_count);
for (i = 0; i < count; i++) {
struct net_iov *niov = binding->freelist[--binding->free_count];
niov = net_iov_free_pop(&binding->area, binding->freelist,
&binding->free_count);
netmems[i] = net_iov_to_netmem(niov);
}
@ -84,12 +106,8 @@ void net_devmem_free_dmabuf(struct net_iov *niov)
struct net_devmem_dmabuf_binding *binding = net_devmem_iov_binding(niov);
spin_lock_bh(&binding->freelist_lock);
if (WARN_ON_ONCE(binding->free_count >= binding->area.num_niovs)) {
spin_unlock_bh(&binding->freelist_lock);
return;
}
binding->freelist[binding->free_count++] = niov;
net_iov_free_push(&binding->area, binding->freelist,
&binding->free_count, niov);
spin_unlock_bh(&binding->freelist_lock);
}
@ -228,6 +246,12 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
goto err_unmap;
}
if ((dmabuf->size >> niov_shift) > U32_MAX) {
err = -E2BIG;
NL_SET_ERR_MSG(extack, "dmabuf contains too many net_iovs");
goto err_unmap;
}
binding->area.base_virtual = 0;
binding->area.num_niovs = dmabuf->size >> niov_shift;
if (direction == DMA_TO_DEVICE) {
@ -247,7 +271,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
goto err_unmap;
}
}
binding->area.niovs = kvmalloc_objs(*binding->area.niovs,
binding->area.niovs = kvzalloc_objs(*binding->area.niovs,
binding->area.num_niovs);
if (!binding->area.niovs) {
err = -ENOMEM;
@ -274,12 +298,18 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
for (i = 0; i < nr_niovs; i++, niov_idx++) {
niov = &binding->area.niovs[niov_idx];
net_iov_init(niov, &binding->area, NET_IOV_DMABUF);
page_pool_set_dma_addr_netmem(net_iov_to_netmem(niov),
dma_addr);
if (net_mp_niov_set_dma_addr(niov, dma_addr)) {
err = -EFAULT;
NL_SET_ERR_MSG(extack,
"dmabuf DMA address cannot be represented");
goto err_free_niovs;
}
if (direction == DMA_TO_DEVICE)
binding->tx_vec[niov_idx] = niov;
else
binding->freelist[binding->free_count++] = niov;
net_iov_free_push(&binding->area,
binding->freelist,
&binding->free_count, niov);
dma_addr += niov_size;
}
}
@ -439,19 +469,9 @@ netmem_ref mp_dmabuf_devmem_alloc_netmems(struct page_pool *pool, gfp_t gfp)
if (unlikely(!allocated))
return 0;
for (i = 0; i < allocated; i++) {
struct net_iov *niov = netmem_to_net_iov(netmems[i]);
niov->desc.pp_magic = 0;
niov->desc.pp = NULL;
atomic_long_set(&niov->desc.pp_ref_count, 0);
page_pool_set_pp_info(pool, netmems[i]);
pool->pages_state_hold_cnt++;
trace_page_pool_state_hold(pool, netmems[i],
pool->pages_state_hold_cnt);
}
for (i = 0; i < allocated; i++)
net_mp_niov_set_page_pool(pool,
netmem_to_net_iov(netmems[i]));
/* Return the last one, the rest stay in the page_pool cache. */
allocated--;
@ -476,8 +496,7 @@ bool mp_dmabuf_devmem_release_page(struct page_pool *pool, netmem_ref netmem)
if (WARN_ON_ONCE(refcount != 1))
return false;
page_pool_clear_pp_info(netmem);
net_mp_niov_clear_page_pool(netmem_to_net_iov(netmem));
net_devmem_free_dmabuf(netmem_to_net_iov(netmem));
/* We don't want the page pool put_page()ing our net_iovs. */

View File

@ -58,8 +58,8 @@ struct net_devmem_dmabuf_binding {
struct xarray bound_rxqs;
spinlock_t freelist_lock ____cacheline_aligned_in_smp;
size_t free_count;
struct net_iov **freelist;
u32 free_count;
u32 *freelist;
/* ID of this binding. Globally unique to all bindings currently
* active.

View File

@ -18,18 +18,8 @@ void page_pool_unlist(struct page_pool *pool);
static inline bool
page_pool_set_dma_addr_netmem(netmem_ref netmem, dma_addr_t addr)
{
if (PAGE_POOL_32BIT_ARCH_WITH_64BIT_DMA) {
netmem_set_dma_addr(netmem, addr >> PAGE_SHIFT);
/* We assume page alignment to shave off bottom bits,
* if this "compression" doesn't work we need to drop.
*/
return addr != (dma_addr_t)netmem_get_dma_addr(netmem)
<< PAGE_SHIFT;
}
netmem_set_dma_addr(netmem, addr);
return false;
netmem_set_dma_addr(netmem, netmem_dma_addr_encode(addr));
return !netmem_dma_addr_fits(addr);
}
static inline bool page_pool_set_dma_addr(struct page *page, dma_addr_t addr)