Re: [PATCH 2/2] evl/sched/quota: Correct budget tracking for preempted threads

Philippe Gerum <[email protected]> Mon, 15 Jun 2026 10:47:00 +0200
Newsgroups dev.linux.lists.xenomai
Message-ID <[email protected]>
Jan Kiszka <[email protected]> writes:

> 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) {

otg can't be NULL by construction since quota_out() is called for thread->sched_class.

-- 
Philippe.