Skip to content

Commit 8fff52f

Browse files
vireshkKAGA-KOKO
authored andcommitted
clockevents: Introduce CLOCK_EVT_STATE_ONESHOT_STOPPED state
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 at least fire one more time (unnecessarily). Timers on few implementations (like arm_arch_timer, etc.) only support PERIODIC mode and their drivers emulate ONESHOT over that. Which means that on these platforms we will get spurious interrupts periodically (at last programmed interval rate, normally tick rate). In order to avoid spurious interrupts, 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 state ONESHOT_STOPPED to solve this problem: lkml.org/lkml/2014/5/9/508. This patch adds support for ONESHOT_STOPPED state in clockevents core. It will only be available to drivers that implement the state-specific callbacks instead of the legacy ->set_mode() callback. Signed-off-by: Viresh Kumar <[email protected]> Reviewed-by: Preeti U. Murthy <[email protected]> Cc: [email protected] Cc: Frederic Weisbecker <[email protected]> Cc: Kevin Hilman <[email protected]> Cc: Daniel Lezcano <[email protected]> Cc: Peter Zijlstra <[email protected]> Link: http://lkml.kernel.org/r/b8b383a03ac07b13312c16850b5106b82e4245b5.1428031396.git.viresh.kumar@linaro.org Signed-off-by: Thomas Gleixner <[email protected]>
1 parent c3b5d3c commit 8fff52f

File tree

3 files changed

+25
-2
lines changed

3 files changed

+25
-2
lines changed

include/linux/clockchips.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,15 @@ enum clock_event_mode {
3737
* reached from DETACHED or SHUTDOWN.
3838
* ONESHOT: Device is programmed to generate event only once. Can be reached
3939
* from DETACHED or SHUTDOWN.
40+
* ONESHOT_STOPPED: Device was programmed in ONESHOT mode and is temporarily
41+
* stopped.
4042
*/
4143
enum clock_event_state {
4244
CLOCK_EVT_STATE_DETACHED,
4345
CLOCK_EVT_STATE_SHUTDOWN,
4446
CLOCK_EVT_STATE_PERIODIC,
4547
CLOCK_EVT_STATE_ONESHOT,
48+
CLOCK_EVT_STATE_ONESHOT_STOPPED,
4649
};
4750

4851
/*
@@ -90,6 +93,7 @@ enum clock_event_state {
9093
* @set_mode: legacy set mode function, only for modes <= CLOCK_EVT_MODE_RESUME.
9194
* @set_state_periodic: switch state to periodic, if !set_mode
9295
* @set_state_oneshot: switch state to oneshot, if !set_mode
96+
* @set_state_oneshot_stopped: switch state to oneshot_stopped, if !set_mode
9397
* @set_state_shutdown: switch state to shutdown, if !set_mode
9498
* @tick_resume: resume clkevt device, if !set_mode
9599
* @broadcast: function to broadcast events
@@ -121,11 +125,12 @@ struct clock_event_device {
121125
* State transition callback(s): Only one of the two groups should be
122126
* defined:
123127
* - set_mode(), only for modes <= CLOCK_EVT_MODE_RESUME.
124-
* - set_state_{shutdown|periodic|oneshot}(), tick_resume().
128+
* - set_state_{shutdown|periodic|oneshot|oneshot_stopped}(), tick_resume().
125129
*/
126130
void (*set_mode)(enum clock_event_mode mode, struct clock_event_device *);
127131
int (*set_state_periodic)(struct clock_event_device *);
128132
int (*set_state_oneshot)(struct clock_event_device *);
133+
int (*set_state_oneshot_stopped)(struct clock_event_device *);
129134
int (*set_state_shutdown)(struct clock_event_device *);
130135
int (*tick_resume)(struct clock_event_device *);
131136

kernel/time/clockevents.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,17 @@ static int __clockevents_set_state(struct clock_event_device *dev,
134134
return -ENOSYS;
135135
return dev->set_state_oneshot(dev);
136136

137+
case CLOCK_EVT_STATE_ONESHOT_STOPPED:
138+
/* Core internal bug */
139+
if (WARN_ONCE(dev->state != CLOCK_EVT_STATE_ONESHOT,
140+
"Current state: %d\n", dev->state))
141+
return -EINVAL;
142+
143+
if (dev->set_state_oneshot_stopped)
144+
return dev->set_state_oneshot_stopped(dev);
145+
else
146+
return -ENOSYS;
147+
137148
default:
138149
return -ENOSYS;
139150
}
@@ -445,7 +456,8 @@ static int clockevents_sanity_check(struct clock_event_device *dev)
445456
if (dev->set_mode) {
446457
/* We shouldn't be supporting new modes now */
447458
WARN_ON(dev->set_state_periodic || dev->set_state_oneshot ||
448-
dev->set_state_shutdown || dev->tick_resume);
459+
dev->set_state_shutdown || dev->tick_resume ||
460+
dev->set_state_oneshot_stopped);
449461

450462
BUG_ON(dev->mode != CLOCK_EVT_MODE_UNUSED);
451463
return 0;

kernel/time/timer_list.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,12 @@ print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu)
258258
SEQ_printf(m, "\n");
259259
}
260260

261+
if (dev->set_state_oneshot_stopped) {
262+
SEQ_printf(m, " oneshot stopped: ");
263+
print_name_offset(m, dev->set_state_oneshot_stopped);
264+
SEQ_printf(m, "\n");
265+
}
266+
261267
if (dev->tick_resume) {
262268
SEQ_printf(m, " resume: ");
263269
print_name_offset(m, dev->tick_resume);

0 commit comments

Comments
 (0)