* [PATCH 01/15] Revert "net: Avoid the IPI to free the"
2026-10-13 17:58 [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush wen.yang
@ 2026-10-13 17:58 ` wen.yang
2026-10-13 17:58 ` [PATCH 02/15] net: avoid irqsave in skb_defer_free_flush wen.yang
` (14 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: wen.yang @ 2026-10-13 17:58 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Eric Dumazet, Jesper Dangaard Brouer, Jakub Kicinski,
Paolo Abeni, linux-rt-devel, linux-kernel, Wen Yang
From: Wen Yang <wen.yang@linux.dev>
This reverts commit 8d152d22d9f056f87875f97aec016a65622abbf0.
That commit is an RT-only workaround. skb_attempt_defer_free() collects
skbs allocated on a remote CPU on a per-CPU list and, once the list passes
half its watermark, pokes that CPU with an IPI to raise NET_RX_SOFTIRQ so
the list gets drained. On PREEMPT_RT that IPI wakes ksoftirqd from hardirq
context, which is undesirable, so the commit redirected the deferred skb
recycling to a workqueue (sd->defer_work) instead.
It is reverted here for two reasons.
First, it is incomplete. It only covers the defer_list path. RPS steering
still pokes the remote CPU with an IPI from napi_schedule_rps(), and that
IPI handler (rps_trigger_softirq) still calls ____napi_schedule() ->
raise_softirq_irqoff(NET_RX_SOFTIRQ). That is the path that trips
do_softirq_post_smp_call_flush() on PREEMPT_RT, and the workaround does
nothing about it.
Second, it is superseded. The following patches backport the upstream
backlog-NAPI smpboot threads (dad6b9770263 and friends). With those,
use_backlog_threads() is unconditionally true on PREEMPT_RT, so both the
cross-CPU backlog enqueue and the defer_list purge wake a per-CPU
"backlog_napi/%u" thread with wake_up_process() instead of sending an IPI.
Deferred skbs are then freed from the same NAPI loop that is already going
to run, without the workqueue round-trip.
Reverting first restores the upstream 6.1 code shape so the rest of the
series applies unmodified.
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
include/linux/netdevice.h | 4 ----
net/core/dev.c | 37 +++++++++----------------------------
net/core/skbuff.c | 7 +------
3 files changed, 10 insertions(+), 38 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index fbd3b17463da..e97967ca2760 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3199,11 +3199,7 @@ struct softnet_data {
int defer_count;
int defer_ipi_scheduled;
struct sk_buff *defer_list;
-#ifndef CONFIG_PREEMPT_RT
call_single_data_t defer_csd;
-#else
- struct work_struct defer_work;
-#endif
};
static inline void input_queue_head_incr(struct softnet_data *sd)
diff --git a/net/core/dev.c b/net/core/dev.c
index 5d6ca8510227..f44ffac6cba6 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4712,6 +4712,15 @@ static void rps_trigger_softirq(void *data)
#endif /* CONFIG_RPS */
+/* Called from hardirq (IPI) context */
+static void trigger_rx_softirq(void *data)
+{
+ struct softnet_data *sd = data;
+
+ __raise_softirq_irqoff(NET_RX_SOFTIRQ);
+ smp_store_release(&sd->defer_ipi_scheduled, 0);
+}
+
/*
* Check if this softnet_data structure is another cpu one
* If yes, queue it to our IPI list and return 1
@@ -6772,30 +6781,6 @@ static void skb_defer_free_flush(struct softnet_data *sd)
}
}
-#ifndef CONFIG_PREEMPT_RT
-/* Called from hardirq (IPI) context */
-static void trigger_rx_softirq(void *data)
-{
- struct softnet_data *sd = data;
-
- __raise_softirq_irqoff(NET_RX_SOFTIRQ);
- smp_store_release(&sd->defer_ipi_scheduled, 0);
-}
-
-#else
-
-static void trigger_rx_softirq(struct work_struct *defer_work)
-{
- struct softnet_data *sd;
-
- sd = container_of(defer_work, struct softnet_data, defer_work);
- smp_store_release(&sd->defer_ipi_scheduled, 0);
- local_bh_disable();
- skb_defer_free_flush(sd);
- local_bh_enable();
-}
-#endif
-
static __latent_entropy void net_rx_action(struct softirq_action *h)
{
struct softnet_data *sd = this_cpu_ptr(&softnet_data);
@@ -11620,11 +11605,7 @@ static int __init net_dev_init(void)
INIT_CSD(&sd->csd, rps_trigger_softirq, sd);
sd->cpu = i;
#endif
-#ifndef CONFIG_PREEMPT_RT
INIT_CSD(&sd->defer_csd, trigger_rx_softirq, sd);
-#else
- INIT_WORK(&sd->defer_work, trigger_rx_softirq);
-#endif
spin_lock_init(&sd->defer_lock);
init_gro_hash(&sd->backlog);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 665be0ad4b8d..a66066bc9861 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -6746,11 +6746,6 @@ nodefer: __kfree_skb(skb);
/* Make sure to trigger NET_RX_SOFTIRQ on the remote CPU
* if we are unlucky enough (this seems very unlikely).
*/
- if (unlikely(kick) && !cmpxchg(&sd->defer_ipi_scheduled, 0, 1)) {
-#ifndef CONFIG_PREEMPT_RT
+ if (unlikely(kick) && !cmpxchg(&sd->defer_ipi_scheduled, 0, 1))
smp_call_function_single_async(cpu, &sd->defer_csd);
-#else
- schedule_work_on(cpu, &sd->defer_work);
-#endif
- }
}
--
2.25.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 02/15] net: avoid irqsave in skb_defer_free_flush
2026-10-13 17:58 [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush wen.yang
2026-10-13 17:58 ` [PATCH 01/15] Revert "net: Avoid the IPI to free the" wen.yang
@ 2026-10-13 17:58 ` wen.yang
2026-10-13 17:58 ` [PATCH 03/15] net: napi_schedule_rps() cleanup wen.yang
` (13 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: wen.yang @ 2026-10-13 17:58 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Eric Dumazet, Jesper Dangaard Brouer, Jakub Kicinski,
Paolo Abeni, linux-rt-devel, linux-kernel, Jacob Keller,
Wen Yang
From: Jesper Dangaard Brouer <brouer@redhat.com>
commit 3176eb82681ec9c8af31c6588ddedcc6cfb9e445 upstream.
The spin_lock irqsave/restore API variant in skb_defer_free_flush can
be replaced with the faster spin_lock irq variant, which doesn't need
to read and restore the CPU flags.
Using the unconditional irq "disable/enable" API variant is safe,
because the skb_defer_free_flush() function is only called during
NAPI-RX processing in net_rx_action(), where it is known the IRQs
are enabled.
Expected gain is 14 cycles from avoiding reading and restoring CPU
flags in a spin_lock_irqsave/restore operation, measured via a
microbencmark kernel module[1] on CPU E5-1650 v4 @ 3.60GHz.
Microbenchmark overhead of spin_lock+unlock:
- spin_lock_unlock_irq cost: 34 cycles(tsc) 9.486 ns
- spin_lock_unlock_irqsave cost: 48 cycles(tsc) 13.567 ns
We don't expect to see a measurable packet performance gain, as
skb_defer_free_flush() is called infrequently once per NIC device NAPI
bulk cycle and conditionally only if SKBs have been deferred by other
CPUs via skb_attempt_defer_free().
[1] https://github.com/netoptimizer/prototype-kernel/blob/master/kernel/lib/time_bench_sample.c
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Link: https://lore.kernel.org/r/167421646327.1321776.7390743166998776914.stgit@firesoul
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
net/core/dev.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index f44ffac6cba6..efe483d7e03f 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -6762,17 +6762,16 @@ static int napi_threaded_poll(void *data)
static void skb_defer_free_flush(struct softnet_data *sd)
{
struct sk_buff *skb, *next;
- unsigned long flags;
/* Paired with WRITE_ONCE() in skb_attempt_defer_free() */
if (!READ_ONCE(sd->defer_list))
return;
- spin_lock_irqsave(&sd->defer_lock, flags);
+ spin_lock_irq(&sd->defer_lock);
skb = sd->defer_list;
sd->defer_list = NULL;
sd->defer_count = 0;
- spin_unlock_irqrestore(&sd->defer_lock, flags);
+ spin_unlock_irq(&sd->defer_lock);
while (skb != NULL) {
next = skb->next;
--
2.25.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 03/15] net: napi_schedule_rps() cleanup
2026-10-13 17:58 [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush wen.yang
2026-10-13 17:58 ` [PATCH 01/15] Revert "net: Avoid the IPI to free the" wen.yang
2026-10-13 17:58 ` [PATCH 02/15] net: avoid irqsave in skb_defer_free_flush wen.yang
@ 2026-10-13 17:58 ` wen.yang
2026-10-13 17:58 ` [PATCH 04/15] net: add softnet_data.in_net_rx_action wen.yang
` (12 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: wen.yang @ 2026-10-13 17:58 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Eric Dumazet, Jesper Dangaard Brouer, Jakub Kicinski,
Paolo Abeni, linux-rt-devel, linux-kernel, Jason Xing, Wen Yang
From: Eric Dumazet <edumazet@google.com>
napi_schedule_rps() return value is ignored, remove it.
commit 8fcb76b934daff12cde76adeab3d502eeb0734b1 upstream.
Change the comment to clarify the intent.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Jason Xing <kerneljasonxing@gmail.com>
Tested-by: Jason Xing <kerneljasonxing@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
net/core/dev.c | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index efe483d7e03f..36dbb86496ec 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4722,11 +4722,18 @@ static void trigger_rx_softirq(void *data)
}
/*
- * Check if this softnet_data structure is another cpu one
- * If yes, queue it to our IPI list and return 1
- * If no, return 0
+ * After we queued a packet into sd->input_pkt_queue,
+ * we need to make sure this queue is serviced soon.
+ *
+ * - If this is another cpu queue, link it to our rps_ipi_list,
+ * and make sure we will process rps_ipi_list from net_rx_action().
+ * As we do not know yet if we are called from net_rx_action(),
+ * we have to raise NET_RX_SOFTIRQ. This might change in the future.
+ *
+ * - If this is our own queue, NAPI schedule our backlog.
+ * Note that this also raises NET_RX_SOFTIRQ.
*/
-static int napi_schedule_rps(struct softnet_data *sd)
+static void napi_schedule_rps(struct softnet_data *sd)
{
struct softnet_data *mysd = this_cpu_ptr(&softnet_data);
@@ -4736,11 +4743,10 @@ static int napi_schedule_rps(struct softnet_data *sd)
mysd->rps_ipi_list = sd;
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
- return 1;
+ return;
}
#endif /* CONFIG_RPS */
__napi_schedule_irqoff(&mysd->backlog);
- return 0;
}
#ifdef CONFIG_NET_FLOW_LIMIT
--
2.25.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 04/15] net: add softnet_data.in_net_rx_action
2026-10-13 17:58 [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush wen.yang
` (2 preceding siblings ...)
2026-10-13 17:58 ` [PATCH 03/15] net: napi_schedule_rps() cleanup wen.yang
@ 2026-10-13 17:58 ` wen.yang
2026-10-13 17:58 ` [PATCH 05/15] net: optimize napi_schedule_rps() wen.yang
` (11 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: wen.yang @ 2026-10-13 17:58 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Eric Dumazet, Jesper Dangaard Brouer, Jakub Kicinski,
Paolo Abeni, linux-rt-devel, linux-kernel, Jason Xing, Wen Yang
From: Eric Dumazet <edumazet@google.com>
commit c59647c0dc679008886756a888368da1c6d4ccd3 upstream.
We want to make two optimizations in napi_schedule_rps() and
____napi_schedule() which require to know if these helpers are
called from net_rx_action(), instead of being called from
other contexts.
sd.in_net_rx_action is only read/written by the owning cpu.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Jason Xing <kerneljasonxing@gmail.com>
Tested-by: Jason Xing <kerneljasonxing@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
include/linux/netdevice.h | 1 +
net/core/dev.c | 4 ++++
2 files changed, 5 insertions(+)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index e97967ca2760..987177cebe7c 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3161,6 +3161,7 @@ struct softnet_data {
#ifdef CONFIG_RPS
struct softnet_data *rps_ipi_list;
#endif
+ bool in_net_rx_action;
#ifdef CONFIG_NET_FLOW_LIMIT
struct sd_flow_limit __rcu *flow_limit;
#endif
diff --git a/net/core/dev.c b/net/core/dev.c
index 36dbb86496ec..a46caebb06f7 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -6795,6 +6795,7 @@ static __latent_entropy void net_rx_action(struct softirq_action *h)
LIST_HEAD(list);
LIST_HEAD(repoll);
+ sd->in_net_rx_action = true;
local_irq_disable();
list_splice_init(&sd->poll_list, &list);
local_irq_enable();
@@ -6805,6 +6806,7 @@ static __latent_entropy void net_rx_action(struct softirq_action *h)
skb_defer_free_flush(sd);
if (list_empty(&list)) {
+ sd->in_net_rx_action = false;
if (!sd_has_rps_ipi_waiting(sd) && list_empty(&repoll))
goto end;
break;
@@ -6831,6 +6833,8 @@ static __latent_entropy void net_rx_action(struct softirq_action *h)
list_splice(&list, &sd->poll_list);
if (!list_empty(&sd->poll_list))
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
+ else
+ sd->in_net_rx_action = false;
net_rps_action_and_irq_enable(sd);
end:;
--
2.25.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 05/15] net: optimize napi_schedule_rps()
2026-10-13 17:58 [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush wen.yang
` (3 preceding siblings ...)
2026-10-13 17:58 ` [PATCH 04/15] net: add softnet_data.in_net_rx_action wen.yang
@ 2026-10-13 17:58 ` wen.yang
2026-10-13 17:58 ` [PATCH 06/15] net: optimize ____napi_schedule() to avoid extra NET_RX_SOFTIRQ wen.yang
` (10 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: wen.yang @ 2026-10-13 17:58 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Eric Dumazet, Jesper Dangaard Brouer, Jakub Kicinski,
Paolo Abeni, linux-rt-devel, linux-kernel, Jason Xing,
Jason Xing, Wen Yang
From: Eric Dumazet <edumazet@google.com>
commit 821eba962d95806beb0440742c4062a9da8a386b upstream.
Based on initial patch from Jason Xing.
Idea is to not raise NET_RX_SOFTIRQ from napi_schedule_rps()
when we queued a packet into another cpu backlog.
We can do this only in the context of us being called indirectly
from net_rx_action(), to have the guarantee our rps_ipi_list
will be processed before we exit from net_rx_action().
Link: https://lore.kernel.org/lkml/20230325152417.5403-1-kerneljasonxing@gmail.com/
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Jason Xing <kernelxing@tencent.com>
Reviewed-by: Jason Xing <kerneljasonxing@gmail.com>
Tested-by: Jason Xing <kerneljasonxing@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
net/core/dev.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index a46caebb06f7..10a8883ae490 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4727,8 +4727,6 @@ static void trigger_rx_softirq(void *data)
*
* - If this is another cpu queue, link it to our rps_ipi_list,
* and make sure we will process rps_ipi_list from net_rx_action().
- * As we do not know yet if we are called from net_rx_action(),
- * we have to raise NET_RX_SOFTIRQ. This might change in the future.
*
* - If this is our own queue, NAPI schedule our backlog.
* Note that this also raises NET_RX_SOFTIRQ.
@@ -4742,7 +4740,11 @@ static void napi_schedule_rps(struct softnet_data *sd)
sd->rps_ipi_next = mysd->rps_ipi_list;
mysd->rps_ipi_list = sd;
- __raise_softirq_irqoff(NET_RX_SOFTIRQ);
+ /* If not called from net_rx_action()
+ * we have to raise NET_RX_SOFTIRQ.
+ */
+ if (!mysd->in_net_rx_action)
+ __raise_softirq_irqoff(NET_RX_SOFTIRQ);
return;
}
#endif /* CONFIG_RPS */
--
2.25.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 06/15] net: optimize ____napi_schedule() to avoid extra NET_RX_SOFTIRQ
2026-10-13 17:58 [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush wen.yang
` (4 preceding siblings ...)
2026-10-13 17:58 ` [PATCH 05/15] net: optimize napi_schedule_rps() wen.yang
@ 2026-10-13 17:58 ` wen.yang
2026-10-13 17:58 ` [PATCH 07/15] net: add debugging checks in skb_attempt_defer_free() wen.yang
` (9 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: wen.yang @ 2026-10-13 17:58 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Eric Dumazet, Jesper Dangaard Brouer, Jakub Kicinski,
Paolo Abeni, linux-rt-devel, linux-kernel, Jason Xing,
Jason Xing, Wen Yang
From: Eric Dumazet <edumazet@google.com>
commit 8b43fd3d1d7d88293eb15e92090826e6b7cc13e4 upstream.
____napi_schedule() adds a napi into current cpu softnet_data poll_list,
then raises NET_RX_SOFTIRQ to make sure net_rx_action() will process it.
Idea of this patch is to not raise NET_RX_SOFTIRQ when being called indirectly
from net_rx_action(), because we can process poll_list from this point,
without going to full softirq loop.
This needs a change in net_rx_action() to make sure we restart
its main loop if sd->poll_list was updated without NET_RX_SOFTIRQ
being raised.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Jason Xing <kernelxing@tencent.com>
Reviewed-by: Jason Xing <kerneljasonxing@gmail.com>
Tested-by: Jason Xing <kerneljasonxing@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
net/core/dev.c | 22 ++++++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index 10a8883ae490..db1fe85ebc90 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4498,7 +4498,11 @@ static inline void ____napi_schedule(struct softnet_data *sd,
}
list_add_tail(&napi->poll_list, &sd->poll_list);
- raise_softirq_irqoff(NET_RX_SOFTIRQ);
+ /* If not called from net_rx_action()
+ * we have to raise NET_RX_SOFTIRQ.
+ */
+ if (!sd->in_net_rx_action)
+ raise_softirq_irqoff(NET_RX_SOFTIRQ);
}
#ifdef CONFIG_RPS
@@ -6797,6 +6801,7 @@ static __latent_entropy void net_rx_action(struct softirq_action *h)
LIST_HEAD(list);
LIST_HEAD(repoll);
+start:
sd->in_net_rx_action = true;
local_irq_disable();
list_splice_init(&sd->poll_list, &list);
@@ -6808,9 +6813,18 @@ static __latent_entropy void net_rx_action(struct softirq_action *h)
skb_defer_free_flush(sd);
if (list_empty(&list)) {
- sd->in_net_rx_action = false;
- if (!sd_has_rps_ipi_waiting(sd) && list_empty(&repoll))
- goto end;
+ if (list_empty(&repoll)) {
+ sd->in_net_rx_action = false;
+ barrier();
+ /* We need to check if ____napi_schedule()
+ * had refilled poll_list while
+ * sd->in_net_rx_action was true.
+ */
+ if (!list_empty(&sd->poll_list))
+ goto start;
+ if (!sd_has_rps_ipi_waiting(sd))
+ goto end;
+ }
break;
}
--
2.25.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 07/15] net: add debugging checks in skb_attempt_defer_free()
2026-10-13 17:58 [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush wen.yang
` (5 preceding siblings ...)
2026-10-13 17:58 ` [PATCH 06/15] net: optimize ____napi_schedule() to avoid extra NET_RX_SOFTIRQ wen.yang
@ 2026-10-13 17:58 ` wen.yang
2026-10-13 17:58 ` [PATCH 08/15] net: do not provide hard irq safety for sd->defer_lock wen.yang
` (8 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: wen.yang @ 2026-10-13 17:58 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Eric Dumazet, Jesper Dangaard Brouer, Jakub Kicinski,
Paolo Abeni, linux-rt-devel, linux-kernel, David S . Miller,
Wen Yang
From: Eric Dumazet <edumazet@google.com>
commit e8e1ce8454c9cc8ad2e4422bef346428e52455e3 upstream.
Make sure skbs that are stored in softnet_data.defer_list
do not have a dst attached.
Also make sure the the skb was orphaned.
Link: https://lore.kernel.org/netdev/CANn89iJuEVe72bPmEftyEJHLzzN=QNR2yueFjTxYXCEpS5S8HQ@mail.gmail.com/T/
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
net/core/skbuff.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index a66066bc9861..919ad5a2c5da 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -6727,6 +6727,9 @@ nodefer: __kfree_skb(skb);
return;
}
+ DEBUG_NET_WARN_ON_ONCE(skb_dst(skb));
+ DEBUG_NET_WARN_ON_ONCE(skb->destructor);
+
sd = &per_cpu(softnet_data, cpu);
defer_max = READ_ONCE(sysctl_skb_defer_max);
if (READ_ONCE(sd->defer_count) >= defer_max)
--
2.25.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 08/15] net: do not provide hard irq safety for sd->defer_lock
2026-10-13 17:58 [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush wen.yang
` (6 preceding siblings ...)
2026-10-13 17:58 ` [PATCH 07/15] net: add debugging checks in skb_attempt_defer_free() wen.yang
@ 2026-10-13 17:58 ` wen.yang
2026-10-13 17:58 ` [PATCH 09/15] net: move skb_defer_free_flush() up wen.yang
` (7 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: wen.yang @ 2026-10-13 17:58 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Eric Dumazet, Jesper Dangaard Brouer, Jakub Kicinski,
Paolo Abeni, linux-rt-devel, linux-kernel, David S . Miller,
Wen Yang
From: Eric Dumazet <edumazet@google.com>
commit 931e93bdf8ca71cef1f8759c43bc2c5385392b8b upstream.
kfree_skb() can be called from hard irq handlers,
but skb_attempt_defer_free() is meant to be used
from process or BH contexts, and skb_defer_free_flush()
is meant to be called from BH contexts.
Not having to mask hard irq can save some cycles.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
net/core/dev.c | 4 ++--
net/core/skbuff.c | 5 ++---
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index db1fe85ebc90..c8789241f4a7 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -6779,11 +6779,11 @@ static void skb_defer_free_flush(struct softnet_data *sd)
if (!READ_ONCE(sd->defer_list))
return;
- spin_lock_irq(&sd->defer_lock);
+ spin_lock(&sd->defer_lock);
skb = sd->defer_list;
sd->defer_list = NULL;
sd->defer_count = 0;
- spin_unlock_irq(&sd->defer_lock);
+ spin_unlock(&sd->defer_lock);
while (skb != NULL) {
next = skb->next;
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 919ad5a2c5da..6111c1bba704 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -6716,7 +6716,6 @@ void skb_attempt_defer_free(struct sk_buff *skb)
{
int cpu = skb->alloc_cpu;
struct softnet_data *sd;
- unsigned long flags;
unsigned int defer_max;
bool kick;
@@ -6735,7 +6734,7 @@ nodefer: __kfree_skb(skb);
if (READ_ONCE(sd->defer_count) >= defer_max)
goto nodefer;
- spin_lock_irqsave(&sd->defer_lock, flags);
+ spin_lock_bh(&sd->defer_lock);
/* Send an IPI every time queue reaches half capacity. */
kick = sd->defer_count == (defer_max >> 1);
/* Paired with the READ_ONCE() few lines above */
@@ -6744,7 +6743,7 @@ nodefer: __kfree_skb(skb);
skb->next = sd->defer_list;
/* Paired with READ_ONCE() in skb_defer_free_flush() */
WRITE_ONCE(sd->defer_list, skb);
- spin_unlock_irqrestore(&sd->defer_lock, flags);
+ spin_unlock_bh(&sd->defer_lock);
/* Make sure to trigger NET_RX_SOFTIRQ on the remote CPU
* if we are unlucky enough (this seems very unlikely).
--
2.25.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 09/15] net: move skb_defer_free_flush() up
2026-10-13 17:58 [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush wen.yang
` (7 preceding siblings ...)
2026-10-13 17:58 ` [PATCH 08/15] net: do not provide hard irq safety for sd->defer_lock wen.yang
@ 2026-10-13 17:58 ` wen.yang
2026-10-13 18:02 ` [PATCH 10/15] net: make napi_threaded_poll() aware of sd->defer_list wen.yang
` (6 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: wen.yang @ 2026-10-13 17:58 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Eric Dumazet, Jesper Dangaard Brouer, Jakub Kicinski,
Paolo Abeni, linux-rt-devel, linux-kernel, David S . Miller,
Wen Yang
From: Eric Dumazet <edumazet@google.com>
commit e6f50edfef046cf8ad541b4d5972bf38fdcdec39 upstream.
We plan using skb_defer_free_flush() from napi_threaded_poll()
in the following patch.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
net/core/dev.c | 42 +++++++++++++++++++++---------------------
1 file changed, 21 insertions(+), 21 deletions(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index c8789241f4a7..df7df594e7f0 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -6742,6 +6742,27 @@ static int napi_thread_wait(struct napi_struct *napi)
return -1;
}
+static void skb_defer_free_flush(struct softnet_data *sd)
+{
+ struct sk_buff *skb, *next;
+
+ /* Paired with WRITE_ONCE() in skb_attempt_defer_free() */
+ if (!READ_ONCE(sd->defer_list))
+ return;
+
+ spin_lock(&sd->defer_lock);
+ skb = sd->defer_list;
+ sd->defer_list = NULL;
+ sd->defer_count = 0;
+ spin_unlock(&sd->defer_lock);
+
+ while (skb != NULL) {
+ next = skb->next;
+ napi_consume_skb(skb, 1);
+ skb = next;
+ }
+}
+
static int napi_threaded_poll(void *data)
{
struct napi_struct *napi = data;
@@ -6771,27 +6792,6 @@ static int napi_threaded_poll(void *data)
return 0;
}
-static void skb_defer_free_flush(struct softnet_data *sd)
-{
- struct sk_buff *skb, *next;
-
- /* Paired with WRITE_ONCE() in skb_attempt_defer_free() */
- if (!READ_ONCE(sd->defer_list))
- return;
-
- spin_lock(&sd->defer_lock);
- skb = sd->defer_list;
- sd->defer_list = NULL;
- sd->defer_count = 0;
- spin_unlock(&sd->defer_lock);
-
- while (skb != NULL) {
- next = skb->next;
- napi_consume_skb(skb, 1);
- skb = next;
- }
-}
-
static __latent_entropy void net_rx_action(struct softirq_action *h)
{
struct softnet_data *sd = this_cpu_ptr(&softnet_data);
--
2.25.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 10/15] net: make napi_threaded_poll() aware of sd->defer_list
2026-10-13 17:58 [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush wen.yang
` (8 preceding siblings ...)
2026-10-13 17:58 ` [PATCH 09/15] net: move skb_defer_free_flush() up wen.yang
@ 2026-10-13 18:02 ` wen.yang
2026-10-13 18:02 ` [PATCH 11/15] net: optimize napi_threaded_poll() vs RPS/RFS wen.yang
` (5 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: wen.yang @ 2026-10-13 18:02 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Eric Dumazet, Jesper Dangaard Brouer, Jakub Kicinski,
Paolo Abeni, linux-rt-devel, linux-kernel, David S . Miller,
Wen Yang
From: Eric Dumazet <edumazet@google.com>
commit a1aaee7f8f79d1b0595e24f8c3caed24630d6cb6 upstream.
If we call skb_defer_free_flush() from napi_threaded_poll(),
we can avoid to raise IPI from skb_attempt_defer_free()
when the list becomes too big.
This allows napi_threaded_poll() to rely less on softirqs,
and lowers latency caused by a too big list.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
net/core/dev.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/net/core/dev.c b/net/core/dev.c
index df7df594e7f0..cac16e17ccf9 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -6766,6 +6766,7 @@ static void skb_defer_free_flush(struct softnet_data *sd)
static int napi_threaded_poll(void *data)
{
struct napi_struct *napi = data;
+ struct softnet_data *sd;
void *have;
while (!napi_thread_wait(napi)) {
@@ -6775,11 +6776,13 @@ static int napi_threaded_poll(void *data)
bool repoll = false;
local_bh_disable();
+ sd = this_cpu_ptr(&softnet_data);
have = netpoll_poll_lock(napi);
__napi_poll(napi, &repoll);
netpoll_poll_unlock(have);
+ skb_defer_free_flush(sd);
local_bh_enable();
if (!repoll)
--
2.25.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 11/15] net: optimize napi_threaded_poll() vs RPS/RFS
2026-10-13 17:58 [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush wen.yang
` (9 preceding siblings ...)
2026-10-13 18:02 ` [PATCH 10/15] net: make napi_threaded_poll() aware of sd->defer_list wen.yang
@ 2026-10-13 18:02 ` wen.yang
2026-10-13 18:02 ` [PATCH 12/15] net: Remove conditional threaded-NAPI wakeup based on task state wen.yang
` (4 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: wen.yang @ 2026-10-13 18:02 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Eric Dumazet, Jesper Dangaard Brouer, Jakub Kicinski,
Paolo Abeni, linux-rt-devel, linux-kernel, David S . Miller,
Wen Yang
From: Eric Dumazet <edumazet@google.com>
commit 87eff2ec57b6d68d294013d8dd21e839a1175e3a upstream.
We use napi_threaded_poll() in order to reduce our softirq dependency.
We can add a followup of 821eba962d95 ("net: optimize napi_schedule_rps()")
to further remove the need of firing NET_RX_SOFTIRQ whenever
RPS/RFS are used.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
include/linux/netdevice.h | 3 +++
net/core/dev.c | 12 ++++++++++--
2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 987177cebe7c..b399773d123b 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3161,7 +3161,10 @@ struct softnet_data {
#ifdef CONFIG_RPS
struct softnet_data *rps_ipi_list;
#endif
+
bool in_net_rx_action;
+ bool in_napi_threaded_poll;
+
#ifdef CONFIG_NET_FLOW_LIMIT
struct sd_flow_limit __rcu *flow_limit;
#endif
diff --git a/net/core/dev.c b/net/core/dev.c
index cac16e17ccf9..ab012a7eee94 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4744,10 +4744,10 @@ static void napi_schedule_rps(struct softnet_data *sd)
sd->rps_ipi_next = mysd->rps_ipi_list;
mysd->rps_ipi_list = sd;
- /* If not called from net_rx_action()
+ /* If not called from net_rx_action() or napi_threaded_poll()
* we have to raise NET_RX_SOFTIRQ.
*/
- if (!mysd->in_net_rx_action)
+ if (!mysd->in_net_rx_action && !mysd->in_napi_threaded_poll)
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
return;
}
@@ -6777,11 +6777,19 @@ static int napi_threaded_poll(void *data)
local_bh_disable();
sd = this_cpu_ptr(&softnet_data);
+ sd->in_napi_threaded_poll = true;
have = netpoll_poll_lock(napi);
__napi_poll(napi, &repoll);
netpoll_poll_unlock(have);
+ sd->in_napi_threaded_poll = false;
+ barrier();
+
+ if (sd_has_rps_ipi_waiting(sd)) {
+ local_irq_disable();
+ net_rps_action_and_irq_enable(sd);
+ }
skb_defer_free_flush(sd);
local_bh_enable();
--
2.25.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 12/15] net: Remove conditional threaded-NAPI wakeup based on task state.
2026-10-13 17:58 [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush wen.yang
` (10 preceding siblings ...)
2026-10-13 18:02 ` [PATCH 11/15] net: optimize napi_threaded_poll() vs RPS/RFS wen.yang
@ 2026-10-13 18:02 ` wen.yang
2026-10-13 18:02 ` [PATCH 13/15] net: Use backlog-NAPI to clean up the defer_list wen.yang
` (3 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: wen.yang @ 2026-10-13 18:02 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Eric Dumazet, Jesper Dangaard Brouer, Jakub Kicinski,
Paolo Abeni, linux-rt-devel, linux-kernel, Wen Yang
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
commit 56364c910691f6d10ba88c964c9041b9ab777bd6 upstream.
A NAPI thread is scheduled by first setting NAPI_STATE_SCHED bit. If
successful (the bit was not yet set) then the NAPI_STATE_SCHED_THREADED
is set but only if thread's state is not TASK_INTERRUPTIBLE (is
TASK_RUNNING) followed by task wakeup.
If the task is idle (TASK_INTERRUPTIBLE) then the
NAPI_STATE_SCHED_THREADED bit is not set. The thread is no relying on
the bit but always leaving the wait-loop after returning from schedule()
because there must have been a wakeup.
The smpboot-threads implementation for per-CPU threads requires an
explicit condition and does not support "if we get out of schedule()
then there must be something to do".
Removing this optimisation simplifies the following integration.
Set NAPI_STATE_SCHED_THREADED unconditionally on wakeup and rely on it
in the wait path by removing the `woken' condition.
Acked-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
net/core/dev.c | 14 ++------------
1 file changed, 2 insertions(+), 12 deletions(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index ab012a7eee94..0b2cf2f0f5a4 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4485,13 +4485,7 @@ static inline void ____napi_schedule(struct softnet_data *sd,
*/
thread = READ_ONCE(napi->thread);
if (thread) {
- /* Avoid doing set_bit() if the thread is in
- * INTERRUPTIBLE state, cause napi_thread_wait()
- * makes sure to proceed with napi polling
- * if the thread is explicitly woken from here.
- */
- if (READ_ONCE(thread->__state) != TASK_INTERRUPTIBLE)
- set_bit(NAPI_STATE_SCHED_THREADED, &napi->state);
+ set_bit(NAPI_STATE_SCHED_THREADED, &napi->state);
wake_up_process(thread);
return;
}
@@ -6716,8 +6710,6 @@ static int napi_poll(struct napi_struct *n, struct list_head *repoll)
static int napi_thread_wait(struct napi_struct *napi)
{
- bool woken = false;
-
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
@@ -6726,15 +6718,13 @@ static int napi_thread_wait(struct napi_struct *napi)
* Testing SCHED bit is not enough because SCHED bit might be
* set by some other busy poll thread or by napi_disable().
*/
- if (test_bit(NAPI_STATE_SCHED_THREADED, &napi->state) || woken) {
+ if (test_bit(NAPI_STATE_SCHED_THREADED, &napi->state)) {
WARN_ON(!list_empty(&napi->poll_list));
__set_current_state(TASK_RUNNING);
return 0;
}
schedule();
- /* woken being true indicates this thread owns this napi. */
- woken = true;
set_current_state(TASK_INTERRUPTIBLE);
}
__set_current_state(TASK_RUNNING);
--
2.25.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 13/15] net: Use backlog-NAPI to clean up the defer_list.
2026-10-13 17:58 [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush wen.yang
` (11 preceding siblings ...)
2026-10-13 18:02 ` [PATCH 12/15] net: Remove conditional threaded-NAPI wakeup based on task state wen.yang
@ 2026-10-13 18:02 ` wen.yang
2026-10-13 18:02 ` [PATCH 14/15] net: Allow to use SMP threads for backlog NAPI wen.yang
` (2 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: wen.yang @ 2026-10-13 18:02 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Eric Dumazet, Jesper Dangaard Brouer, Jakub Kicinski,
Paolo Abeni, linux-rt-devel, linux-kernel, Wen Yang
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
commit 80d2eefcb4c84aa9018b2a997ab3a4c567bc821a upstream.
The defer_list is a per-CPU list which is used to free skbs outside of
the socket lock and on the CPU on which they have been allocated.
The list is processed during NAPI callbacks so ideally the list is
cleaned up.
Should the amount of skbs on the list exceed a certain water mark then
the softirq is triggered remotely on the target CPU by invoking a remote
function call. The raise of the softirqs via a remote function call
leads to waking the ksoftirqd on PREEMPT_RT which is undesired.
The backlog-NAPI threads already provide the infrastructure which can be
utilized to perform the cleanup of the defer_list.
The NAPI state is updated with the input_pkt_queue.lock acquired. It
order not to break the state, it is needed to also wake the backlog-NAPI
thread with the lock held. This requires to acquire the use the lock in
rps_lock_irq*() if the backlog-NAPI threads are used even with RPS
disabled.
Move the logic of remotely starting softirqs to clean up the defer_list
into kick_defer_list_purge(). Make sure a lock is held in
rps_lock_irq*() if backlog-NAPI threads are used. Schedule backlog-NAPI
for defer_list cleanup if backlog-NAPI is available.
Acked-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
include/linux/netdevice.h | 1 +
net/core/dev.c | 25 +++++++++++++++++++++----
net/core/skbuff.c | 4 ++--
3 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index b399773d123b..d7361bc7635e 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3245,6 +3245,7 @@ static inline void dev_xmit_recursion_dec(void)
__this_cpu_dec(softnet_data.xmit.recursion);
}
+void kick_defer_list_purge(struct softnet_data *sd, unsigned int cpu);
void __netif_schedule(struct Qdisc *q);
void netif_schedule_queue(struct netdev_queue *txq);
diff --git a/net/core/dev.c b/net/core/dev.c
index 0b2cf2f0f5a4..251985e85954 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -220,7 +220,7 @@ static inline struct hlist_head *dev_index_hash(struct net *net, int ifindex)
static inline void rps_lock_irqsave(struct softnet_data *sd,
unsigned long *flags)
{
- if (IS_ENABLED(CONFIG_RPS))
+ if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
spin_lock_irqsave(&sd->input_pkt_queue.lock, *flags);
else if (!IS_ENABLED(CONFIG_PREEMPT_RT))
local_irq_save(*flags);
@@ -228,7 +228,7 @@ static inline void rps_lock_irqsave(struct softnet_data *sd,
static inline void rps_lock_irq_disable(struct softnet_data *sd)
{
- if (IS_ENABLED(CONFIG_RPS))
+ if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
spin_lock_irq(&sd->input_pkt_queue.lock);
else if (!IS_ENABLED(CONFIG_PREEMPT_RT))
local_irq_disable();
@@ -237,7 +237,7 @@ static inline void rps_lock_irq_disable(struct softnet_data *sd)
static inline void rps_unlock_irq_restore(struct softnet_data *sd,
unsigned long *flags)
{
- if (IS_ENABLED(CONFIG_RPS))
+ if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
spin_unlock_irqrestore(&sd->input_pkt_queue.lock, *flags);
else if (!IS_ENABLED(CONFIG_PREEMPT_RT))
local_irq_restore(*flags);
@@ -245,7 +245,7 @@ static inline void rps_unlock_irq_restore(struct softnet_data *sd,
static inline void rps_unlock_irq_enable(struct softnet_data *sd)
{
- if (IS_ENABLED(CONFIG_RPS))
+ if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
spin_unlock_irq(&sd->input_pkt_queue.lock);
else if (!IS_ENABLED(CONFIG_PREEMPT_RT))
local_irq_enable();
@@ -4749,6 +4749,23 @@ static void napi_schedule_rps(struct softnet_data *sd)
__napi_schedule_irqoff(&mysd->backlog);
}
+void kick_defer_list_purge(struct softnet_data *sd, unsigned int cpu)
+{
+ unsigned long flags;
+
+ if (use_backlog_threads()) {
+ rps_lock_irqsave(sd, &flags);
+
+ if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state))
+ __napi_schedule_irqoff(&sd->backlog);
+
+ rps_unlock_irq_restore(sd, &flags);
+
+ } else if (!cmpxchg(&sd->defer_ipi_scheduled, 0, 1)) {
+ smp_call_function_single_async(cpu, &sd->defer_csd);
+ }
+}
+
#ifdef CONFIG_NET_FLOW_LIMIT
int netdev_flow_limit_table_len __read_mostly = (1 << 12);
#endif
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 6111c1bba704..717f23563ea9 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -6748,6 +6748,6 @@ nodefer: __kfree_skb(skb);
/* Make sure to trigger NET_RX_SOFTIRQ on the remote CPU
* if we are unlucky enough (this seems very unlikely).
*/
- if (unlikely(kick) && !cmpxchg(&sd->defer_ipi_scheduled, 0, 1))
- smp_call_function_single_async(cpu, &sd->defer_csd);
+ if (unlikely(kick))
+ kick_defer_list_purge(sd, cpu);
}
--
2.25.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 14/15] net: Allow to use SMP threads for backlog NAPI.
2026-10-13 17:58 [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush wen.yang
` (12 preceding siblings ...)
2026-10-13 18:02 ` [PATCH 13/15] net: Use backlog-NAPI to clean up the defer_list wen.yang
@ 2026-10-13 18:02 ` wen.yang
2026-10-13 18:02 ` [PATCH 15/15] net: Rename rps_lock to backlog_lock wen.yang
2026-10-14 15:08 ` [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush Sebastian Andrzej Siewior
15 siblings, 0 replies; 17+ messages in thread
From: wen.yang @ 2026-10-13 18:02 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Eric Dumazet, Jesper Dangaard Brouer, Jakub Kicinski,
Paolo Abeni, linux-rt-devel, linux-kernel, Wen Yang
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
commit dad6b97702639fba27a2bd3e986982ad6f0db3a7 upstream.
Backlog NAPI is a per-CPU NAPI struct only (with no device behind it)
used by drivers which don't do NAPI them self, RPS and parts of the
stack which need to avoid recursive deadlocks while processing a packet.
The non-NAPI driver use the CPU local backlog NAPI. If RPS is enabled
then a flow for the skb is computed and based on the flow the skb can be
enqueued on a remote CPU. Scheduling/ raising the softirq (for backlog's
NAPI) on the remote CPU isn't trivial because the softirq is only
scheduled on the local CPU and performed after the hardirq is done.
In order to schedule a softirq on the remote CPU, an IPI is sent to the
remote CPU which schedules the backlog-NAPI on the then local CPU.
On PREEMPT_RT interrupts are force-threaded. The soft interrupts are
raised within the interrupt thread and processed after the interrupt
handler completed still within the context of the interrupt thread. The
softirq is handled in the context where it originated.
With force-threaded interrupts enabled, ksoftirqd is woken up if a
softirq is raised from hardirq context. This is the case if it is raised
from an IPI. Additionally there is a warning on PREEMPT_RT if the
softirq is raised from the idle thread.
This was done for two reasons:
- With threaded interrupts the processing should happen in thread
context (where it originated) and ksoftirqd is the only thread for
this context if raised from hardirq. Using the currently running task
instead would "punish" a random task.
- Once ksoftirqd is active it consumes all further softirqs until it
stops running. This changed recently and is no longer the case.
Instead of keeping the backlog NAPI in ksoftirqd (in force-threaded/
PREEMPT_RT setups) I am proposing NAPI-threads for backlog.
The "proper" setup with threaded-NAPI is not doable because the threads
are not pinned to an individual CPU and can be modified by the user.
Additionally a dummy network device would have to be assigned. Also
CPU-hotplug has to be considered if additional CPUs show up.
All this can be probably done/ solved but the smpboot-threads already
provide this infrastructure.
Sending UDP packets over loopback expects that the packet is processed
within the call. Delaying it by handing it over to the thread hurts
performance. It is not beneficial to the outcome if the context switch
happens immediately after enqueue or after a while to process a few
packets in a batch.
There is no need to always use the thread if the backlog NAPI is
requested on the local CPU. This restores the loopback throuput. The
performance drops mostly to the same value after enabling RPS on the
loopback comparing the IPI and the tread result.
Create NAPI-threads for backlog if request during boot. The thread runs
the inner loop from napi_threaded_poll(), the wait part is different. It
checks for NAPI_STATE_SCHED (the backlog NAPI can not be disabled).
The NAPI threads for backlog are optional, it has to be enabled via the boot
argument "thread_backlog_napi". It is mandatory for PREEMPT_RT to avoid the
wakeup of ksoftirqd from the IPI.
6.1-rt port notes:
- The upstream context for this patch is the v6.9 net/core/dev.c. Some
hunks could not be applied verbatim against the 6.1.y base and were
applied by hand; each one is a pure base difference, not a behavioural
change:
* backlog_napi percpu definition: the 6.1.y anchor is
"int netdev_max_backlog" instead of "unsigned int sysctl_skb_defer_max".
* ____napi_schedule(): the WRITE_ONCE(napi->list_owner, ...) line added
by "net: add napi_struct.list_owner" does not exist on 6.1.y, so only
the "use_local_napi" goto/label was added.
* napi_threaded_poll() split: applied against the 6.1.y function body
which now matches the upstream pre-image after the prerequisite
commits.
* smpboot block and net_dev_init(): the 6.1.y net_dev_init() has no
net_page_pool_create() call, so the block was anchored on the comment
preceding net_dev_init() instead.
Acked-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
net/core/dev.c | 148 +++++++++++++++++++++++++++++++++++++------------
1 file changed, 113 insertions(+), 35 deletions(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index 251985e85954..317c221c99c2 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -78,6 +78,7 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/sched/mm.h>
+#include <linux/smpboot.h>
#include <linux/mutex.h>
#include <linux/rwsem.h>
#include <linux/string.h>
@@ -217,6 +218,31 @@ static inline struct hlist_head *dev_index_hash(struct net *net, int ifindex)
return &net->dev_index_head[ifindex & (NETDEV_HASHENTRIES - 1)];
}
+#ifndef CONFIG_PREEMPT_RT
+
+static DEFINE_STATIC_KEY_FALSE(use_backlog_threads_key);
+
+static int __init setup_backlog_napi_threads(char *arg)
+{
+ static_branch_enable(&use_backlog_threads_key);
+ return 0;
+}
+early_param("thread_backlog_napi", setup_backlog_napi_threads);
+
+static bool use_backlog_threads(void)
+{
+ return static_branch_unlikely(&use_backlog_threads_key);
+}
+
+#else
+
+static bool use_backlog_threads(void)
+{
+ return true;
+}
+
+#endif
+
static inline void rps_lock_irqsave(struct softnet_data *sd,
unsigned long *flags)
{
@@ -4453,6 +4479,7 @@ EXPORT_SYMBOL(__dev_direct_xmit);
/*************************************************************************
* Receiver routines
*************************************************************************/
+static DEFINE_PER_CPU(struct task_struct *, backlog_napi);
int netdev_max_backlog __read_mostly = 1000;
EXPORT_SYMBOL(netdev_max_backlog);
@@ -4485,12 +4512,16 @@ static inline void ____napi_schedule(struct softnet_data *sd,
*/
thread = READ_ONCE(napi->thread);
if (thread) {
+ if (use_backlog_threads() && thread == raw_cpu_read(backlog_napi))
+ goto use_local_napi;
+
set_bit(NAPI_STATE_SCHED_THREADED, &napi->state);
wake_up_process(thread);
return;
}
}
+use_local_napi:
list_add_tail(&napi->poll_list, &sd->poll_list);
/* If not called from net_rx_action()
* we have to raise NET_RX_SOFTIRQ.
@@ -4735,6 +4766,11 @@ static void napi_schedule_rps(struct softnet_data *sd)
#ifdef CONFIG_RPS
if (sd != mysd) {
+ if (use_backlog_threads()) {
+ __napi_schedule_irqoff(&sd->backlog);
+ return;
+ }
+
sd->rps_ipi_next = mysd->rps_ipi_list;
mysd->rps_ipi_list = sd;
@@ -6040,7 +6076,7 @@ static void net_rps_action_and_irq_enable(struct softnet_data *sd)
#ifdef CONFIG_RPS
struct softnet_data *remsd = sd->rps_ipi_list;
- if (remsd) {
+ if (!use_backlog_threads() && remsd) {
sd->rps_ipi_list = NULL;
local_irq_enable();
@@ -6055,7 +6091,7 @@ static void net_rps_action_and_irq_enable(struct softnet_data *sd)
static bool sd_has_rps_ipi_waiting(struct softnet_data *sd)
{
#ifdef CONFIG_RPS
- return sd->rps_ipi_list != NULL;
+ return !use_backlog_threads() && sd->rps_ipi_list;
#else
return false;
#endif
@@ -6099,7 +6135,7 @@ static int process_backlog(struct napi_struct *napi, int quota)
* We can use a plain write instead of clear_bit(),
* and we dont need an smp_mb() memory barrier.
*/
- napi->state = 0;
+ napi->state &= NAPIF_STATE_THREADED;
again = false;
} else {
skb_queue_splice_tail_init(&sd->input_pkt_queue,
@@ -6770,43 +6806,48 @@ static void skb_defer_free_flush(struct softnet_data *sd)
}
}
-static int napi_threaded_poll(void *data)
+static void napi_threaded_poll_loop(struct napi_struct *napi)
{
- struct napi_struct *napi = data;
struct softnet_data *sd;
- void *have;
+ unsigned long last_qs = jiffies;
- while (!napi_thread_wait(napi)) {
- unsigned long last_qs = jiffies;
-
- for (;;) {
- bool repoll = false;
+ for (;;) {
+ bool repoll = false;
+ void *have;
- local_bh_disable();
- sd = this_cpu_ptr(&softnet_data);
- sd->in_napi_threaded_poll = true;
+ local_bh_disable();
+ sd = this_cpu_ptr(&softnet_data);
+ sd->in_napi_threaded_poll = true;
- have = netpoll_poll_lock(napi);
- __napi_poll(napi, &repoll);
- netpoll_poll_unlock(have);
+ have = netpoll_poll_lock(napi);
+ __napi_poll(napi, &repoll);
+ netpoll_poll_unlock(have);
- sd->in_napi_threaded_poll = false;
- barrier();
+ sd->in_napi_threaded_poll = false;
+ barrier();
- if (sd_has_rps_ipi_waiting(sd)) {
- local_irq_disable();
- net_rps_action_and_irq_enable(sd);
- }
- skb_defer_free_flush(sd);
- local_bh_enable();
+ if (sd_has_rps_ipi_waiting(sd)) {
+ local_irq_disable();
+ net_rps_action_and_irq_enable(sd);
+ }
+ skb_defer_free_flush(sd);
+ local_bh_enable();
- if (!repoll)
- break;
+ if (!repoll)
+ break;
- rcu_softirq_qs_periodic(last_qs);
- cond_resched();
- }
+ rcu_softirq_qs_periodic(last_qs);
+ cond_resched();
}
+}
+
+static int napi_threaded_poll(void *data)
+{
+ struct napi_struct *napi = data;
+
+ while (!napi_thread_wait(napi))
+ napi_threaded_poll_loop(napi);
+
return 0;
}
@@ -11331,7 +11372,7 @@ static int dev_cpu_dead(unsigned int oldcpu)
list_del_init(&napi->poll_list);
if (napi->poll == process_backlog)
- napi->state = 0;
+ napi->state &= NAPIF_STATE_THREADED;
else
____napi_schedule(sd, napi);
}
@@ -11339,12 +11380,14 @@ static int dev_cpu_dead(unsigned int oldcpu)
raise_softirq_irqoff(NET_TX_SOFTIRQ);
local_irq_enable();
+ if (!use_backlog_threads()) {
#ifdef CONFIG_RPS
- remsd = oldsd->rps_ipi_list;
- oldsd->rps_ipi_list = NULL;
+ remsd = oldsd->rps_ipi_list;
+ oldsd->rps_ipi_list = NULL;
#endif
- /* send out pending IPI's on offline CPU */
- net_rps_send_ipi(remsd);
+ /* send out pending IPI's on offline CPU */
+ net_rps_send_ipi(remsd);
+ }
/* Process offline CPU's input_pkt_queue */
while ((skb = __skb_dequeue(&oldsd->process_queue))) {
@@ -11604,6 +11647,38 @@ static struct pernet_operations __net_initdata default_device_ops = {
*
*/
+static int backlog_napi_should_run(unsigned int cpu)
+{
+ struct softnet_data *sd = per_cpu_ptr(&softnet_data, cpu);
+ struct napi_struct *napi = &sd->backlog;
+
+ return test_bit(NAPI_STATE_SCHED_THREADED, &napi->state);
+}
+
+static void run_backlog_napi(unsigned int cpu)
+{
+ struct softnet_data *sd = per_cpu_ptr(&softnet_data, cpu);
+
+ napi_threaded_poll_loop(&sd->backlog);
+}
+
+static void backlog_napi_setup(unsigned int cpu)
+{
+ struct softnet_data *sd = per_cpu_ptr(&softnet_data, cpu);
+ struct napi_struct *napi = &sd->backlog;
+
+ napi->thread = this_cpu_read(backlog_napi);
+ set_bit(NAPI_STATE_THREADED, &napi->state);
+}
+
+static struct smp_hotplug_thread backlog_threads = {
+ .store = &backlog_napi,
+ .thread_should_run = backlog_napi_should_run,
+ .thread_fn = run_backlog_napi,
+ .thread_comm = "backlog_napi/%u",
+ .setup = backlog_napi_setup,
+};
+
/*
* This is called single threaded during boot, so no need
* to take the rtnl semaphore.
@@ -11654,7 +11729,10 @@ static int __init net_dev_init(void)
init_gro_hash(&sd->backlog);
sd->backlog.poll = process_backlog;
sd->backlog.weight = weight_p;
+ INIT_LIST_HEAD(&sd->backlog.poll_list);
}
+ if (use_backlog_threads())
+ smpboot_register_percpu_thread(&backlog_threads);
dev_boot_phase = 0;
--
2.25.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 15/15] net: Rename rps_lock to backlog_lock.
2026-10-13 17:58 [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush wen.yang
` (13 preceding siblings ...)
2026-10-13 18:02 ` [PATCH 14/15] net: Allow to use SMP threads for backlog NAPI wen.yang
@ 2026-10-13 18:02 ` wen.yang
2026-10-14 15:08 ` [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush Sebastian Andrzej Siewior
15 siblings, 0 replies; 17+ messages in thread
From: wen.yang @ 2026-10-13 18:02 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt
Cc: Eric Dumazet, Jesper Dangaard Brouer, Jakub Kicinski,
Paolo Abeni, linux-rt-devel, linux-kernel, Wen Yang
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
commit 765b11f8f4e20b7433e4ba4a3e9106a0d59501ed upstream.
The rps_lock.*() functions use the inner lock of a sk_buff_head for
locking. This lock is used if RPS is enabled, otherwise the list is
accessed lockless and disabling interrupts is enough for the
synchronisation because it is only accessed CPU local. Not only the list
is protected but also the NAPI state protected.
With the addition of backlog threads, the lock is also needed because of
the cross CPU access even without RPS. The clean up of the defer_list
list is also done via backlog threads (if enabled).
It has been suggested to rename the locking function since it is no
longer just RPS.
Rename the rps_lock*() functions to backlog_lock*().
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Acked-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
net/core/dev.c | 34 +++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index 317c221c99c2..f1ac29d7144e 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -243,8 +243,8 @@ static bool use_backlog_threads(void)
#endif
-static inline void rps_lock_irqsave(struct softnet_data *sd,
- unsigned long *flags)
+static inline void backlog_lock_irq_save(struct softnet_data *sd,
+ unsigned long *flags)
{
if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
spin_lock_irqsave(&sd->input_pkt_queue.lock, *flags);
@@ -252,7 +252,7 @@ static inline void rps_lock_irqsave(struct softnet_data *sd,
local_irq_save(*flags);
}
-static inline void rps_lock_irq_disable(struct softnet_data *sd)
+static inline void backlog_lock_irq_disable(struct softnet_data *sd)
{
if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
spin_lock_irq(&sd->input_pkt_queue.lock);
@@ -260,8 +260,8 @@ static inline void rps_lock_irq_disable(struct softnet_data *sd)
local_irq_disable();
}
-static inline void rps_unlock_irq_restore(struct softnet_data *sd,
- unsigned long *flags)
+static inline void backlog_unlock_irq_restore(struct softnet_data *sd,
+ unsigned long *flags)
{
if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
spin_unlock_irqrestore(&sd->input_pkt_queue.lock, *flags);
@@ -269,7 +269,7 @@ static inline void rps_unlock_irq_restore(struct softnet_data *sd,
local_irq_restore(*flags);
}
-static inline void rps_unlock_irq_enable(struct softnet_data *sd)
+static inline void backlog_unlock_irq_enable(struct softnet_data *sd)
{
if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
spin_unlock_irq(&sd->input_pkt_queue.lock);
@@ -4790,12 +4790,12 @@ void kick_defer_list_purge(struct softnet_data *sd, unsigned int cpu)
unsigned long flags;
if (use_backlog_threads()) {
- rps_lock_irqsave(sd, &flags);
+ backlog_lock_irq_save(sd, &flags);
if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state))
__napi_schedule_irqoff(&sd->backlog);
- rps_unlock_irq_restore(sd, &flags);
+ backlog_unlock_irq_restore(sd, &flags);
} else if (!cmpxchg(&sd->defer_ipi_scheduled, 0, 1)) {
smp_call_function_single_async(cpu, &sd->defer_csd);
@@ -4857,7 +4857,7 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
reason = SKB_DROP_REASON_NOT_SPECIFIED;
sd = &per_cpu(softnet_data, cpu);
- rps_lock_irqsave(sd, &flags);
+ backlog_lock_irq_save(sd, &flags);
if (!netif_running(skb->dev))
goto drop;
qlen = skb_queue_len(&sd->input_pkt_queue);
@@ -4866,7 +4866,7 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
enqueue:
__skb_queue_tail(&sd->input_pkt_queue, skb);
input_queue_tail_incr_save(sd, qtail);
- rps_unlock_irq_restore(sd, &flags);
+ backlog_unlock_irq_restore(sd, &flags);
return NET_RX_SUCCESS;
}
@@ -4881,7 +4881,7 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
drop:
sd->dropped++;
- rps_unlock_irq_restore(sd, &flags);
+ backlog_unlock_irq_restore(sd, &flags);
dev_core_stats_rx_dropped_inc(skb->dev);
kfree_skb_reason(skb, reason);
@@ -5977,7 +5977,7 @@ static void flush_backlog(struct work_struct *work)
local_bh_disable();
sd = this_cpu_ptr(&softnet_data);
- rps_lock_irq_disable(sd);
+ backlog_lock_irq_disable(sd);
skb_queue_walk_safe(&sd->input_pkt_queue, skb, tmp) {
if (skb->dev->reg_state == NETREG_UNREGISTERING) {
__skb_unlink(skb, &sd->input_pkt_queue);
@@ -5985,7 +5985,7 @@ static void flush_backlog(struct work_struct *work)
input_queue_head_incr(sd);
}
}
- rps_unlock_irq_enable(sd);
+ backlog_unlock_irq_enable(sd);
skb_queue_walk_safe(&sd->process_queue, skb, tmp) {
if (skb->dev->reg_state == NETREG_UNREGISTERING) {
@@ -6003,14 +6003,14 @@ static bool flush_required(int cpu)
struct softnet_data *sd = &per_cpu(softnet_data, cpu);
bool do_flush;
- rps_lock_irq_disable(sd);
+ backlog_lock_irq_disable(sd);
/* as insertion into process_queue happens with the rps lock held,
* process_queue access may race only with dequeue
*/
do_flush = !skb_queue_empty(&sd->input_pkt_queue) ||
!skb_queue_empty_lockless(&sd->process_queue);
- rps_unlock_irq_enable(sd);
+ backlog_unlock_irq_enable(sd);
return do_flush;
#endif
@@ -6125,7 +6125,7 @@ static int process_backlog(struct napi_struct *napi, int quota)
}
- rps_lock_irq_disable(sd);
+ backlog_lock_irq_disable(sd);
if (skb_queue_empty(&sd->input_pkt_queue)) {
/*
* Inline a custom version of __napi_complete().
@@ -6141,7 +6141,7 @@ static int process_backlog(struct napi_struct *napi, int quota)
skb_queue_splice_tail_init(&sd->input_pkt_queue,
&sd->process_queue);
}
- rps_unlock_irq_enable(sd);
+ backlog_unlock_irq_enable(sd);
}
return work;
--
2.25.1
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush
2026-10-13 17:58 [PATCH v6.1-rt 00/15] net: Backport backlog-NAPI threads to resolve PREEMPT_RT softirq warning in SMP flush wen.yang
` (14 preceding siblings ...)
2026-10-13 18:02 ` [PATCH 15/15] net: Rename rps_lock to backlog_lock wen.yang
@ 2026-10-14 15:08 ` Sebastian Andrzej Siewior
15 siblings, 0 replies; 17+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-10-14 15:08 UTC (permalink / raw)
To: wen.yang, Clark Williams
Cc: Steven Rostedt, Eric Dumazet, Jesper Dangaard Brouer,
Jakub Kicinski, Paolo Abeni, linux-rt-devel, linux-kernel
Please keep the networking folks out of the loop. This is a backport
request which goes entirely via the RT-queue.
On 2026-10-14 01:58:34 [+0800], wen.yang@linux.dev wrote:
> From: Wen Yang <wen.yang@linux.dev>
>
> *** Overview ***
> Backport the backlog-NAPI smpboot-thread mechanism and its prerequisite
> NAPI/RPS/defer_list work from upstream (v6.4 - v6.9) to v6.1-rt-next.
>
> *** Problem ***
> On PREEMPT_RT, RPS steers packets to a remote CPU's backlog by sending an
> IPI. The IPI handler (rps_trigger_softirq) calls ____napi_schedule(), which
> raises NET_RX_SOFTIRQ. do_softirq_post_smp_call_flush() then sees a softirq
> pending that was not pending before the flush, and NET_RX_SOFTIRQ is not
> masked by SCHED_SOFTIRQ_MASK, so it fires:
I was about to point out that v6.1-RT should have duct tape for this but
from reading 1/15 is see that this duct is not complete and is missing
RPS. Indeed.
A backport of what is merged is my preference, too. However, we should
get v6.6-rt fixed first then v6.1-rt. Otherwise users upgrading from
v6.1 to v6.6 will have this case broken.
Clark, any objections here from your side?
Sebastian
^ permalink raw reply [flat|nested] 17+ messages in thread