From: "syzbot" <syzbot@kernel.org>
To: syzkaller-upstream-moderation@googlegroups.com
Cc: syzbot@lists.linux.dev
Subject: [PATCH RFC] net/sched: taprio: fix hrtimer interrupt storm on small intervals
Date: Thu, 9 Jul 2026 12:33:55 +0000 (UTC) [thread overview]
Message-ID: <bc6a8890-9230-489a-bbce-5c255c1ef01a@mail.kernel.org> (raw)
The taprio qdisc allows configuring extremely small intervals (e.g., 255
ns) which are valid for hardware offload but completely overwhelm the CPU
when using software timers. When the interval is smaller than the time it
takes to process the timer interrupt, the timer's expiration time is always
in the past. This causes the hrtimer subsystem to continuously re-enqueue
and fire the timer, leading to an interrupt storm that starves the CPU and
triggers an RCU stall:
rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
rcu: 1-...!: (1 GPs behind) idle=9714/0/0x1 softirq=132447/132447 fqs=1
rcu: (detected by 0, t=10505 jiffies, g=152477, q=510 ncpus=2)
rcu: rcu_preempt kthread starved for 10501 jiffies! g152477 f0x0
RCU_GP_WAIT_FQS(5) ->state=0x0 ->cpu=0
rcu: Unless rcu_preempt kthread gets sufficient CPU time, OOM is now
expected behavior.
To fix this, implement two strategies. First, enforce sensible minimums in
software mode by rejecting intervals and cycle times smaller than
NSEC_PER_USEC (1 microsecond) when hardware offload is not enabled. Second,
prevent timer stalls in advance_sched() by fast-forwarding the schedule to
the current time instead of blindly restarting the timer in the past. This
is done efficiently by skipping full cycles using division, and then
looping through the remaining entries. To prevent the loop itself from
causing a stall, cap the iterations at 2048 and yield the CPU if necessary
by setting the timer to fire 1 microsecond in the future.
Fixes: 5a781ccbd19e ("tc: Add support for configuring the taprio scheduler")
Assisted-by: Gemini:gemini-3.1-pro-preview syzbot
Reported-by: syzbot+f8850bc3986562f79619@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=f8850bc3986562f79619
Link: https://syzkaller.appspot.com/ai_job?id=4e81d0c1-77be-4527-8698-9eadc019d766
To: "David S. Miller" <davem@davemloft.net>
To: "Eric Dumazet" <edumazet@google.com>
To: "Jamal Hadi Salim" <jhs@mojatatu.com>
To: "Jiri Pirko" <jiri@resnulli.us>
To: "Jakub Kicinski" <kuba@kernel.org>
To: <netdev@vger.kernel.org>
To: "Paolo Abeni" <pabeni@redhat.com>
To: "Vinicius Costa Gomes" <vinicius.gomes@intel.com>
Cc: "Simon Horman" <horms@kernel.org>
Cc: <linux-kernel@vger.kernel.org>
---
diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
index 299234a5f..910de14c4 100644
--- a/net/sched/sch_taprio.c
+++ b/net/sched/sch_taprio.c
@@ -944,46 +944,83 @@ static enum hrtimer_restart advance_sched(struct hrtimer *timer)
* entry of all schedules are pre-calculated during the
* schedule initialization.
*/
- if (unlikely(!entry || entry->end_time == oper->base_time)) {
- next = list_first_entry(&oper->entries, struct sched_entry,
- list);
- end_time = next->end_time;
- goto first_run;
- }
-
- if (should_restart_cycle(oper, entry)) {
- next = list_first_entry(&oper->entries, struct sched_entry,
- list);
- oper->cycle_end_time = ktime_add_ns(oper->cycle_end_time,
- oper->cycle_time);
- } else {
- next = list_next_entry(entry, list);
- }
+ int max_iter = 2048;
+ ktime_t now = taprio_get_time(q);
- end_time = ktime_add_ns(entry->end_time, next->interval);
- end_time = min_t(ktime_t, end_time, oper->cycle_end_time);
+ do {
+ if (unlikely(!entry || entry->end_time == oper->base_time)) {
+ next = list_first_entry(&oper->entries,
+ struct sched_entry, list);
+ end_time = next->end_time;
+ goto first_run;
+ }
- for (tc = 0; tc < num_tc; tc++) {
- if (next->gate_duration[tc] == oper->cycle_time)
- next->gate_close_time[tc] = KTIME_MAX;
- else
- next->gate_close_time[tc] = ktime_add_ns(entry->end_time,
- next->gate_duration[tc]);
- }
+ if (should_restart_cycle(oper, entry)) {
+ next = list_first_entry(&oper->entries,
+ struct sched_entry, list);
+ oper->cycle_end_time = ktime_add_ns(
+ oper->cycle_end_time, oper->cycle_time);
+ } else {
+ next = list_next_entry(entry, list);
+ }
- if (should_change_schedules(admin, oper, end_time)) {
- switch_schedules(q, &admin, &oper);
- /* After changing schedules, the next entry is the first one
- * in the new schedule, with a pre-calculated end_time.
- */
- next = list_first_entry(&oper->entries, struct sched_entry, list);
- end_time = next->end_time;
- }
+ end_time = ktime_add_ns(entry->end_time, next->interval);
+ end_time = min_t(ktime_t, end_time, oper->cycle_end_time);
+
+ for (tc = 0; tc < num_tc; tc++) {
+ if (next->gate_duration[tc] == oper->cycle_time)
+ next->gate_close_time[tc] = KTIME_MAX;
+ else
+ next->gate_close_time[tc] =
+ ktime_add_ns(entry->end_time,
+ next->gate_duration[tc]);
+ }
+
+ if (should_change_schedules(admin, oper, end_time)) {
+ switch_schedules(q, &admin, &oper);
+ /* After changing schedules, the next entry is the first one
+ * in the new schedule, with a pre-calculated end_time.
+ */
+ next = list_first_entry(&oper->entries,
+ struct sched_entry, list);
+ end_time = next->end_time;
+ }
- next->end_time = end_time;
- taprio_set_budgets(q, oper, next);
+ next->end_time = end_time;
+ taprio_set_budgets(q, oper, next);
first_run:
+ if (ktime_after(end_time, now))
+ break;
+
+ if (!admin && ktime_before(end_time, now)) {
+ s64 diff = ktime_sub(now, end_time);
+ s64 cycles = div64_s64(diff, oper->cycle_time);
+ if (cycles > 0) {
+ oper->cycle_end_time =
+ ktime_add_ns(oper->cycle_end_time,
+ cycles * oper->cycle_time);
+ end_time = ktime_add_ns(
+ end_time, cycles * oper->cycle_time);
+ next->end_time = end_time;
+ for (tc = 0; tc < num_tc; tc++) {
+ if (next->gate_close_time[tc] !=
+ KTIME_MAX)
+ next->gate_close_time
+ [tc] = ktime_add_ns(
+ next->gate_close_time[tc],
+ cycles *
+ oper->cycle_time);
+ }
+ }
+ }
+
+ entry = next;
+ } while (--max_iter > 0);
+
+ if (max_iter == 0)
+ end_time = ktime_add_ns(now, NSEC_PER_USEC);
+
rcu_assign_pointer(q->current_entry, next);
spin_unlock(&q->current_entry_lock);
@@ -1061,6 +1098,13 @@ static int fill_sched_entry(struct taprio_sched *q, struct nlattr **tb,
return -EINVAL;
}
+ if (!FULL_OFFLOAD_IS_ENABLED(q->flags) && interval < NSEC_PER_USEC) {
+ NL_SET_ERR_MSG(
+ extack,
+ "Invalid interval for software taprio, must be at least 1 us");
+ return -EINVAL;
+ }
+
entry->interval = interval;
return 0;
@@ -1171,6 +1215,13 @@ static int parse_taprio_schedule(struct taprio_sched *q, struct nlattr **tb,
return -EINVAL;
}
+ if (!FULL_OFFLOAD_IS_ENABLED(q->flags) &&
+ new->cycle_time < NSEC_PER_USEC) {
+ NL_SET_ERR_MSG(extack,
+ "'cycle_time' is too small for software taprio");
+ return -EINVAL;
+ }
+
taprio_calculate_gate_durations(q, new);
return 0;
base-commit: 8cdeaa50eae8dad34885515f62559ee83e7e8dda
--
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.
next reply other threads:[~2026-07-09 12:33 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-09 12:33 syzbot [this message]
2026-07-24 18:19 ` [PATCH RFC] net/sched: taprio: fix hrtimer interrupt storm on small intervals Bartosz Chronowski
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=bc6a8890-9230-489a-bbce-5c255c1ef01a@mail.kernel.org \
--to=syzbot@kernel.org \
--cc=syzbot@lists.linux.dev \
--cc=syzkaller-upstream-moderation@googlegroups.com \
/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