Skip to content

Commit

Permalink
sched: Split scheduler and execution contexts
Browse files Browse the repository at this point in the history
Let's define the "scheduling context" as all the scheduler state
in task_struct for the task chosen to run, which we'll call the
donor task, and the "execution context" as all state required to
actually run the task.

Currently both are intertwined in task_struct. We want to
logically split these such that we can use the scheduling
context of the donor task selected to be scheduled, but use
the execution context of a different task to actually be run.

To this purpose, introduce rq->donor field to point to the
task_struct chosen from the runqueue by the scheduler, and will
be used for scheduler state, and preserve rq->curr to indicate
the execution context of the task that will actually be run.

This patch introduces the donor field as a union with curr, so it
doesn't cause the contexts to be split yet, but adds the logic to
handle everything separately.

[add additional comments and update more sched_class code to use
 rq::proxy]
[jstultz: Rebased and resolved minor collisions, reworked to use
 accessors, tweaked update_curr_common to use rq_proxy fixing rt
 scheduling issues]

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Juri Lelli <juri.lelli@redhat.com>
Signed-off-by: Connor O'Brien <connoro@google.com>
Signed-off-by: John Stultz <jstultz@google.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Metin Kaya <metin.kaya@arm.com>
Tested-by: K Prateek Nayak <kprateek.nayak@amd.com>
Tested-by: Metin Kaya <metin.kaya@arm.com>
Link: https://lore.kernel.org/r/20241009235352.1614323-8-jstultz@google.com
  • Loading branch information
Peter Zijlstra committed Oct 14, 2024
1 parent 7b3d61f commit af0c8b2
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 80 deletions.
45 changes: 28 additions & 17 deletions kernel/sched/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,7 @@ static enum hrtimer_restart hrtick(struct hrtimer *timer)

rq_lock(rq, &rf);
update_rq_clock(rq);
rq->curr->sched_class->task_tick(rq, rq->curr, 1);
rq->donor->sched_class->task_tick(rq, rq->curr, 1);
rq_unlock(rq, &rf);

