preempt_lazy: remove redundant test_thread_flag() call
Subsystems (4)
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH 1/6 preempt_lazy: remove redundant test_thread_flag() call
__preempt_count_dec_and_test() already does it.
--- include/linux/preempt.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
include/linux/preempt.h +1 -2
@@ -126,8 +126,7 @@ do { \#define preempt_enable_notrace () \ do { \ barrier(); \ - if (unlikely(__preempt_count_dec_and_test() || \ - test_thread_flag(TIF_NEED_RESCHED_LAZY))) \ + if (unlikely(__preempt_count_dec_and_test())) \ __preempt_schedule_context(); \ } while (0)#else
Steven Rostedt
reviewerQuoted 1 line · On Fri, 02 May 2014 13:12:46 +0200 Mike Galbraith <[email protected]>…
__preempt_count_dec_and_test()already does it.
Already does what? :-)
Hmm, do all archs check? Perhaps we need to also update include/asm-generic/preempt.h?
-- Steve
Quoted 20 lines
Signed-off-by Mike Galbraith <[email protected]>--- include/linux/preempt.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)include/linux/preempt.h +1
-2@@ -126,8 +126,7 @@ do { \#define preempt_enable_notrace () \ do { \ barrier(); \ - if (unlikely(__preempt_count_dec_and_test() || \ - test_thread_flag(TIF_NEED_RESCHED_LAZY))) \ + if (unlikely(__preempt_count_dec_and_test())) \ __preempt_schedule_context(); \ } while (0)#else
Quoted 6 lines · Steven Rostedt
Quoted 1 line · On Fri, 02 May 2014 13:12:46 +0200 Mike Galbraith <[email protected]>…
__preempt_count_dec_and_test()already does it.Already does what? :-)
Poo.. yeah that.
Quoted 2 lines
Hmm, do all archs check? Perhaps we need to also update
include/asm-generic/preempt.h?
Per cpu preempt_count is x86, and tif_need_resched() handles laziness, so all should be well. Did I just manage to overlook the obvious for the second time?
-Mike
Quoted 6 lines · * Mike Galbraith | 2014-05-06 07:33:51 [+0200]:
Quoted 2 lines
Hmm, do all archs check? Perhaps we need to also update
include/asm-generic/preempt.h?Per cpu
preempt_countis x86, andtif_need_resched()handles laziness, so all should be well. Did I just manage to overlook the obvious for the second time?
In include/asm-generic/preempt.h we have
| static __always_inline bool __preempt_count_dec_and_test(void)
| {
| return !--*preempt_count_ptr() && tif_need_resched();
| }and tif_need_resched() is in include/linux/thread_info.h
| #ifdef CONFIG_PREEMPT_LAZY | #define tif_need_resched() (test_thread_flag(TIF_NEED_RESCHED) || \ | test_thread_flag(TIF_NEED_RESCHED_LAZY)) | #else | #define tif_need_resched() (test_thread_flag(TIF_NEED_RESCHED)) | #endif
So I guess we are fine.
Quoted 1 line
-Mike
Sebastian
PATCH 2/6 x86/preempt-lazy adapt implementation to percpu __preempt_count
In reply to a message not in the archive--- arch/x86/include/asm/preempt.h | 8 ++++++++ arch/x86/kernel/asm-offsets.c | 1 + arch/x86/kernel/entry_32.S | 9 ++++++--- arch/x86/kernel/entry_64.S | 7 +++++-- 4 files changed, 20 insertions(+), 5 deletions(-)
arch/x86/include/asm/preempt.h +8 -0
@@ -94,7 +94,11 @@ static __always_inline bool __preempt_co {if (____preempt_count_dec_and_test ())return true; +#ifdef CONFIG_PREEMPT_LAZY return test_thread_flag (TIF_NEED_RESCHED_LAZY ); +#else +return false; +#endif }/*
@@ -102,8 +106,12 @@ static __always_inline bool __preempt_co*/ static __always_inline bool should_resched(void) { +#ifdef CONFIG_PREEMPT_LAZY return unlikely (!__this_cpu_read_4 (__preempt_count) || \test_thread_flag (TIF_NEED_RESCHED_LAZY )); +#else +return unlikely (!__this_cpu_read_4 (__preempt_count)); +#endif }#ifdef CONFIG_PREEMPT
arch/x86/kernel/asm-offsets.c +1 -0
@@ -72,4 +72,5 @@ void common(void) {BLANK ();DEFINE (PTREGS_SIZE ,sizeof (struct pt_regs )); +DEFINE (_PREEMPT_ENABLED,PREEMPT_ENABLED ); }
arch/x86/kernel/entry_32.S +6 -3
@@ -365,19 +365,22 @@ ENTRY(resume_kernel) need_resched: # preempt count == 0 + NEED_RS set? cmpl $0,PER_CPU_VAR(__preempt_count) +#ifndef CONFIG_PREEMPT_LAZY + jnz restore_all +#else jz test_int_off # atleast preempt count == 0 ? - cmpl $_TIF_NEED_RESCHED,PER_CPU_VAR(__preempt_count) + cmpl $_PREEMPT_ENABLED,PER_CPU_VAR(__preempt_count) jne restore_all cmpl $0,TI_preempt_lazy_count(%ebp) # non-zero preempt_lazy_count ? jnz restore_all - testl $_TIF_NEED_RESCHED_LAZY, %ecx + testl $_TIF_NEED_RESCHED_LAZY, TI_flags(%ebp) jz restore_all - test_int_off: +#endif testl $X86_EFLAGS_IF,PT_EFLAGS(%esp) # interrupts off (exception path) ? jz restore_all call preempt_schedule_irq
arch/x86/kernel/entry_64.S +4 -1
@@ -1104,10 +1104,13 @@ ENTRY(native_iret) /* rcx: threadinfo. interrupts off. */ ENTRY(retint_kernel) cmpl $0,PER_CPU_VAR(__preempt_count) +#ifndef CONFIG_PREEMPT_LAZY + jnz retint_restore_args +#else jz check_int_off # atleast preempt count == 0 ? - cmpl $_TIF_NEED_RESCHED,PER_CPU_VAR(__preempt_count) + cmpl $_PREEMPT_ENABLED,PER_CPU_VAR(__preempt_count) jnz retint_restore_args cmpl $0, TI_preempt_lazy_count(%rcx)
@@ -1115,8 +1118,8 @@ ENTRY(retint_kernel) bt $TIF_NEED_RESCHED_LAZY,TI_flags(%rcx) jnc retint_restore_args
PATCH 3/6 stomp-machine: fix typo in kernel/cpu.c
In reply to a message not in the archive--- kernel/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/cpu.c +1 -1
@@ -649,7 +649,7 @@ static int __ref _cpu_down(unsigned int/* CPU didn't die: tell everyone. Can't complain. */ smpboot_unpark_threads (cpu);cpu_notify_nofail (CPU_DOWN_FAILED | mod, hcpu); - goto out_cancel; + goto out_release; }BUG_ON (cpu_online (cpu));
PATCH 4/6 stomp-machine: fix typo in kernel/stop_machine.c
In reply to a message not in the archive--- kernel/stop_machine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/stop_machine.c +1 -1
@@ -671,7 +671,7 @@ int stop_machine_from_inactive_cpu(int ( ret =multi_cpu_stop (&msdata);/* Busy wait for completion. */ -while (!atomic_read (&done.nr_todo)) +while (atomic_read (&done.nr_todo))cpu_relax ();mutex_unlock (&stop_cpus_mutex);
PATCH 5/6 stomp-machine: create lg_global_trylock_relax() primitive
In reply to a message not in the archiveCreate lg_global_trylock_relax() for use by stopper thread when it cannot schedule, to deal with stop_cpus_lock, which is now an lglock.
--- include/linux/lglock.h | 6 ++++++ include/linux/spinlock_rt.h | 1 + kernel/locking/lglock.c | 25 +++++++++++++++++++++++++ kernel/locking/rtmutex.c | 5 +++++ 4 files changed, 37 insertions(+)
include/linux/lglock.h +6 -0
@@ -74,4 +74,10 @@ void lg_local_unlock_cpu(struct lglock *void lg_global_lock (struct lglock *lg);void lg_global_unlock (struct lglock *lg); +#ifndef CONFIG_PREEMPT_RT_FULL +#define lg_global_trylock_relax (name) lg_global_lock(name) +#else +void lg_global_trylock_relax (struct lglock *lg); +#endif +#endif
include/linux/spinlock_rt.h +1 -0
@@ -35,6 +35,7 @@ extern int atomic_dec_and_spin_lock(atom*/ extern void __lockfunc __rt_spin_lock(struct rt_mutex *lock); extern void __lockfunc __rt_spin_unlock(struct rt_mutex *lock); +extern int __lockfunc __rt_spin_trylock(struct rt_mutex *lock);#define spin_lock (lock) \ do { \
kernel/locking/lglock.c +25 -0
@@ -105,3 +105,28 @@ void lg_global_unlock(struct lglock *lg)preempt_enable_nort (); }EXPORT_SYMBOL (lg_global_unlock); + +#ifdef CONFIG_PREEMPT_RT_FULL +/* +* HACK: If you use this, you get to keep the pieces. +* Used in queue_stop_cpus_work() when stop machinery +* is called from inactive CPU, so we can't schedule. +*/ +# define lg_do_trylock_relax (l) \ + do { \ + while (!__rt_spin_trylock(l)) \ + cpu_relax(); \ + } while (0) + +void lg_global_trylock_relax (struct lglock *lg) +{ +int i; + +lock_acquire_exclusive (&lg->lock_dep_map,0 ,0 ,NULL , _RET_IP_); +for_each_possible_cpu (i) { +lg_lock_ptr *lock; + lock =per_cpu_ptr (lg->lock, i); +lg_do_trylock_relax (lock); + } +} +#endif
kernel/locking/rtmutex.c +5 -0
@@ -1001,6 +1001,11 @@ void __lockfunc rt_spin_unlock_wait(spin }EXPORT_SYMBOL (rt_spin_unlock_wait); +int __lockfunc __rt_spin_trylock(struct rt_mutex *lock) +{ +return rt_mutex_trylock (lock); +} + int __lockfunc rt_spin_trylock(spinlock_t *lock) {int ret =rt_mutex_trylock (&lock->lock);
PATCH 6/6 stomp-machine: use lg_global_trylock_relax() to dead with stop_cpus_lock lglock
In reply to a message not in the archiveIf the stop machinery is called from inactive CPU we cannot use lg_global_lock(), because some other stomp machine invocation might be in progress and the lock can be contended. We cannot schedule from this context, so use the lovely new lg_global_trylock_relax() primitive to do what we used to do via one mutex_trylock()/cpu_relax() loop. We now do that trylock()/relax() across an entire herd of locks. Joy.
--- kernel/stop_machine.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-)
kernel/stop_machine.c +14 -10
@@ -266,7 +266,7 @@ int stop_two_cpus(unsigned int cpu1, unsstruct irq_cpu_stop_queue_work_info call_args;struct multi_stop_data msdata; -preempt_disable (); +preempt_disable_nort (); msdata = (struct multi_stop_data ){ .fn = fn, .data = arg,
@@ -299,7 +299,7 @@ int stop_two_cpus(unsigned int cpu1, uns* This relies on the stopper workqueues to be FIFO. */ if (!cpu_active (cpu1) || !cpu_active (cpu2)) { -preempt_enable (); +preempt_enable_nort ();return -ENOENT ; }
@@ -313,7 +313,7 @@ int stop_two_cpus(unsigned int cpu1, uns &irq_cpu_stop_queue_work, &call_args, 1);lg_local_unlock (&stop_cpus_lock); -preempt_enable (); +preempt_enable_nort ();wait_for_stop_done (&done);
@@ -346,7 +346,7 @@ static DEFINE_PER_CPU(struct cpu_stop_wostatic void queue_stop_cpus_work (const struct cpumask *cpumask,cpu_stop_fn_t fn,void *arg, -struct cpu_stop_done *done) +struct cpu_stop_done *done,bool inactive) {struct cpu_stop_work *work;unsigned int cpu;
@@ -360,11 +360,13 @@ static void queue_stop_cpus_work(const s }/* -* Disable preemption while queueing to avoid getting -* preempted by a stopper which might wait for other stoppers -* to enter @fn which can lead to deadlock. +* Make sure that all work is queued on all cpus before +* any of the cpus can execute it. */ -lg_global_lock (&stop_cpus_lock); +if (!inactive) +lg_global_lock (&stop_cpus_lock); +else +lg_global_trylock_relax (&stop_cpus_lock);for_each_cpu (cpu, cpumask)cpu_stop_queue_work (cpu, &per_cpu (stop_cpus_work, cpu));lg_global_unlock (&stop_cpus_lock);
@@ -376,7 +378,7 @@ static int __stop_cpus(const struct cpumstruct cpu_stop_done done;cpu_stop_init_done (&done,cpumask_weight (cpumask)); -queue_stop_cpus_work (cpumask, fn, arg, &done); +queue_stop_cpus_work (cpumask, fn, arg, &done, false);wait_for_stop_done (&done);return done.executed ? done.ret : -ENOENT ; }
@@ -572,6 +574,8 @@ static int __init cpu_stop_init(void)INIT_LIST_HEAD (&stopper->works); } +lg_lock_init (&stop_cpus_lock,"stop_cpus_lock" ); +BUG_ON (smpboot_register_percpu_thread (&cpu_stop_threads)); stop_machine_initialized = true;return 0 ;
@@ -667,7 +671,7 @@ int stop_machine_from_inactive_cpu(int (set_state (&msdata,MULTI_STOP_PREPARE );cpu_stop_init_done (&done,num_active_cpus ());queue_stop_cpus_work (cpu_active_mask, multi_cpu_stop, &msdata, - &done); + &done, true); ret =multi_cpu_stop (&msdata);/* Busy wait for completion. */