timers: Framework for migration of timers
Subsystems (7)
v6 cover timers: Framework for migration of timers
Ingo, Thomas, all,
In an SMP system, tasks are scheduled on different CPUs by the scheduler, interrupts are managed by irqbalancer daemon, but timers are still stuck to the CPUs that they have been initialised. Timers queued by tasks gets re-queued on the CPU where the task gets to run next, but timers from IRQ context like the ones in device drivers are still stuck on the CPU they were initialised. This framework will help move all 'movable timers' using a sysctl interface.
Please consider for inclusion into -tip
Testing Carried Out:
* Kernbench on a 2-package, quad-core machine results are as follows:
-----------------------------------------------------------------------
| No. of Threads | Time(s) - Without | Time(s) - With the | | | patches applied | patches applied | ----------------------------------------------------------------------- | 2 | 106.9 | 106.3 | | 4 | 54.7 | 54.4 | | 8 | 31.5 | 31.1 | | 16 | 28.0 | 27.5 | | 32 | 28.1 | 28.9 |
-----------------------------------------------------------------------
* I have cross-compiled my patches against alpha architecture in order to test if my patches have any issues for architectures without clockevents support.
The patches cross-compile without any issues for alpha architecture, but I need help to test if there is any performance regression. So I'm Cc-ing the maintainers of architectures without clockevents support.
The following patches are included:
PATCH 1/4 - framework to identify pinned timers. PATCH 2/4 - identifying the existing pinned hrtimers. PATCH 3/4 - /proc/sys sysctl hook to enable timer migration. PATCH 4/4 - logic to enable timer migration.
The patchset is based on the latest tip/master.
Timer migration is enabled by default.
It can be turned off when CONFIG_SCHED_DEBUG=y by
echo 0 > /proc/sys/kernel/timer_migration
--arun
PATCH v6 1/4 timers: Framework for identifying pinned timers
* Arun R Bharadwaj <[email protected]> [2009-04-16 12:11:36]:
This patch creates a new framework for identifying cpu-pinned timers and hrtimers.
This framework is needed because pinned timers are expected to fire on the same CPU on which they are queued. So it is essential to identify these and not migrate them, in case there are any.
For regular timers, the currently existing add_timer_on() can be used queue pinned timers and subsequently mod_timer_pinned() can be used to modify the 'expires' field.
For hrtimers, a new interface hrtimer_start_pinned() is created, which can be used to queue cpu-pinned hrtimer.
--- include/linux/hrtimer.h | 24 ++++++++++++++++++++---- include/linux/timer.h | 3 +++ kernel/hrtimer.c | 34 ++++++++++++++++++++++++++++------ kernel/timer.c | 31 +++++++++++++++++++++++++++---- 4 files changed, 78 insertions(+), 14 deletions(-)
include/linux/hrtimer.h +20 -4
@@ -331,23 +331,39 @@ static inline void hrtimer_init_on_stackstatic inline void destroy_hrtimer_on_stack (struct hrtimer *timer) { }#endif +#define HRTIMER_NOT_PINNED 0 +#define HRTIMER_PINNED 1/* Basic timer operations: */ extern int hrtimer_start (struct hrtimer *timer,ktime_t tim,const enum hrtimer_mode mode); +extern int hrtimer_start_pinned (struct hrtimer *timer,ktime_t tim, +const enum hrtimer_mode mode);extern int hrtimer_start_range_ns (struct hrtimer *timer,ktime_t tim, -unsigned long range_ns,const enum hrtimer_mode mode); +unsigned long range_ns,const enum hrtimer_mode mode,int pinned);extern int hrtimer_cancel (struct hrtimer *timer);extern int hrtimer_try_to_cancel (struct hrtimer *timer); -static inline int hrtimer_start_expires (struct hrtimer *timer, -enum hrtimer_mode mode) +static inline int __hrtimer_start_expires (struct hrtimer *timer, +enum hrtimer_mode mode,int pinned) {unsigned long delta;ktime_t soft, hard; soft =hrtimer_get_softexpires (timer); hard =hrtimer_get_expires (timer); delta =ktime_to_ns (ktime_sub (hard, soft)); -return hrtimer_start_range_ns (timer, soft, delta, mode); +return hrtimer_start_range_ns (timer, soft, delta, mode, pinned); +} + +static inline int hrtimer_start_expires (struct hrtimer *timer, +enum hrtimer_mode mode) +{ +return __hrtimer_start_expires (timer, mode,HRTIMER_NOT_PINNED ); +} + +static inline int hrtimer_start_expires_pinned (struct hrtimer *timer, +enum hrtimer_mode mode) +{ +return __hrtimer_start_expires (timer, mode,HRTIMER_PINNED ); }static inline int hrtimer_restart (struct hrtimer *timer)
kernel/hrtimer.c +28 -6
@@ -193,7 +193,8 @@ struct hrtimer_clock_base *lock_hrtimer_* Switch the timer base to the current CPU when possible. */ static inline struct hrtimer_clock_base * -switch_hrtimer_base (struct hrtimer *timer,struct hrtimer_clock_base *base) +switch_hrtimer_base (struct hrtimer *timer,struct hrtimer_clock_base *base, +int pinned) {struct hrtimer_clock_base *new_base;struct hrtimer_cpu_base *new_cpu_base;
@@ -897,9 +898,8 @@ remove_hrtimer(struct hrtimer *timer, st* 0 on success * 1 when the timer was active */ -int -hrtimer_start_range_ns (struct hrtimer *timer,ktime_t tim,unsigned long delta_ns, -const enum hrtimer_mode mode) +int hrtimer_start_range_ns (struct hrtimer *timer,ktime_t tim, +unsigned long delta_ns,const enum hrtimer_mode mode,int pinned) {struct hrtimer_clock_base *base, *new_base;unsigned long flags;
@@ -911,7 +911,7 @@ hrtimer_start_range_ns(struct hrtimer *t ret =remove_hrtimer (timer, base);/* Switch the timer base, if necessary: */ - new_base =switch_hrtimer_base (timer, base); + new_base =switch_hrtimer_base (timer, base, pinned);if (mode ==HRTIMER_MODE_REL ) { tim =ktime_add_safe (tim, new_base->get_time ());
@@ -948,6 +948,12 @@ hrtimer_start_range_ns(struct hrtimer *t }EXPORT_SYMBOL_GPL (hrtimer_start_range_ns); +int __hrtimer_start (struct hrtimer *timer,ktime_t tim, +const enum hrtimer_mode mode,int pinned) +{ +return hrtimer_start_range_ns (timer, tim,0 , mode, pinned); +} +/** * hrtimer_start - (re)start an hrtimer on the current CPU * @timer: the timer to be added
@@ -961,10 +967,26 @@ EXPORT_SYMBOL_GPL(hrtimer_start_range_nsint hrtimer_start (struct hrtimer *timer,ktime_t tim,const enum hrtimer_mode mode) { -return hrtimer_start_range_ns (timer, tim,0 , mode); +return __hrtimer_start (timer, tim, mode,HRTIMER_NOT_PINNED ); }EXPORT_SYMBOL_GPL (hrtimer_start); +/** +* hrtimer_start_pinned - start a CPU-pinned hrtimer +* @timer: the timer to be added +* @tim: expiry time +* @mode: expiry mode: absolute (HRTIMER_ABS) or relative (HRTIMER_REL) +* +* Returns: +* 0 on success +* 1 when the timer was active +*/ +int hrtimer_start_pinned (struct hrtimer *timer, +ktime_t tim,const enum hrtimer_mode mode) +{ +return __hrtimer_start (timer, tim, mode,HRTIMER_PINNED ); +} +EXPORT_SYMBOL_GPL (hrtimer_start_pinned);/** * hrtimer_try_to_cancel - try to deactivate a timer
include/linux/timer.h +3 -0
@@ -163,7 +163,10 @@ extern void add_timer_on(struct timer_liextern int del_timer (struct timer_list * timer);extern int mod_timer (struct timer_list *timer,unsigned long expires);extern int mod_timer_pending (struct timer_list *timer,unsigned long expires); +extern int mod_timer_pinned (struct timer_list *timer,unsigned long expires); +#define TIMER_NOT_PINNED 0 +#define TIMER_PINNED 1/* * The jiffies value which is added to now, when there is no timer * in the timer wheel:
kernel/timer.c +27 -4
@@ -601,7 +601,8 @@ static struct tvec_base *lock_timer_base }static inline int -__mod_timer (struct timer_list *timer,unsigned long expires,bool pending_only) +__mod_timer (struct timer_list *timer,unsigned long expires, +bool pending_only,int pinned) {struct tvec_base *base, *new_base;unsigned long flags;
@@ -665,7 +666,7 @@ out_unlock:*/ int mod_timer_pending (struct timer_list *timer,unsigned long expires) { -return __mod_timer (timer, expires, true); +return __mod_timer (timer, expires, true,TIMER_NOT_PINNED ); }EXPORT_SYMBOL (mod_timer_pending);
@@ -699,11 +700,33 @@ int mod_timer(struct timer_list *timer,if (timer->expires == expires &&timer_pending (timer))return 1 ; -return __mod_timer (timer, expires, false); +return __mod_timer (timer, expires, false,TIMER_NOT_PINNED ); }EXPORT_SYMBOL (mod_timer);/** +* mod_timer_pinned - modify a timer's timeout +* @timer: the timer to be modified +* @expires: new timeout in jiffies +* +* mod_timer_pinned() is a way to update the expire field of an +* active timer (if the timer is inactive it will be activated) +* and not allow the timer to be migrated to a different CPU. +* +* mod_timer_pinned(timer, expires) is equivalent to: +* +* del_timer(timer); timer->expires = expires; add_timer(timer); +*/ +int mod_timer_pinned (struct timer_list *timer,unsigned long expires) +{ +if (timer->expires == expires &&timer_pending (timer)) +return 1 ; + +return __mod_timer (timer, expires, false,TIMER_PINNED ); +} +EXPORT_SYMBOL (mod_timer_pinned); + +/** * add_timer - start a timer * @timer: the timer to be added *
@@ -1350,7 +1373,7 @@ signed long __sched schedule_timeout(sig expire = timeout + jiffies;setup_timer_on_stack (&timer, process_timeout, (unsigned long )current); -__mod_timer (&timer, expire, false); +__mod_timer (&timer, expire, false,TIMER_NOT_PINNED );schedule ();del_singleshot_timer_sync (&timer);
PATCH v6 1/4 timers: Framework for identifying pinned timers
In reply to Arun R Bharadwaj* Arun R Bharadwaj <[email protected]> [2009-04-16 12:11:36]:
This patch creates a new framework for identifying cpu-pinned timers and hrtimers.
This framework is needed because pinned timers are expected to fire on the same CPU on which they are queued. So it is essential to identify these and not migrate them, in case there are any.
For regular timers, the currently existing add_timer_on() can be used queue pinned timers and subsequently mod_timer_pinned() can be used to modify the 'expires' field.
For hrtimers, a new interface hrtimer_start_pinned() is created, which can be used to queue cpu-pinned hrtimer.
--- include/linux/hrtimer.h | 24 ++++++++++++++++++++---- include/linux/timer.h | 3 +++ kernel/hrtimer.c | 34 ++++++++++++++++++++++++++++------ kernel/timer.c | 31 +++++++++++++++++++++++++++---- 4 files changed, 78 insertions(+), 14 deletions(-)
include/linux/hrtimer.h +20 -4
@@ -331,23 +331,39 @@ static inline void hrtimer_init_on_stackstatic inline void destroy_hrtimer_on_stack (struct hrtimer *timer) { }#endif +#define HRTIMER_NOT_PINNED 0 +#define HRTIMER_PINNED 1/* Basic timer operations: */ extern int hrtimer_start (struct hrtimer *timer,ktime_t tim,const enum hrtimer_mode mode); +extern int hrtimer_start_pinned (struct hrtimer *timer,ktime_t tim, +const enum hrtimer_mode mode);extern int hrtimer_start_range_ns (struct hrtimer *timer,ktime_t tim, -unsigned long range_ns,const enum hrtimer_mode mode); +unsigned long range_ns,const enum hrtimer_mode mode,int pinned);extern int hrtimer_cancel (struct hrtimer *timer);extern int hrtimer_try_to_cancel (struct hrtimer *timer); -static inline int hrtimer_start_expires (struct hrtimer *timer, -enum hrtimer_mode mode) +static inline int __hrtimer_start_expires (struct hrtimer *timer, +enum hrtimer_mode mode,int pinned) {unsigned long delta;ktime_t soft, hard; soft =hrtimer_get_softexpires (timer); hard =hrtimer_get_expires (timer); delta =ktime_to_ns (ktime_sub (hard, soft)); -return hrtimer_start_range_ns (timer, soft, delta, mode); +return hrtimer_start_range_ns (timer, soft, delta, mode, pinned); +} + +static inline int hrtimer_start_expires (struct hrtimer *timer, +enum hrtimer_mode mode) +{ +return __hrtimer_start_expires (timer, mode,HRTIMER_NOT_PINNED ); +} + +static inline int hrtimer_start_expires_pinned (struct hrtimer *timer, +enum hrtimer_mode mode) +{ +return __hrtimer_start_expires (timer, mode,HRTIMER_PINNED ); }static inline int hrtimer_restart (struct hrtimer *timer)
kernel/hrtimer.c +28 -6
@@ -193,7 +193,8 @@ struct hrtimer_clock_base *lock_hrtimer_* Switch the timer base to the current CPU when possible. */ static inline struct hrtimer_clock_base * -switch_hrtimer_base (struct hrtimer *timer,struct hrtimer_clock_base *base) +switch_hrtimer_base (struct hrtimer *timer,struct hrtimer_clock_base *base, +int pinned) {struct hrtimer_clock_base *new_base;struct hrtimer_cpu_base *new_cpu_base;
@@ -897,9 +898,8 @@ remove_hrtimer(struct hrtimer *timer, st* 0 on success * 1 when the timer was active */ -int -hrtimer_start_range_ns (struct hrtimer *timer,ktime_t tim,unsigned long delta_ns, -const enum hrtimer_mode mode) +int hrtimer_start_range_ns (struct hrtimer *timer,ktime_t tim, +unsigned long delta_ns,const enum hrtimer_mode mode,int pinned) {struct hrtimer_clock_base *base, *new_base;unsigned long flags;
@@ -911,7 +911,7 @@ hrtimer_start_range_ns(struct hrtimer *t ret =remove_hrtimer (timer, base);/* Switch the timer base, if necessary: */ - new_base =switch_hrtimer_base (timer, base); + new_base =switch_hrtimer_base (timer, base, pinned);if (mode ==HRTIMER_MODE_REL ) { tim =ktime_add_safe (tim, new_base->get_time ());
@@ -948,6 +948,12 @@ hrtimer_start_range_ns(struct hrtimer *t }EXPORT_SYMBOL_GPL (hrtimer_start_range_ns); +int __hrtimer_start (struct hrtimer *timer,ktime_t tim, +const enum hrtimer_mode mode,int pinned) +{ +return hrtimer_start_range_ns (timer, tim,0 , mode, pinned); +} +/** * hrtimer_start - (re)start an hrtimer on the current CPU * @timer: the timer to be added
@@ -961,10 +967,26 @@ EXPORT_SYMBOL_GPL(hrtimer_start_range_nsint hrtimer_start (struct hrtimer *timer,ktime_t tim,const enum hrtimer_mode mode) { -return hrtimer_start_range_ns (timer, tim,0 , mode); +return __hrtimer_start (timer, tim, mode,HRTIMER_NOT_PINNED ); }EXPORT_SYMBOL_GPL (hrtimer_start); +/** +* hrtimer_start_pinned - start a CPU-pinned hrtimer +* @timer: the timer to be added +* @tim: expiry time +* @mode: expiry mode: absolute (HRTIMER_ABS) or relative (HRTIMER_REL) +* +* Returns: +* 0 on success +* 1 when the timer was active +*/ +int hrtimer_start_pinned (struct hrtimer *timer, +ktime_t tim,const enum hrtimer_mode mode) +{ +return __hrtimer_start (timer, tim, mode,HRTIMER_PINNED ); +} +EXPORT_SYMBOL_GPL (hrtimer_start_pinned);/** * hrtimer_try_to_cancel - try to deactivate a timer
include/linux/timer.h +3 -0
@@ -163,7 +163,10 @@ extern void add_timer_on(struct timer_liextern int del_timer (struct timer_list * timer);extern int mod_timer (struct timer_list *timer,unsigned long expires);extern int mod_timer_pending (struct timer_list *timer,unsigned long expires); +extern int mod_timer_pinned (struct timer_list *timer,unsigned long expires); +#define TIMER_NOT_PINNED 0 +#define TIMER_PINNED 1/* * The jiffies value which is added to now, when there is no timer * in the timer wheel:
kernel/timer.c +27 -4
@@ -601,7 +601,8 @@ static struct tvec_base *lock_timer_base }static inline int -__mod_timer (struct timer_list *timer,unsigned long expires,bool pending_only) +__mod_timer (struct timer_list *timer,unsigned long expires, +bool pending_only,int pinned) {struct tvec_base *base, *new_base;unsigned long flags;
@@ -665,7 +666,7 @@ out_unlock:*/ int mod_timer_pending (struct timer_list *timer,unsigned long expires) { -return __mod_timer (timer, expires, true); +return __mod_timer (timer, expires, true,TIMER_NOT_PINNED ); }EXPORT_SYMBOL (mod_timer_pending);
@@ -699,11 +700,33 @@ int mod_timer(struct timer_list *timer,if (timer->expires == expires &&timer_pending (timer))return 1 ; -return __mod_timer (timer, expires, false); +return __mod_timer (timer, expires, false,TIMER_NOT_PINNED ); }EXPORT_SYMBOL (mod_timer);/** +* mod_timer_pinned - modify a timer's timeout +* @timer: the timer to be modified +* @expires: new timeout in jiffies +* +* mod_timer_pinned() is a way to update the expire field of an +* active timer (if the timer is inactive it will be activated) +* and not allow the timer to be migrated to a different CPU. +* +* mod_timer_pinned(timer, expires) is equivalent to: +* +* del_timer(timer); timer->expires = expires; add_timer(timer); +*/ +int mod_timer_pinned (struct timer_list *timer,unsigned long expires) +{ +if (timer->expires == expires &&timer_pending (timer)) +return 1 ; + +return __mod_timer (timer, expires, false,TIMER_PINNED ); +} +EXPORT_SYMBOL (mod_timer_pinned); + +/** * add_timer - start a timer * @timer: the timer to be added *
@@ -1350,7 +1373,7 @@ signed long __sched schedule_timeout(sig expire = timeout + jiffies;setup_timer_on_stack (&timer, process_timeout, (unsigned long )current); -__mod_timer (&timer, expire, false); +__mod_timer (&timer, expire, false,TIMER_NOT_PINNED );schedule ();del_singleshot_timer_sync (&timer);
PATCH v6 2/4 timers: Identifying the existing pinned timers
In reply to Arun R Bharadwaj* Arun R Bharadwaj <[email protected]> [2009-04-16 12:11:36]:
The following pinned hrtimers have been identified and marked:
1)sched_rt_period_timer
2)tick_sched_timer
3)stack_trace_timer_fn
--- arch/x86/kernel/apic/x2apic_uv_x.c | 2 +- kernel/sched.c | 5 +++-- kernel/time/tick-sched.c | 7 ++++--- kernel/trace/trace_sysprof.c | 3 ++- 4 files changed, 10 insertions(+), 7 deletions(-)
kernel/sched.c +3 -2
@@ -236,7 +236,7 @@ static void start_rt_bandwidth(struct rt now =hrtimer_cb_get_time (&rt_b->rt_period_timer);hrtimer_forward (&rt_b->rt_period_timer, now, rt_b->rt_period); -hrtimer_start_expires (&rt_b->rt_period_timer, +hrtimer_start_expires_pinned (&rt_b->rt_period_timer,HRTIMER_MODE_ABS ); }spin_unlock (&rt_b->rt_runtime_lock);
@@ -1156,7 +1156,8 @@ static __init void init_hrtick(void)*/ static void hrtick_start (struct rq *rq,u64 delay) { -hrtimer_start (&rq->hrtick_timer,ns_to_ktime (delay),HRTIMER_MODE_REL ); +hrtimer_start_pinned (&rq->hrtick_timer,ns_to_ktime (delay), +HRTIMER_MODE_REL ); }static inline void init_hrtick (void )
kernel/time/tick-sched.c +4 -3
@@ -348,7 +348,7 @@ void tick_nohz_stop_sched_tick(int inidl ts->idle_expires = expires;if (ts->nohz_mode ==NOHZ_MODE_HIGHRES ) { -hrtimer_start (&ts->sched_timer, expires, +hrtimer_start_pinned (&ts->sched_timer, expires,HRTIMER_MODE_ABS );/* Check, if the timer was already in the past */ if (hrtimer_active (&ts->sched_timer))
@@ -394,7 +394,7 @@ static void tick_nohz_restart(struct tichrtimer_forward (&ts->sched_timer, now, tick_period);if (ts->nohz_mode ==NOHZ_MODE_HIGHRES ) { -hrtimer_start_expires (&ts->sched_timer, +hrtimer_start_expires_pinned (&ts->sched_timer,HRTIMER_MODE_ABS );/* Check, if the timer was already in the past */ if (hrtimer_active (&ts->sched_timer))
@@ -698,7 +698,8 @@ void tick_setup_sched_timer(void)for (;;) {hrtimer_forward (&ts->sched_timer, now, tick_period); -hrtimer_start_expires (&ts->sched_timer,HRTIMER_MODE_ABS ); +hrtimer_start_expires_pinned (&ts->sched_timer, +HRTIMER_MODE_ABS );/* Check, if the timer was already in the past */ if (hrtimer_active (&ts->sched_timer))break ;
kernel/trace/trace_sysprof.c +2 -1
@@ -203,7 +203,8 @@ static void start_stack_timer(void *unushrtimer_init (hrtimer,CLOCK_MONOTONIC ,HRTIMER_MODE_REL ); hrtimer->function = stack_trace_timer_fn; -hrtimer_start (hrtimer,ns_to_ktime (sample_period),HRTIMER_MODE_REL ); +hrtimer_start_pinned (hrtimer,ns_to_ktime (sample_period), +HRTIMER_MODE_REL ); }static void start_stack_timers (void )
arch/x86/kernel/apic/x2apic_uv_x.c +1 -1
@@ -455,7 +455,7 @@ static void uv_heartbeat(unsigned long iuv_set_scir_bits (bits);/* enable next timer period */ -mod_timer (timer, jiffies +SCIR_CPU_HB_INTERVAL ); +mod_timer_pinned (timer, jiffies +SCIR_CPU_HB_INTERVAL ); }static void __cpuinit uv_heartbeat_enable (int cpu)
PATCH v6 2/4 timers: Identifying the existing pinned timers
In reply to Arun R Bharadwaj* Arun R Bharadwaj <[email protected]> [2009-04-16 12:11:36]:
The following pinned hrtimers have been identified and marked:
1)sched_rt_period_timer
2)tick_sched_timer
3)stack_trace_timer_fn
--- arch/x86/kernel/apic/x2apic_uv_x.c | 2 +- kernel/sched.c | 5 +++-- kernel/time/tick-sched.c | 7 ++++--- kernel/trace/trace_sysprof.c | 3 ++- 4 files changed, 10 insertions(+), 7 deletions(-)
kernel/sched.c +3 -2
@@ -236,7 +236,7 @@ static void start_rt_bandwidth(struct rt now =hrtimer_cb_get_time (&rt_b->rt_period_timer);hrtimer_forward (&rt_b->rt_period_timer, now, rt_b->rt_period); -hrtimer_start_expires (&rt_b->rt_period_timer, +hrtimer_start_expires_pinned (&rt_b->rt_period_timer,HRTIMER_MODE_ABS ); }spin_unlock (&rt_b->rt_runtime_lock);
@@ -1156,7 +1156,8 @@ static __init void init_hrtick(void)*/ static void hrtick_start (struct rq *rq,u64 delay) { -hrtimer_start (&rq->hrtick_timer,ns_to_ktime (delay),HRTIMER_MODE_REL ); +hrtimer_start_pinned (&rq->hrtick_timer,ns_to_ktime (delay), +HRTIMER_MODE_REL ); }static inline void init_hrtick (void )
kernel/time/tick-sched.c +4 -3
@@ -348,7 +348,7 @@ void tick_nohz_stop_sched_tick(int inidl ts->idle_expires = expires;if (ts->nohz_mode ==NOHZ_MODE_HIGHRES ) { -hrtimer_start (&ts->sched_timer, expires, +hrtimer_start_pinned (&ts->sched_timer, expires,HRTIMER_MODE_ABS );/* Check, if the timer was already in the past */ if (hrtimer_active (&ts->sched_timer))
@@ -394,7 +394,7 @@ static void tick_nohz_restart(struct tichrtimer_forward (&ts->sched_timer, now, tick_period);if (ts->nohz_mode ==NOHZ_MODE_HIGHRES ) { -hrtimer_start_expires (&ts->sched_timer, +hrtimer_start_expires_pinned (&ts->sched_timer,HRTIMER_MODE_ABS );/* Check, if the timer was already in the past */ if (hrtimer_active (&ts->sched_timer))
@@ -698,7 +698,8 @@ void tick_setup_sched_timer(void)for (;;) {hrtimer_forward (&ts->sched_timer, now, tick_period); -hrtimer_start_expires (&ts->sched_timer,HRTIMER_MODE_ABS ); +hrtimer_start_expires_pinned (&ts->sched_timer, +HRTIMER_MODE_ABS );/* Check, if the timer was already in the past */ if (hrtimer_active (&ts->sched_timer))break ;
kernel/trace/trace_sysprof.c +2 -1
@@ -203,7 +203,8 @@ static void start_stack_timer(void *unushrtimer_init (hrtimer,CLOCK_MONOTONIC ,HRTIMER_MODE_REL ); hrtimer->function = stack_trace_timer_fn; -hrtimer_start (hrtimer,ns_to_ktime (sample_period),HRTIMER_MODE_REL ); +hrtimer_start_pinned (hrtimer,ns_to_ktime (sample_period), +HRTIMER_MODE_REL ); }static void start_stack_timers (void )
arch/x86/kernel/apic/x2apic_uv_x.c +1 -1
@@ -455,7 +455,7 @@ static void uv_heartbeat(unsigned long iuv_set_scir_bits (bits);/* enable next timer period */ -mod_timer (timer, jiffies +SCIR_CPU_HB_INTERVAL ); +mod_timer_pinned (timer, jiffies +SCIR_CPU_HB_INTERVAL ); }static void __cpuinit uv_heartbeat_enable (int cpu)
PATCH v6 3/4 timers: /proc/sys sysctl hook to enable timer migration
In reply to Arun R Bharadwaj* Arun R Bharadwaj <[email protected]> [2009-04-16 12:11:36]:
This patch creates the /proc/sys sysctl interface at /proc/sys/kernel/timer_migration
Timer migration is enabled by default.
To disable timer migration, when CONFIG_SCHED_DEBUG = y,
echo 0 > /proc/sys/kernel/timer_migration
--- include/linux/sched.h | 1 + kernel/sched.c | 2 ++ kernel/sysctl.c | 8 ++++++++ 3 files changed, 11 insertions(+)
include/linux/sched.h +1 -0
@@ -1763,6 +1763,7 @@ extern unsigned int sysctl_sched_child_rextern unsigned int sysctl_sched_features;extern unsigned int sysctl_sched_migration_cost;extern unsigned int sysctl_sched_nr_migrate; +extern unsigned int sysctl_timer_migration; int sched_nr_latency_handler(struct ctl_table *table, int write, struct file *file, void __user *buffer, size_t *length,
kernel/sysctl.c +8 -0
@@ -328,6 +328,14 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = &proc_dointvec, }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "timer_migration", + .data = &sysctl_timer_migration, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = &proc_dointvec, + },#endif { .ctl_name = CTL_UNNUMBERED,
kernel/sched.c +2 -0
@@ -8426,6 +8426,8 @@ void __init sched_init_smp(void) }#endif /* CONFIG_SMP */ +const_debug unsigned int sysctl_timer_migration = 1; +int in_sched_functions (unsigned long addr) { return in_lock_functions(addr) ||
PATCH v6 3/4 timers: /proc/sys sysctl hook to enable timer migration
In reply to Arun R Bharadwaj* Arun R Bharadwaj <[email protected]> [2009-04-16 12:11:36]:
This patch creates the /proc/sys sysctl interface at /proc/sys/kernel/timer_migration
Timer migration is enabled by default.
To disable timer migration, when CONFIG_SCHED_DEBUG = y,
echo 0 > /proc/sys/kernel/timer_migration
--- include/linux/sched.h | 1 + kernel/sched.c | 2 ++ kernel/sysctl.c | 8 ++++++++ 3 files changed, 11 insertions(+)
include/linux/sched.h +1 -0
@@ -1763,6 +1763,7 @@ extern unsigned int sysctl_sched_child_rextern unsigned int sysctl_sched_features;extern unsigned int sysctl_sched_migration_cost;extern unsigned int sysctl_sched_nr_migrate; +extern unsigned int sysctl_timer_migration; int sched_nr_latency_handler(struct ctl_table *table, int write, struct file *file, void __user *buffer, size_t *length,
kernel/sysctl.c +8 -0
@@ -328,6 +328,14 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = &proc_dointvec, }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "timer_migration", + .data = &sysctl_timer_migration, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = &proc_dointvec, + },#endif { .ctl_name = CTL_UNNUMBERED,
kernel/sched.c +2 -0
@@ -8426,6 +8426,8 @@ void __init sched_init_smp(void) }#endif /* CONFIG_SMP */ +const_debug unsigned int sysctl_timer_migration = 1; +int in_sched_functions (unsigned long addr) { return in_lock_functions(addr) ||
PATCH v6 4/4 timers: Logic to move non pinned timers
In reply to Arun R Bharadwaj* Arun R Bharadwaj <[email protected]> [2009-04-16 12:11:36]:
This patch migrates all non pinned timers and hrtimers to the current idle load balancer, from all the idle CPUs. Timers firing on busy CPUs are not migrated.
While migrating hrtimers, care should be taken to check if migrating a hrtimer would result in a latency or not. So we compare the expiry of the hrtimer with the next timer interrupt on the target cpu and migrate the hrtimer only if it expires *after* the next interrupt on the target cpu. So, added a clockevents_get_next_event() helper function to return the next_event on the target cpu's clock_event_device.
--- include/linux/clockchips.h | 11 +++++++++ include/linux/sched.h | 12 ++++++++++ kernel/hrtimer.c | 50 +++++++++++++++++++++++++++++++++++++++++++-- kernel/sched.c | 5 ++++ kernel/time/clockevents.c | 14 ++++++++++++ kernel/timer.c | 14 +++++++++++- 6 files changed, 103 insertions(+), 3 deletions(-)
kernel/timer.c +13 -1
@@ -37,6 +37,7 @@#include <linux/delay.h> #include <linux/tick.h> #include <linux/kallsyms.h> +#include <linux/sched.h> #include <asm/uaccess.h> #include <asm/unistd.h>
@@ -606,7 +607,7 @@ __mod_timer(struct timer_list *timer, un {struct tvec_base *base, *new_base;unsigned long flags; -int ret; +int ret, preferred_cpu =-1 , cpu; ret =0 ;
@@ -627,6 +628,17 @@ __mod_timer(struct timer_list *timer, un new_base =__get_cpu_var (tvec_bases); + cpu =smp_processor_id (); +if (get_sysctl_timer_migration () &&idle_cpu (cpu) && !pinned) { +#if defined(CONFIG_NO_HZ ) && (CONFIG_SMP ) + preferred_cpu =get_nohz_load_balancer (); +#endif +if (preferred_cpu >=0 ) + cpu = preferred_cpu; + } + + new_base =per_cpu (tvec_bases, cpu); +if (base != new_base) {/* * We are trying to schedule the timer on the local CPU.
kernel/hrtimer.c +48 -2
75 lines not on this page · raw message
include/linux/sched.h +12 -0
@@ -258,6 +258,7 @@ extern void task_rq_unlock_wait(struct textern cpumask_var_t nohz_cpu_mask;#if defined(CONFIG_SMP ) && defined(CONFIG_NO_HZ )extern int select_nohz_load_balancer (int cpu); +extern int get_nohz_load_balancer (void );#else static inline int select_nohz_load_balancer (int cpu) {
@@ -1769,6 +1770,17 @@ int sched_nr_latency_handler(struct ctl_ struct file *file, void __user *buffer, size_t *length, loff_t *ppos);#endif +#ifdef CONFIG_SCHED_DEBUG +static inline unsigned int get_sysctl_timer_migration (void ) +{ +return sysctl_timer_migration; +} +#else +static inline unsigned int get_sysctl_timer_migration (void ) +{ +return 1 ; +} +#endif extern unsigned int sysctl_sched_rt_period;extern int sysctl_sched_rt_runtime;
kernel/sched.c +5 -0
@@ -4009,6 +4009,11 @@ static struct { .load_balancer = ATOMIC_INIT(-1), }; +int get_nohz_load_balancer (void ) +{ +return atomic_read (&nohz.load_balancer); +} +/* * This routine will try to nominate the ilb (idle load balancing) * owner among the cpus whose ticks are stopped. ilb owner will do the idle
kernel/time/clockevents.c +14 -0
@@ -18,6 +18,7 @@#include <linux/notifier.h> #include <linux/smp.h> #include <linux/sysdev.h> +#include <linux/tick.h> /* The registered clock event devices */ static LIST_HEAD (clockevent_devices);
@@ -252,3 +253,16 @@ void clockevents_notify(unsigned long re }EXPORT_SYMBOL_GPL (clockevents_notify);#endif + +#ifdef CONFIG_GENERIC_CLOCKEVENTS +ktime_t clockevents_get_next_event (int cpu) +{ +struct tick_device *td; +struct clock_event_device *dev; + + td = &per_cpu (tick_cpu_device, cpu); + dev = td->evtdev; + +return dev->next_event; +} +#endif
include/linux/clockchips.h +11 -0
@@ -143,3 +143,14 @@ extern void clockevents_notify(unsigned#endif #endif + +#ifdef CONFIG_GENERIC_CLOCKEVENTS +extern ktime_t clockevents_get_next_event (int cpu); +#else +static inline ktime_t clockevents_get_next_event (int cpu) +{ +ktime_t ret; + ret.tv64 =KTIME_MAX ; +return ret; +} +#endif
PATCH v6 4/4 timers: Logic to move non pinned timers
In reply to Arun R Bharadwaj* Arun R Bharadwaj <[email protected]> [2009-04-16 12:11:36]:
This patch migrates all non pinned timers and hrtimers to the current idle load balancer, from all the idle CPUs. Timers firing on busy CPUs are not migrated.
While migrating hrtimers, care should be taken to check if migrating a hrtimer would result in a latency or not. So we compare the expiry of the hrtimer with the next timer interrupt on the target cpu and migrate the hrtimer only if it expires *after* the next interrupt on the target cpu. So, added a clockevents_get_next_event() helper function to return the next_event on the target cpu's clock_event_device.
--- include/linux/clockchips.h | 11 +++++++++ include/linux/sched.h | 12 ++++++++++ kernel/hrtimer.c | 50 +++++++++++++++++++++++++++++++++++++++++++-- kernel/sched.c | 5 ++++ kernel/time/clockevents.c | 14 ++++++++++++ kernel/timer.c | 14 +++++++++++- 6 files changed, 103 insertions(+), 3 deletions(-)
kernel/timer.c +13 -1
@@ -37,6 +37,7 @@#include <linux/delay.h> #include <linux/tick.h> #include <linux/kallsyms.h> +#include <linux/sched.h> #include <asm/uaccess.h> #include <asm/unistd.h>
@@ -606,7 +607,7 @@ __mod_timer(struct timer_list *timer, un {struct tvec_base *base, *new_base;unsigned long flags; -int ret; +int ret, preferred_cpu =-1 , cpu; ret =0 ;
@@ -627,6 +628,17 @@ __mod_timer(struct timer_list *timer, un new_base =__get_cpu_var (tvec_bases); + cpu =smp_processor_id (); +if (get_sysctl_timer_migration () &&idle_cpu (cpu) && !pinned) { +#if defined(CONFIG_NO_HZ ) && (CONFIG_SMP ) + preferred_cpu =get_nohz_load_balancer (); +#endif +if (preferred_cpu >=0 ) + cpu = preferred_cpu; + } + + new_base =per_cpu (tvec_bases, cpu); +if (base != new_base) {/* * We are trying to schedule the timer on the local CPU.
kernel/hrtimer.c +48 -2
75 lines not on this page · raw message
include/linux/sched.h +12 -0
@@ -258,6 +258,7 @@ extern void task_rq_unlock_wait(struct textern cpumask_var_t nohz_cpu_mask;#if defined(CONFIG_SMP ) && defined(CONFIG_NO_HZ )extern int select_nohz_load_balancer (int cpu); +extern int get_nohz_load_balancer (void );#else static inline int select_nohz_load_balancer (int cpu) {
@@ -1769,6 +1770,17 @@ int sched_nr_latency_handler(struct ctl_ struct file *file, void __user *buffer, size_t *length, loff_t *ppos);#endif +#ifdef CONFIG_SCHED_DEBUG +static inline unsigned int get_sysctl_timer_migration (void ) +{ +return sysctl_timer_migration; +} +#else +static inline unsigned int get_sysctl_timer_migration (void ) +{ +return 1 ; +} +#endif extern unsigned int sysctl_sched_rt_period;extern int sysctl_sched_rt_runtime;
kernel/sched.c +5 -0
@@ -4009,6 +4009,11 @@ static struct { .load_balancer = ATOMIC_INIT(-1), }; +int get_nohz_load_balancer (void ) +{ +return atomic_read (&nohz.load_balancer); +} +/* * This routine will try to nominate the ilb (idle load balancing) * owner among the cpus whose ticks are stopped. ilb owner will do the idle
kernel/time/clockevents.c +14 -0
@@ -18,6 +18,7 @@#include <linux/notifier.h> #include <linux/smp.h> #include <linux/sysdev.h> +#include <linux/tick.h> /* The registered clock event devices */ static LIST_HEAD (clockevent_devices);
@@ -252,3 +253,16 @@ void clockevents_notify(unsigned long re }EXPORT_SYMBOL_GPL (clockevents_notify);#endif + +#ifdef CONFIG_GENERIC_CLOCKEVENTS +ktime_t clockevents_get_next_event (int cpu) +{ +struct tick_device *td; +struct clock_event_device *dev; + + td = &per_cpu (tick_cpu_device, cpu); + dev = td->evtdev; + +return dev->next_event; +} +#endif
include/linux/clockchips.h +11 -0
@@ -143,3 +143,14 @@ extern void clockevents_notify(unsigned#endif #endif + +#ifdef CONFIG_GENERIC_CLOCKEVENTS +extern ktime_t clockevents_get_next_event (int cpu); +#else +static inline ktime_t clockevents_get_next_event (int cpu) +{ +ktime_t ret; + ret.tv64 =KTIME_MAX ; +return ret; +} +#endif