mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	Vikram reported that his ARM64 compiler managed to 'optimize' away the
preempt_count manipulations in code like:
	preempt_enable_no_resched();
	put_user();
	preempt_disable();
Irrespective of that fact that that is horrible code that should be
fixed for many reasons, it does highlight a deficiency in the generic
preempt_count manipulators. As it is never right to combine/elide
preempt_count manipulations like this.
Therefore sprinkle some volatile in the two generic accessors to
ensure the compiler is aware of the fact that the preempt_count is
observed outside of the regular program-order view and thus cannot be
optimized away like this.
x86; the only arch not using the generic code is not affected as we
do all this in asm in order to use the segment base per-cpu stuff.
Reported-by: Vikram Mulukutla <markivx@codeaurora.org>
Tested-by: Vikram Mulukutla <markivx@codeaurora.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: a787870924 ("sched, arch: Create asm/preempt.h")
Link: http://lkml.kernel.org/r/20160516131751.GH3205@twins.programming.kicks-ass.net
Signed-off-by: Ingo Molnar <mingo@kernel.org>
		
	
			
		
			
				
	
	
		
			87 lines
		
	
	
	
		
			1.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			87 lines
		
	
	
	
		
			1.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#ifndef __ASM_PREEMPT_H
 | 
						|
#define __ASM_PREEMPT_H
 | 
						|
 | 
						|
#include <linux/thread_info.h>
 | 
						|
 | 
						|
#define PREEMPT_ENABLED	(0)
 | 
						|
 | 
						|
static __always_inline int preempt_count(void)
 | 
						|
{
 | 
						|
	return READ_ONCE(current_thread_info()->preempt_count);
 | 
						|
}
 | 
						|
 | 
						|
static __always_inline volatile int *preempt_count_ptr(void)
 | 
						|
{
 | 
						|
	return ¤t_thread_info()->preempt_count;
 | 
						|
}
 | 
						|
 | 
						|
static __always_inline void preempt_count_set(int pc)
 | 
						|
{
 | 
						|
	*preempt_count_ptr() = pc;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * must be macros to avoid header recursion hell
 | 
						|
 */
 | 
						|
#define init_task_preempt_count(p) do { \
 | 
						|
	task_thread_info(p)->preempt_count = FORK_PREEMPT_COUNT; \
 | 
						|
} while (0)
 | 
						|
 | 
						|
#define init_idle_preempt_count(p, cpu) do { \
 | 
						|
	task_thread_info(p)->preempt_count = PREEMPT_ENABLED; \
 | 
						|
} while (0)
 | 
						|
 | 
						|
static __always_inline void set_preempt_need_resched(void)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
static __always_inline void clear_preempt_need_resched(void)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
static __always_inline bool test_preempt_need_resched(void)
 | 
						|
{
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * The various preempt_count add/sub methods
 | 
						|
 */
 | 
						|
 | 
						|
static __always_inline void __preempt_count_add(int val)
 | 
						|
{
 | 
						|
	*preempt_count_ptr() += val;
 | 
						|
}
 | 
						|
 | 
						|
static __always_inline void __preempt_count_sub(int val)
 | 
						|
{
 | 
						|
	*preempt_count_ptr() -= val;
 | 
						|
}
 | 
						|
 | 
						|
static __always_inline bool __preempt_count_dec_and_test(void)
 | 
						|
{
 | 
						|
	/*
 | 
						|
	 * Because of load-store architectures cannot do per-cpu atomic
 | 
						|
	 * operations; we cannot use PREEMPT_NEED_RESCHED because it might get
 | 
						|
	 * lost.
 | 
						|
	 */
 | 
						|
	return !--*preempt_count_ptr() && tif_need_resched();
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Returns true when we need to resched and can (barring IRQ state).
 | 
						|
 */
 | 
						|
static __always_inline bool should_resched(int preempt_offset)
 | 
						|
{
 | 
						|
	return unlikely(preempt_count() == preempt_offset &&
 | 
						|
			tif_need_resched());
 | 
						|
}
 | 
						|
 | 
						|
#ifdef CONFIG_PREEMPT
 | 
						|
extern asmlinkage void preempt_schedule(void);
 | 
						|
#define __preempt_schedule() preempt_schedule()
 | 
						|
extern asmlinkage void preempt_schedule_notrace(void);
 | 
						|
#define __preempt_schedule_notrace() preempt_schedule_notrace()
 | 
						|
#endif /* CONFIG_PREEMPT */
 | 
						|
 | 
						|
#endif /* __ASM_PREEMPT_H */
 |