diff options
| author | Peter Zijlstra <peterz@infradead.org> | 2015-06-11 08:46:45 -0400 |
|---|---|---|
| committer | Thomas Gleixner <tglx@linutronix.de> | 2015-06-18 18:09:56 -0400 |
| commit | 8edfb0362e8e52dec2de08fa163af01c9da2c9d0 (patch) | |
| tree | 5f607cf35b8c625695eb7cc3d357432f733185fa /kernel/time | |
| parent | c04dca02bc73096435a5c36efd5ccb2171edcbe1 (diff) | |
hrtimer: Fix hrtimer_is_queued() hole
A queued hrtimer that gets restarted (hrtimer_start*() while
hrtimer_is_queued()) will briefly appear as unqueued/inactive, even
though the timer has always been active, we just moved it.
Close this hole by preserving timer->state in
hrtimer_start_range_ns()'s remove_hrtimer() call.
Reported-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: ktkhai@parallels.com
Cc: rostedt@goodmis.org
Cc: juri.lelli@gmail.com
Cc: pang.xunlei@linaro.org
Cc: wanpeng.li@linux.intel.com
Cc: umgwanakikbuti@gmail.com
Link: http://lkml.kernel.org/r/20150611124743.175989138@infradead.org
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/time')
| -rw-r--r-- | kernel/time/hrtimer.c | 23 |
1 files changed, 13 insertions, 10 deletions
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index b1b795e5e0b1..1604157374d7 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c | |||
| @@ -889,10 +889,10 @@ static void __remove_hrtimer(struct hrtimer *timer, | |||
| 889 | * remove hrtimer, called with base lock held | 889 | * remove hrtimer, called with base lock held |
| 890 | */ | 890 | */ |
| 891 | static inline int | 891 | static inline int |
| 892 | remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base) | 892 | remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base, bool restart) |
| 893 | { | 893 | { |
| 894 | if (hrtimer_is_queued(timer)) { | 894 | if (hrtimer_is_queued(timer)) { |
| 895 | unsigned long state; | 895 | unsigned long state = timer->state; |
| 896 | int reprogram; | 896 | int reprogram; |
| 897 | 897 | ||
| 898 | /* | 898 | /* |
| @@ -906,12 +906,15 @@ remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base) | |||
| 906 | debug_deactivate(timer); | 906 | debug_deactivate(timer); |
| 907 | timer_stats_hrtimer_clear_start_info(timer); | 907 | timer_stats_hrtimer_clear_start_info(timer); |
| 908 | reprogram = base->cpu_base == this_cpu_ptr(&hrtimer_bases); | 908 | reprogram = base->cpu_base == this_cpu_ptr(&hrtimer_bases); |
| 909 | /* | 909 | |
| 910 | * We must preserve the CALLBACK state flag here, | 910 | if (!restart) { |
| 911 | * otherwise we could move the timer base in | 911 | /* |
| 912 | * switch_hrtimer_base. | 912 | * We must preserve the CALLBACK state flag here, |
| 913 | */ | 913 | * otherwise we could move the timer base in |
| 914 | state = timer->state & HRTIMER_STATE_CALLBACK; | 914 | * switch_hrtimer_base. |
| 915 | */ | ||
| 916 | state &= HRTIMER_STATE_CALLBACK; | ||
| 917 | } | ||
| 915 | __remove_hrtimer(timer, base, state, reprogram); | 918 | __remove_hrtimer(timer, base, state, reprogram); |
| 916 | return 1; | 919 | return 1; |
| 917 | } | 920 | } |
| @@ -936,7 +939,7 @@ void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, | |||
| 936 | base = lock_hrtimer_base(timer, &flags); | 939 | base = lock_hrtimer_base(timer, &flags); |
| 937 | 940 | ||
| 938 | /* Remove an active timer from the queue: */ | 941 | /* Remove an active timer from the queue: */ |
| 939 | remove_hrtimer(timer, base); | 942 | remove_hrtimer(timer, base, true); |
| 940 | 943 | ||
| 941 | if (mode & HRTIMER_MODE_REL) { | 944 | if (mode & HRTIMER_MODE_REL) { |
| 942 | tim = ktime_add_safe(tim, base->get_time()); | 945 | tim = ktime_add_safe(tim, base->get_time()); |
| @@ -1005,7 +1008,7 @@ int hrtimer_try_to_cancel(struct hrtimer *timer) | |||
| 1005 | base = lock_hrtimer_base(timer, &flags); | 1008 | base = lock_hrtimer_base(timer, &flags); |
| 1006 | 1009 | ||
| 1007 | if (!hrtimer_callback_running(timer)) | 1010 | if (!hrtimer_callback_running(timer)) |
| 1008 | ret = remove_hrtimer(timer, base); | 1011 | ret = remove_hrtimer(timer, base, false); |
| 1009 | 1012 | ||
| 1010 | unlock_hrtimer_base(timer, &flags); | 1013 | unlock_hrtimer_base(timer, &flags); |
| 1011 | 1014 | ||
