aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2015-01-29 13:45:54 +0530
committerGary S. Robertson <gary.robertson@linaro.org>2015-07-22 16:17:11 -0500
commita83a8fb64304b7697c9c92bddcb86ff24a7f5b9b (patch)
treee9a7ed42ae2efbb7061e9f71fd0cb474c7ccd7ca
parent60a3281ced1f2ece96266343b585ac83861566a3 (diff)
clockevents: Restart clockevent device before using it
Clockevent device might have been switched to ONESHOT_STOPPED mode to avoid getting spurious interrupts on a tickless CPU. Before reprogramming next event, we must reconfigure clockevent device to ONESHOT mode if required. This patch switches mode to ONESHOT at three different places and following is the reasoning behind them. 1.) NOHZ_MODE_LOWRES Timers & hrtimers are dependent on tick for their working in this mode and the only place from where clockevent device is programmed is the tick-code. So, we need to switch clockevent device to ONESHOT mode before we starting using it. Two routines can restart ticks here in LOWRES mode: tick_nohz_stop_sched_tick() and tick_nohz_restart(). 2.) NOHZ_MODE_HIGHRES Tick & timers are dependent on hrtimers for their working in this mode and the only place from where clockevent device is programmed is the hrtimer-code. Only hrtimer_reprogram() is responsible for programming the clockevent device for next event, if the clockevent device is stopped earlier. And updating that alone is sufficient here. To make sure we haven't missed any corner case, add a WARN() for the case where we try to reprogram clockevent device while we aren't configured in ONESHOT_STOPPED mode. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
-rw-r--r--kernel/time/clockevents.c4
-rw-r--r--kernel/time/hrtimer.c5
-rw-r--r--kernel/time/tick-sched.c14
3 files changed, 22 insertions, 1 deletions
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c
index 808ae0902379..95dcdbc269b9 100644
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -318,6 +318,10 @@ int clockevents_program_event(struct clock_event_device *dev, ktime_t expires,
if (dev->mode == CLOCK_EVT_MODE_SHUTDOWN)
return 0;
+ /* We must be in ONESHOT mode here */
+ WARN_ONCE(dev->mode != CLOCK_EVT_MODE_ONESHOT, "Current mode: %d\n",
+ dev->mode);
+
/* Shortcut for clockevent devices that can deal with ktime. */
if (dev->features & CLOCK_EVT_FEAT_KTIME)
return dev->set_next_ktime(expires, dev);
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index c0572ef3b404..db4ea5d7cb41 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -561,6 +561,7 @@ static int hrtimer_reprogram(struct hrtimer *timer,
{
struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
ktime_t expires = ktime_sub(hrtimer_get_expires(timer), base->offset);
+ struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
int res;
WARN_ON_ONCE(hrtimer_get_expires_tv64(timer) < 0);
@@ -595,6 +596,10 @@ static int hrtimer_reprogram(struct hrtimer *timer,
if (cpu_base->hang_detected)
return 0;
+ /* Switchback to ONESHOT mode */
+ if (unlikely(dev->mode == CLOCK_EVT_MODE_ONESHOT_STOPPED))
+ clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
+
/*
* Clockevents returns -ETIME, when the event was in the past.
*/
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index d7cef36a443c..5f5f3c7486a1 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -717,8 +717,14 @@ static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
/* Check, if the timer was already in the past */
if (hrtimer_active(&ts->sched_timer))
goto out;
- } else if (!tick_program_event(expires, 0))
+ } else {
+ /* Switchback to ONESHOT mode */
+ if (unlikely(dev->mode == CLOCK_EVT_MODE_ONESHOT_STOPPED))
+ clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
+
+ if (!tick_program_event(expires, 0))
goto out;
+ }
/*
* We are past the event already. So we crossed a
* jiffie boundary. Update jiffies and raise the
@@ -889,6 +895,8 @@ ktime_t tick_nohz_get_sleep_length(void)
static void tick_nohz_restart(struct tick_sched *ts, ktime_t now)
{
+ struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
+
hrtimer_cancel(&ts->sched_timer);
hrtimer_set_expires(&ts->sched_timer, ts->last_tick);
@@ -903,6 +911,10 @@ static void tick_nohz_restart(struct tick_sched *ts, ktime_t now)
if (hrtimer_active(&ts->sched_timer))
break;
} else {
+ /* Switchback to ONESHOT mode */
+ if (likely(dev->mode == CLOCK_EVT_MODE_ONESHOT_STOPPED))
+ clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
+
if (!tick_program_event(
hrtimer_get_expires(&ts->sched_timer), 0))
break;