[PATCH 2/2] evl/sched/quota: Correct budget tracking for preempted threads
Jan Kiszka <[email protected]> Sun, 14 Jun 2026 21:35:55 +0200
| Newsgroups | dev.linux.lists.xenomai |
|---|---|
| Message-ID | <[email protected]> |
From: Jan Kiszka <[email protected]> This was so far not possible due to a missing sched_class callback, causing threads being credited for the preemption time of threads from higher-weighted classes. Signed-off-by: Jan Kiszka <[email protected]> --- kernel/evl/sched/quota.c | 56 +++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/kernel/evl/sched/quota.c b/kernel/evl/sched/quota.c index 0829da711a66a..d331dc2e8f7ac 100644 --- a/kernel/evl/sched/quota.c +++ b/kernel/evl/sched/quota.c @@ -187,11 +187,28 @@ static void quota_refill_handler(struct evl_timer *timer) /* oob stage stalled * raw_spin_unlock(&rq->lock); } +static void charge_usage(struct evl_quota_group *tg, ktime_t now) +{ + ktime_t elapsed; + + elapsed = ktime_sub(now, tg->run_start); + if (elapsed < tg->run_budget) + tg->run_budget = ktime_sub(tg->run_budget, elapsed); + else + tg->run_budget = 0; +} + static void quota_limit_handler(struct evl_timer *timer) /* oob stage stalled */ { + struct evl_quota_group *tg; struct evl_rq *rq; rq = container_of(timer, struct evl_rq, quota.limit_timer); + + tg = rq->curr->quota; + if (tg) + charge_usage(tg, evl_ktime_monotonic()); + /* * Force a rescheduling on the return path of the current * interrupt, so that the budget is re-evaluated for the @@ -253,6 +270,8 @@ static bool quota_setparam(struct evl_thread *thread, /* Dequeued earlier by our caller. */ list_del(&thread->quota_next); thread->quota->nr_threads--; + } else if (thread == evl_current()) { + tg->run_start = evl_ktime_monotonic(); } thread->quota = tg; list_add(&thread->quota_next, &tg->members); @@ -400,21 +419,11 @@ 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 *otg, *tg; - ktime_t now, elapsed; + ktime_t now; now = evl_ktime_monotonic(); otg = curr->quota; - if (otg == NULL) - goto pick; - /* - * Charge the time consumed by the outgoing thread to the - * group it belongs to. - */ - elapsed = ktime_sub(now, otg->run_start); - if (elapsed < otg->run_budget) - otg->run_budget = ktime_sub(otg->run_budget, elapsed); - else - otg->run_budget = 0; + pick: next = evl_get_schedq(&rq->fifo.runnable); if (next == NULL) { @@ -430,8 +439,6 @@ static struct evl_thread *quota_pick(struct evl_rq *rq) if (tg == NULL) return next; - tg->run_start = now; - /* * Don't consider budget if kicked, we have to allow this * thread to run until it eventually switches to in-band @@ -448,9 +455,12 @@ static struct evl_thread *quota_pick(struct evl_rq *rq) goto pick; } - if (otg == tg && evl_timer_is_running(&qs->limit_timer)) + if (otg != tg) { + tg->run_start = now; + } else if (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, @@ -462,6 +472,21 @@ static struct evl_thread *quota_pick(struct evl_rq *rq) return next; } +static void quota_out(struct evl_thread *thread, struct evl_thread *next) +{ + struct evl_quota_group *otg = thread->quota; + struct evl_quota_group *ntg = next->quota; + ktime_t now; + + if (otg && otg != ntg) { + if (ntg) + now = ntg->run_start; + else + now = evl_ktime_monotonic(); + charge_usage(otg, now); + } +} + static void quota_migrate(struct evl_thread *thread, struct evl_rq *rq) { union evl_sched_param param; @@ -786,6 +811,7 @@ struct evl_sched_class evl_sched_quota = { .sched_dequeue = quota_dequeue, .sched_requeue = quota_requeue, .sched_pick = quota_pick, + .sched_out = quota_out, .sched_migrate = quota_migrate, .sched_chkparam = quota_chkparam, .sched_setparam = quota_setparam, -- 2.47.3