Hi Yury,

On 7/25/26 3:34 AM, Yury Norov wrote:
On Fri, Jul 24, 2026 at 07:37:27PM +0530, Shrikanth Hegde wrote:
Actively push out task running on a non-preferred CPU. Since the task is
running on the CPU, need to stop the cpu and push the task out.
However, if the task is pinned only to non-preferred CPUs, it will continue
running there. This will help in maintaining the userspace affinities
unlike CPU hotplug or isolated cpusets.

Though code is similar to  __balance_push_cpu_stop and quite close to
push_cpu_stop, it is being kept separate as it provides a cleaner
implementation with CONFIG_PREFERRED_CPU.

Add push_task_work_done flag to protect work buffer.
Works only with FAIR class.

For now, only current running task is pushed out. This keeps the code
simpler. In future optimization maybe done to move all the queued
task on the rq.

Signed-off-by: Shrikanth Hegde <[email protected]>
---
  kernel/sched/core.c  | 78 ++++++++++++++++++++++++++++++++++++++++++++
  kernel/sched/sched.h |  8 +++++
  2 files changed, 86 insertions(+)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 9e8eec4451b6..704043531b24 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -5774,6 +5774,9 @@ void sched_tick(void)
        unsigned long hw_pressure;
        u64 resched_latency;
+ if (!cpu_preferred(cpu))
+               sched_push_current_non_preferred_cpu(rq);
+
        if (housekeeping_cpu(cpu, HK_TYPE_KERNEL_NOISE))
                arch_scale_freq_tick();
@@ -11292,3 +11295,78 @@ void sched_change_end(struct sched_change_ctx *ctx)
                p->sched_class->prio_changed(rq, p, ctx->prio);
        }
  }
+
+#ifdef CONFIG_PREFERRED_CPU
+static DEFINE_PER_CPU(struct cpu_stop_work, npc_push_task_work);
+
+static int sched_non_preferred_cpu_push_stop(void *arg)
+{
+       struct task_struct *p = arg;
+       struct rq *rq = this_rq();
+       struct rq_flags rf;
+       int cpu;
+
+       if (cpu_preferred(rq->cpu)) {
+               scoped_guard(rq_lock, rq)
+                       rq->push_task_work_done = false;
+               put_task_struct(p);
+               return 0;
+       }
+
+       raw_spin_lock_irq(&p->pi_lock);
+
+       /* This could take rq lock. So call it before rq lock is taken */
+       cpu = select_fallback_rq(rq->cpu, p);
+       rq_lock(rq, &rf);
+       rq->push_task_work_done = false;
+       update_rq_clock(rq);
+
+       context_unsafe_alias(rq);
+
+       if (task_rq(p) == rq && task_on_rq_queued(p) &&
+           !is_migration_disabled(p))
+               rq = __migrate_task(rq, &rf, p, cpu);
+
+       rq_unlock(rq, &rf);
+       raw_spin_unlock_irq(&p->pi_lock);
+       put_task_struct(p);
+
+       return 0;

You always return 0, and don't test the return value. Just make it
void, or return (and handle) some error, please.


Currently all the function callbacks of stop_one_cpu_nowait return 0.
This can't be changed to void today since signature mandates int.

typedef int (*cpu_stop_fn_t)(void *arg);

Also even if return value is of some error, it doesn't make any difference.
This is because in cpu_stopper_thread, return value is propagated only if it
wait for work to complete semantic. stop_one_cpu_nowait sets done=NULL.

bool stop_one_cpu_nowait(unsigned int cpu, cpu_stop_fn_t fn, void *arg,
                        struct cpu_stop_work *work_buf)
{
        *work_buf = (struct cpu_stop_work){ .fn = fn, .arg = arg, .caller = 
_RET_IP_, };
        return cpu_stop_queue_work(cpu, work_buf);
}

cpu_stopper_thread:
                ret = fn(arg);
                if (done) {
                        if (ret)
                                done->ret = ret;
                        cpu_stop_signal_done(done);
                }

If we really need to return void then we need a new function signature for 
nowait
variant. Adding separate function signature just for nowait isn't probably 
worth.
What do you think?


Reply via email to