Commit daf639f6 authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Luis Henriques

sched: Handle priority boosted tasks proper in setscheduler()

commit 0782e63b upstream.

Ronny reported that the following scenario is not handled correctly:

	T1 (prio = 10)
	   lock(rtmutex);

	T2 (prio = 20)
	   lock(rtmutex)
	      boost T1

	T1 (prio = 20)
	   sys_set_scheduler(prio = 30)
	   T1 prio = 30
	   ....
	   sys_set_scheduler(prio = 10)
	   T1 prio = 30

The last step is wrong as T1 should now be back at prio 20.

Commit c365c292 ("sched: Consider pi boosting in setscheduler()")
only handles the case where a boosted tasks tries to lower its
priority.

Fix it by taking the new effective priority into account for the
decision whether a change of the priority is required.
Reported-by: default avatarRonny Meeus <ronny.meeus@gmail.com>
Tested-by: default avatarSteven Rostedt <rostedt@goodmis.org>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: default avatarSteven Rostedt <rostedt@goodmis.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Mike Galbraith <umgwanakikbuti@gmail.com>
Fixes: c365c292 ("sched: Consider pi boosting in setscheduler()")
Link: http://lkml.kernel.org/r/alpine.DEB.2.11.1505051806060.4225@nanosSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
[ luis: backported to 3.16: adjusted context ]
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent a369f12e
...@@ -18,7 +18,7 @@ static inline int rt_task(struct task_struct *p) ...@@ -18,7 +18,7 @@ static inline int rt_task(struct task_struct *p)
#ifdef CONFIG_RT_MUTEXES #ifdef CONFIG_RT_MUTEXES
extern int rt_mutex_getprio(struct task_struct *p); extern int rt_mutex_getprio(struct task_struct *p);
extern void rt_mutex_setprio(struct task_struct *p, int prio); extern void rt_mutex_setprio(struct task_struct *p, int prio);
extern int rt_mutex_check_prio(struct task_struct *task, int newprio); extern int rt_mutex_get_effective_prio(struct task_struct *task, int newprio);
extern struct task_struct *rt_mutex_get_top_task(struct task_struct *task); extern struct task_struct *rt_mutex_get_top_task(struct task_struct *task);
extern void rt_mutex_adjust_pi(struct task_struct *p); extern void rt_mutex_adjust_pi(struct task_struct *p);
static inline bool tsk_is_pi_blocked(struct task_struct *tsk) static inline bool tsk_is_pi_blocked(struct task_struct *tsk)
...@@ -31,9 +31,10 @@ static inline int rt_mutex_getprio(struct task_struct *p) ...@@ -31,9 +31,10 @@ static inline int rt_mutex_getprio(struct task_struct *p)
return p->normal_prio; return p->normal_prio;
} }
static inline int rt_mutex_check_prio(struct task_struct *task, int newprio) static inline int rt_mutex_get_effective_prio(struct task_struct *task,
int newprio)
{ {
return 0; return newprio;
} }
static inline struct task_struct *rt_mutex_get_top_task(struct task_struct *task) static inline struct task_struct *rt_mutex_get_top_task(struct task_struct *task)
......
...@@ -265,15 +265,17 @@ struct task_struct *rt_mutex_get_top_task(struct task_struct *task) ...@@ -265,15 +265,17 @@ struct task_struct *rt_mutex_get_top_task(struct task_struct *task)
} }
/* /*
* Called by sched_setscheduler() to check whether the priority change * Called by sched_setscheduler() to get the priority which will be
* is overruled by a possible priority boosting. * effective after the change.
*/ */
int rt_mutex_check_prio(struct task_struct *task, int newprio) int rt_mutex_get_effective_prio(struct task_struct *task, int newprio)
{ {
if (!task_has_pi_waiters(task)) if (!task_has_pi_waiters(task))
return 0; return newprio;
return task_top_pi_waiter(task)->task->prio <= newprio; if (task_top_pi_waiter(task)->task->prio <= newprio)
return task_top_pi_waiter(task)->task->prio;
return newprio;
} }
/* /*
......
...@@ -3251,15 +3251,18 @@ static void __setscheduler_params(struct task_struct *p, ...@@ -3251,15 +3251,18 @@ static void __setscheduler_params(struct task_struct *p,
/* Actually do priority change: must hold pi & rq lock. */ /* Actually do priority change: must hold pi & rq lock. */
static void __setscheduler(struct rq *rq, struct task_struct *p, static void __setscheduler(struct rq *rq, struct task_struct *p,
const struct sched_attr *attr) const struct sched_attr *attr, bool keep_boost)
{ {
__setscheduler_params(p, attr); __setscheduler_params(p, attr);
/* /*
* If we get here, there was no pi waiters boosting the * Keep a potential priority boosting if called from
* task. It is safe to use the normal prio. * sched_setscheduler().
*/ */
p->prio = normal_prio(p); if (keep_boost)
p->prio = rt_mutex_get_effective_prio(p, normal_prio(p));
else
p->prio = normal_prio(p);
if (dl_prio(p->prio)) if (dl_prio(p->prio))
p->sched_class = &dl_sched_class; p->sched_class = &dl_sched_class;
...@@ -3345,7 +3348,7 @@ static int __sched_setscheduler(struct task_struct *p, ...@@ -3345,7 +3348,7 @@ static int __sched_setscheduler(struct task_struct *p,
int newprio = dl_policy(attr->sched_policy) ? MAX_DL_PRIO - 1 : int newprio = dl_policy(attr->sched_policy) ? MAX_DL_PRIO - 1 :
MAX_RT_PRIO - 1 - attr->sched_priority; MAX_RT_PRIO - 1 - attr->sched_priority;
int retval, oldprio, oldpolicy = -1, on_rq, running; int retval, oldprio, oldpolicy = -1, on_rq, running;
int policy = attr->sched_policy; int new_effective_prio, policy = attr->sched_policy;
unsigned long flags; unsigned long flags;
const struct sched_class *prev_class; const struct sched_class *prev_class;
struct rq *rq; struct rq *rq;
...@@ -3527,15 +3530,14 @@ static int __sched_setscheduler(struct task_struct *p, ...@@ -3527,15 +3530,14 @@ static int __sched_setscheduler(struct task_struct *p,
oldprio = p->prio; oldprio = p->prio;
/* /*
* Special case for priority boosted tasks. * Take priority boosted tasks into account. If the new
* * effective priority is unchanged, we just store the new
* If the new priority is lower or equal (user space view)
* than the current (boosted) priority, we just store the new
* normal parameters and do not touch the scheduler class and * normal parameters and do not touch the scheduler class and
* the runqueue. This will be done when the task deboost * the runqueue. This will be done when the task deboost
* itself. * itself.
*/ */
if (rt_mutex_check_prio(p, newprio)) { new_effective_prio = rt_mutex_get_effective_prio(p, newprio);
if (new_effective_prio == oldprio) {
__setscheduler_params(p, attr); __setscheduler_params(p, attr);
task_rq_unlock(rq, p, &flags); task_rq_unlock(rq, p, &flags);
return 0; return 0;
...@@ -3549,7 +3551,7 @@ static int __sched_setscheduler(struct task_struct *p, ...@@ -3549,7 +3551,7 @@ static int __sched_setscheduler(struct task_struct *p,
p->sched_class->put_prev_task(rq, p); p->sched_class->put_prev_task(rq, p);
prev_class = p->sched_class; prev_class = p->sched_class;
__setscheduler(rq, p, attr); __setscheduler(rq, p, attr, true);
if (running) if (running)
p->sched_class->set_curr_task(rq); p->sched_class->set_curr_task(rq);
...@@ -7112,7 +7114,7 @@ static void normalize_task(struct rq *rq, struct task_struct *p) ...@@ -7112,7 +7114,7 @@ static void normalize_task(struct rq *rq, struct task_struct *p)
on_rq = p->on_rq; on_rq = p->on_rq;
if (on_rq) if (on_rq)
dequeue_task(rq, p, 0); dequeue_task(rq, p, 0);
__setscheduler(rq, p, &attr); __setscheduler(rq, p, &attr, false);
if (on_rq) { if (on_rq) {
enqueue_task(rq, p, 0); enqueue_task(rq, p, 0);
resched_task(rq->curr); resched_task(rq->curr);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment