[PATCH v5 2/4] sched/numa: Drive NUMA task tick from execution context

From: Hui Su

Date: Sun Sep 13 2026 - 02:48:59 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.

Order task_tick_fair() as one donor-owned block followed by one
execution-context block. Keep the cache tick in the donor block for this
patch; the following patch moves it with the other execution-context work.
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>
Suggested-by: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
Link: https://lore.kernel.org/r/20260909110348.GZ4120091@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Signed-off-by: Hui Su <sh_def@xxxxxxx>
---
kernel/sched/fair.c | 46 +++++++++++++++++++++++----------------------
1 file changed, 24 insertions(+), 22 deletions(-)

diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 6f1777799371..002456cf9f8d 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) {
+ task_tick_cache(rq, donor);
+ update_misfit_status(donor, rq);
+ check_update_overutilized_status(task_rq(donor));
+
+ task_tick_core(rq, donor);
+ }
}

if (queued)
return;

- if (static_branch_unlikely(&sched_numa_balancing))
- task_tick_numa(rq, donor);
-
- task_tick_cache(rq, donor);
-
- update_misfit_status(donor, rq);
- check_update_overutilized_status(task_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_core(rq, donor);
}

/*
--
2.55.0