From: Stanislav Fomichev <sdf.kernel@gmail.com>
To: Mina Almasry <almasrymina@google.com>
Cc: Kaifeng Wang <kaifengw@google.com>,
netdev@vger.kernel.org, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
horms@kernel.org, sdf@fomichev.me, bobbyeshleman@meta.com,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH net-next 0/2] net: devmem: remove gen_pool from dma-buf allocations
Date: Wed, 2 Sep 2026 11:36:59 -0700 [thread overview]
Message-ID: <aphk-b52PGfESvjL@devvm7509.cco0.facebook.com> (raw)
In-Reply-To: <CAHS8izM5+CrZG77PxKpfG9k5kEWYMgN=uAuYvy21aabyztBt0w@mail.gmail.com>
On 08/31, Mina Almasry wrote:
> On Mon, Aug 31, 2026 at 11:35 AM Stanislav Fomichev
> <sdf.kernel@gmail.com> wrote:
> >
> > Replace devmem's gen_pool based fixed-size allocator with a binding-level
> > freelist similar to the one used by io_uring zero-copy receive.
> >
> > This is motivated by allocation latency observed in the NAPI receive path:
> >
> > [ 1036.228913] ? gen_pool_create+0x90/0x90
> > [ 1036.228915] net_devmem_alloc_dmabuf+0x1f/0x60
> > [ 1036.228918] mp_dmabuf_devmem_alloc_netmems+0x17/0x80
> > [ 1036.228920] mlx5e_post_rx_mpwqes+0xdbe/0xdd0
> > [ 1036.228926] mlx5e_napi_poll+0x113/0x830
> > [ 1036.228928] ? sched_clock+0x5/0x10
> > [ 1036.228931] ? wake_up_process+0x778/0x14b0
> > [ 1036.228933] net_rx_action+0x15d/0x570
> > [ 1036.228934] ? update_rq_clock+0x31/0x240
> > [ 1036.228937] ? __napi_schedule+0x55/0xa0
> > [ 1036.228938] ? mlx5_eq_comp_int+0x137/0x230
> > [ 1036.228940] ? atomic_notifier_call_chain+0x36/0x90
> > [ 1036.228943] ? sched_clock+0x5/0x10
> > [ 1036.228944] ? sched_clock_cpu+0xc/0x170
> > [ 1036.228947] irq_exit_rcu+0x12b/0x370
> > [ 1036.228950] common_interrupt+0x85/0x90
> >
> > udmabuf can create a very large number of SG entries. In the worst case,
> > devmem ends up adding one gen_pool chunk for each net_iov allocation
> > unit backed by those entries. The gen_pool allocation path then has to
> > traverse a linked list that can become too long for this hot path.
> >
> > Patch 1 removes the gen_pool and replaces it with a simple freelist of
> > net_iov pointers protected by the same spin_lock_bh() pattern used by
> > io_uring zcrx. Patch 2 removes the now-unnecessary chunk owner wrapper by
> > embedding the net_iov_area directly in the dma-buf binding.
> >
>
> Oh boy, this is going to be a bit tricky.
>
> I ran into this exact horrible perf bug (sorry for it in the first
> place), but my solution was different. My solution [1] was to coalesce
> the SG entries that are contigious (and they usually are in practice),
> and I got 'acceptable' perf after that. Kaifeng is actually working on
> cleaning up my hacky patch up to send it upstream now.
As you mention, coalescing might happen to work or it might not :-(
I'd like us to have something that's less probabilistic.
> Now I don't know which approach is better. Thinking about the pros and
> cons of your approach:
>
> + your approach is much simpler, and removes gen_pool overheads for a
> single queue case. It should be (much?) faster for that case.
> - your approach adds a lock and allocations from multiple queues in
> parallel will contend on this lock. There should be some value of # of
> queues N where your approach starts to completely trash. gen_pool is
> lockless so I wouldn't expect it to degrade significantly in the
> multi-queue case.
>
> The question for me is what the performance is for a real use case
> (NCCL all-to-all for example) over a realistic number of shared queues
> (it's 4-8 for me). I need that perf data to be honest before judging
> this.
>
> The io_uring zcrx comparision is not completely valid. io_uring zcrx
> is built from the ground up to be one-buffer-is-bound-to-one-rx-queue,
> and devmem tcp is built from the ground up to be
> one-buffer-can-be-bound-to-N-rx-queues.
What if we add batching similar to io_pp_zc_alloc_netmems? So we don't
have to spin lock on every netmem (and move PP_ALLOC_CACHE_REFILL-worth
of chunks). Untested, on top of this series:
diff --git a/net/core/devmem.c b/net/core/devmem.c
index 84d6c30516c8..5a1c996ba515 100644
--- a/net/core/devmem.c
+++ b/net/core/devmem.c
@@ -58,25 +58,25 @@ void __net_devmem_dmabuf_binding_free(struct work_struct *wq)
kfree(binding);
}
-struct net_iov *
-net_devmem_alloc_dmabuf(struct net_devmem_dmabuf_binding *binding)
+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);
- if (unlikely(!binding->free_count)) {
- spin_unlock_bh(&binding->freelist_lock);
- return NULL;
+
+ count = min_t(size_t, count, binding->free_count);
+ for (i = 0; i < count; i++) {
+ struct net_iov *niov = binding->freelist[--binding->free_count];
+
+ binding->freelist[binding->free_count] = NULL;
+ netmems[i] = net_iov_to_netmem(niov);
}
- niov = binding->freelist[--binding->free_count];
- binding->freelist[binding->free_count] = NULL;
spin_unlock_bh(&binding->freelist_lock);
- niov->desc.pp_magic = 0;
- niov->desc.pp = NULL;
- atomic_long_set(&niov->desc.pp_ref_count, 0);
-
- return niov;
+ return count;
}
void net_devmem_free_dmabuf(struct net_iov *niov)
@@ -433,20 +433,35 @@ int mp_dmabuf_devmem_init(struct page_pool *pool)
netmem_ref mp_dmabuf_devmem_alloc_netmems(struct page_pool *pool, gfp_t gfp)
{
struct net_devmem_dmabuf_binding *binding = pool->mp_priv;
- struct net_iov *niov;
- netmem_ref netmem;
+ netmem_ref *netmems = pool->alloc.cache;
+ unsigned int allocated, i;
+
+ if (WARN_ON_ONCE(pool->alloc.count))
+ return 0;
- niov = net_devmem_alloc_dmabuf(binding);
- if (!niov)
+ allocated = net_devmem_alloc_dmabuf_bulk(binding, netmems,
+ PP_ALLOC_CACHE_REFILL);
+ if (unlikely(!allocated))
return 0;
- netmem = net_iov_to_netmem(niov);
+ for (i = 0; i < allocated; i++) {
+ struct net_iov *niov = netmem_to_net_iov(netmems[i]);
- page_pool_set_pp_info(pool, netmem);
+ 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);
+ }
- pool->pages_state_hold_cnt++;
- trace_page_pool_state_hold(pool, netmem, pool->pages_state_hold_cnt);
- return netmem;
+ /* Return the last one, the rest stay in the page_pool cache. */
+ allocated--;
+ pool->alloc.count = allocated;
+ return netmems[allocated];
}
void mp_dmabuf_devmem_destroy(struct page_pool *pool)
diff --git a/net/core/devmem.h b/net/core/devmem.h
index 20a3eb90ea7f..7195769b8bd1 100644
--- a/net/core/devmem.h
+++ b/net/core/devmem.h
@@ -133,8 +133,6 @@ net_devmem_dmabuf_binding_put(struct net_devmem_dmabuf_binding *binding)
void net_devmem_get_net_iov(struct net_iov *niov);
void net_devmem_put_net_iov(struct net_iov *niov);
-struct net_iov *
-net_devmem_alloc_dmabuf(struct net_devmem_dmabuf_binding *binding);
void net_devmem_free_dmabuf(struct net_iov *ppiov);
@@ -191,12 +189,6 @@ net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx,
return -EOPNOTSUPP;
}
-static inline struct net_iov *
-net_devmem_alloc_dmabuf(struct net_devmem_dmabuf_binding *binding)
-{
- return NULL;
-}
-
static inline void net_devmem_free_dmabuf(struct net_iov *ppiov)
{
}
> Are you able to get NCCL all-to-all tests for N=4/8 yourself?
> Otherwise please wait for me to backport this to my release kernel and
> test it. ETA sometime this week, I hope.
I can definitely wait for you to access the perf impact on your side. Wonder
if we need to have a selftest to do that properly in NIPA. Doesn't have to
be a red/green signal, but some number for humans to compare
before-after (specifically this bind one dmabuf to multiple queues and pass
a lot of traffic). I can probably sketch something..
prev parent reply other threads:[~2026-09-02 18:37 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-31 18:35 Stanislav Fomichev
2026-08-31 18:35 ` [PATCH net-next 1/2] net: devmem: replace gen_pool with freelist Stanislav Fomichev
2026-09-04 2:22 ` Jakub Kicinski
2026-08-31 18:35 ` [PATCH net-next 2/2] net: devmem: embed net_iov_area in binding Stanislav Fomichev
2026-08-31 19:16 ` [PATCH net-next 0/2] net: devmem: remove gen_pool from dma-buf allocations Mina Almasry
2026-09-02 18:36 ` Stanislav Fomichev [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aphk-b52PGfESvjL@devvm7509.cco0.facebook.com \
--to=sdf.kernel@gmail.com \
--cc=almasrymina@google.com \
--cc=bobbyeshleman@meta.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=kaifengw@google.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=sdf@fomichev.me \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®