summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2015-01-29 13:45:52 +0530
committerSantosh Shukla <sshukla@mvista.com>2015-04-15 09:38:39 +0000
commit92e946b6394b7513a9c207523ef35af23bf99ab7 (patch)
tree2f82c994c8c973aec1302d10176b468c8644639e /kernel
parent2f254590995c19e4404eef999353b08bd8b6cb42 (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.c13
-rw-r--r--kernel/time/timer_list.c6
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);