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: 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 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 To: "David S. Miller" To: "Eric Dumazet" To: "Jamal Hadi Salim" To: "Jiri Pirko" To: "Jakub Kicinski" To: To: "Paolo Abeni" To: "Vinicius Costa Gomes" Cc: "Simon Horman" Cc: --- v2: - Increased the minimum software scheduling interval from 1us to 100us to cover the timer service cost. - Explained how virtual devices (veth, bonding) bypass the link-speed validation. - Clarified that fully offloaded schedules are unaffected. - Refactored the minimum interval check into a new taprio_min_interval() helper. v1: https://lore.kernel.org/all/be92b2ea-2bf9-4d7f-a833-de781ee804c8@mail.kernel.org/T/ --- 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 -- This is an AI-generated patch subject to moderation. Reply with '#syz upstream' to Sign-off the patch as a human author and send it to the upstream kernel mailing lists. Reply with '#syz reject' to reject it ('#syz unreject' to undo). See https://goo.gle/syzbot-ai-patches for information about AI-generated patches. You can comment on the patch as usual, syzbot will try to address the comments and send a new version of the patch if necessary. syzbot engineers can be reached at syzkaller@googlegroups.com.