[PATCH net-next v2 1/3] net: netmem: move to index based freelist
From: Stanislav Fomichev
Date: Fri Sep 25 2026 - 16:20:51 EST
Move devmem's freelist to an index-based approach similar to io_uring.
This halves the memory requirements for the freelist.
Reviewed-by: Mina Almasry <almasrymina@xxxxxxxxxx>
Signed-off-by: Stanislav Fomichev <sdf@xxxxxxxxxxx>
---
net/core/devmem.c | 50 ++++++++++++++++++++++++++++++++++++-----------
net/core/devmem.h | 4 ++--
2 files changed, 41 insertions(+), 13 deletions(-)
diff --git a/net/core/devmem.c b/net/core/devmem.c
index a9d86b5a5588..704d02bd8bc8 100644
--- a/net/core/devmem.c
+++ b/net/core/devmem.c
@@ -29,6 +29,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 +67,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 +86,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 +108,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 +248,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) {
@@ -279,7 +305,9 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
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;
}
}
diff --git a/net/core/devmem.h b/net/core/devmem.h
index f71d9a2bcb3d..28d6fa7bcb57 100644
--- a/net/core/devmem.h
+++ b/net/core/devmem.h
@@ -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.
--
2.53.0-Meta