diff options
Diffstat (limited to 'kernel/time/tick-common.c')
-rw-r--r-- | kernel/time/tick-common.c | 53 |
1 files changed, 44 insertions, 9 deletions
diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 46e4381c26ea..48167a6ae55c 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c | |||
@@ -20,17 +20,19 @@ | |||
20 | #include <linux/sched.h> | 20 | #include <linux/sched.h> |
21 | #include <linux/tick.h> | 21 | #include <linux/tick.h> |
22 | 22 | ||
23 | #include "tick-internal.h" | ||
24 | |||
23 | /* | 25 | /* |
24 | * Tick devices | 26 | * Tick devices |
25 | */ | 27 | */ |
26 | static DEFINE_PER_CPU(struct tick_device, tick_cpu_device); | 28 | DEFINE_PER_CPU(struct tick_device, tick_cpu_device); |
27 | /* | 29 | /* |
28 | * Tick next event: keeps track of the tick time | 30 | * Tick next event: keeps track of the tick time |
29 | */ | 31 | */ |
30 | static ktime_t tick_next_period; | 32 | ktime_t tick_next_period; |
31 | static ktime_t tick_period; | 33 | ktime_t tick_period; |
32 | static int tick_do_timer_cpu = -1; | 34 | static int tick_do_timer_cpu = -1; |
33 | static DEFINE_SPINLOCK(tick_device_lock); | 35 | DEFINE_SPINLOCK(tick_device_lock); |
34 | 36 | ||
35 | /* | 37 | /* |
36 | * Periodic tick | 38 | * Periodic tick |
@@ -78,9 +80,13 @@ void tick_handle_periodic(struct clock_event_device *dev) | |||
78 | /* | 80 | /* |
79 | * Setup the device for a periodic tick | 81 | * Setup the device for a periodic tick |
80 | */ | 82 | */ |
81 | void tick_setup_periodic(struct clock_event_device *dev) | 83 | void tick_setup_periodic(struct clock_event_device *dev, int broadcast) |
82 | { | 84 | { |
83 | dev->event_handler = tick_handle_periodic; | 85 | tick_set_periodic_handler(dev, broadcast); |
86 | |||
87 | /* Broadcast setup ? */ | ||
88 | if (!tick_device_is_functional(dev)) | ||
89 | return; | ||
84 | 90 | ||
85 | if (dev->features & CLOCK_EVT_FEAT_PERIODIC) { | 91 | if (dev->features & CLOCK_EVT_FEAT_PERIODIC) { |
86 | clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC); | 92 | clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC); |
@@ -145,6 +151,15 @@ static void tick_setup_device(struct tick_device *td, | |||
145 | if (!cpus_equal(newdev->cpumask, cpumask)) | 151 | if (!cpus_equal(newdev->cpumask, cpumask)) |
146 | irq_set_affinity(newdev->irq, cpumask); | 152 | irq_set_affinity(newdev->irq, cpumask); |
147 | 153 | ||
154 | /* | ||
155 | * When global broadcasting is active, check if the current | ||
156 | * device is registered as a placeholder for broadcast mode. | ||
157 | * This allows us to handle this x86 misfeature in a generic | ||
158 | * way. | ||
159 | */ | ||
160 | if (tick_device_uses_broadcast(newdev, cpu)) | ||
161 | return; | ||
162 | |||
148 | if (td->mode == TICKDEV_MODE_PERIODIC) | 163 | if (td->mode == TICKDEV_MODE_PERIODIC) |
149 | tick_setup_periodic(newdev, 0); | 164 | tick_setup_periodic(newdev, 0); |
150 | } | 165 | } |
@@ -197,19 +212,33 @@ static int tick_check_new_device(struct clock_event_device *newdev) | |||
197 | * Check the rating | 212 | * Check the rating |
198 | */ | 213 | */ |
199 | if (curdev->rating >= newdev->rating) | 214 | if (curdev->rating >= newdev->rating) |
200 | goto out; | 215 | goto out_bc; |
201 | } | 216 | } |
202 | 217 | ||
203 | /* | 218 | /* |
204 | * Replace the eventually existing device by the new | 219 | * Replace the eventually existing device by the new |
205 | * device. | 220 | * device. If the current device is the broadcast device, do |
221 | * not give it back to the clockevents layer ! | ||
206 | */ | 222 | */ |
223 | if (tick_is_broadcast_device(curdev)) { | ||
224 | clockevents_set_mode(curdev, CLOCK_EVT_MODE_SHUTDOWN); | ||
225 | curdev = NULL; | ||
226 | } | ||
207 | clockevents_exchange_device(curdev, newdev); | 227 | clockevents_exchange_device(curdev, newdev); |
208 | tick_setup_device(td, newdev, cpu, cpumask); | 228 | tick_setup_device(td, newdev, cpu, cpumask); |
209 | ret = NOTIFY_STOP; | ||
210 | 229 | ||
230 | spin_unlock_irqrestore(&tick_device_lock, flags); | ||
231 | return NOTIFY_STOP; | ||
232 | |||
233 | out_bc: | ||
234 | /* | ||
235 | * Can the new device be used as a broadcast device ? | ||
236 | */ | ||
237 | if (tick_check_broadcast_device(newdev)) | ||
238 | ret = NOTIFY_STOP; | ||
211 | out: | 239 | out: |
212 | spin_unlock_irqrestore(&tick_device_lock, flags); | 240 | spin_unlock_irqrestore(&tick_device_lock, flags); |
241 | |||
213 | return ret; | 242 | return ret; |
214 | } | 243 | } |
215 | 244 | ||
@@ -251,7 +280,13 @@ static int tick_notify(struct notifier_block *nb, unsigned long reason, | |||
251 | case CLOCK_EVT_NOTIFY_ADD: | 280 | case CLOCK_EVT_NOTIFY_ADD: |
252 | return tick_check_new_device(dev); | 281 | return tick_check_new_device(dev); |
253 | 282 | ||
283 | case CLOCK_EVT_NOTIFY_BROADCAST_ON: | ||
284 | case CLOCK_EVT_NOTIFY_BROADCAST_OFF: | ||
285 | tick_broadcast_on_off(reason, dev); | ||
286 | break; | ||
287 | |||
254 | case CLOCK_EVT_NOTIFY_CPU_DEAD: | 288 | case CLOCK_EVT_NOTIFY_CPU_DEAD: |
289 | tick_shutdown_broadcast(dev); | ||
255 | tick_shutdown(dev); | 290 | tick_shutdown(dev); |
256 | break; | 291 | break; |
257 | 292 | ||