return HRTIMER_NORESTART;
Expand Down Expand Up @@ -2135,16 +2135,18 @@ void check_class_changed(struct rq *rq, struct task_struct *p,

void wakeup_preempt(struct rq *rq, struct task_struct *p, int flags)
{
if (p->sched_class == rq->curr->sched_class)
rq->curr->sched_class->wakeup_preempt(rq, p, flags);
else if (sched_class_above(p->sched_class, rq->curr->sched_class))
struct task_struct *donor = rq->donor;

if (p->sched_class == donor->sched_class)
donor->sched_class->wakeup_preempt(rq, p, flags);
else if (sched_class_above(p->sched_class, donor->sched_class))
resched_curr(rq);

/*
* A queue event has occurred, and we're going to schedule. In
* this case, we can save a useless back to back clock update.
*/
if (task_on_rq_queued(rq->curr) && test_tsk_need_resched(rq->curr))
if (task_on_rq_queued(donor) && test_tsk_need_resched(rq->curr))
rq_clock_skip_update(rq);
}

Expand Down Expand Up @@ -2680,7 +2682,7 @@ __do_set_cpus_allowed(struct task_struct *p, struct affinity_context *ctx)
lockdep_assert_held(&p->pi_lock);

queued = task_on_rq_queued(p);
running = task_current(rq, p);
running = task_current_donor(rq, p);

if (queued) {
/*
Expand Down Expand Up @@ -5507,7 +5509,7 @@ unsigned long long task_sched_runtime(struct task_struct *p)
* project cycles that may never be accounted to this
* thread, breaking clock_gettime().
*/
if (task_current(rq, p) && task_on_rq_queued(p)) {
if (task_current_donor(rq, p) && task_on_rq_queued(p)) {
prefetch_curr_exec_start(p);
update_rq_clock(rq);
p->sched_class->update_curr(rq);
Expand Down Expand Up @@ -5575,7 +5577,8 @@ void sched_tick(void)
{
int cpu = smp_processor_id();
struct rq *rq = cpu_rq(cpu);
struct task_struct *curr;
/* accounting goes to the donor task */
struct task_struct *donor;
struct rq_flags rf;
unsigned long hw_pressure;
u64 resched_latency;
Expand All @@ -5586,19 +5589,19 @@ void sched_tick(void)
sched_clock_tick();

rq_lock(rq, &rf);
donor = rq->donor;

curr = rq->curr;
psi_account_irqtime(rq, curr, NULL);
psi_account_irqtime(rq, donor, NULL);

update_rq_clock(rq);
hw_pressure = arch_scale_hw_pressure(cpu_of(rq));
update_hw_load_avg(rq_clock_task(rq), rq, hw_pressure);
curr->sched_class->task_tick(rq, curr, 0);
donor->sched_class->task_tick(rq, donor, 0);
if (sched_feat(LATENCY_WARN))
resched_latency = cpu_resched_latency(rq);
calc_global_load_tick(rq);
sched_core_tick(rq);
task_tick_mm_cid(rq, curr);
task_tick_mm_cid(rq, donor);
scx_tick(rq);

rq_unlock(rq, &rf);
Expand All @@ -5608,8 +5611,8 @@ void sched_tick(void)

perf_event_task_tick();

if (curr->flags & PF_WQ_WORKER)
wq_worker_tick(curr);
if (donor->flags & PF_WQ_WORKER)
wq_worker_tick(donor);

#ifdef CONFIG_SMP
if (!scx_switched_all()) {
Expand Down Expand Up @@ -5676,6 +5679,12 @@ static void sched_tick_remote(struct work_struct *work)
struct task_struct *curr = rq->curr;

if (cpu_online(cpu)) {
/*
* Since this is a remote tick for full dynticks mode,
* we are always sure that there is no proxy (only a
* single task is running).
*/
SCHED_WARN_ON(rq->curr != rq->donor);
update_rq_clock(rq);

if (!is_idle_task(curr)) {
Expand Down Expand Up @@ -6642,6 +6651,7 @@ static void __sched notrace __schedule(int sched_mode)
}

next = pick_next_task(rq, prev, &rf);
rq_set_donor(rq, next);
picked:
clear_tsk_need_resched(prev);
clear_preempt_need_resched();
Expand Down Expand Up @@ -7148,7 +7158,7 @@ void rt_mutex_setprio(struct task_struct *p, struct task_struct *pi_task)
dequeue_task(rq, p, DEQUEUE_SLEEP | DEQUEUE_DELAYED | DEQUEUE_NOCLOCK);

queued = task_on_rq_queued(p);
running = task_current(rq, p);
running = task_current_donor(rq, p);
if (queued)
dequeue_task(rq, p, queue_flag);
if (running)
Expand Down Expand Up @@ -7718,6 +7728,7 @@ void __init init_idle(struct task_struct *idle, int cpu)
rcu_read_unlock();

rq->idle = idle;
rq_set_donor(rq, idle);
rcu_assign_pointer(rq->curr, idle);
idle->on_rq = TASK_ON_RQ_QUEUED;
#ifdef CONFIG_SMP
Expand Down Expand Up @@ -7807,7 +7818,7 @@ void sched_setnuma(struct task_struct *p, int nid)

rq = task_rq_lock(p, &rf);
queued = task_on_rq_queued(p);
running = task_current(rq, p);
running = task_current_donor(rq, p);

if (queued)
dequeue_task(rq, p, DEQUEUE_SAVE);
Expand Down Expand Up @@ -8957,7 +8968,7 @@ void sched_move_task(struct task_struct *tsk)

update_rq_clock(rq);

running = task_current(rq, tsk);
running = task_current_donor(rq, tsk);
queued = task_on_rq_queued(tsk);

if (queued)
Expand Down
39 changes: 20 additions & 19 deletions kernel/sched/deadline.c
Original file line number Diff line number Diff line change
Expand Up @@ -1339,7 +1339,7 @@ static enum hrtimer_restart dl_task_timer(struct hrtimer *timer)
#endif

enqueue_task_dl(rq, p, ENQUEUE_REPLENISH);
if (dl_task(rq->curr))
if (dl_task(rq->donor))
wakeup_preempt_dl(rq, p, 0);
else
resched_curr(rq);
Expand Down Expand Up @@ -1736,11 +1736,11 @@ int dl_server_apply_params(struct sched_dl_entity *dl_se, u64 runtime, u64 perio
*/
static void update_curr_dl(struct rq *rq)
{
struct task_struct *curr = rq->curr;
struct sched_dl_entity *dl_se = &curr->dl;
struct task_struct *donor = rq->donor;
struct sched_dl_entity *dl_se = &donor->dl;
s64 delta_exec;

if (!dl_task(curr) || !on_dl_rq(dl_se))
if (!dl_task(donor) || !on_dl_rq(dl_se))
return;

/*
Expand Down Expand Up @@ -2213,7 +2213,7 @@ static int find_later_rq(struct task_struct *task);
static int
select_task_rq_dl(struct task_struct *p, int cpu, int flags)
{
struct task_struct *curr;
struct task_struct *curr, *donor;
bool select_rq;
struct rq *rq;

Expand All @@ -2224,6 +2224,7 @@ select_task_rq_dl(struct task_struct *p, int cpu, int flags)

rcu_read_lock();
curr = READ_ONCE(rq->curr); /* unlocked access */
donor = READ_ONCE(rq->donor);

/*
* If we are dealing with a -deadline task, we must
Expand All @@ -2234,9 +2235,9 @@ select_task_rq_dl(struct task_struct *p, int cpu, int flags)
* other hand, if it has a shorter deadline, we
* try to make it stay here, it might be important.
*/
select_rq = unlikely(dl_task(curr)) &&
select_rq = unlikely(dl_task(donor)) &&
(curr->nr_cpus_allowed < 2 ||
!dl_entity_preempt(&p->dl, &curr->dl)) &&
!dl_entity_preempt(&p->dl, &donor->dl)) &&
p->nr_cpus_allowed > 1;

/*
Expand Down Expand Up @@ -2299,7 +2300,7 @@ static void check_preempt_equal_dl(struct rq *rq, struct task_struct *p)
* let's hope p can move out.
*/
if (rq->curr->nr_cpus_allowed == 1 ||
!cpudl_find(&rq->rd->cpudl, rq->curr, NULL))
!cpudl_find(&rq->rd->cpudl, rq->donor, NULL))
return;

/*
Expand Down Expand Up @@ -2338,7 +2339,7 @@ static int balance_dl(struct rq *rq, struct task_struct *p, struct rq_flags *rf)
static void wakeup_preempt_dl(struct rq *rq, struct task_struct *p,
int flags)
{
if (dl_entity_preempt(&p->dl, &rq->curr->dl)) {
if (dl_entity_preempt(&p->dl, &rq->donor->dl)) {
resched_curr(rq);
return;
}
Expand All @@ -2348,7 +2349,7 @@ static void wakeup_preempt_dl(struct rq *rq, struct task_struct *p,
* In the unlikely case current and p have the same deadline
* let us try to decide what's the best thing to do...
*/
if ((p->dl.deadline == rq->curr->dl.deadline) &&
if ((p->dl.deadline == rq->donor->dl.deadline) &&
!test_tsk_need_resched(rq->curr))
check_preempt_equal_dl(rq, p);
#endif /* CONFIG_SMP */
Expand Down Expand Up @@ -2380,7 +2381,7 @@ static void set_next_task_dl(struct rq *rq, struct task_struct *p, bool first)
if (!first)
return;

if (rq->curr->sched_class != &dl_sched_class)
if (rq->donor->sched_class != &dl_sched_class)
update_dl_rq_load_avg(rq_clock_pelt(rq), rq, 0);

deadline_queue_push_tasks(rq);
Expand Down Expand Up @@ -2699,8 +2700,8 @@ static int push_dl_task(struct rq *rq)
* can move away, it makes sense to just reschedule
* without going further in pushing next_task.
*/
if (dl_task(rq->curr) &&
dl_time_before(next_task->dl.deadline, rq->curr->dl.deadline) &&
if (dl_task(rq->donor) &&
dl_time_before(next_task->dl.deadline, rq->donor->dl.deadline) &&
rq->curr->nr_cpus_allowed > 1) {
resched_curr(rq);
return 0;
Expand Down Expand Up @@ -2823,7 +2824,7 @@ static void pull_dl_task(struct rq *this_rq)
* deadline than the current task of its runqueue.
*/
if (dl_time_before(p->dl.deadline,
src_rq->curr->dl.deadline))
src_rq->donor->dl.deadline))
goto skip;

if (is_migration_disabled(p)) {
Expand Down Expand Up @@ -2862,9 +2863,9 @@ static void task_woken_dl(struct rq *rq, struct task_struct *p)
if (!task_on_cpu(rq, p) &&
!test_tsk_need_resched(rq->curr) &&
p->nr_cpus_allowed > 1 &&
dl_task(rq->curr) &&
dl_task(rq->donor) &&
(rq->curr->nr_cpus_allowed < 2 ||
!dl_entity_preempt(&p->dl, &rq->curr->dl))) {
!dl_entity_preempt(&p->dl, &rq->donor->dl))) {
push_dl_tasks(rq);
}
}
Expand Down Expand Up @@ -3039,12 +3040,12 @@ static void switched_to_dl(struct rq *rq, struct task_struct *p)
return;
}

if (rq->curr != p) {
if (rq->donor != p) {
#ifdef CONFIG_SMP
if (p->nr_cpus_allowed > 1 && rq->dl.overloaded)
deadline_queue_push_tasks(rq);
#endif
if (dl_task(rq->curr))
if (dl_task(rq->donor))
wakeup_preempt_dl(rq, p, 0);
else
resched_curr(rq);
Expand Down Expand Up @@ -3073,7 +3074,7 @@ static void prio_changed_dl(struct rq *rq, struct task_struct *p,
if (!rq->dl.overloaded)
deadline_queue_pull_task(rq);

if (task_current(rq, p)) {
if (task_current_donor(rq, p)) {
/*
* If we now have a earlier deadline task than p,
* then reschedule, provided p is still on this
Expand Down
28 changes: 14 additions & 14 deletions kernel/sched/fair.c
Original file line number Diff line number Diff line change
Expand Up @@ -1200,12 +1200,12 @@ static inline bool do_preempt_short(struct cfs_rq *cfs_rq,
*/
s64 update_curr_common(struct rq *rq)
{
struct task_struct *curr = rq->curr;
struct task_struct *donor = rq->donor;
s64 delta_exec;

delta_exec = update_curr_se(rq, &curr->se);
delta_exec = update_curr_se(rq, &donor->se);
if (likely(delta_exec > 0))
update_curr_task(curr, delta_exec);
update_curr_task(donor, delta_exec);

return delta_exec;
}
Expand Down Expand Up @@ -1258,7 +1258,7 @@ static void update_curr(struct cfs_rq *cfs_rq)

static void update_curr_fair(struct rq *rq)
{
update_curr(cfs_rq_of(&rq->curr->se));
update_curr(cfs_rq_of(&rq->donor->se));
}

static inline void
Expand Down Expand Up @@ -6815,7 +6815,7 @@ static void hrtick_start_fair(struct rq *rq, struct task_struct *p)
s64 delta = slice - ran;

if (delta < 0) {
if (task_current(rq, p))
if (task_current_donor(rq, p))
resched_curr(rq);
return;
}
Expand All @@ -6830,12 +6830,12 @@ static void hrtick_start_fair(struct rq *rq, struct task_struct *p)
*/
static void hrtick_update(struct rq *rq)
{
struct task_struct *curr = rq->curr;
struct task_struct *donor = rq->donor;

if (!hrtick_enabled_fair(rq) || curr->sched_class != &fair_sched_class)
if (!hrtick_enabled_fair(rq) || donor->sched_class != &fair_sched_class)
return;

hrtick_start_fair(rq, curr);
hrtick_start_fair(rq, donor);
}
#else /* !CONFIG_SCHED_HRTICK */
static inline void
Expand Down Expand Up @@ -8750,9 +8750,9 @@ static void set_next_buddy(struct sched_entity *se)
*/
static void check_preempt_wakeup_fair(struct rq *rq, struct task_struct *p, int wake_flags)
{
struct task_struct *curr = rq->curr;
struct sched_entity *se = &curr->se, *pse = &p->se;
struct cfs_rq *cfs_rq = task_cfs_rq(curr);
struct task_struct *donor = rq->donor;
struct sched_entity *se = &donor->se, *pse = &p->se;
struct cfs_rq *cfs_rq = task_cfs_rq(donor);
int cse_is_idle, pse_is_idle;

if (unlikely(se == pse))
Expand Down Expand Up @@ -8781,7 +8781,7 @@ static void check_preempt_wakeup_fair(struct rq *rq, struct task_struct *p, int
* prevents us from potentially nominating it as a false LAST_BUDDY
* below.
*/
if (test_tsk_need_resched(curr))
if (test_tsk_need_resched(rq->curr))
return;

if (!sched_feat(WAKEUP_PREEMPTION))
Expand Down Expand Up @@ -13080,7 +13080,7 @@ prio_changed_fair(struct rq *rq, struct task_struct *p, int oldprio)
* our priority decreased, or if we are not currently running on
* this runqueue and our priority is higher than the current's
*/
if (task_current(rq, p)) {
if (task_current_donor(rq, p)) {
if (p->prio > oldprio)
resched_curr(rq);
} else
Expand Down Expand Up @@ -13187,7 +13187,7 @@ static void switched_to_fair(struct rq *rq, struct task_struct *p)
* kick off the schedule if running, otherwise just see
* if we can still preempt the current task.
*/
if (task_current(rq, p))
if (task_current_donor(rq, p))
resched_curr(rq);
else
wakeup_preempt(rq, p, 0);
Expand Down
2 changes: 1 addition & 1 deletion kernel/sched/pelt.c
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ int update_irq_load_avg(struct rq *rq, u64 running)
bool update_other_load_avgs(struct rq *rq)
{
u64 now = rq_clock_pelt(rq);
const struct sched_class *curr_class = rq->curr->sched_class;
const struct sched_class *curr_class = rq->donor->sched_class;
unsigned long hw_pressure = arch_scale_hw_pressure(cpu_of(rq));

lockdep_assert_rq_held(rq);
Expand Down
Loading

0 comments on commit af0c8b2

Please sign in to comment.