--- a/net/sched/sch_taprio.c~net-sched-taprio-catch-up-in-bounded-time-when-the-schedule-falls-behind +++ a/net/sched/sch_taprio.c @@ -916,6 +916,51 @@ static bool should_change_schedules(cons return false; } +/* The operational schedule fell behind, e.g. because the timer was delayed + * or the reference clock stepped forward. Advancing one entry per timer + * expiry would replay the whole backlog from hrtimer context, so skip + * complete cycles arithmetically and walk the remaining entries to land on + * the entry covering the current time. + */ +static void taprio_catch_up(struct sched_gate_list *oper, + struct sched_entry **next, ktime_t *next_start, + ktime_t *end_time, ktime_t now) +{ + int budget = 2 * oper->num_entries + 1; + struct sched_entry *entry = *next; + ktime_t start = *next_start; + ktime_t end = *end_time; + s64 behind = ktime_sub(now, end); + + if (oper->cycle_time > 0 && behind >= oper->cycle_time) { + s64 jump = div64_s64(behind, oper->cycle_time) * oper->cycle_time; + + start = ktime_add_ns(start, jump); + end = ktime_add_ns(end, jump); + oper->cycle_end_time = ktime_add_ns(oper->cycle_end_time, jump); + } + + while (ktime_before(end, now) && --budget) { + if (list_is_last(&entry->list, &oper->entries) || + ktime_compare(end, oper->cycle_end_time) == 0) { + entry = list_first_entry(&oper->entries, + struct sched_entry, list); + oper->cycle_end_time = ktime_add_ns(oper->cycle_end_time, + oper->cycle_time); + } else { + entry = list_next_entry(entry, list); + } + + start = end; + end = ktime_add_ns(end, entry->interval); + end = min_t(ktime_t, end, oper->cycle_end_time); + } + + *next = entry; + *next_start = start; + *end_time = end; +} + static enum hrtimer_restart advance_sched(struct hrtimer *timer) { struct taprio_sched *q = container_of(timer, struct taprio_sched, @@ -925,7 +970,7 @@ static enum hrtimer_restart advance_sche int num_tc = netdev_get_num_tc(dev); struct sched_entry *entry, *next; struct Qdisc *sch = q->root; - ktime_t end_time; + ktime_t end_time, next_start, now; int tc; spin_lock(&q->current_entry_lock); @@ -961,14 +1006,19 @@ static enum hrtimer_restart advance_sche next = list_next_entry(entry, list); } - end_time = ktime_add_ns(entry->end_time, next->interval); + next_start = entry->end_time; + end_time = ktime_add_ns(next_start, next->interval); end_time = min_t(ktime_t, end_time, oper->cycle_end_time); + now = hrtimer_cb_get_time(timer); + if (unlikely(ktime_before(end_time, now))) + taprio_catch_up(oper, &next, &next_start, &end_time, now); + 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_close_time[tc] = ktime_add_ns(next_start, next->gate_duration[tc]); }