From: Uladzislau Zhauniarovich The interval validation only requires an entry to cover the transmission of a minimum sized frame at link speed. Virtual devices inflate that budget: veth advertises 10Gb/s and bonding sums the speeds of its members, so length_to_duration(ETH_ZLEN) evaluates to a few tens of nanoseconds and schedules with nanosecond intervals pass validation. In software mode each entry expiry is an hrtimer callback costing on the order of 10us on a debug configuration and about a microsecond on a release build; intervals below that cost rearm the timer with an expiry already in the past, storming the CPU with back to back timer interrupts until RCU stalls. Require 100us per entry in software mode, leaving margin above the timer service cost. Offloaded and txtime-assist schedules never arm the per-entry hrtimer and keep the frame-length based minimum only. Fixes: b5b73b26b3ca ("taprio: Fix allowing too small intervals") Reported-by: syzbot+19d01f6082ec61dd45b2@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=19d01f6082ec61dd45b2 Reported-by: syzbot+8785aaf121cfb2141e0d@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=8785aaf121cfb2141e0d Reported-by: syzbot+2642f347f7309b4880dc@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=2642f347f7309b4880dc Tested-by: syzbot+19d01f6082ec61dd45b2@syzkaller.appspotmail.com Tested-by: syzbot+8785aaf121cfb2141e0d@syzkaller.appspotmail.com Tested-by: syzbot+2642f347f7309b4880dc@syzkaller.appspotmail.com Link: https://lore.kernel.org/all/afe041f6-ef7d-4434-b2d0-096be49b5bcb@mail.kernel.org/ Signed-off-by: Uladzislau Zhauniarovich [jc: exempt txtime-assist, use s64 to keep rejecting negative cycle_time, rework commit message] Signed-off-by: Junjie Cao --- net/sched/sch_taprio.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index f3f90c5d2dca..7519bc5c1aff 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -259,6 +259,26 @@ static int length_to_duration(struct taprio_sched *q, int len) return div_u64(len * atomic64_read(&q->picos_per_byte), PSEC_PER_NSEC); } +/* Software schedules service one hrtimer expiry per entry; intervals + * shorter than the expiry service cost rearm the timer with an expiry + * already in the past and storm the CPU. 100us leaves margin above the + * measured cost on debug configurations. + */ +#define TAPRIO_MIN_SW_INTERVAL_NS (100 * NSEC_PER_USEC) + +static s64 taprio_min_interval(struct taprio_sched *q) +{ + s64 min_interval = length_to_duration(q, ETH_ZLEN); + + /* Only pure software schedules arm the per-entry hrtimer. */ + if (!FULL_OFFLOAD_IS_ENABLED(q->flags) && + !TXTIME_ASSIST_IS_ENABLED(q->flags)) + min_interval = max_t(s64, min_interval, + TAPRIO_MIN_SW_INTERVAL_NS); + + return min_interval; +} + static int duration_to_length(struct taprio_sched *q, u64 duration) { return div_u64(duration * PSEC_PER_NSEC, atomic64_read(&q->picos_per_byte)); @@ -1088,7 +1108,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); + s64 min_duration = taprio_min_interval(q); u32 interval = 0; if (tb[TCA_TAPRIO_SCHED_ENTRY_CMD]) @@ -1216,7 +1236,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; } -- 2.43.0