diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2014-02-03 17:34:31 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2014-02-07 09:34:28 -0500 |
commit | 627ee7947e2e83ba565c31c5c9373d6e364b1ecd (patch) | |
tree | 30fc40a08542f0411e28ba9becc00e07f7ec3b44 /kernel/time | |
parent | e8b175946c16d7001b22620f52d78ab497efc9d0 (diff) |
clockevents: Serialize calls to clockevents_update_freq() in the core
We can identify the broadcast device in the core and serialize all
callers including interrupts on a different CPU against the update.
Also, disabling interrupts is moved into the core allowing callers to
leave interrutps enabled when calling clockevents_update_freq().
Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: Soeren Brinkmann <soren.brinkmann@xilinx.com>
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: Michal Simek <michal.simek@xilinx.com>
Link: http://lkml.kernel.org/r/1391466877-28908-2-git-send-email-soren.brinkmann@xilinx.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/time')
-rw-r--r-- | kernel/time/clockevents.c | 29 | ||||
-rw-r--r-- | kernel/time/tick-broadcast.c | 25 | ||||
-rw-r--r-- | kernel/time/tick-internal.h | 4 |
3 files changed, 45 insertions, 13 deletions
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index 086ad6043bcb..641d91003a45 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c | |||
@@ -439,6 +439,16 @@ void clockevents_config_and_register(struct clock_event_device *dev, | |||
439 | } | 439 | } |
440 | EXPORT_SYMBOL_GPL(clockevents_config_and_register); | 440 | EXPORT_SYMBOL_GPL(clockevents_config_and_register); |
441 | 441 | ||
442 | int __clockevents_update_freq(struct clock_event_device *dev, u32 freq) | ||
443 | { | ||
444 | clockevents_config(dev, freq); | ||
445 | |||
446 | if (dev->mode != CLOCK_EVT_MODE_ONESHOT) | ||
447 | return 0; | ||
448 | |||
449 | return clockevents_program_event(dev, dev->next_event, false); | ||
450 | } | ||
451 | |||
442 | /** | 452 | /** |
443 | * clockevents_update_freq - Update frequency and reprogram a clock event device. | 453 | * clockevents_update_freq - Update frequency and reprogram a clock event device. |
444 | * @dev: device to modify | 454 | * @dev: device to modify |
@@ -446,17 +456,22 @@ EXPORT_SYMBOL_GPL(clockevents_config_and_register); | |||
446 | * | 456 | * |
447 | * Reconfigure and reprogram a clock event device in oneshot | 457 | * Reconfigure and reprogram a clock event device in oneshot |
448 | * mode. Must be called on the cpu for which the device delivers per | 458 | * mode. Must be called on the cpu for which the device delivers per |
449 | * cpu timer events with interrupts disabled! Returns 0 on success, | 459 | * cpu timer events. If called for the broadcast device the core takes |
450 | * -ETIME when the event is in the past. | 460 | * care of serialization. |
461 | * | ||
462 | * Returns 0 on success, -ETIME when the event is in the past. | ||
451 | */ | 463 | */ |
452 | int clockevents_update_freq(struct clock_event_device *dev, u32 freq) | 464 | int clockevents_update_freq(struct clock_event_device *dev, u32 freq) |
453 | { | 465 | { |
454 | clockevents_config(dev, freq); | 466 | unsigned long flags; |
455 | 467 | int ret; | |
456 | if (dev->mode != CLOCK_EVT_MODE_ONESHOT) | ||
457 | return 0; | ||
458 | 468 | ||
459 | return clockevents_program_event(dev, dev->next_event, false); | 469 | local_irq_save(flags); |
470 | ret = tick_broadcast_update_freq(dev, freq); | ||
471 | if (ret == -ENODEV) | ||
472 | ret = __clockevents_update_freq(dev, freq); | ||
473 | local_irq_restore(flags); | ||
474 | return ret; | ||
460 | } | 475 | } |
461 | 476 | ||
462 | /* | 477 | /* |
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 43780ab5e279..003e6c3663b1 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c | |||
@@ -120,6 +120,19 @@ int tick_is_broadcast_device(struct clock_event_device *dev) | |||
120 | return (dev && tick_broadcast_device.evtdev == dev); | 120 | return (dev && tick_broadcast_device.evtdev == dev); |
121 | } | 121 | } |
122 | 122 | ||
123 | int tick_broadcast_update_freq(struct clock_event_device *dev, u32 freq) | ||
124 | { | ||
125 | int ret = -ENODEV; | ||
126 | |||
127 | if (tick_is_broadcast_device(dev)) { | ||
128 | raw_spin_lock(&tick_broadcast_lock); | ||
129 | ret = __clockevents_update_freq(dev, freq); | ||
130 | raw_spin_unlock(&tick_broadcast_lock); | ||
131 | } | ||
132 | return ret; | ||
133 | } | ||
134 | |||
135 | |||
123 | static void err_broadcast(const struct cpumask *mask) | 136 | static void err_broadcast(const struct cpumask *mask) |
124 | { | 137 | { |
125 | pr_crit_once("Failed to broadcast timer tick. Some CPUs may be unresponsive.\n"); | 138 | pr_crit_once("Failed to broadcast timer tick. Some CPUs may be unresponsive.\n"); |
@@ -272,12 +285,8 @@ static void tick_do_broadcast(struct cpumask *mask) | |||
272 | */ | 285 | */ |
273 | static void tick_do_periodic_broadcast(void) | 286 | static void tick_do_periodic_broadcast(void) |
274 | { | 287 | { |
275 | raw_spin_lock(&tick_broadcast_lock); | ||
276 | |||
277 | cpumask_and(tmpmask, cpu_online_mask, tick_broadcast_mask); | 288 | cpumask_and(tmpmask, cpu_online_mask, tick_broadcast_mask); |
278 | tick_do_broadcast(tmpmask); | 289 | tick_do_broadcast(tmpmask); |
279 | |||
280 | raw_spin_unlock(&tick_broadcast_lock); | ||
281 | } | 290 | } |
282 | 291 | ||
283 | /* | 292 | /* |
@@ -287,13 +296,15 @@ static void tick_handle_periodic_broadcast(struct clock_event_device *dev) | |||
287 | { | 296 | { |
288 | ktime_t next; | 297 | ktime_t next; |
289 | 298 | ||
299 | raw_spin_lock(&tick_broadcast_lock); | ||
300 | |||
290 | tick_do_periodic_broadcast(); | 301 | tick_do_periodic_broadcast(); |
291 | 302 | ||
292 | /* | 303 | /* |
293 | * The device is in periodic mode. No reprogramming necessary: | 304 | * The device is in periodic mode. No reprogramming necessary: |
294 | */ | 305 | */ |
295 | if (dev->mode == CLOCK_EVT_MODE_PERIODIC) | 306 | if (dev->mode == CLOCK_EVT_MODE_PERIODIC) |
296 | return; | 307 | goto unlock; |
297 | 308 | ||
298 | /* | 309 | /* |
299 | * Setup the next period for devices, which do not have | 310 | * Setup the next period for devices, which do not have |
@@ -306,9 +317,11 @@ static void tick_handle_periodic_broadcast(struct clock_event_device *dev) | |||
306 | next = ktime_add(next, tick_period); | 317 | next = ktime_add(next, tick_period); |
307 | 318 | ||
308 | if (!clockevents_program_event(dev, next, false)) | 319 | if (!clockevents_program_event(dev, next, false)) |
309 | return; | 320 | goto unlock; |
310 | tick_do_periodic_broadcast(); | 321 | tick_do_periodic_broadcast(); |
311 | } | 322 | } |
323 | unlock: | ||
324 | raw_spin_unlock(&tick_broadcast_lock); | ||
312 | } | 325 | } |
313 | 326 | ||
314 | /* | 327 | /* |
diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h index 8329669b51ec..26f1c0ba9d81 100644 --- a/kernel/time/tick-internal.h +++ b/kernel/time/tick-internal.h | |||
@@ -111,6 +111,7 @@ extern int tick_resume_broadcast(void); | |||
111 | extern void tick_broadcast_init(void); | 111 | extern void tick_broadcast_init(void); |
112 | extern void | 112 | extern void |
113 | tick_set_periodic_handler(struct clock_event_device *dev, int broadcast); | 113 | tick_set_periodic_handler(struct clock_event_device *dev, int broadcast); |
114 | int tick_broadcast_update_freq(struct clock_event_device *dev, u32 freq); | ||
114 | 115 | ||
115 | #else /* !BROADCAST */ | 116 | #else /* !BROADCAST */ |
116 | 117 | ||
@@ -133,6 +134,8 @@ static inline void tick_shutdown_broadcast(unsigned int *cpup) { } | |||
133 | static inline void tick_suspend_broadcast(void) { } | 134 | static inline void tick_suspend_broadcast(void) { } |
134 | static inline int tick_resume_broadcast(void) { return 0; } | 135 | static inline int tick_resume_broadcast(void) { return 0; } |
135 | static inline void tick_broadcast_init(void) { } | 136 | static inline void tick_broadcast_init(void) { } |
137 | static inline int tick_broadcast_update_freq(struct clock_event_device *dev, | ||
138 | u32 freq) { return -ENODEV; } | ||
136 | 139 | ||
137 | /* | 140 | /* |
138 | * Set the periodic handler in non broadcast mode | 141 | * Set the periodic handler in non broadcast mode |
@@ -154,5 +157,6 @@ static inline int tick_device_is_functional(struct clock_event_device *dev) | |||
154 | 157 | ||
155 | #endif | 158 | #endif |
156 | 159 | ||
160 | int __clockevents_update_freq(struct clock_event_device *dev, u32 freq); | ||
157 | extern void do_timer(unsigned long ticks); | 161 | extern void do_timer(unsigned long ticks); |
158 | extern void update_wall_time(void); | 162 | extern void update_wall_time(void); |