diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2015-01-29 13:45:52 +0530 |
---|---|---|
committer | Santosh Shukla <sshukla@mvista.com> | 2015-04-15 09:38:39 +0000 |
commit | 92e946b6394b7513a9c207523ef35af23bf99ab7 (patch) | |
tree | 2f82c994c8c973aec1302d10176b468c8644639e /kernel | |
parent | 2f254590995c19e4404eef999353b08bd8b6cb42 (diff) |
clockevents: Introduce CLOCK_EVT_MODE_ONESHOT_STOPPED mode
When no timers/hrtimers are pending, the expiry time is set to a special value:
'KTIME_MAX'. This normally happens with NO_HZ_{IDLE|FULL} in both LOWRES/HIGHRES
modes.
When 'expiry == KTIME_MAX', we either cancel the 'tick-sched' hrtimer
(NOHZ_MODE_HIGHRES) or skip reprogramming clockevent device (NOHZ_MODE_LOWRES).
But, the clockevent device is already reprogrammed from the tick-handler for
next tick.
As the clock event device is programmed in ONESHOT mode it will atleast fire one
more time (unnecessarily). Timers on many implementations (like arm_arch_timer,
powerpc, etc.) only support PERIODIC mode and their drivers emulate ONESHOT over
that. Which means that on these platforms we will get spurious interrupts at
last programmed interval rate, normally tick rate.
In order to avoid spurious interrupts/wakeups, the clockevent device should be
stopped or its interrupts should be masked.
A simple (yet hacky) solution to get this fixed could be: update
hrtimer_force_reprogram() to always reprogram clockevent device and update
clockevent drivers to STOP generating events (or delay it to max time) when
'expires' is set to KTIME_MAX. But the drawback here is that every clockevent
driver has to be hacked for this particular case and its very easy for new ones
to miss this.
However, Thomas suggested to add an optional mode ONESHOT_STOPPED to solve this
problem: lkml.org/lkml/2014/5/9/508.
This patch adds support for ONESHOT_STOPPED mode in clockevents core. It will
only be available to drivers that implement the mode-specific set-mode callbacks
instead of the legacy ->set_mode() callback.
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/time/clockevents.c | 13 | ||||
-rw-r--r-- | kernel/time/timer_list.c | 6 |
2 files changed, 18 insertions, 1 deletions
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index 489642b08d6..808ae090237 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -133,6 +133,16 @@ static int __clockevents_set_mode(struct clock_event_device *dev, return -ENOSYS; return dev->set_mode_oneshot(dev); + case CLOCK_EVT_MODE_ONESHOT_STOPPED: + /* Core internal bug */ + WARN_ONCE(dev->mode != CLOCK_EVT_MODE_ONESHOT, + "Current mode: %d\n", dev->mode); + + if (dev->set_mode_stop_oneshot) + return dev->set_mode_stop_oneshot(dev); + else + return -ENOSYS; + case CLOCK_EVT_MODE_RESUME: /* Optional callback */ if (dev->set_mode_resume) @@ -433,7 +443,8 @@ static int clockevents_sanity_check(struct clock_event_device *dev) if (dev->set_mode) { /* We shouldn't be supporting new modes now */ WARN_ON(dev->set_mode_periodic || dev->set_mode_oneshot || - dev->set_mode_shutdown || dev->set_mode_resume); + dev->set_mode_shutdown || dev->set_mode_resume || + dev->set_mode_stop_oneshot); return 0; } diff --git a/kernel/time/timer_list.c b/kernel/time/timer_list.c index 2cfd1948582..ee3ff65b679 100644 --- a/kernel/time/timer_list.c +++ b/kernel/time/timer_list.c @@ -251,6 +251,12 @@ print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu) SEQ_printf(m, "\n"); } + if (dev->set_mode_stop_oneshot) { + SEQ_printf(m, " stop_oneshot: "); + print_name_offset(m, dev->set_mode_stop_oneshot); + SEQ_printf(m, "\n"); + } + if (dev->set_mode_resume) { SEQ_printf(m, " resume: "); print_name_offset(m, dev->set_mode_resume); |