diff options
-rw-r--r-- | include/linux/timer.h | 10 | ||||
-rw-r--r-- | kernel/timer.c | 56 |
2 files changed, 65 insertions, 1 deletions
diff --git a/include/linux/timer.h b/include/linux/timer.h index a2d1eb6cb3f0..ea965b857a50 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h | |||
@@ -10,13 +10,19 @@ | |||
10 | struct tvec_base; | 10 | struct tvec_base; |
11 | 11 | ||
12 | struct timer_list { | 12 | struct timer_list { |
13 | /* | ||
14 | * All fields that change during normal runtime grouped to the | ||
15 | * same cacheline | ||
16 | */ | ||
13 | struct list_head entry; | 17 | struct list_head entry; |
14 | unsigned long expires; | 18 | unsigned long expires; |
19 | struct tvec_base *base; | ||
15 | 20 | ||
16 | void (*function)(unsigned long); | 21 | void (*function)(unsigned long); |
17 | unsigned long data; | 22 | unsigned long data; |
18 | 23 | ||
19 | struct tvec_base *base; | 24 | int slack; |
25 | |||
20 | #ifdef CONFIG_TIMER_STATS | 26 | #ifdef CONFIG_TIMER_STATS |
21 | void *start_site; | 27 | void *start_site; |
22 | char start_comm[16]; | 28 | char start_comm[16]; |
@@ -165,6 +171,8 @@ extern int mod_timer(struct timer_list *timer, unsigned long expires); | |||
165 | extern int mod_timer_pending(struct timer_list *timer, unsigned long expires); | 171 | extern int mod_timer_pending(struct timer_list *timer, unsigned long expires); |
166 | extern int mod_timer_pinned(struct timer_list *timer, unsigned long expires); | 172 | extern int mod_timer_pinned(struct timer_list *timer, unsigned long expires); |
167 | 173 | ||
174 | extern void set_timer_slack(struct timer_list *time, int slack_hz); | ||
175 | |||
168 | #define TIMER_NOT_PINNED 0 | 176 | #define TIMER_NOT_PINNED 0 |
169 | #define TIMER_PINNED 1 | 177 | #define TIMER_PINNED 1 |
170 | /* | 178 | /* |
diff --git a/kernel/timer.c b/kernel/timer.c index 7e12e7bc7ce6..49773f38c9bc 100644 --- a/kernel/timer.c +++ b/kernel/timer.c | |||
@@ -318,6 +318,24 @@ unsigned long round_jiffies_up_relative(unsigned long j) | |||
318 | } | 318 | } |
319 | EXPORT_SYMBOL_GPL(round_jiffies_up_relative); | 319 | EXPORT_SYMBOL_GPL(round_jiffies_up_relative); |
320 | 320 | ||
321 | /** | ||
322 | * set_timer_slack - set the allowed slack for a timer | ||
323 | * @slack_hz: the amount of time (in jiffies) allowed for rounding | ||
324 | * | ||
325 | * Set the amount of time, in jiffies, that a certain timer has | ||
326 | * in terms of slack. By setting this value, the timer subsystem | ||
327 | * will schedule the actual timer somewhere between | ||
328 | * the time mod_timer() asks for, and that time plus the slack. | ||
329 | * | ||
330 | * By setting the slack to -1, a percentage of the delay is used | ||
331 | * instead. | ||
332 | */ | ||
333 | void set_timer_slack(struct timer_list *timer, int slack_hz) | ||
334 | { | ||
335 | timer->slack = slack_hz; | ||
336 | } | ||
337 | EXPORT_SYMBOL_GPL(set_timer_slack); | ||
338 | |||
321 | 339 | ||
322 | static inline void set_running_timer(struct tvec_base *base, | 340 | static inline void set_running_timer(struct tvec_base *base, |
323 | struct timer_list *timer) | 341 | struct timer_list *timer) |
@@ -549,6 +567,7 @@ static void __init_timer(struct timer_list *timer, | |||
549 | { | 567 | { |
550 | timer->entry.next = NULL; | 568 | timer->entry.next = NULL; |
551 | timer->base = __raw_get_cpu_var(tvec_bases); | 569 | timer->base = __raw_get_cpu_var(tvec_bases); |
570 | timer->slack = -1; | ||
552 | #ifdef CONFIG_TIMER_STATS | 571 | #ifdef CONFIG_TIMER_STATS |
553 | timer->start_site = NULL; | 572 | timer->start_site = NULL; |
554 | timer->start_pid = -1; | 573 | timer->start_pid = -1; |
@@ -714,6 +733,41 @@ int mod_timer_pending(struct timer_list *timer, unsigned long expires) | |||
714 | } | 733 | } |
715 | EXPORT_SYMBOL(mod_timer_pending); | 734 | EXPORT_SYMBOL(mod_timer_pending); |
716 | 735 | ||
736 | /* | ||
737 | * Decide where to put the timer while taking the slack into account | ||
738 | * | ||
739 | * Algorithm: | ||
740 | * 1) calculate the maximum (absolute) time | ||
741 | * 2) calculate the highest bit where the expires and new max are different | ||
742 | * 3) use this bit to make a mask | ||
743 | * 4) use the bitmask to round down the maximum time, so that all last | ||
744 | * bits are zeros | ||
745 | */ | ||
746 | static inline | ||
747 | unsigned long apply_slack(struct timer_list *timer, unsigned long expires) | ||
748 | { | ||
749 | unsigned long expires_limit, mask; | ||
750 | int bit; | ||
751 | |||
752 | expires_limit = expires + timer->slack; | ||
753 | |||
754 | if (timer->slack < 0) /* auto slack: use 0.4% */ | ||
755 | expires_limit = expires + (expires - jiffies)/256; | ||
756 | |||
757 | mask = expires ^ expires_limit; | ||
758 | |||
759 | if (mask == 0) | ||
760 | return expires; | ||
761 | |||
762 | bit = find_last_bit(&mask, BITS_PER_LONG); | ||
763 | |||
764 | mask = (1 << bit) - 1; | ||
765 | |||
766 | expires_limit = expires_limit & ~(mask); | ||
767 | |||
768 | return expires_limit; | ||
769 | } | ||
770 | |||
717 | /** | 771 | /** |
718 | * mod_timer - modify a timer's timeout | 772 | * mod_timer - modify a timer's timeout |
719 | * @timer: the timer to be modified | 773 | * @timer: the timer to be modified |
@@ -744,6 +798,8 @@ int mod_timer(struct timer_list *timer, unsigned long expires) | |||
744 | if (timer_pending(timer) && timer->expires == expires) | 798 | if (timer_pending(timer) && timer->expires == expires) |
745 | return 1; | 799 | return 1; |
746 | 800 | ||
801 | expires = apply_slack(timer, expires); | ||
802 | |||
747 | return __mod_timer(timer, expires, false, TIMER_NOT_PINNED); | 803 | return __mod_timer(timer, expires, false, TIMER_NOT_PINNED); |
748 | } | 804 | } |
749 | EXPORT_SYMBOL(mod_timer); | 805 | EXPORT_SYMBOL(mod_timer); |