Netdev List
 help / color / mirror / Atom feed
* [PATCH] net/sched: taprio: enforce minimum software scheduling interval
@ 2026-08-13  2:15 syzbot
  2026-08-14 16:13 ` Simon Horman
  2026-08-16 14:28 ` Junjie Cao
  0 siblings, 2 replies; 3+ messages in thread
From: syzbot @ 2026-08-13  2:15 UTC (permalink / raw)
  To: syzkaller-bugs, Uladzislau Zhauniarovich, David S. Miller,
	Eric Dumazet, Jamal Hadi Salim, Jiri Pirko, Jakub Kicinski,
	netdev, Paolo Abeni, Vinicius Costa Gomes
  Cc: horms, linux-kernel, syzbot

From: Uladzislau Zhauniarovich <uladzislau.zhauniarovich@gmail.com>

When configuring taprio with a very small schedule interval (e.g., 129 ns),
the kernel validates the interval against the time it takes to transmit a
minimum-sized Ethernet frame (60 bytes). On high-speed links, this minimum
duration is extremely small (e.g., 48 ns at 10 Gbps). Since the requested
interval is larger than this, the validation passes. Virtual devices like
veth or bonding can defeat this link-speed minimum check because they
report inflated link speeds (e.g., veth reports 10 Gbps, and bonding sums
member speeds).

However, when hardware offload is not used, taprio falls back to software
scheduling and arms an hrtimer. The hrtimer is programmed to fire at the
configured interval. If this interval is too small, it cannot sustain the
timer service cost of one advance_sched() invocation, which includes lock
acquisition, budget recomputation, and TX softirq processing. As a result,
the timer constantly falls behind, and the CPU is livelocked in hardirq
context endlessly servicing the advance_sched() hrtimer. This starves the
RCU grace-period kthreads, leading to an RCU stall panic:

rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
rcu: 	1-...!: (1 GPs behind) idle=4854/0/0x1 softirq=136062/136068 fqs=0
rcu: 	(detected by 0, t=10506 jiffies, g=161469, q=1866 ncpus=2)
Sending NMI from CPU 0 to CPUs 1:
NMI backtrace for cpu 1
CPU: 1 UID: 0 PID: 0 Comm: swapper/1 Not tainted
Call Trace:
 <IRQ>
 lock_is_held include/linux/lockdep.h:249 [inline]
 enqueue_hrtimer+0x79/0x2c0 kernel/time/hrtimer.c:1107
 __run_hrtimer kernel/time/hrtimer.c:1946 [inline]
 __hrtimer_run_queues+0x4ce/0xa10 kernel/time/hrtimer.c:1994
 hrtimer_interrupt+0x448/0x910 kernel/time/hrtimer.c:2113
 local_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1050 [inline]
 __sysvec_apic_timer_interrupt+0x102/0x430 arch/x86/kernel/apic/apic.c:1067
 instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1061
 [inline]
 sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1061
 </IRQ>

To fix this, enforce a hard absolute minimum interval of 100 microseconds
(TAPRIO_MIN_SW_INTERVAL_NS) for software-based scheduling, which provides
enough margin over the timer service cost. Fully offloaded schedules are
unaffected since they do not rely on the CPU's hrtimer. Introduce a helper
taprio_min_interval() to consolidate the minimum interval logic for both
individual schedule entries and the overall cycle_time validation.

Fixes: b5b73b26b3ca ("taprio: Fix allowing too small intervals")
Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot
Reported-by: syzbot+19d01f6082ec61dd45b2@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=19d01f6082ec61dd45b2
Link: https://syzkaller.appspot.com/ai_job?id=37f064ee-e021-43aa-8cfc-9f2a5a7c35d7
Signed-off-by: Uladzislau Zhauniarovich <uladzislau.zhauniarovich@gmail.com>

