mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	sched: Handle priority boosted tasks proper in setscheduler()
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. Commitc365c292d0("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: Ronny Meeus <ronny.meeus@gmail.com> Tested-by: Steven Rostedt <rostedt@goodmis.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Reviewed-by: Steven Rostedt <rostedt@goodmis.org> Cc: <stable@vger.kernel.org> Cc: Borislav Petkov <bp@alien8.de> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Mike Galbraith <umgwanakikbuti@gmail.com> Fixes:c365c292d0("sched: Consider pi boosting in setscheduler()") Link: http://lkml.kernel.org/r/alpine.DEB.2.11.1505051806060.4225@nanos Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
		
							parent
							
								
									3e0283a53f
								
							
						
					
					
						commit
						0782e63bc6
					
				
					 3 changed files with 25 additions and 20 deletions
				
			
		| 
						 | 
					@ -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)
 | 
				
			||||||
	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)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * 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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3300,14 +3300,17 @@ 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().
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
 | 
						if (keep_boost)
 | 
				
			||||||
 | 
							p->prio = rt_mutex_get_effective_prio(p, normal_prio(p));
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
		p->prio = normal_prio(p);
 | 
							p->prio = normal_prio(p);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dl_prio(p->prio))
 | 
						if (dl_prio(p->prio))
 | 
				
			||||||
| 
						 | 
					@ -3408,7 +3411,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, queued, running;
 | 
						int retval, oldprio, oldpolicy = -1, queued, 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;
 | 
				
			||||||
| 
						 | 
					@ -3590,15 +3593,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;
 | 
				
			||||||
| 
						 | 
					@ -3612,7 +3614,7 @@ static int __sched_setscheduler(struct task_struct *p,
 | 
				
			||||||
		put_prev_task(rq, p);
 | 
							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);
 | 
				
			||||||
| 
						 | 
					@ -7346,7 +7348,7 @@ static void normalize_task(struct rq *rq, struct task_struct *p)
 | 
				
			||||||
	queued = task_on_rq_queued(p);
 | 
						queued = task_on_rq_queued(p);
 | 
				
			||||||
	if (queued)
 | 
						if (queued)
 | 
				
			||||||
		dequeue_task(rq, p, 0);
 | 
							dequeue_task(rq, p, 0);
 | 
				
			||||||
	__setscheduler(rq, p, &attr);
 | 
						__setscheduler(rq, p, &attr, false);
 | 
				
			||||||
	if (queued) {
 | 
						if (queued) {
 | 
				
			||||||
		enqueue_task(rq, p, 0);
 | 
							enqueue_task(rq, p, 0);
 | 
				
			||||||
		resched_curr(rq);
 | 
							resched_curr(rq);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue