preempt_count rework
Subsystems (22)
Peter Zijlstra
maintainercover preempt_count rework
These patches optimize preempt_enable by firstly folding the preempt and need_resched tests into one -- this should work for all architectures. And secondly by providing per-arch preempt_count implementations; with x86 using per-cpu preempt_count for fastest access.
These patches have so far only been compiled for defconfig-x86_64 + CONFIG_PREEMPT=y and boot tested with kvm -smp 4 upto wanting to mount root.
It still needs asm volatile("call preempt_schedule": : :"memory"); as per Andi's other patches to avoid the C calling convention cluttering the preempt_enable() sites.
--- arch/alpha/include/asm/Kbuild | 1 + arch/arc/include/asm/Kbuild | 1 + arch/arm/include/asm/Kbuild | 1 + arch/arm64/include/asm/Kbuild | 1 + arch/avr32/include/asm/Kbuild | 1 + arch/blackfin/include/asm/Kbuild | 1 + arch/c6x/include/asm/Kbuild | 1 + arch/cris/include/asm/Kbuild | 1 + arch/frv/include/asm/Kbuild | 1 + arch/h8300/include/asm/Kbuild | 1 + arch/hexagon/include/asm/Kbuild | 1 + arch/ia64/include/asm/Kbuild | 1 + arch/m32r/include/asm/Kbuild | 1 + arch/m68k/include/asm/Kbuild | 1 + arch/metag/include/asm/Kbuild | 1 + arch/microblaze/include/asm/Kbuild | 1 + arch/mips/include/asm/Kbuild | 1 + arch/mn10300/include/asm/Kbuild | 1 + arch/openrisc/include/asm/Kbuild | 1 + arch/parisc/include/asm/Kbuild | 1 + arch/powerpc/include/asm/Kbuild | 1 + arch/s390/include/asm/Kbuild | 1 + arch/score/include/asm/Kbuild | 1 + arch/sh/include/asm/Kbuild | 1 + arch/sparc/include/asm/Kbuild | 1 + arch/tile/include/asm/Kbuild | 1 + arch/um/include/asm/Kbuild | 1 + arch/unicore32/include/asm/Kbuild | 1 + arch/x86/include/asm/thread_info.h | 5 ++--- arch/x86/kernel/asm-offsets.c | 1 - arch/x86/kernel/cpu/common.c | 5 +++++ arch/x86/kernel/entry_32.S | 7 ++----- arch/x86/kernel/entry_64.S | 4 +--- arch/x86/kernel/process_32.c | 10 ++++++++++ arch/x86/kernel/process_64.c | 10 ++++++++++ arch/xtensa/include/asm/Kbuild | 1 + include/linux/preempt.h | 49 ++++++++++++++++++++++++++++++++++++++----------- include/linux/sched.h | 2 +- include/linux/thread_info.h | 1 + include/trace/events/sched.h | 2 +- init/main.c | 2 +- kernel/context_tracking.c | 3 +-- kernel/cpu/idle.c | 10 ++++++++++ kernel/sched/core.c | 31 +++++++++++++++++++------------ kernel/softirq.c | 4 ++-- kernel/timer.c | 8 ++++---- lib/smp_processor_id.c | 3 +-- 47 files changed, 138 insertions(+), 48 deletions(-)
Peter Zijlstra
maintainerRFC PATCH 1/5 sched: Introduce preempt_count accessor functions
Replace the single preempt_count() 'function' that's an lvalue with two proper functions:
preempt_count() - returns the preempt_count value as rvalue preempt_count_ptr() - returns a pointer to the preempt_count
Then change all sites that modify the preempt count to use preempt_count_ptr().
--- include/linux/preempt.h | 20 ++++++++++++++------ init/main.c | 2 +- kernel/sched/core.c | 4 ++-- kernel/softirq.c | 4 ++-- kernel/timer.c | 8 ++++---- lib/smp_processor_id.c | 3 +-- 6 files changed, 24 insertions(+), 17 deletions(-)
include/linux/preempt.h +14 -6
@@ -10,19 +10,27 @@#include <linux/linkage.h> #include <linux/list.h> +static __always_inline int preempt_count(void) +{ +return current_thread_info ()->preempt_count; +} + +static __always_inline int *preempt_count_ptr(void) +{ +return ¤t_thread_info ()->preempt_count; +} +#if defined(CONFIG_DEBUG_PREEMPT ) || defined(CONFIG_PREEMPT_TRACER )extern void add_preempt_count (int val);extern void sub_preempt_count (int val);#else -# define add_preempt_count (val) do { preempt_count() += (val); } while (0) -# define sub_preempt_count (val) do { preempt_count() -= (val); } while (0) +# define add_preempt_count (val) do { *preempt_count_ptr() += (val); } while (0) +# define sub_preempt_count (val) do { *preempt_count_ptr() -= (val); } while (0)#endif #define inc_preempt_count () add_preempt_count(1)#define dec_preempt_count () sub_preempt_count(1) -#define preempt_count () (current_thread_info()->preempt_count) -#ifdef CONFIG_PREEMPT asmlinkage void preempt_schedule(void);
@@ -81,9 +89,9 @@ do { \/* For debugging and tracer internals only! */ #define add_preempt_count_notrace (val) \ - do { preempt_count() += (val); } while (0) + do { *preempt_count_ptr() += (val); } while (0)#define sub_preempt_count_notrace (val) \ - do { preempt_count() -= (val); } while (0) + do { *preempt_count_ptr() -= (val); } while (0)#define inc_preempt_count_notrace () add_preempt_count_notrace(1)#define dec_preempt_count_notrace () sub_preempt_count_notrace(1)
init/main.c +1 -1
@@ -690,7 +690,7 @@ int __init_or_module do_one_initcall(iniif (preempt_count () != count) {sprintf (msgbuf,"preemption imbalance " ); -preempt_count () = count; + *preempt_count_ptr () = count; }if (irqs_disabled ()) {strlcat (msgbuf,"disabled interrupts " ,sizeof (msgbuf));
kernel/sched/core.c +2 -2
@@ -2233,7 +2233,7 @@ void __kprobes add_preempt_count(int valif (DEBUG_LOCKS_WARN_ON ((preempt_count () <0 )))return ;#endif -preempt_count () += val; + *preempt_count_ptr () += val;#ifdef CONFIG_DEBUG_PREEMPT /* * Spinlock count overflowing soon?
@@ -2264,7 +2264,7 @@ void __kprobes sub_preempt_count(int valif (preempt_count () == val)trace_preempt_on (CALLER_ADDR0 ,get_parent_ip (CALLER_ADDR1 )); -preempt_count () -= val; + *preempt_count_ptr () -= val; }EXPORT_SYMBOL (sub_preempt_count);
kernel/softirq.c +2 -2
@@ -106,7 +106,7 @@ static void __local_bh_disable(unsigned* We must manually increment preempt_count here and manually * call the trace_preempt_off later. */ -preempt_count () += cnt; + *preempt_count_ptr () += cnt;/* * Were softirqs turned off above: */
@@ -256,7 +256,7 @@ asmlinkage void __do_softirq(void)" exited with %08x?\n" , vec_nr, softirq_to_name[vec_nr], h->action, prev_count, preempt_count()); -preempt_count () = prev_count; + *preempt_count_ptr () = prev_count; }rcu_bh_qs (cpu);
kernel/timer.c +4 -4
@@ -1092,7 +1092,7 @@ static int cascade(struct tvec_base *basstatic void call_timer_fn (struct timer_list *timer,void (*fn)(unsigned long ),unsigned long data) { -int preempt_count =preempt_count (); +int count =preempt_count ();#ifdef CONFIG_LOCKDEP /*
@@ -1119,16 +1119,16 @@ static void call_timer_fn(struct timer_llock_map_release (&lockdep_map); -if (preempt_count !=preempt_count ()) { +if (count !=preempt_count ()) {WARN_ONCE (1 ,"timer: %pF preempt leak: %08x -> %08x\n" , - fn, preempt_count,preempt_count ()); + fn, count,preempt_count ());/* * Restore the preempt count. That gives us a decent * chance to survive and extract information. If the * callback kept a lock held, bad luck, but not worse * than the BUG() we had. */ -preempt_count () = preempt_count; + *preempt_count_ptr () = count; } }
lib/smp_processor_id.c +1 -2
@@ -9,10 +9,9 @@ notrace unsigned int debug_smp_processor_id(void) { -unsigned long preempt_count =preempt_count ();int this_cpu =raw_smp_processor_id (); -if (likely (preempt_count)) +if (likely (preempt_count ())) goto out;if (irqs_disabled ())
Peter Zijlstra
maintainerRFC PATCH 2/5 sched: Add NEED_RESCHED to the preempt_count
In reply to Peter ZijlstraIn order to combine the preemption and need_resched test we need to fold the need_resched information into the preempt_count value.
We keep the existing TIF_NEED_RESCHED infrastructure in place but at 3 sites test it and fold its value into preempt_count; namely:
- resched_task() when setting TIF_NEED_RESCHED on the current task
- scheduler_ipi() when resched_task() sets TIF_NEED_RESCHED on a remote task it follows it up with a reschedule IPI and we can modify the cpu local preempt_count from there.
- cpu_idle_loop() for when resched_task() found tsk_is_polling().
We use an inverted bitmask to indicate need_resched so that a 0 means both need_resched and !atomic.
Also remove the barrier() in preempt_enable() between preempt_enable_no_resched() and preempt_check_resched() to avoid having to reload the preemption value and allow the compiler to use the flags of the previuos decrement. I couldn't come up with any sane reason for this barrier() to be there as preempt_enable_no_resched() already has a barrier() before doing the decrement.
--- include/linux/preempt.h | 42 +++++++++++++++++++++++++++++++++++++----- include/linux/sched.h | 2 +- include/linux/thread_info.h | 1 + kernel/context_tracking.c | 3 +-- kernel/cpu/idle.c | 10 ++++++++++ kernel/sched/core.c | 20 ++++++++++++++------ 6 files changed, 64 insertions(+), 14 deletions(-)
include/linux/preempt.h +37 -5
@@ -10,9 +10,19 @@#include <linux/linkage.h> #include <linux/list.h> +/* +* We use the MSB mostly because its available; see <linux/hardirq.h> for +* the other bits. +*/ +#define PREEMPT_NEED_RESCHED 0x80000000 + +/* +* We mask the PREEMPT_NEED_RESCHED bit so as not to confuse all current users +* that think a non-zero value indicates we cannot preempt. +*/ static __always_inline int preempt_count(void) { -return current_thread_info ()->preempt_count; +return current_thread_info ()->preempt_count & ~PREEMPT_NEED_RESCHED ; } static __always_inline int *preempt_count_ptr(void)
@@ -20,6 +30,30 @@ static __always_inline int *preempt_counreturn ¤t_thread_info ()->preempt_count; } +/* +* We fold the NEED_RESCHED bit into the preempt count such that +* preempt_enable() can decrement and test for needing to reschedule with a +* single instruction. +* +* We invert the actual bit, so that when the decrement hits 0 we know we both +* need to resched (the bit is cleared) and can resched (no preempt count). +*/ + +static __always_inline void set_preempt_need_resched(void) +{ + *preempt_count_ptr () &= ~PREEMPT_NEED_RESCHED ; +} + +static __always_inline void clear_preempt_need_resched(void) +{ + *preempt_count_ptr () |=PREEMPT_NEED_RESCHED ; +} + +static __always_inline bool test_preempt_need_resched(void) +{ +return !(*preempt_count_ptr () &PREEMPT_NEED_RESCHED ); +} +#if defined(CONFIG_DEBUG_PREEMPT ) || defined(CONFIG_PREEMPT_TRACER )extern void add_preempt_count (int val);extern void sub_preempt_count (int val);
@@ -37,7 +71,7 @@ asmlinkage void preempt_schedule(void);#define preempt_check_resched () \ do { \ - if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) \ + if (unlikely(!*preempt_count_ptr())) \ preempt_schedule(); \ } while (0)
@@ -47,7 +81,7 @@ void preempt_schedule_context(void);#define preempt_check_resched_context () \ do { \ - if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) \ + if (unlikely(!*preempt_count_ptr())) \ preempt_schedule_context(); \ } while (0)#else
@@ -83,7 +117,6 @@ do { \#define preempt_enable () \ do { \ preempt_enable_no_resched(); \ - barrier(); \ preempt_check_resched(); \ } while (0)
@@ -111,7 +144,6 @@ do { \#define preempt_enable_notrace () \ do { \ preempt_enable_no_resched_notrace(); \ - barrier(); \ preempt_check_resched_context(); \ } while (0)
include/linux/sched.h +1 -1
@@ -2405,7 +2405,7 @@ static inline int signal_pending_state(lstatic inline int need_resched (void ) { -return unlikely (test_thread_flag (TIF_NEED_RESCHED )); +return unlikely (test_preempt_need_resched ()); }/*
include/linux/thread_info.h +1 -0
@@ -104,6 +104,7 @@ static inline int test_ti_thread_flag(st#define test_thread_flag (flag) \ test_ti_thread_flag(current_thread_info(), flag) +#define test_need_resched () test_thread_flag(TIF_NEED_RESCHED)#define set_need_resched () set_thread_flag(TIF_NEED_RESCHED)#define clear_need_resched () clear_thread_flag(TIF_NEED_RESCHED)
kernel/context_tracking.c +1 -2
@@ -87,10 +87,9 @@ void user_enter(void)*/ void __sched notrace preempt_schedule_context(void) { -struct thread_info *ti =current_thread_info ();enum ctx_state prev_ctx; -if (likely (ti->preempt_count ||irqs_disabled ())) +if (likely (preempt_count () ||irqs_disabled ()))return ;/*
kernel/cpu/idle.c +10 -0
@@ -106,6 +106,16 @@ static void cpu_idle_loop(void)current_set_polling (); }arch_cpu_idle_exit (); +/* +* We need to test and propagate the TIF_NEED_RESCHED +* bit here because we might not have send the +* reschedule IPI to idle tasks. +* +* XXX is there a better place in the generic idle code +* for this? +*/ +if (test_need_resched ()) +set_preempt_need_resched (); }tick_nohz_idle_exit ();schedule_preempt_disabled ();
kernel/sched/core.c +14 -6
@@ -526,8 +526,10 @@ void resched_task(struct task_struct *p)set_tsk_need_resched (p); cpu =task_cpu (p); -if (cpu ==smp_processor_id ()) +if (cpu ==smp_processor_id ()) { +set_preempt_need_resched ();return ; + }/* NEED_RESCHED must be visible before we test polling */ smp_mb ();
@@ -1411,6 +1413,14 @@ static void sched_ttwu_pending(void)void scheduler_ipi (void ) { +/* +* Fold TIF_NEED_RESCHED into the preempt_count; anybody setting +* TIF_NEED_RESCHED remotely (for the first time) will also send +* this IPI. +*/ +if (test_need_resched ()) +set_preempt_need_resched (); +if (llist_empty (&this_rq ()->wake_list) && !tick_nohz_full_cpu (smp_processor_id ()) && !got_nohz_idle_kick ())
@@ -2433,6 +2443,7 @@ static void __sched __schedule(void)put_prev_task (rq, prev); next =pick_next_task (rq);clear_tsk_need_resched (prev); +clear_preempt_need_resched (); rq->skip_clock_update =0 ;if (likely (prev != next)) {
@@ -2515,13 +2526,11 @@ void __sched schedule_preempt_disabled(v*/ asmlinkage void __sched notrace preempt_schedule(void) { -struct thread_info *ti =current_thread_info (); -/* * If there is a non-zero preempt_count or interrupts are disabled, * we do not want to preempt the current task. Just return.. */ -if (likely (ti->preempt_count ||irqs_disabled ())) +if (likely (preempt_count () ||irqs_disabled ()))return ; do {
@@ -2546,11 +2555,10 @@ EXPORT_SYMBOL(preempt_schedule);*/ asmlinkage void __sched preempt_schedule_irq(void) { -struct thread_info *ti =current_thread_info ();enum ctx_state prev_state;/* Catch callers which need to be fixed */ -BUG_ON (ti->preempt_count || !irqs_disabled ()); +BUG_ON (preempt_count () || !irqs_disabled ()); prev_state =exception_enter ();
Peter Zijlstra
maintainerRFC PATCH 3/5 sched, arch: Create asm/preempt.h
In reply to Peter ZijlstraIn order to prepare to per-arch implementations of preempt_count move the required bits into an asm-generic header and use this for all archs.
--- arch/alpha/include/asm/Kbuild | 1 + arch/arc/include/asm/Kbuild | 1 + arch/arm/include/asm/Kbuild | 1 + arch/arm64/include/asm/Kbuild | 1 + arch/avr32/include/asm/Kbuild | 1 + arch/blackfin/include/asm/Kbuild | 1 + arch/c6x/include/asm/Kbuild | 1 + arch/cris/include/asm/Kbuild | 1 + arch/frv/include/asm/Kbuild | 1 + arch/h8300/include/asm/Kbuild | 1 + arch/hexagon/include/asm/Kbuild | 1 + arch/ia64/include/asm/Kbuild | 1 + arch/m32r/include/asm/Kbuild | 1 + arch/m68k/include/asm/Kbuild | 1 + arch/metag/include/asm/Kbuild | 1 + arch/microblaze/include/asm/Kbuild | 1 + arch/mips/include/asm/Kbuild | 1 + arch/mn10300/include/asm/Kbuild | 1 + arch/openrisc/include/asm/Kbuild | 1 + arch/parisc/include/asm/Kbuild | 1 + arch/powerpc/include/asm/Kbuild | 1 + arch/s390/include/asm/Kbuild | 1 + arch/score/include/asm/Kbuild | 1 + arch/sh/include/asm/Kbuild | 1 + arch/sparc/include/asm/Kbuild | 1 + arch/tile/include/asm/Kbuild | 1 + arch/um/include/asm/Kbuild | 1 + arch/unicore32/include/asm/Kbuild | 1 + arch/x86/include/asm/Kbuild | 1 + arch/xtensa/include/asm/Kbuild | 1 + include/asm-generic/preempt.h | 20 ++++++++++++++++++++ include/linux/preempt.h | 15 +-------------- 32 files changed, 51 insertions(+), 14 deletions(-)
arch/alpha/include/asm/Kbuild +1 -0
@@ -3,3 +3,4 @@ generic-y += clkdev.h generic-y += exec.h generic-y += trace_clock.h +generic-y += preempt.h
arch/arc/include/asm/Kbuild +1 -0
@@ -46,3 +46,4 @@ generic-y += ucontext.h generic-y += user.h generic-y += vga.h generic-y += xor.h +generic-y += preempt.h
arch/arm/include/asm/Kbuild +1 -0
@@ -33,3 +33,4 @@ generic-y += timex.h generic-y += trace_clock.h generic-y += types.h generic-y += unaligned.h +generic-y += preempt.h
arch/arm64/include/asm/Kbuild +1 -0
@@ -50,3 +50,4 @@ generic-y += unaligned.h generic-y += user.h generic-y += vga.h generic-y += xor.h +generic-y += preempt.h
arch/avr32/include/asm/Kbuild +1 -0
@@ -3,3 +3,4 @@ generic-y += clkdev.h generic-y += exec.h generic-y += trace_clock.h generic-y += param.h +generic-y += preempt.h
arch/blackfin/include/asm/Kbuild +1 -0
@@ -44,3 +44,4 @@ generic-y += ucontext.h generic-y += unaligned.h generic-y += user.h generic-y += xor.h +generic-y += preempt.h
arch/c6x/include/asm/Kbuild +1 -0
@@ -56,3 +56,4 @@ generic-y += ucontext.h generic-y += user.h generic-y += vga.h generic-y += xor.h +generic-y += preempt.h
arch/cris/include/asm/Kbuild +1 -0
@@ -11,3 +11,4 @@ generic-y += module.h generic-y += trace_clock.h generic-y += vga.h generic-y += xor.h +generic-y += preempt.h
arch/frv/include/asm/Kbuild +1 -0
@@ -2,3 +2,4 @@ generic-y += clkdev.h generic-y += exec.h generic-y += trace_clock.h +generic-y += preempt.h
arch/h8300/include/asm/Kbuild +1 -0
@@ -6,3 +6,4 @@ generic-y += mmu.h generic-y += module.h generic-y += trace_clock.h generic-y += xor.h +generic-y += preempt.h
arch/hexagon/include/asm/Kbuild +1 -0
@@ -53,3 +53,4 @@ generic-y += types.h generic-y += ucontext.h generic-y += unaligned.h generic-y += xor.h +generic-y += preempt.h
arch/ia64/include/asm/Kbuild +1 -0
@@ -3,3 +3,4 @@ generic-y += clkdev.h generic-y += exec.h generic-y += kvm_para.h generic-y += trace_clock.h +generic-y += preempt.h
arch/m32r/include/asm/Kbuild +1 -0
@@ -3,3 +3,4 @@ generic-y += clkdev.h generic-y += exec.h generic-y += module.h generic-y += trace_clock.h +generic-y += preempt.h
arch/m68k/include/asm/Kbuild +1 -0
@@ -31,3 +31,4 @@ generic-y += trace_clock.h generic-y += types.h generic-y += word-at-a-time.h generic-y += xor.h +generic-y += preempt.h
arch/metag/include/asm/Kbuild +1 -0
@@ -52,3 +52,4 @@ generic-y += unaligned.h generic-y += user.h generic-y += vga.h generic-y += xor.h +generic-y += preempt.h
arch/microblaze/include/asm/Kbuild +1 -0
@@ -3,3 +3,4 @@ generic-y += clkdev.h generic-y += exec.h generic-y += trace_clock.h generic-y += syscalls.h +generic-y += preempt.h
arch/mips/include/asm/Kbuild +1 -0
@@ -1,2 +1,3 @@ # MIPS headers generic-y += trace_clock.h +generic-y += preempt.h
arch/mn10300/include/asm/Kbuild +1 -0
@@ -2,3 +2,4 @@ generic-y += clkdev.h generic-y += exec.h generic-y += trace_clock.h +generic-y += preempt.h
arch/openrisc/include/asm/Kbuild +1 -0
@@ -67,3 +67,4 @@ generic-y += ucontext.h generic-y += user.h generic-y += word-at-a-time.h generic-y += xor.h +generic-y += preempt.h
arch/parisc/include/asm/Kbuild +1 -0
@@ -4,3 +4,4 @@ generic-y += word-at-a-time.h auxvec.h u div64.h irq_regs.h kdebug.h kvm_para.h local64.h local.h param.h \ poll.h xor.h clkdev.h exec.h generic-y += trace_clock.h +generic-y += preempt.h
arch/powerpc/include/asm/Kbuild +1 -0
@@ -2,3 +2,4 @@ generic-y += clkdev.h generic-y += rwsem.h generic-y += trace_clock.h +generic-y += preempt.h
arch/s390/include/asm/Kbuild +1 -0
@@ -2,3 +2,4 @@ generic-y += clkdev.h generic-y += trace_clock.h +generic-y += preempt.h
arch/score/include/asm/Kbuild +1 -0
@@ -4,3 +4,4 @@ header-y += generic-y += clkdev.h generic-y += trace_clock.h generic-y += xor.h +generic-y += preempt.h
arch/sh/include/asm/Kbuild +1 -0
@@ -34,3 +34,4 @@ generic-y += termios.h generic-y += trace_clock.h generic-y += ucontext.h generic-y += xor.h +generic-y += preempt.h
arch/sparc/include/asm/Kbuild +1 -0
@@ -16,3 +16,4 @@ generic-y += serial.h generic-y += trace_clock.h generic-y += types.h generic-y += word-at-a-time.h +generic-y += preempt.h
arch/tile/include/asm/Kbuild +1 -0
@@ -37,3 +37,4 @@ generic-y += termios.h generic-y += trace_clock.h generic-y += types.h generic-y += xor.h +generic-y += preempt.h
arch/um/include/asm/Kbuild +1 -0
@@ -3,3 +3,4 @@ generic-y += hw_irq.h irq_regs.h kdebug. generic-y += ftrace.h pci.h io.h param.h delay.h mutex.h current.h exec.h generic-y += switch_to.h clkdev.h generic-y += trace_clock.h +generic-y += preempt.h
arch/unicore32/include/asm/Kbuild +1 -0
@@ -60,3 +60,4 @@ generic-y += unaligned.h generic-y += user.h generic-y += vga.h generic-y += xor.h +generic-y += preempt.h
arch/x86/include/asm/Kbuild +1 -0
@@ -5,3 +5,4 @@ genhdr-y += unistd_64.h genhdr-y += unistd_x32.h generic-y += clkdev.h +generic-y += preempt.h
arch/xtensa/include/asm/Kbuild +1 -0
@@ -28,3 +28,4 @@ generic-y += termios.h generic-y += topology.h generic-y += trace_clock.h generic-y += xor.h +generic-y += preempt.h
include/asm-generic/preempt.h new file +20 -0
@@ -0,0 +1,20 @@ +#ifndef __ASM_PREEMPT_H +#define __ASM_PREEMPT_H + +#include <linux/thread_info.h> + +/* +* We mask the PREEMPT_NEED_RESCHED bit so as not to confuse all current users +* that think a non-zero value indicates we cannot preempt. +*/ +static __always_inline int preempt_count(void) +{ +return current_thread_info ()->preempt_count & ~PREEMPT_NEED_RESCHED ; +} + +static __always_inline int *preempt_count_ptr(void) +{ +return ¤t_thread_info ()->preempt_count; +} + +#endif /* __ASM_PREEMPT_H */
include/linux/preempt.h +1 -14
@@ -6,7 +6,6 @@* preempt_count (used for kernel preemption, interrupt count, etc.) */ -#include <linux/thread_info.h> #include <linux/linkage.h> #include <linux/list.h>
@@ -16,19 +15,7 @@*/ #define PREEMPT_NEED_RESCHED 0x80000000 -/* -* We mask the PREEMPT_NEED_RESCHED bit so as not to confuse all current users -* that think a non-zero value indicates we cannot preempt. -*/ -static __always_inline int preempt_count(void) -{ -return current_thread_info ()->preempt_count & ~PREEMPT_NEED_RESCHED ; -} - -static __always_inline int *preempt_count_ptr(void) -{ -return ¤t_thread_info ()->preempt_count; -} +#include <asm/preempt.h> /* * We fold the NEED_RESCHED bit into the preempt count such that
Peter Zijlstra
maintainerRFC PATCH 4/5 sched: Create more preempt_count accessors
In reply to Peter ZijlstraWe need a few special preempt_count accessors:
- task_preempt_count() for when we're interested in the preemption count of another (non-running) task.
- init_task_preempt_count() for properly initializing the preemption count.
- init_idle_preempt_count() a special case of the above for the idle threads.
With these no generic code ever touches thread_info::preempt_count anymore and architectures could choose to remove it.
--- include/asm-generic/preempt.h | 14 ++++++++++++++ include/trace/events/sched.h | 2 +- kernel/sched/core.c | 7 +++---- 3 files changed, 18 insertions(+), 5 deletions(-)
include/asm-generic/preempt.h +14 -0
@@ -17,4 +17,18 @@ static __always_inline int *preempt_coun return ¤t_thread_info()->preempt_count; } +/* +* must be macros to avoid header recursion hell +*/ +#define task_preempt_count (p) \ + (task_thread_info(p)->preempt_count & ~PREEMPT_NEED_RESCHED) + +#define init_task_preempt_count (p) do { \ + task_thread_info(p)->preempt_count = 1 | PREEMPT_NEED_RESCHED; \ +} while (0) + +#define init_idle_preempt_count (p, cpu) do { \ + task_thread_info(p)->preempt_count = 0; \ +} while (0) +#endif /* __ASM_PREEMPT_H */
include/trace/events/sched.h +1 -1
@@ -103,7 +103,7 @@ static inline long __trace_sched_switch_/* * For all intents and purposes a preempted task is a running task. */ -if (task_thread_info (p)->preempt_count &PREEMPT_ACTIVE ) +if (task_preempt_count (p) &PREEMPT_ACTIVE ) state =TASK_RUNNING |TASK_STATE_MAX ;#endif
kernel/sched/core.c +3 -4
@@ -996,7 +996,7 @@ void set_task_cpu(struct task_struct *p,* ttwu() will sort out the placement. */ WARN_ON_ONCE (p->state !=TASK_RUNNING && p->state !=TASK_WAKING && - !(task_thread_info (p)->preempt_count &PREEMPT_ACTIVE )); + !(task_preempt_count (p) &PREEMPT_ACTIVE ));#ifdef CONFIG_LOCKDEP /*
@@ -1737,8 +1737,7 @@ void sched_fork(struct task_struct *p) p->on_cpu =0 ;#endif #ifdef CONFIG_PREEMPT_COUNT -/* Want to start with kernel preemption disabled. */ -task_thread_info (p)->preempt_count =1 ; +init_task_preempt_count (p);#endif #ifdef CONFIG_SMP plist_node_init (&p->pushable_tasks,MAX_PRIO );
@@ -4225,7 +4224,7 @@ void init_idle(struct task_struct *idle,raw_spin_unlock_irqrestore (&rq->lock, flags);/* Set the preempt count _outside_ the spinlocks! */ -task_thread_info (idle)->preempt_count =0 ; +init_idle_preempt_count (idle, cpu);/* * The idle tasks have their own, simple scheduling class:
Peter Zijlstra
maintainerRFC PATCH 5/5 sched, x86: Provide a per-cpu preempt_count implementation
In reply to Peter ZijlstraConvert x86 to use a per-cpu preemption count. The reason for doing so is that accessing per-cpu variables is a lot cheaper than accessing thread_info variables.
With this preempt_enable() reduces to:
decl fs:__preempt_count
jnz 1f
call preempt_schedule
1:
We still need to save/restore the actual preemption count due to PREEMPT_ACTIVE so we place the per-cpu __preempt_count variable in the same cache-line as the other hot __switch_to() variables such as current_task.
Also rename thread_info::preempt_count to ensure nobody is 'accidentally' still poking at it.
--- arch/x86/include/asm/Kbuild | 1 arch/x86/include/asm/preempt.h | 38 +++++++++++++++++++++++++++++++++++++ arch/x86/include/asm/thread_info.h | 5 +--- arch/x86/kernel/asm-offsets.c | 1 arch/x86/kernel/cpu/common.c | 5 ++++ arch/x86/kernel/entry_32.S | 7 +----- arch/x86/kernel/entry_64.S | 4 --- arch/x86/kernel/process_32.c | 10 +++++++++ arch/x86/kernel/process_64.c | 10 +++++++++ 9 files changed, 68 insertions(+), 13 deletions(-)
arch/x86/include/asm/Kbuild +0 -1
@@ -5,4 +5,3 @@ genhdr-y += unistd_64.h genhdr-y += unistd_x32.h generic-y += clkdev.h -generic-y += preempt.h
arch/x86/include/asm/preempt.h new file +38 -0
@@ -0,0 +1,38 @@ +#ifndef __ASM_PREEMPT_H +#define __ASM_PREEMPT_H + +#include <asm/percpu.h> +#include <linux/thread_info.h> + +DECLARE_PER_CPU (int, __preempt_count); + +/* +* We mask the PREEMPT_NEED_RESCHED bit so as not to confuse all current users +* that think a non-zero value indicates we cannot preempt. +*/ +static __always_inline int preempt_count(void) +{ +return __raw_get_cpu_var (__preempt_count) & ~PREEMPT_NEED_RESCHED ; +} + +static __always_inline int *preempt_count_ptr(void) +{ +return &__raw_get_cpu_var (__preempt_count); +} + +/* +* must be macros to avoid header recursion hell +*/ +#define task_preempt_count (p) \ + (task_thread_info(p)->saved_preempt_count & ~PREEMPT_NEED_RESCHED) + +#define init_task_preempt_count (p) do { \ + task_thread_info(p)->saved_preempt_count = 1 | PREEMPT_NEED_RESCHED; \ +} while (0) + +#define init_idle_preempt_count (p, cpu) do { \ + task_thread_info(p)->saved_preempt_count = 0; \ + per_cpu(__preempt_count, (cpu)) = 0; \ +} while (0) + +#endif /* __ASM_PREEMPT_H */
arch/x86/include/asm/thread_info.h +2 -3
@@ -28,8 +28,7 @@ struct thread_info {__u32 flags;/* low level flags */ __u32 status;/* thread synchronous flags */ __u32 cpu;/* current CPU */ -int preempt_count;/* 0 => preemptable, -<0 => BUG */ +int saved_preempt_count;mm_segment_t addr_limit;struct restart_block restart_block;void __user *sysenter_return;
@@ -49,7 +48,7 @@ struct thread_info { .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ - .preempt_count = INIT_PREEMPT_COUNT, \ + .saved_preempt_count = INIT_PREEMPT_COUNT, \ .addr_limit = KERNEL_DS, \ .restart_block = { \ .fn = do_no_restart_syscall, \
arch/x86/kernel/asm-offsets.c +0 -1
@@ -32,7 +32,6 @@ void common(void) {OFFSET (TI_flags, thread_info, flags);OFFSET (TI_status, thread_info, status);OFFSET (TI_addr_limit, thread_info, addr_limit); -OFFSET (TI_preempt_count, thread_info, preempt_count);BLANK ();OFFSET (crypto_tfm_ctx_offset, crypto_tfm, __crt_ctx);
arch/x86/kernel/cpu/common.c +5 -0
@@ -1095,6 +1095,9 @@ DEFINE_PER_CPU(char *, irq_stack_ptr) = DEFINE_PER_CPU(unsigned int, irq_count) = -1; +DEFINE_PER_CPU (int, __preempt_count) =INIT_PREEMPT_COUNT ; +EXPORT_PER_CPU_SYMBOL (__preempt_count); + DEFINE_PER_CPU(struct task_struct *, fpu_owner_task);/*
@@ -1169,6 +1172,8 @@ void debug_stack_reset(void) DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task;EXPORT_PER_CPU_SYMBOL (current_task); +DEFINE_PER_CPU (int, __preempt_count) =INIT_PREEMPT_COUNT ; +EXPORT_PER_CPU_SYMBOL (__preempt_count); DEFINE_PER_CPU(struct task_struct *, fpu_owner_task);#ifdef CONFIG_CC_STACKPROTECTOR
arch/x86/kernel/entry_32.S +2 -5
@@ -362,12 +362,9 @@ END(ret_from_exception) #ifdef CONFIG_PREEMPT ENTRY(resume_kernel) DISABLE_INTERRUPTS(CLBR_ANY) - cmpl $0,TI_preempt_count(%ebp) # non-zero preempt_count ? - jnz restore_all need_resched: - movl TI_flags(%ebp), %ecx # need_resched set ? - testb $_TIF_NEED_RESCHED, %cl - jz restore_all + cmpl $0,PER_CPU_VAR(__preempt_count) + jnz restore_all testl $X86_EFLAGS_IF,PT_EFLAGS(%esp) # interrupts off (exception path) ? jz restore_all call preempt_schedule_irq
arch/x86/kernel/entry_64.S +1 -3
@@ -1118,10 +1118,8 @@ ENTRY(native_iret) /* Returning to kernel space. Check if we need preemption */ /* rcx: threadinfo. interrupts off. */ ENTRY(retint_kernel) - cmpl $0,TI_preempt_count(%rcx) + cmpl $0,PER_CPU_VAR(__preempt_count) jnz retint_restore_args - bt $TIF_NEED_RESCHED,TI_flags(%rcx) - jnc retint_restore_args bt $9,EFLAGS-ARGOFFSET(%rsp) /* interrupts off? */ jnc retint_restore_args call preempt_schedule_irq
arch/x86/kernel/process_32.c +10 -0
@@ -291,6 +291,16 @@ __switch_to(struct task_struct *prev_p,if (get_kernel_rpl () &&unlikely (prev->iopl != next->iopl))set_iopl_mask (next->iopl); +#ifdef CONFIG_PREEMPT_COUNT +/* +* If it were not for PREEMPT_ACTIVE we could guarantee that the +* preempt_count of all tasks was equal here and this would not be +* needed. +*/ +task_thread_info (prev_p)->saved_preempt_count =__raw_get_cpu_var (__preempt_count); +__raw_get_cpu_var (__preempt_count) =task_thread_info (next_p)->saved_preempt_count; +#endif +/* * Now maybe handle debug registers and/or IO bitmaps */
arch/x86/kernel/process_64.c +10 -0
@@ -363,6 +363,16 @@ __switch_to(struct task_struct *prev_p,this_cpu_write (old_rsp, next->usersp);this_cpu_write (current_task, next_p); +#ifdef CONFIG_PREEMPT_COUNT +/* +* If it were not for PREEMPT_ACTIVE we could guarantee that the +* preempt_count of all tasks was equal here and this would not be +* needed. +*/ +task_thread_info (prev_p)->saved_preempt_count =__raw_get_cpu_var (__preempt_count); +__raw_get_cpu_var (__preempt_count) =task_thread_info (next_p)->saved_preempt_count; +#endif +this_cpu_write (kernel_stack, (unsigned long )task_stack_page (next_p) +THREAD_SIZE -KERNEL_STACK_OFFSET );
H. Peter Anvin
reviewerQuoted 11 lines · Peter Zijlstra
These patches optimize
preempt_enableby firstly folding the preempt andneed_reschedtests into one -- this should work for all architectures. And secondly by providing per-archpreempt_countimplementations; with x86 using per-cpupreempt_countfor fastest access.These patches have so far only been compiled for defconfig-x86_64 +
CONFIG_PREEMPT=yand boot tested with kvm -smp 4 upto wanting to mount root.It still needs asm
volatile("call preempt_schedule": : :"memory"); as per Andi's other patches to avoid the C calling convention cluttering thepreempt_enable()sites.
Hi,
I still don't see this using a decrement of the percpu variable anywhere. The C compiler doesn't know how to generate those, so if I'm not completely wet we will end up relying on sub_preempt_count()... which, because it relies on taking the address of the percpu variable will generate absolutely horrific code.
On x86, you never want to take the address of a percpu variable if you can avoid it, as you end up generating code like:
movq %fs:0,%rax subl $1,(%rax)
... for absolutely no good reason. You can use the existing accessors for percpu variables, but that would make you lose the flags output which was part of the point, so I think the whole sequence needs to be in assembly (note that once you are manipulating percpu state you are already in assembly.)
-hpa
Quoted 5 lines · H. Peter Anvin
On x86, you never want to take the address of a percpu variable if you can avoid it, as you end up generating code like:
movq %fs:0,%rax subl $1,(%rax)
Hmmm..
#define cpu_rq(cpu) (&per_cpu(runqueues, (cpu)))
#define this_rq() (&__get_cpu_var(runqueues))
ffffffff81438c7f: 48 c7 c3 80 11 01 00 mov $0x11180,%rbx
/*
* this_rq must be evaluated again because prev may have moved
* CPUs since it called schedule(), thus the 'rq' on its stack
* frame will be invalid.
*/
finish_task_switch(this_rq(), prev);ffffffff81438c86: e8 25 b4 c0 ff callq ffffffff810440b0 <finish_task_switch>
* The context switch have flipped the stack from under us
* and restored the local variables which were saved when
* this task called schedule() in the past. prev == current
* is still correct, but it can be moved to another cpu/rq. */
cpu = smp_processor_id();
ffffffff81438c8b: 65 8b 04 25 b8 c5 00 mov %gs:0xc5b8,%eax
ffffffff81438c92: 00
rq = cpu_rq(cpu);
ffffffff81438c93: 48 98 cltq
ffffffff81438c95: 48 03 1c c5 00 f3 bb add -0x7e440d00(,%rax,8),%rbx..so could the rq = cpu_rq(cpu) sequence be improved cycle expenditure wise by squirreling rq pointer away in a percpu this_rq, and replacing cpu_rq(cpu) above with a __this_cpu_read(this_rq) version of this_rq()?
-Mike
H. Peter Anvin
reviewerQuoted 5 lines · Mike Galbraith
..so could the rq =
cpu_rq(cpu)sequence be improved cycle expenditure wise by squirreling rq pointer away in a percputhis_rq, and replacingcpu_rq(cpu)above with a__this_cpu_read(this_rq)version ofthis_rq()?
Yes.
-hpa
Quoted 8 lines · H. Peter Anvin
Quoted 5 lines · Mike Galbraith
..so could the rq =
cpu_rq(cpu)sequence be improved cycle expenditure wise by squirreling rq pointer away in a percputhis_rq, and replacingcpu_rq(cpu)above with a__this_cpu_read(this_rq)version ofthis_rq()?Yes.
Oh darn, that worked out about as you'd expect. Cycles are so far down in the frog hair as to be invisible, so not be worth the space cost.
pinned sched_yield proggy, switches/sec, 3 boots/5 runs each:
avg
pre: 1650522 1580422 1604430 1611697 1612928 1611999.8
1682789 1609103 1603866 1559040 1607424 1612444.4
1608265 1607513 1606730 1607079 1635914 1613100.2
1612514.8 avg avg 1.000post: 1649396 1595364 1621720 1643665 1641829 1630394.8
1571322 1591638 1575406 1629960 1592129 1592091.0
1641807 1622591 1620581 1651145 1663025 1639829.8
1620771.8 avg avg 1.005--- kernel/sched/core.c | 8 ++++---- kernel/sched/sched.h | 12 +++++++++--- 2 files changed, 13 insertions(+), 7 deletions(-)
kernel/sched/core.c +4 -4
@@ -111,6 +111,7 @@ void start_bandwidth_timer(struct hrtimeDEFINE_MUTEX (sched_domains_mutex); DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues); +DEFINE_PER_CPU_SHARED_ALIGNED(struct rq *, runqueue);static void update_rq_clock_task (struct rq *rq,s64 delta);
@@ -2390,7 +2391,7 @@ static void __sched __schedule(void) need_resched:preempt_disable (); cpu =smp_processor_id (); - rq =cpu_rq (cpu); + rq =this_rq ();rcu_note_context_switch (cpu); prev = rq->curr;
@@ -2447,8 +2448,7 @@ static void __sched __schedule(void)* this task called schedule() in the past. prev == current * is still correct, but it can be moved to another cpu/rq. */ - cpu =smp_processor_id (); - rq =cpu_rq (cpu); + rq =this_rq (); }else raw_spin_unlock_irq (&rq->lock);
@@ -6470,7 +6470,7 @@ void __init sched_init(void)for_each_possible_cpu (i) {struct rq *rq; - rq =cpu_rq (i); + rq =per_cpu (runqueue, i) = &per_cpu (runqueues, i);raw_spin_lock_init (&rq->lock); rq->nr_running =0 ; rq->calc_load_active =0 ;
kernel/sched/sched.h +9 -3
@@ -537,11 +537,17 @@ static inline int cpu_of(struct rq *rq) DECLARE_PER_CPU(struct rq, runqueues); -#define cpu_rq (cpu) (&per_cpu(runqueues, (cpu))) -#define this_rq () (&__get_cpu_var(runqueues)) +/* +* Runqueue pointer for use by macros to avoid costly code generated +* by taking the address of percpu variables. +*/ +DECLARE_PER_CPU(struct rq *, runqueue); + +#define cpu_rq (cpu) (per_cpu(runqueue, (cpu))) +#define this_rq () (__this_cpu_read(runqueue))#define task_rq (p) cpu_rq(task_cpu(p))#define cpu_curr (cpu) (cpu_rq(cpu)->curr) -#define raw_rq () (&__raw_get_cpu_var(runqueues)) +#define raw_rq () (__raw_get_cpu_var(runqueue))static inline u64 rq_clock (struct rq *rq) {
Peter Zijlstra
maintainerQuoted 33 lines · Mike Galbraith
Quoted 5 lines · H. Peter Anvin
On x86, you never want to take the address of a percpu variable if you can avoid it, as you end up generating code like:
movq %fs:0,%rax subl $1,(%rax)Hmmm..
#define
cpu_rq(cpu)(&per_cpu(runqueues, (cpu)))
#definethis_rq()(&__get_cpu_var(runqueues))
ffffffff81438c7f: 48 c7 c3 80 11 01 00 mov $0x11180,%rbx
/*
*this_rqmust be evaluated again because prev may have moved
* CPUs since it calledschedule(), thus the 'rq' on its stack
* frame will be invalid.
*/
finish_task_switch(this_rq(), prev);ffffffff81438c86: e8 25 b4 c0 ff callqffffffff810440b0<finish_task_switch>
* The context switch have flipped the stack from under us
* and restored the local variables which were saved when
* this task calledschedule()in the past. prev == current
* is still correct, but it can be moved to another cpu/rq. */
cpu =smp_processor_id();ffffffff81438c8b: 65 8b 04 25 b8 c5 00 mov %gs:0xc5b8,%eax ffffffff81438c92: 00 rq = cpu_rq(cpu); ffffffff81438c93: 48 98 cltq ffffffff81438c95: 48 03 1c c5 00 f3 bb add -0x7e440d00(,%rax,8),%rbx
Quoted 3 lines
..so could the rq =
cpu_rq(cpu)sequence be improved cycle expenditure wise by squirreling rq pointer away in a percputhis_rq, and replacingcpu_rq(cpu)above with a__this_cpu_read(this_rq)version ofthis_rq()?
Well, this_rq() should already get you that. The above code sucks for using cpu_rq() when we know cpu == smp_processor_id().
H. Peter Anvin
reviewerQuoted 34 lines · Peter Zijlstra
Quoted 33 lines · Mike Galbraith
Quoted 5 lines · H. Peter Anvin
On x86, you never want to take the address of a percpu variable if you can avoid it, as you end up generating code like:
movq %fs:0,%rax subl $1,(%rax)Hmmm..
#define
cpu_rq(cpu)(&per_cpu(runqueues, (cpu)))
#definethis_rq()(&__get_cpu_var(runqueues))
ffffffff81438c7f: 48 c7 c3 80 11 01 00 mov $0x11180,%rbx
/*
*this_rqmust be evaluated again because prev may have moved
* CPUs since it calledschedule(), thus the 'rq' on its stack
* frame will be invalid.
*/
finish_task_switch(this_rq(), prev);ffffffff81438c86: e8 25 b4 c0 ff callqffffffff810440b0<finish_task_switch>
* The context switch have flipped the stack from under us
* and restored the local variables which were saved when
* this task calledschedule()in the past. prev == current
* is still correct, but it can be moved to another cpu/rq. */
cpu =smp_processor_id();ffffffff81438c8b: 65 8b 04 25 b8 c5 00 mov %gs:0xc5b8,%eax ffffffff81438c92: 00 rq = cpu_rq(cpu); ffffffff81438c93: 48 98 cltq ffffffff81438c95: 48 03 1c c5 00 f3 bb add -0x7e440d00(,%rax,8),%rbx
Quoted 7 lines
Quoted 3 lines
..so could the rq =
cpu_rq(cpu)sequence be improved cycle expenditure wise by squirreling rq pointer away in a percputhis_rq, and replacingcpu_rq(cpu)above with a__this_cpu_read(this_rq)version ofthis_rq()?Well,
this_rq()should already get you that. The above code sucks for usingcpu_rq()when we know cpu ==smp_processor_id().
Even so, this_rq() uses __get_cpu_var() and takes its address, which turns into a sequence like:
leaq __percpu_runqueues(%rip),%rax
addq %gs:this_cpu_off,%rax
... which is better than the above but still more heavyweight than it would be if the pointer was itself a percpu variable.
-hpa
Peter Zijlstra
maintainerQuoted 8 lines · H. Peter Anvin
Even so,
this_rq()uses__get_cpu_var()and takes its address, which turns into a sequence like:leaq __percpu_runqueues(%rip),%rax
addq %gs:this_cpu_off,%rax... which is better than the above but still more heavyweight than it would be if the pointer was itself a percpu variable.
Oh curses, this is because lea can't do segment offsets? So there's no sane way to get addresses of per-cpu variables.
Because ideally we'd have something like:
lea %gs:__percpu_runqueues,%rax
So in this case it makes sense to also store the actual pointer; how unfortunate.
H. Peter Anvin
reviewerQuoted 11 lines · Peter Zijlstra
Oh curses, this is because lea can't do segment offsets? So there's no sane way to get addresses of per-cpu variables.
Because ideally we'd have something like:
lea %gs:__percpu_runqueues,%rax
So in this case it makes sense to also store the actual pointer; how unfortunate.
Correct; the segment offset is not part of the effective address, even in 64-bit mode.
-hpa
Peter Zijlstra
maintainerQuoted 6 lines · H. Peter Anvin
I still don't see this using a decrement of the percpu variable anywhere. The C compiler doesn't know how to generate those, so if I'm not completely wet we will end up relying on
sub_preempt_count()... which, because it relies on taking the address of the percpu variable will generate absolutely horrific code.
Quoted 6 lines
On x86, you never want to take the address of a percpu variable if you can avoid it, as you end up generating code like:
movq %fs:0,%rax subl $1,(%rax)
Urgh,. yes you're right. I keep forgetting GCC doesn't know how to merge those :/
OK, so something like the below would cure the worst of that I suppose. It compiles but doesn't boot; must've done something wrong.
Someone please look at it because my asm-foo blows. I pretty much copy/pasted this from asm/percpu.h.
---
arch/x86/include/asm/preempt.h +22 -0
@@ -20,6 +20,28 @@ static __always_inline int *preempt_counreturn &__raw_get_cpu_var (__preempt_count); } +#define __preempt_count_add (x) do { \ + asm("addl %1," __percpu_arg(0) \ + : "+m" (__preempt_count) \ + : "ri" ((int)x) \ + : "memory"); \ +} while (0) + +#define __preempt_count_sub (x) do { \ + asm("subl %1," __percpu_arg(0) \ + : "+m" (__preempt_count) \ + : "ri" ((int)x) \ + : "memory"); \ +} while (0) + +#define preempt_enable () do { \ + asm("\nsubl $1," __percpu_arg(0) \ + "\njnz 1f" \ + "\ncall preempt_schedule" \ + "\n1:" : "+m" (__preempt_count) \ + : : "memory"); \ +} while (0) +/* * must be macros to avoid header recursion hell */
include/asm-generic/preempt.h +3 -0
@@ -17,6 +17,9 @@ static __always_inline int *preempt_counreturn ¤t_thread_info ()->preempt_count; } +#define __preempt_count_add (x) do { current_thread_info()->preempt_count += (x); } while (0) +#define __preempt_count_sub (x) __preempt_count_add(-(x)) +/* * must be macros to avoid header recursion hell */
include/linux/preempt.h +6 -6
@@ -45,8 +45,8 @@ static __always_inline bool test_preemptextern void add_preempt_count (int val);extern void sub_preempt_count (int val);#else -# define add_preempt_count (val) do { *preempt_count_ptr() += (val); } while (0) -# define sub_preempt_count (val) do { *preempt_count_ptr() -= (val); } while (0) +# define add_preempt_count (val) __preempt_count_add(val) +# define sub_preempt_count (val) __preempt_count_sub(val)#endif #define inc_preempt_count () add_preempt_count(1)
@@ -101,17 +101,17 @@ do { \#define preempt_enable_no_resched () sched_preempt_enable_no_resched() +#ifndef preempt_enable#define preempt_enable () \ do { \ preempt_enable_no_resched(); \ preempt_check_resched(); \ } while (0) +#endif /* For debugging and tracer internals only! */ -#define add_preempt_count_notrace (val) \ - do { *preempt_count_ptr() += (val); } while (0) -#define sub_preempt_count_notrace (val) \ - do { *preempt_count_ptr() -= (val); } while (0) +#define add_preempt_count_notrace (val) __preempt_count_add(val) +#define sub_preempt_count_notrace (val) __preempt_count_sub(val)#define inc_preempt_count_notrace () add_preempt_count_notrace(1)#define dec_preempt_count_notrace () sub_preempt_count_notrace(1)
Peter Zijlstra
maintainerQuoted 5 lines · Peter Zijlstra
OK, so something like the below would cure the worst of that I suppose. It compiles but doesn't boot; must've done something wrong.
Someone please look at it because my asm-foo blows. I pretty much copy/pasted this from
asm/percpu.h.
OK, another hatchet job, now with bits borrowed from Andi's voluntary-preempt v1. This one actually boots.
---
arch/x86/include/asm/calling.h +50 -0
@@ -48,6 +48,8 @@ For 32-bit we have the following convent#include <asm/dwarf2.h> +#ifdef CONFIG_X86_64 +/* * 64-bit system call stack frame layout defines and helpers, * for assembly code:
@@ -192,3 +194,51 @@ For 32-bit we have the following convent .macro icebp .byte 0xf1 .endm + +#else /* CONFIG_X86_64 */ + +/* +* For 32bit only simplified versions of SAVE_ALL/RESTORE_ALL. These +* are different from the entry_32.S versions in not changing the segment +* registers. So only suitable for in kernel use, not when transitioning +* from or to user space. The resulting stack frame is not a standard +* pt_regs frame. The main use case is calling C code from assembler +* when all the registers need to be preserved. +*/ + + .macro SAVE_ALL + pushl_cfi %eax + CFI_REL_OFFSET eax, 0 + pushl_cfi %ebp + CFI_REL_OFFSET ebp, 0 + pushl_cfi %edi + CFI_REL_OFFSET edi, 0 + pushl_cfi %esi + CFI_REL_OFFSET esi, 0 + pushl_cfi %edx + CFI_REL_OFFSET edx, 0 + pushl_cfi %ecx + CFI_REL_OFFSET ecx, 0 + pushl_cfi %ebx + CFI_REL_OFFSET ebx, 0 + .endm + + .macro RESTORE_ALL + popl_cfi %ebx + CFI_RESTORE ebx + popl_cfi %ecx + CFI_RESTORE ecx + popl_cfi %edx + CFI_RESTORE edx + popl_cfi %esi + CFI_RESTORE esi + popl_cfi %edi + CFI_RESTORE edi + popl_cfi %ebp + CFI_RESTORE ebp + popl_cfi %eax + CFI_RESTORE eax + .endm + +#endif /* CONFIG_X86_64 */ +
arch/x86/include/asm/preempt.h +22 -0
@@ -20,6 +20,28 @@ static __always_inline int *preempt_counreturn &__raw_get_cpu_var (__preempt_count); } +#define __preempt_count_add (x) do { \ + asm volatile("addl %1," __percpu_arg(0) \ + : "+m" (__preempt_count) \ + : "ri" ((int)x) \ + : "memory"); \ +} while (0) + +#define __preempt_count_sub (x) do { \ + asm volatile("subl %1," __percpu_arg(0) \ + : "+m" (__preempt_count) \ + : "ri" ((int)x) \ + : "memory"); \ +} while (0) + +#define __preempt_enable () do { \ + asm volatile("\nsubl $1," __percpu_arg(0) \ + "\njnz 1f" \ + "\ncall __preempt_schedule" \ + "\n1:" \ + : "+m" (__preempt_count) : : "memory"); \ +} while (0) +/* * must be macros to avoid header recursion hell */
arch/x86/kernel/Makefile +2 -0
@@ -36,6 +36,8 @@ obj-y += tsc.o io_delay.o rtc.o obj-y += pci-iommu_table.o obj-y += resource.o +obj-$(CONFIG_PREEMPT) += preempt.o + obj-y += process.o obj-y += i387.o xsave.o obj-y += ptrace.o
arch/x86/kernel/preempt.S new file +25 -0
@@ -0,0 +1,25 @@ + +#include <linux/linkage.h> +#include <asm/dwarf2.h> +#include <asm/asm.h> +#include <asm/calling.h> + +ENTRY(__preempt_schedule) + CFI_STARTPROC + SAVE_ALL + call preempt_schedule + RESTORE_ALL + ret + CFI_ENDPROC + +#ifdef CONFIG_CONTEXT_TRACKING + +ENTRY(__preempt_schedule_context) + CFI_STARTPROC + SAVE_ALL + call preempt_schedule_context + RESTORE_ALL + ret + CFI_ENDPROC + +#endif
include/asm-generic/preempt.h +3 -0
@@ -17,6 +17,9 @@ static __always_inline int *preempt_counreturn ¤t_thread_info ()->preempt_count; } +#define __preempt_count_add (x) do { current_thread_info()->preempt_count += (x); } while (0) +#define __preempt_count_sub (x) __preempt_count_add(-(x)) +/* * must be macros to avoid header recursion hell */
include/linux/preempt.h +9 -7
@@ -45,8 +45,8 @@ static __always_inline bool test_preemptextern void add_preempt_count (int val);extern void sub_preempt_count (int val);#else -# define add_preempt_count (val) do { *preempt_count_ptr() += (val); } while (0) -# define sub_preempt_count (val) do { *preempt_count_ptr() -= (val); } while (0) +# define add_preempt_count (val) __preempt_count_add(val) +# define sub_preempt_count (val) __preempt_count_sub(val)#endif #define inc_preempt_count () add_preempt_count(1)
@@ -64,7 +64,7 @@ do { \#ifdef CONFIG_CONTEXT_TRACKING -void preempt_schedule_context (void ); +asmlinkage void preempt_schedule_context(void);#define preempt_check_resched_context () \ do { \
@@ -101,17 +101,19 @@ do { \#define preempt_enable_no_resched () sched_preempt_enable_no_resched() +#ifndef __preempt_enable#define preempt_enable () \ do { \ preempt_enable_no_resched(); \ preempt_check_resched(); \ } while (0) +#else +#define preempt_enable () __preempt_enable() +#endif /* For debugging and tracer internals only! */ -#define add_preempt_count_notrace (val) \ - do { *preempt_count_ptr() += (val); } while (0) -#define sub_preempt_count_notrace (val) \ - do { *preempt_count_ptr() -= (val); } while (0) +#define add_preempt_count_notrace (val) __preempt_count_add(val) +#define sub_preempt_count_notrace (val) __preempt_count_sub(val)#define inc_preempt_count_notrace () add_preempt_count_notrace(1)#define dec_preempt_count_notrace () sub_preempt_count_notrace(1)
Quoted 11 lines · Peter Zijlstra
These patches optimize
preempt_enableby firstly folding the preempt andneed_reschedtests into one -- this should work for all architectures. And secondly by providing per-archpreempt_countimplementations; with x86 using per-cpupreempt_countfor fastest access.These patches have so far only been compiled for defconfig-x86_64 +
CONFIG_PREEMPT=yand boot tested with kvm -smp 4 upto wanting to mount root.It still needs asm
volatile("call preempt_schedule": : :"memory"); as per Andi's other patches to avoid the C calling convention cluttering thepreempt_enable()sites.
FWIW I removed the user_schedule in v2 because I don't need it anymore. Feel free to pick it up from v1 though.
It needs two patches: the one adding SAVE_ALL for 32bit and the parts of the put_user() patch adding user_schedule
When it's not used in user_ anymore it should probably be renamed too, to preempt_schedule or somesuch, and probably moved to a different file.
-Andi
Peter Zijlstra
maintainerQuoted 2 lines · Andi Kleen
FWIW I removed the
user_schedulein v2 because I don't need it anymore. Feel free to pick it up from v1 though.
Ah, I had a quick look through your v2 because I got a link into it from Ingo but didn't find it. I'll have to ask Google to locate this v1. I suppose that's what's wrong with my last patch. It directly does a call preempt_schedule -- which I had hoped would work due to its asmlinkage, but apparently there's more to it.
Also, I suppose there's a few volatile thingies missing.
Quoted 7 lines · Peter Zijlstra
Quoted 2 lines · Andi Kleen
FWIW I removed the
user_schedulein v2 because I don't need it anymore. Feel free to pick it up from v1 though.Ah, I had a quick look through your v2 because I got a link into it from Ingo but didn't find it. I'll have to ask Google to locate this v1. I
Sorry, it's the thread starting with
http://permalink.gmane.org/gmane.linux.kernel/1541950
I also pushed the branch to git://git.kernel.org/pub/scm/linux/kernel/git/ak/linux-misc.git
uaccess-opt311 is v1
uaccess-opt311-2 is v2 (without user_schedule)
Quoted 3 lines
suppose that's what's wrong with my last patch. It directly does a call
preempt_schedule-- which I had hoped would work due to its asmlinkage, but apparently there's more to it.
preempt_schedule does not preserve registers, so yes that would explain a lot of problems.
-Andi
Signature · 1 line
[email protected] -- Speaking for myself only