---
diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
index 45245157e..2bf4cae16 100644
--- a/net/sched/sch_taprio.c
+++ b/net/sched/sch_taprio.c
@@ -49,6 +49,13 @@ static struct static_key_false taprio_have_working_mqprio;
  */
 #define TAPRIO_PICOS_PER_BYTE_MIN 17
 
+/* The software scheduler cannot sustain hrtimer intervals smaller than
+ * this without livelocking the CPU. The value must exceed the cost of
+ * one advance_sched() invocation (lock acquisition, budget recomputation,
+ * TX softirq) with margin.
+ */
+#define TAPRIO_MIN_SW_INTERVAL_NS (100 * NSEC_PER_USEC)
+
 struct sched_entry {
 	/* Durations between this GCL entry and the GCL entry where the
 	 * respective traffic class gate closes
@@ -259,6 +266,16 @@ static int length_to_duration(struct taprio_sched *q, int len)
 	return div_u64(len * atomic64_read(&q->picos_per_byte), PSEC_PER_NSEC);
 }
 
+static int taprio_min_interval(struct taprio_sched *q)
+{
+	int min_duration = length_to_duration(q, ETH_ZLEN);
+
+	if (!FULL_OFFLOAD_IS_ENABLED(q->flags))
+		min_duration = max_t(int, min_duration, TAPRIO_MIN_SW_INTERVAL_NS);
+
+	return min_duration;
+}
+
 static int duration_to_length(struct taprio_sched *q, u64 duration)
 {
 	return div_u64(duration * PSEC_PER_NSEC, atomic64_read(&q->picos_per_byte));
@@ -1038,7 +1055,7 @@ static int fill_sched_entry(struct taprio_sched *q, struct nlattr **tb,
 			    struct sched_entry *entry,
 			    struct netlink_ext_ack *extack)
 {
-	int min_duration = length_to_duration(q, ETH_ZLEN);
+	int min_duration = taprio_min_interval(q);
 	u32 interval = 0;
 
 	if (tb[TCA_TAPRIO_SCHED_ENTRY_CMD])
@@ -1054,7 +1071,8 @@ static int fill_sched_entry(struct taprio_sched *q, struct nlattr **tb,
 			tb[TCA_TAPRIO_SCHED_ENTRY_INTERVAL]);
 
 	/* The interval should allow at least the minimum ethernet
-	 * frame to go out.
+	 * frame to go out. When software scheduling is used, the
+	 * interval must also be at least the minimum hrtimer interval.
 	 */
 	if (interval < min_duration) {
 		NL_SET_ERR_MSG(extack, "Invalid interval for schedule entry");
@@ -1166,7 +1184,7 @@ static int parse_taprio_schedule(struct taprio_sched *q, struct nlattr **tb,
 		new->cycle_time = cycle;
 	}
 
-	if (new->cycle_time < new->num_entries * length_to_duration(q, ETH_ZLEN)) {
+	if (new->cycle_time < (s64)new->num_entries * taprio_min_interval(q)) {
 		NL_SET_ERR_MSG(extack, "'cycle_time' is too small");
 		return -EINVAL;
 	}


base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
-- 
See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
The person who has signed off on the patch is responsible for
addressing comments.
syzbot engineers can be reached at syzkaller@googlegroups.com.

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

* Re: [PATCH] net/sched: taprio: enforce minimum software scheduling interval
  2026-08-13  2:15 [PATCH] net/sched: taprio: enforce minimum software scheduling interval syzbot
@ 2026-08-14 16:13 ` Simon Horman
  2026-08-16 14:28 ` Junjie Cao
  1 sibling, 0 replies; 3+ messages in thread
From: Simon Horman @ 2026-08-14 16:13 UTC (permalink / raw)
  To: syzbot
  Cc: Simon Horman, syzkaller-bugs, uladzislau.zhauniarovich, davem,
	edumazet, jhs, jiri, kuba, netdev, pabeni, vinicius.gomes,
	linux-kernel, syzbot

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev/#/patchset/afe041f6-ef7d-4434-b2d0-096be49b5bcb@mail.kernel.org?part=1
---
--- Patch [1]: [PATCH] net/sched: taprio: enforce minimum software scheduling interval ---
commit 984087f4873d7bad874b78ae4728e68b305dd5dc
Author: Uladzislau Zhauniarovich <uladzislau.zhauniarovich@gmail.com>
net/sched: taprio: enforce minimum software scheduling interval

This commit introduces a minimum interval of 100 microseconds for the
software scheduler in taprio to prevent CPU livelocking from excessive
hrtimer interrupts. It skips this check for fully offloaded schedules.

Link: https://syzkaller.appspot.com/bug?extid=19d01f6082ec61dd45b2
Link: https://syzkaller.appspot.com/ai_job?id=37f064ee-e021-43aa-8cfc-9f2a5a7c35d7

> diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
> index 45245157e00a6..2bf4cae164bc2 100644
> --- a/net/sched/sch_taprio.c
> +++ b/net/sched/sch_taprio.c

[ ... ]

> @@ -259,6 +266,16 @@ static int length_to_duration(struct taprio_sched *q, int len)
>  	return div_u64(len * atomic64_read(&q->picos_per_byte), PSEC_PER_NSEC);
>  }
>  
> +static int taprio_min_interval(struct taprio_sched *q)
> +{
> +	int min_duration = length_to_duration(q, ETH_ZLEN);
> +
> +	if (!FULL_OFFLOAD_IS_ENABLED(q->flags))
> +		min_duration = max_t(int, min_duration, TAPRIO_MIN_SW_INTERVAL_NS);

[Severity: High]
Does this limit erroneously catch TXTIME_ASSIST offload configurations?

The code applies the software interval limit when FULL_OFFLOAD_IS_ENABLED
is false, which subjects TXTIME_ASSIST setups to the 100 microseconds limit.
However, TXTIME_ASSIST mode does not use the software advance_sched
hrtimer; it relies on Earliest Departure Time and NIC pacing instead.

Could this break valid hardware-assisted TSN schedules that require smaller
intervals by rejecting them during netlink configuration?

> +
> +	return min_duration;
> +}

[ ... ]


[Severity: High]
This isn't a bug introduced by this patch, but does the budget calculation
suffer from truncation on high-speed links?

When converting long gate durations to byte lengths, truncation from a 64-bit
unsigned integer to a 32-bit signed integer can occur:

net/sched/sch_taprio.c:taprio_set_budgets() {
    ...
    budget = div64_u64((u64)entry->gate_duration[tc] * PSEC_PER_NSEC,
                       atomic64_read(&q->picos_per_byte));
    atomic_set(&entry->budget[tc], budget);
    ...
}

A valid schedule with a gate duration long enough to exceed INT_MAX bytes at
high link speeds (e.g., 400 Gbps where picos_per_byte = 20) yields a budget
exceeding 2,147,483,647 bytes.

Because the budget is a signed 32-bit int, it overflows to a negative value.
Can this cause taprio_update_budgets() to return a negative value and
taprio_dequeue_from_txq() to perpetually stall and drop packets?


[Severity: High]
This is a pre-existing issue, but do failed configuration changes leave the
taprio qdisc in a partially modified state?

If a taprio_change() request passes early validation but fails later, it
appears to leave hardware and software out of sync:

net/sched/sch_taprio.c:taprio_change() {
    ...
    if (FULL_OFFLOAD_IS_ENABLED(q->flags))
        err = taprio_enable_offload(dev, q, new_admin, extack);
    ...
    if (tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]) {
        if (!TXTIME_ASSIST_IS_ENABLED(q->flags)) {
            NL_SET_ERR_MSG_MOD(extack, "txtime-delay can only be set when txtime-assist mode is enabled");
            err = -EINVAL;
            goto unlock;
        }
    ...
}

Hardware offload is enabled with the new schedule, but a subsequent failure
(like the TXTIME_DELAY check) jumps to the unlock and free_sched labels.
Does this discard the software schedule without reverting the hardware
offload, causing the two to become permanently desynchronized?

Additionally, in taprio_parse_tc_entries():

net/sched/sch_taprio.c:taprio_parse_tc_entries() {
    for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++) {
        q->max_sdu[tc] = max_sdu[tc];
        q->fp[tc] = fp[tc];
    ...
    if (have_preemption) {
        if (!FULL_OFFLOAD_IS_ENABLED(q->flags)) {
            ...
            return -EOPNOTSUPP;
        }
    ...
}

Internal state like q->max_sdu and q->fp are updated directly in the
software state before checking if preemption is actually supported. Does this
return an error without reverting them on failure?

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

* Re: [PATCH] net/sched: taprio: enforce minimum software scheduling interval
  2026-08-13  2:15 [PATCH] net/sched: taprio: enforce minimum software scheduling interval syzbot
  2026-08-14 16:13 ` Simon Horman
@ 2026-08-16 14:28 ` Junjie Cao
  1 sibling, 0 replies; 3+ messages in thread
From: Junjie Cao @ 2026-08-16 14:28 UTC (permalink / raw)
  To: Uladzislau Zhauniarovich
  Cc: Simon Horman, Hillf Danton, Vinicius Costa Gomes,
	Jamal Hadi Salim, Jiri Pirko, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, netdev, linux-kernel,
	syzbot+19d01f6082ec61dd45b2

On Fri, 14 Aug 2026, Simon Horman wrote:
> Does this limit erroneously catch TXTIME_ASSIST offload configurations?

It does. Only pure software mode arms the per-entry hrtimer:
taprio_change() doesn't call taprio_start_sched() in txtime-assist
mode, and taprio_start_sched() returns early for full offload. So I
think the check wants to be

      if (!FULL_OFFLOAD_IS_ENABLED(q->flags) &&
          !TXTIME_ASSIST_IS_ENABLED(q->flags))

I gave that a spin on a patched kernel: a 2x50us txtime-assist
schedule on veth is still accepted, while the same 50us software
schedule is rejected.

On Fri, 14 Aug 2026, Hillf Danton wrote:
> Better if you specify why interval like 2us is ruled out.

The yardstick is the service cost of one expiry. On a release build I
see ~5.1M local timer interrupts in 5s on the owning CPU for a 700ns
single-entry schedule on veth, so the whole per-expiry service path is
around a microsecond; Uladzislau estimated ~10us per invocation on the
syzbot debug config earlier in the moderation thread. A 2us interval
still livelocks a debug build, and on a release build it pins a
permanent ~500k irqs/s on one CPU. 100us keeps margin above the
debug-config cost.

One more thing that came out of testing this: the floor only covers
half of the problem. A valid schedule that falls behind replays its
whole backlog one hrtimer expiry at a time. With a 4x200us schedule
and CLOCK_TAI stepped forward 72h (think ptp4l's first big step, or a
VM pause) I get an RCU stall with the owning CPU stuck in hrtimer
expiry processing, and no admission check can catch that; syzbot's
reports show the same stall with advance_sched() on the stack. A
bounded catch-up in advance_sched() fixes it. Conversely, catch-up
alone doesn't help the storm case: the 700ns schedule is still
admitted and sustains ~1M irqs/s.

I have both halves ready as a series - a bounded catch-up in
advance_sched() plus this patch with the exemption folded in - tested
by syzbot against both reproducer buckets on net.git dd057113ac7b,
with a tc-testing case. I plan to post it in a day, keeping your
Signed-off-by on this one, unless you'd prefer to respin it yourself.

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

end of thread, other threads:[~2026-08-16 14:28 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-13  2:15 [PATCH] net/sched: taprio: enforce minimum software scheduling interval syzbot
2026-08-14 16:13 ` Simon Horman
2026-08-16 14:28 ` Junjie Cao

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox