mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net v2] net: do not let defer_count outlive an empty defer_list
@ 2026-10-08 22:02 Joseph Kain
  2026-10-08 22:16 ` netdev-bot+sinfo
  0 siblings, 1 reply; 4+ messages in thread
From: Joseph Kain @ 2026-10-08 22:02 UTC (permalink / raw)
  To: netdev
  Cc: edumazet, davem, kuba, pabeni, horms, bigeasy, clrkwllms,
	rostedt, linux-rt-devel, linux-kernel, Joseph Kain

__skb_defer_free_flush() can leave defer_count at or above skb_defer_max
with an empty defer_list. That state is absorbing: every producer then
takes the nodefer path without queueing, so the list never refills, and
the flush keeps returning early without clearing. Deferred freeing stays
off for that (cpu, node) until reboot, and cross-CPU frees fall back to
the page allocator, which is the contention deferral exists to avoid.

Two changes from commit 844c9db7f7f5 ("net: use llist for sd->defer_list")
combine to produce it.

skb_attempt_defer_free() increments before testing the limit and does not
undo the increment when it rejects, so a rejected free leaves the counter
raised with nothing queued.

The flush clears defer_count before llist_del_all() rather than together
with it under defer_lock as it did before. A remote producer that queues
between the two has its skb drained by that same llist_del_all() but its
increment retained.

That part is not cumulative: any later flush finding a non-empty list
clears the counter again, so lost increments do not build up across
flushes. Reaching the limit takes skb_defer_max - 1 queued increments
inside a single clear-to-drain window. At a small skb_defer_max that
needs nothing special. At the default of 128 the flusher has to be
interrupted or preempted between those two lines for long enough, which
is where PREEMPT_RT matters: local_bh_disable() does not disable
preemption there, so the flusher can be scheduled out at that point.

Once the counter is stuck at or above the limit with an empty list,
nothing lowers it again, and that half needs no race at all:

  sysctl -w net.core.skb_defer_max=1    # frees increment, then reject
  sysctl -w net.core.skb_defer_max=128  # does not recover

skb_defer_max has .extra1 = SYSCTL_ZERO and no .extra2, so 1 is settable.
From v7.1 on, 0 is safe because proc_do_skb_defer_max() flips
skb_defer_disable_key and the producer returns before incrementing. On
6.18 through 7.0, which carry the llist conversion but not commit
08dc30de1a40 ("net: add skb_defer_disable_key static key"), setting 0 and
restoring the previous value strands the counter the same way -- and 0 is
the documented way to turn the feature off. Recovering needs a
skb_defer_max above the accumulated count, which an operator has no way
to determine.

Drain before clearing, so the error can only under-read, is bounded by
the producers in flight, and self-heals on the next flush. Clear the
counter on the empty-list path as well, so no count can outlive an empty
list and an already-stranded counter recovers.

llist_empty() has already loaded that cache line, so the added read is
free; the unlikely() keeps the store off the hot path, where it would
otherwise bounce the line against remote producers.

Fixes: 844c9db7f7f5 ("net: use llist for sd->defer_list")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Joseph Kain <jkain@nuro.ai>
---
v2:
 - Reworded the drift description: it is not cumulative, since any later
   flush finding a non-empty list clears the counter again. Reaching the
   limit takes skb_defer_max - 1 queued increments inside a single
   clear-to-drain window, and said where PREEMPT_RT actually matters
   (Eric Dumazet).
 - Noted that 6.18..7.0 strand the counter via skb_defer_max=0, the
   documented way to disable the feature, since they carry the llist
   conversion but not 08dc30de1a40 (Eric Dumazet).
 - Dropped the paragraph describing where this was first reproduced
   (Eric Dumazet).
 - Added Assisted-by.

Built for x86_64 and arm64, with and without CONFIG_PREEMPT_RT. On
v7.3-rc6 with skb_defer_max=1 and a cross-CPU TCP load, defer_count
reached 1918118 unpatched and did not recover when the default was
restored; with this patch it peaked at 1.

v1: https://lore.kernel.org/netdev/20261007180056.1394868-1-jkain@nuro.ai/
 net/core/dev.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/net/core/dev.c b/net/core/dev.c
index 18dc88990510..043a7f9166a9 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -6938,10 +6938,13 @@ static void __skb_defer_free_flush(struct skb_defer_node *sdn, int budget)
 	struct llist_node *free_list;
 	struct sk_buff *skb, *next;
 
-	if (llist_empty(&sdn->defer_list))
+	if (llist_empty(&sdn->defer_list)) {
+		if (unlikely(atomic_long_read(&sdn->defer_count)))
+			atomic_long_set(&sdn->defer_count, 0);
 		return;
-	atomic_long_set(&sdn->defer_count, 0);
+	}
 	free_list = llist_del_all(&sdn->defer_list);
+	atomic_long_set(&sdn->defer_count, 0);
 
 	llist_for_each_entry_safe(skb, next, free_list, ll_node) {
 		prefetch(next);
-- 
2.55.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH net v2] net: do not let defer_count outlive an empty defer_list
  2026-10-08 22:02 [PATCH net v2] net: do not let defer_count outlive an empty defer_list Joseph Kain
@ 2026-10-08 22:16 ` netdev-bot+sinfo
  2026-10-08 22:23   ` Joseph Kain
  0 siblings, 1 reply; 4+ messages in thread
From: netdev-bot+sinfo @ 2026-10-08 22:16 UTC (permalink / raw)
  To: Joseph Kain
  Cc: netdev, edumazet, davem, kuba, pabeni, horms, bigeasy, clrkwllms,
	rostedt, linux-rt-devel, linux-kernel

Hi!

This is an automated message. This series looks like a fix, but its
commit messages seem to be missing some information:

 - How the issue was discovered, e.g. hit in production, hit during
   development, syzbot report, manual code inspection, LLM or static
   analysis tool scan.

Please do not repost the series just to address the above. Instead,
reply to this email with the missing information, so that reviewers
can take it into account. If the series needs another revision for
other reasons, please include the information in the commit messages
then.

The evaluation is done by an LLM so it may be wrong, if you think
that is the case please reply and explain.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH net v2] net: do not let defer_count outlive an empty defer_list
  2026-10-08 22:16 ` netdev-bot+sinfo
