mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-01 00:58:39 +02:00 
			
		
		
		
	sched: Fix TASK_state comparisons
Task state is fundamentally a bitmask; direct comparisons are probably
not working as intended. Specifically the normal wait-state have
a number of possible modifiers:
  TASK_UNINTERRUPTIBLE:	TASK_WAKEKILL, TASK_NOLOAD, TASK_FREEZABLE
  TASK_INTERRUPTIBLE:   TASK_FREEZABLE
Specifically, the addition of TASK_FREEZABLE wrecked
__wait_is_interruptible(). This however led to an audit of direct
comparisons yielding the rest of the changes.
Fixes: f5d39b0208 ("freezer,sched: Rewrite core freezer logic")
Reported-by: Christian Borntraeger <borntraeger@linux.ibm.com>
Debugged-by: Christian Borntraeger <borntraeger@linux.ibm.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Tested-by: Christian Borntraeger <borntraeger@linux.ibm.com>
			
			
This commit is contained in:
		
							parent
							
								
									7e9518baed
								
							
						
					
					
						commit
						5aec788aeb
					
				
					 3 changed files with 8 additions and 4 deletions
				
			
		|  | @ -281,7 +281,7 @@ static inline void wake_up_pollfree(struct wait_queue_head *wq_head) | ||||||
| 
 | 
 | ||||||
| #define ___wait_is_interruptible(state)						\ | #define ___wait_is_interruptible(state)						\ | ||||||
| 	(!__builtin_constant_p(state) ||					\ | 	(!__builtin_constant_p(state) ||					\ | ||||||
| 		state == TASK_INTERRUPTIBLE || state == TASK_KILLABLE)		\ | 	 (state & (TASK_INTERRUPTIBLE | TASK_WAKEKILL))) | ||||||
| 
 | 
 | ||||||
| extern void init_wait_entry(struct wait_queue_entry *wq_entry, int flags); | extern void init_wait_entry(struct wait_queue_entry *wq_entry, int flags); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -191,6 +191,8 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) | ||||||
| 	hung_task_show_lock = false; | 	hung_task_show_lock = false; | ||||||
| 	rcu_read_lock(); | 	rcu_read_lock(); | ||||||
| 	for_each_process_thread(g, t) { | 	for_each_process_thread(g, t) { | ||||||
|  | 		unsigned int state; | ||||||
|  | 
 | ||||||
| 		if (!max_count--) | 		if (!max_count--) | ||||||
| 			goto unlock; | 			goto unlock; | ||||||
| 		if (time_after(jiffies, last_break + HUNG_TASK_LOCK_BREAK)) { | 		if (time_after(jiffies, last_break + HUNG_TASK_LOCK_BREAK)) { | ||||||
|  | @ -198,8 +200,10 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) | ||||||
| 				goto unlock; | 				goto unlock; | ||||||
| 			last_break = jiffies; | 			last_break = jiffies; | ||||||
| 		} | 		} | ||||||
| 		/* use "==" to skip the TASK_KILLABLE tasks waiting on NFS */ | 		/* skip the TASK_KILLABLE tasks -- these can be killed */ | ||||||
| 		if (READ_ONCE(t->__state) == TASK_UNINTERRUPTIBLE) | 		state = READ_ONCE(t->__state); | ||||||
|  | 		if ((state & TASK_UNINTERRUPTIBLE) && | ||||||
|  | 		    !(state & TASK_WAKEKILL)) | ||||||
| 			check_hung_task(t, timeout); | 			check_hung_task(t, timeout); | ||||||
| 	} | 	} | ||||||
|  unlock: |  unlock: | ||||||
|  |  | ||||||
|  | @ -8884,7 +8884,7 @@ state_filter_match(unsigned long state_filter, struct task_struct *p) | ||||||
| 	 * When looking for TASK_UNINTERRUPTIBLE skip TASK_IDLE (allows | 	 * When looking for TASK_UNINTERRUPTIBLE skip TASK_IDLE (allows | ||||||
| 	 * TASK_KILLABLE). | 	 * TASK_KILLABLE). | ||||||
| 	 */ | 	 */ | ||||||
| 	if (state_filter == TASK_UNINTERRUPTIBLE && state == TASK_IDLE) | 	if (state_filter == TASK_UNINTERRUPTIBLE && (state & TASK_NOLOAD)) | ||||||
| 		return false; | 		return false; | ||||||
| 
 | 
 | ||||||
| 	return true; | 	return true; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Peter Zijlstra
						Peter Zijlstra