Re: [PATCH v2 1/2] sched/numa: Drive NUMA task tick from execution context

From: Tim Chen

Date: Thu Sep 03 2026 - 17:31:30 EST


On Thu, 2026-09-03 at 20:41 +0800, Chen, Yu C wrote:
> Hi Su,
>
> On 9/3/2026 12:11 PM, Hui Su wrote:
> > Proxy execution separates the scheduling context in rq->donor from the
> > execution context in rq->curr. sched_tick() invokes task_tick() for the
> > donor's scheduling class.
> >
> > task_tick_numa() is currently called from task_tick_fair(). This works
> > when the donor is a fair task, but not when a fair task executes on
> > behalf of an RT or deadline donor. In that case the donor's task_tick()
> > still updates the execution task's sum_exec_runtime through
> > update_curr_common(), but task_tick_fair() is not invoked and NUMA scan
> > work for the execution task is not driven.
>
> Thanks for bringing this up. Previously Prateek has suggested to fix the
> rq->donor
> issue [1] and unfortunately I missed the task_tick_cache() part.
>
> Regarding above line in the commit log, although I agree that
> task_tick_numa()
> should be moved one level up, I did not quite get the reason why
> sum_exec_runtime
> is mentioned here, could you please elaborate a little more?
> I guess what you mean is that, in task_tick_numa(), the
> curr->se.sum_exec_runtime
> is used to check if there is a timeout to launch the task_numa_work(), so
> curr->se.sum_exec_runtime has to be up-to-date. With proxy execution, the
> se.sum_exec_runtime is only accumulated in rq->curr rather than rq->donor,
> so passing a "paused" rq->donor.sum_exec_runtime to task_tick_numa() is
> inaccurate?
>
> But I also see that in task_tick_core(), the sum_exec_runtime is also
> leveraged
> to calculate the delta "wall time" via __entity_slice_used():
> se->sum_exec_runtime - se->prev_sum_exec_runtime
> does it mean task_tick_core() also needs to be bring one level up to
> sched_tick()
> and passed with rq->curr?

I think task_tick_core() needs to stay with the donor's context
as it is the scheduling context.

task_tick_core() is not about the execution context -- 
it decides whether the current scheduling context has used
up enough of its slice to let a force-idled SMT sibling run. That
slice belongs to the donor, so the donor is the right task to pass.

There is a separate issue lurking here, task_tick_core() measures consumed slice as
se->sum_exec_runtime - se->prev_sum_exec_runtime. Under proxy the
donor's sum_exec_runtime does not advance (update_se() charges the
runtime to rq->curr instead), so that delta stays near zero and the
force-idle resched may never trigger. 

Passing rq->curr does not fix it either. __entity_slice_used() takes
the runtime and the slice from the same entity, so passing rq->curr
just compares the running task against its own slice. But this check
is about the donor: it asks whether the scheduling context that owns
the CPU has used up its slice. The running task is only borrowing the
CPU through proxy, so its slice is not the one we care about here.

Maybe something like below (only compile tested) to fix the issue.
That said, this is somewhat orthogonal to the issue that the execution context
series is trying to solve. It should be fixed separately.

diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 8dff37059faf..cd240bf52d03 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -14748,10 +14748,29 @@ static void rq_offline_fair(struct rq *rq)
static inline bool
__entity_slice_used(struct sched_entity *se, int min_nr_tasks)
{
- u64 rtime = se->sum_exec_runtime - se->prev_sum_exec_runtime;
- u64 slice = se->slice;
+ u64 vslice, vused;

- return (rtime * min_nr_tasks > slice);
+ /*
+ * @se is the scheduling context (rq->donor). Under proxy execution
+ * it need not be the task executing on the CPU, so its
+ * sum_exec_runtime is not advanced and cannot be used to tell how
+ * much of its slice it has consumed. Its vruntime, however, is
+ * advanced by update_curr() with the proxy runtime, and its EEVDF
+ * deadline reflects the granted slice, so measure the consumed
+ * fraction in virtual time instead.
+ *
+ * This is equivalent to the previous real-time comparison in the
+ * non-proxy case: both @vused and @vslice are scaled by the same
+ * weight factor, so the ratio (and thus the min_nr_tasks test) is
+ * unchanged.
+ */
+ if (vruntime_cmp(se->vruntime, ">=", se->deadline))
+ return true;
+
+ vslice = calc_delta_fair(se->slice, se);
+ vused = vslice - (se->deadline - se->vruntime);
+
+ return (vused * min_nr_tasks > vslice);
}

#define MIN_NR_TASKS_DURING_FORCEIDLE 2

>
> On the other hand, as Prateek mentioned in [1], it seems that
> sum_exec_runtime
> might not the reason for passing rq->curr, but it could be:
> "with "rq->curr->mm" being the one that is being used on CPU",
> both sched_cache and NUMA balance fit Prateek's conclusion.

I agree with you on this.

Thanks.

Tim