diff --git a/kernel/sched/core.c b/kernel/sched/core.c index c05d05094e70..eb3a7b8bd3cb 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -3919,7 +3919,7 @@ pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf) p = fair_sched_class.pick_next_task(rq, prev, rf); if (unlikely(p == RETRY_TASK)) - goto restart; + goto again; /* Assumes fair_sched_class->next == idle_sched_class */ if (unlikely(!p)) @@ -3928,19 +3928,14 @@ pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf) return p; } -restart: - /* - * Ensure that we put DL/RT tasks before the pick loop, such that they - * can PULL higher prio tasks when we lower the RQ 'priority'. - */ - prev->sched_class->put_prev_task(rq, prev, rf); - if (!rq->nr_running) - newidle_balance(rq, rf); - +again: for_each_class(class) { - p = class->pick_next_task(rq, NULL, NULL); - if (p) + p = class->pick_next_task(rq, prev, rf); + if (p) { + if (unlikely(p == RETRY_TASK)) + goto again; return p; + } } /* The idle class should always have a runnable task: */ diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index 2dc48720f189..4ed2d10c196c 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -1761,13 +1761,39 @@ pick_next_task_dl(struct rq *rq, struct task_struct *prev, struct rq_flags *rf) struct task_struct *p; struct dl_rq *dl_rq; - WARN_ON_ONCE(prev || rf); - dl_rq = &rq->dl; + if (need_pull_dl_task(rq, prev)) { + /* + * This is OK, because current is on_cpu, which avoids it being + * picked for load-balance and preemption/IRQs are still + * disabled avoiding further scheduler activity on it and we're + * being very careful to re-start the picking loop. + */ + rq_unpin_lock(rq, rf); + pull_dl_task(rq); + rq_repin_lock(rq, rf); + /* + * pull_dl_task() can drop (and re-acquire) rq->lock; this + * means a stop task can slip in, in which case we need to + * re-start task selection. + */ + if (rq->stop && task_on_rq_queued(rq->stop)) + return RETRY_TASK; + } + + /* + * When prev is DL, we may throttle it in put_prev_task(). + * So, we update time before we check for dl_nr_running. + */ + if (prev->sched_class == &dl_sched_class) + update_curr_dl(rq); + if (unlikely(!dl_rq->dl_nr_running)) return NULL; + put_prev_task(rq, prev); + dl_se = pick_next_dl_entity(rq, dl_rq); BUG_ON(!dl_se); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 8f8692baa679..31b3e1a10bb3 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6810,7 +6810,7 @@ again: goto idle; #ifdef CONFIG_FAIR_GROUP_SCHED - if (!prev || prev->sched_class != &fair_sched_class) + if (prev->sched_class != &fair_sched_class) goto simple; /* @@ -6887,8 +6887,8 @@ again: goto done; simple: #endif - if (prev) - put_prev_task(rq, prev); + + put_prev_task(rq, prev); do { se = pick_next_entity(cfs_rq, NULL); @@ -6916,9 +6916,6 @@ done: __maybe_unused; return p; idle: - if (!rf) - return NULL; - new_tasks = newidle_balance(rq, rf); /* diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index 8dad5aa600ea..e397a165b408 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -390,9 +390,7 @@ pick_next_task_idle(struct rq *rq, struct task_struct *prev, struct rq_flags *rf { struct task_struct *next = rq->idle; - if (prev) - put_prev_task(rq, prev); - + put_prev_task(rq, prev); set_next_task_idle(rq, next); return next; diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index ebaa4e619684..f65653b139bc 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -1554,11 +1554,38 @@ pick_next_task_rt(struct rq *rq, struct task_struct *prev, struct rq_flags *rf) struct task_struct *p; struct rt_rq *rt_rq = &rq->rt; - WARN_ON_ONCE(prev || rf); + if (need_pull_rt_task(rq, prev)) { + /* + * This is OK, because current is on_cpu, which avoids it being + * picked for load-balance and preemption/IRQs are still + * disabled avoiding further scheduler activity on it and we're + * being very careful to re-start the picking loop. + */ + rq_unpin_lock(rq, rf); + pull_rt_task(rq); + rq_repin_lock(rq, rf); + /* + * pull_rt_task() can drop (and re-acquire) rq->lock; this + * means a dl or stop task can slip in, in which case we need + * to re-start task selection. + */ + if (unlikely((rq->stop && task_on_rq_queued(rq->stop)) || + rq->dl.dl_nr_running)) + return RETRY_TASK; + } + + /* + * We may dequeue prev's rt_rq in put_prev_task(). + * So, we update time before rt_queued check. + */ + if (prev->sched_class == &rt_sched_class) + update_curr_rt(rq); if (!rt_rq->rt_queued) return NULL; + put_prev_task(rq, prev); + p = _pick_next_task_rt(rq); set_next_task_rt(rq, p); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 4f0d1668c283..e14ae174e0d1 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1724,15 +1724,12 @@ struct sched_class { void (*check_preempt_curr)(struct rq *rq, struct task_struct *p, int flags); /* - * Both @prev and @rf are optional and may be NULL, in which case the - * caller must already have invoked put_prev_task(rq, prev, rf). + * It is the responsibility of the pick_next_task() method that will + * return the next task to call put_prev_task() on the @prev task or + * something equivalent. * - * Otherwise it is the responsibility of the pick_next_task() to call - * put_prev_task() on the @prev task or something equivalent, IFF it - * returns a next task. - * - * In that case (@rf != NULL) it may return RETRY_TASK when it finds a - * higher prio class has runnable tasks. + * May return RETRY_TASK when it finds a higher prio class has runnable + * tasks. */ struct task_struct * (*pick_next_task)(struct rq *rq, struct task_struct *prev, diff --git a/kernel/sched/stop_task.c b/kernel/sched/stop_task.c index 7e1cee4e65b2..8f414018d5e0 100644 --- a/kernel/sched/stop_task.c +++ b/kernel/sched/stop_task.c @@ -33,11 +33,10 @@ pick_next_task_stop(struct rq *rq, struct task_struct *prev, struct rq_flags *rf { struct task_struct *stop = rq->stop; - WARN_ON_ONCE(prev || rf); - if (!stop || !task_on_rq_queued(stop)) return NULL; + put_prev_task(rq, prev); set_next_task_stop(rq, stop); return stop;