[PATCH v4 2/5] sched/numa: Drive NUMA task tick from execution context

From: Hui Su

Date: Wed Sep 09 2026 - 05:50:11 EST


Proxy execution separates the scheduling context in rq->donor from the
execution context in rq->curr. The task tick dispatcher can therefore
invoke task_tick_fair() for either side of a cross-class proxy
relationship.

task_tick_numa() operates on state associated with the task actually
executing, including its mm and NUMA work state. Task-level execution
runtime is likewise accounted to rq->curr, and task_tick_numa() uses that
runtime to drive periodic NUMA scanning.

Split task_tick_fair() into donor and execution-context sections and run
the NUMA tick only when rq->curr belongs to the fair scheduling class. The
donor class tick runs first, so execution runtime is accounted before the
NUMA tick consumes it.

Fixes: 7de9d4f94638 ("sched: Start blocked_on chain processing in find_proxy_task()")
Suggested-by: K Prateek Nayak <kprateek.nayak@xxxxxxx>
Suggested-by: Tim Chen <tim.c.chen@xxxxxxxxxxxxxxx>
Signed-off-by: Hui Su <sh_def@xxxxxxx>
---
kernel/sched/fair.c | 44 +++++++++++++++++++++++---------------------
1 file changed, 23 insertions(+), 21 deletions(-)

diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 6f1777799371..a8c7a9a29ace 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -15061,41 +15061,43 @@ static inline void task_tick_core(struct rq *rq, struct task_struct *curr) {}
*/
static void task_tick_fair(struct rq *rq, int queued)
{
- struct task_struct *donor = rq->donor;
- struct sched_entity *se;
+ struct task_struct *curr = rq->curr, *donor = rq->donor;

- if (donor->sched_class != &fair_sched_class)
- return;
+ if (donor->sched_class == &fair_sched_class) {
+ struct sched_entity *se = &donor->se;

- se = &donor->se;
+ if (se->on_rq) {
+ unsigned long weight = NICE_0_LOAD;
+ struct cfs_rq *cfs_rq;

- if (se->on_rq) {
- unsigned long weight = NICE_0_LOAD;
- struct cfs_rq *cfs_rq;
+ for_each_sched_entity(se) {
+ cfs_rq = cfs_rq_of(se);
+ entity_tick(cfs_rq, se, queued);

- for_each_sched_entity(se) {
- cfs_rq = cfs_rq_of(se);
- entity_tick(cfs_rq, se, queued);
+ weight = __calc_prop_weight(cfs_rq, se, weight);
+ }

- weight = __calc_prop_weight(cfs_rq, se, weight);
+ se = &donor->se;
+ reweight_eevdf(cfs_rq, se, weight, se->on_rq);
}
-
- se = &donor->se;
- reweight_eevdf(cfs_rq, se, weight, se->on_rq);
}

if (queued)
return;

- if (static_branch_unlikely(&sched_numa_balancing))
- task_tick_numa(rq, donor);
+ /* Update state owned by the execution context. */
+ if (curr->sched_class == &fair_sched_class &&
+ static_branch_unlikely(&sched_numa_balancing))
+ task_tick_numa(rq, curr);

- task_tick_cache(rq, donor);
+ if (donor->sched_class == &fair_sched_class) {
+ task_tick_cache(rq, donor);

- update_misfit_status(donor, rq);
- check_update_overutilized_status(task_rq(donor));
+ update_misfit_status(donor, rq);
+ check_update_overutilized_status(task_rq(donor));

- task_tick_core(rq, donor);
+ task_tick_core(rq, donor);
+ }
}

/*
--
2.55.0