@ 2026-10-08 22:23   ` Joseph Kain
  2026-10-08 22:24     ` Joseph Kain
  0 siblings, 1 reply; 4+ messages in thread
From: Joseph Kain @ 2026-10-08 22:23 UTC (permalink / raw)
  To: netdev-bot+sinfo
  Cc: netdev, edumazet, davem, kuba, pabeni, horms, bigeasy, clrkwllms,
	rostedt, linux-rt-devel, linux-kernel

On Thu, Oct 8, 2026 at 3:16 PM <netdev-bot+sinfo@kernel.org> wrote:
>
> Hi!
>
> This is an automated message. This series looks like a fix, but its
> commit messages seem to be missing some information:
>
>  - How the issue was discovered, e.g. hit in production, hit during
>    development, syzbot report, manual code inspection, LLM or static
>    analysis tool scan.

  Manual code inspection, LLM-assisted, during a backport -- not hit in
  production.  I hit the heavy contention on sd->defer_lock in
production and had Claude Opus

  The context: an arm64 PREEMPT_RT system hit the sd->defer_lock convoy that
  844c9db7f7f5 removes, where a descheduled lock holder queued every other
  consumer of that NIC's skbs behind it. While backporting that conversion
  to 6.1 I read through the new code and found the absorbing state this
  patch fixes.


>
> Please do not repost the series just to address the above. Instead,
> reply to this email with the missing information, so that reviewers
> can take it into account. If the series needs another revision for
> other reasons, please include the information in the commit messages
> then.
>
> The evaluation is done by an LLM so it may be wrong, if you think
> that is the case please reply and explain.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH net v2] net: do not let defer_count outlive an empty defer_list
  2026-10-08 22:23   ` Joseph Kain
@ 2026-10-08 22:24     ` Joseph Kain
  0 siblings, 0 replies; 4+ messages in thread
From: Joseph Kain @ 2026-10-08 22:24 UTC (permalink / raw)
  To: netdev-bot+sinfo
  Cc: netdev, edumazet, davem, kuba, pabeni, horms, bigeasy, clrkwllms,
	rostedt, linux-rt-devel, linux-kernel

On Thu, Oct 8, 2026 at 3:23 PM Joseph Kain <jkain@nuro.ai> wrote:
>
> On Thu, Oct 8, 2026 at 3:16 PM <netdev-bot+sinfo@kernel.org> wrote:
> >
> > Hi!
> >
> > This is an automated message. This series looks like a fix, but its
> > commit messages seem to be missing some information:
> >
> >  - How the issue was discovered, e.g. hit in production, hit during
> >    development, syzbot report, manual code inspection, LLM or static
> >    analysis tool scan.
>
>   Manual code inspection, LLM-assisted, during a backport -- not hit in
>   production.  I hit the heavy contention on sd->defer_lock in
> production and had Claude Opus


assess backporting.  In the process it found this issue.

>
>
> >
> > Please do not repost the series just to address the above. Instead,
> > reply to this email with the missing information, so that reviewers
> > can take it into account. If the series needs another revision for
> > other reasons, please include the information in the commit messages
> > then.
> >
> > The evaluation is done by an LLM so it may be wrong, if you think
> > that is the case please reply and explain.



-- 
Thanks,
Joseph Kain

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-10-08 22:24 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-08 22:02 [PATCH net v2] net: do not let defer_count outlive an empty defer_list Joseph Kain
2026-10-08 22:16 ` netdev-bot+sinfo
2026-10-08 22:23   ` Joseph Kain
2026-10-08 22:24     ` Joseph Kain

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®