Re: [PATCH 1/2] evl/sched: Add sched_out handler to sched_class
Philippe Gerum <[email protected]> Mon, 15 Jun 2026 11:59:22 +0200
| Newsgroups | dev.linux.lists.xenomai |
|---|---|
| Message-ID | <[email protected]> |
Jan Kiszka <[email protected]> writes: > On 15.06.26 08:42, Philippe Gerum wrote: >> Philippe Gerum <[email protected]> writes: >> > > You also need to update tg->run_start. Otherwise, you will overcharge > the next time this group is checked here. > Yes, this needs fixing. >> + } else { >> + tg->run_budget = 0; >> + evl_stop_timer(&qs->limit_timer); >> + } >> +} >> + >> +static struct evl_thread *quota_pick(struct evl_rq *rq) >> +{ >> + struct evl_thread *next, *curr = rq->curr; >> + struct evl_sched_quota *qs = &rq->quota; >> + struct evl_quota_group *tg; >> + >> pick: >> - next = evl_get_schedq(&rq->fifo.runnable); >> + next = evl_get_schedq(&qs->runnable); >> if (next == NULL) { >> evl_stop_timer(&qs->limit_timer); >> return NULL; >> } >> >> - /* >> - * As we basically piggyback on the SCHED_FIFO runqueue, make >> - * sure to detect non-quota threads. >> - */ >> tg = next->quota; >> - if (tg == NULL) >> - return next; >> - >> - tg->run_start = now; >> + tg->nr_active--; >> >> /* >> * Don't consider budget if kicked, we have to allow this >> @@ -439,25 +437,29 @@ static struct evl_thread *quota_pick(struct evl_rq *rq) >> */ >> if (next->info & EVL_T_KICKED) { >> evl_stop_timer(&qs->limit_timer); >> - goto out; >> + return next; >> } >> >> + /* >> + * __pick_next_thread() might have requeued the current >> + * thread which is still leading the pack. >> + */ >> + if (curr == next) >> + return next; >> + >> if (ktime_to_ns(tg->run_budget) == 0) { >> - /* Flush expired group members as we go. */ >> + /* Park expired group members as we go. */ >> list_add_tail(&next->quota_expired, &tg->expired); >> goto pick; >> } >> >> - if (otg == tg && evl_timer_is_running(&qs->limit_timer)) >> - /* Same group, leave the running timer untouched. */ >> - goto out; >> - >> - /* Arm limit timer for the new running group. */ >> - evl_start_timer(&qs->limit_timer, >> - ktime_add(now, tg->run_budget), >> - EVL_INFINITE); >> -out: >> - tg->nr_active--; >> + /* Arm new limit timer on change of running group. */ >> + if (curr->quota != tg || !evl_timer_is_running(&qs->limit_timer)) { >> + tg->run_start = evl_ktime_monotonic(); >> + evl_start_timer(&qs->limit_timer, >> + ktime_add(tg->run_start, tg->run_budget), >> + EVL_INFINITE); >> + } > > I still don't fully get how the limit timer should be started on > setparam over the current thread. Will curr be NULL in that case, thus > curr != next? Is that the reason to go for a separate runqueue? > Nope, that one is only to make things clearer, since there is no upside in sharing the runqueues (this was inherited from SCHED_SPORADIC for no obvious reason with hindsight). At some point, I noticed that it would even have introduced a bug in a previous tentative implementation using sched_class as a discriminator between threads, to figure out whether we should call the sched_out() handler. So, the simpler, the better: distinct classes, distinct runqueues. Regarding the limit timer issue, sched_pick() should not shortcut on curr == next. IOW, the rest of the code should run even in this case. By turning on the resched bit (which the quota_setparam() caller does), we would end up in sched_pick() with either of those outcomes out of quota_setparam(): - 'current' switched group, in which case we would arm a limit timer in sched_pick() because curr->quota != next->quota (provided the shortcut on curr == next is gone). - 'current' stays in the same group, so the limit timer still runs unless the budget was already fully consumed. So I would revise my latest patch as follows: diff --git a/kernel/evl/sched/quota.c b/kernel/evl/sched/quota.c index 3ac650c08ccb..8f1321d46cac 100644 --- a/kernel/evl/sched/quota.c +++ b/kernel/evl/sched/quota.c @@ -407,6 +407,7 @@ static void quota_out(struct evl_thread *thread) now = evl_ktime_monotonic(); consumed = ktime_sub(now, tg->run_start); if (consumed < tg->run_budget) { + tg->run_start = now; tg->run_budget = ktime_sub(tg->run_budget, consumed); } else { tg->run_budget = 0; @@ -440,20 +441,13 @@ static struct evl_thread *quota_pick(struct evl_rq *rq) return next; } - /* - * __pick_next_thread() might have requeued the current - * thread which is still leading the pack. - */ - if (curr == next) - return next; - if (ktime_to_ns(tg->run_budget) == 0) { /* Park expired group members as we go. */ list_add_tail(&next->quota_expired, &tg->expired); goto pick; } - /* Arm new limit timer on change of running group. */ + /* Arm new limit timer if need be. */ if (curr->quota != tg || !evl_timer_is_running(&qs->limit_timer)) { tg->run_start = evl_ktime_monotonic(); evl_start_timer(&qs->limit_timer, > > Looks ok otherwise, tests pass as well. > Uh? Ok. That was absolutely unintentional at this stage! -- Philippe.