aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/time/timer.c
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2015-05-26 18:50:33 -0400
committerThomas Gleixner <tglx@linutronix.de>2015-06-19 09:18:28 -0400
commitbc7a34b8b9ebfb0f4b8a35a72a0b134fd6c5ef50 (patch)
treef6324a2a7742e56740e9cc08d9636865ee72ec89 /kernel/time/timer.c
parentc74441a17eb975b604e339ca6c11b9ab9aaca11f (diff)
timer: Reduce timer migration overhead if disabled
Eric reported that the timer_migration sysctl is not really nice performance wise as it needs to check at every timer insertion whether the feature is enabled or not. Further the check does not live in the timer code, so we have an extra function call which checks an extra cache line to figure out that it is disabled. We can do better and store that information in the per cpu (hr)timer bases. I pondered to use a static key, but that's a nightmare to update from the nohz code and the timer base cache line is hot anyway when we select a timer base. The old logic enabled the timer migration unconditionally if CONFIG_NO_HZ was set even if nohz was disabled on the kernel command line. With this modification, we start off with migration disabled. The user visible sysctl is still set to enabled. If the kernel switches to NOHZ migration is enabled, if the user did not disable it via the sysctl prior to the switch. If nohz=off is on the kernel command line, migration stays disabled no matter what. Before: 47.76% hog [.] main 14.84% [kernel] [k] _raw_spin_lock_irqsave 9.55% [kernel] [k] _raw_spin_unlock_irqrestore 6.71% [kernel] [k] mod_timer 6.24% [kernel] [k] lock_timer_base.isra.38 3.76% [kernel] [k] detach_if_pending 3.71% [kernel] [k] del_timer 2.50% [kernel] [k] internal_add_timer 1.51% [kernel] [k] get_nohz_timer_target 1.28% [kernel] [k] __internal_add_timer 0.78% [kernel] [k] timerfn 0.48% [kernel] [k] wake_up_nohz_cpu After: 48.10% hog [.] main 15.25% [kernel] [k] _raw_spin_lock_irqsave 9.76% [kernel] [k] _raw_spin_unlock_irqrestore 6.50% [kernel] [k] mod_timer 6.44% [kernel] [k] lock_timer_base.isra.38 3.87% [kernel] [k] detach_if_pending 3.80% [kernel] [k] del_timer 2.67% [kernel] [k] internal_add_timer 1.33% [kernel] [k] __internal_add_timer 0.73% [kernel] [k] timerfn 0.54% [kernel] [k] wake_up_nohz_cpu Reported-by: Eric Dumazet <edumazet@google.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Paul McKenney <paulmck@linux.vnet.ibm.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Viresh Kumar <viresh.kumar@linaro.org> Cc: John Stultz <john.stultz@linaro.org> Cc: Joonwoo Park <joonwoop@codeaurora.org> Cc: Wenbo Wang <wenbo.wang@memblaze.com> Link: http://lkml.kernel.org/r/20150526224512.127050787@linutronix.de Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/time/timer.c')
-rw-r--r--kernel/time/timer.c59
1 files changed, 54 insertions, 5 deletions
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 3398d93c74a7..343142ed996a 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -85,6 +85,7 @@ struct tvec_base {
85 unsigned long active_timers; 85 unsigned long active_timers;
86 unsigned long all_timers; 86 unsigned long all_timers;
87 int cpu; 87 int cpu;
88 bool migration_enabled;
88 struct tvec_root tv1; 89 struct tvec_root tv1;
89 struct tvec tv2; 90 struct tvec tv2;
90 struct tvec tv3; 91 struct tvec tv3;
@@ -95,6 +96,54 @@ struct tvec_base {
95 96
96static DEFINE_PER_CPU(struct tvec_base, tvec_bases); 97static DEFINE_PER_CPU(struct tvec_base, tvec_bases);
97 98
99#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
100unsigned int sysctl_timer_migration = 1;
101
102void timers_update_migration(void)
103{
104 bool on = sysctl_timer_migration && tick_nohz_active;
105 unsigned int cpu;
106
107 /* Avoid the loop, if nothing to update */
108 if (this_cpu_read(tvec_bases.migration_enabled) == on)
109 return;
110
111 for_each_possible_cpu(cpu) {
112 per_cpu(tvec_bases.migration_enabled, cpu) = on;
113 per_cpu(hrtimer_bases.migration_enabled, cpu) = on;
114 }
115}
116
117int timer_migration_handler(struct ctl_table *table, int write,
118 void __user *buffer, size_t *lenp,
119 loff_t *ppos)
120{
121 static DEFINE_MUTEX(mutex);
122 int ret;
123
124 mutex_lock(&mutex);
125 ret = proc_dointvec(table, write, buffer, lenp, ppos);
126 if (!ret && write)
127 timers_update_migration();
128 mutex_unlock(&mutex);
129 return ret;
130}
131
132static inline struct tvec_base *get_target_base(struct tvec_base *base,
133 int pinned)
134{
135 if (pinned || !base->migration_enabled)
136 return this_cpu_ptr(&tvec_bases);
137 return per_cpu_ptr(&tvec_bases, get_nohz_timer_target());
138}
139#else
140static inline struct tvec_base *get_target_base(struct tvec_base *base,
141 int pinned)
142{
143 return this_cpu_ptr(&tvec_bases);
144}
145#endif
146
98static unsigned long round_jiffies_common(unsigned long j, int cpu, 147static unsigned long round_jiffies_common(unsigned long j, int cpu,
99 bool force_up) 148 bool force_up)
100{ 149{
@@ -716,11 +765,11 @@ static struct tvec_base *lock_timer_base(struct timer_list *timer,
716 765
717static inline int 766static inline int
718__mod_timer(struct timer_list *timer, unsigned long expires, 767__mod_timer(struct timer_list *timer, unsigned long expires,
719 bool pending_only, int pinned) 768 bool pending_only, int pinned)
720{ 769{
721 struct tvec_base *base, *new_base; 770 struct tvec_base *base, *new_base;
722 unsigned long flags; 771 unsigned long flags;
723 int ret = 0 , cpu; 772 int ret = 0;
724 773
725 timer_stats_timer_set_start_info(timer); 774 timer_stats_timer_set_start_info(timer);
726 BUG_ON(!timer->function); 775 BUG_ON(!timer->function);
@@ -733,8 +782,7 @@ __mod_timer(struct timer_list *timer, unsigned long expires,
733 782
734 debug_activate(timer, expires); 783 debug_activate(timer, expires);
735 784
736 cpu = get_nohz_timer_target(pinned); 785 new_base = get_target_base(base, pinned);
737 new_base = per_cpu_ptr(&tvec_bases, cpu);
738 786
739 if (base != new_base) { 787 if (base != new_base) {
740 /* 788 /*
@@ -751,7 +799,8 @@ __mod_timer(struct timer_list *timer, unsigned long expires,
751 spin_unlock(&base->lock); 799 spin_unlock(&base->lock);
752 base = new_base; 800 base = new_base;
753 spin_lock(&base->lock); 801 spin_lock(&base->lock);
754 timer->flags = (timer->flags & ~TIMER_BASEMASK) | cpu; 802 timer->flags &= ~TIMER_BASEMASK;
803 timer->flags |= base->cpu;
755 } 804 }
756 } 805 }
757 806