Blame view
kernel/time/tick-common.c
9.51 KB
906568c9c [PATCH] tick-mana... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* * linux/kernel/time/tick-common.c * * This file contains the base functions to manage periodic tick * related events. * * Copyright(C) 2005-2006, Thomas Gleixner <tglx@linutronix.de> * Copyright(C) 2005-2007, Red Hat, Inc., Ingo Molnar * Copyright(C) 2006-2007, Timesys Corp., Thomas Gleixner * * This code is licenced under the GPL version 2. For details see * kernel-base/COPYING. */ #include <linux/cpu.h> #include <linux/err.h> #include <linux/hrtimer.h> |
d7b906897 [S390] genirq/clo... |
17 |
#include <linux/interrupt.h> |
906568c9c [PATCH] tick-mana... |
18 19 20 |
#include <linux/percpu.h> #include <linux/profile.h> #include <linux/sched.h> |
906568c9c [PATCH] tick-mana... |
21 |
|
d7b906897 [S390] genirq/clo... |
22 |
#include <asm/irq_regs.h> |
f8381cba0 [PATCH] tick-mana... |
23 |
#include "tick-internal.h" |
906568c9c [PATCH] tick-mana... |
24 25 26 |
/* * Tick devices */ |
f8381cba0 [PATCH] tick-mana... |
27 |
DEFINE_PER_CPU(struct tick_device, tick_cpu_device); |
906568c9c [PATCH] tick-mana... |
28 29 30 |
/* * Tick next event: keeps track of the tick time */ |
f8381cba0 [PATCH] tick-mana... |
31 32 |
ktime_t tick_next_period; ktime_t tick_period; |
6441402b1 clockevents: prev... |
33 |
int tick_do_timer_cpu __read_mostly = TICK_DO_TIMER_BOOT; |
b5f91da0a clockevents: Conv... |
34 |
static DEFINE_RAW_SPINLOCK(tick_device_lock); |
906568c9c [PATCH] tick-mana... |
35 |
|
289f480af [PATCH] Add debug... |
36 37 38 39 40 41 42 |
/* * Debugging: see timer_list.c */ struct tick_device *tick_get_device(int cpu) { return &per_cpu(tick_cpu_device, cpu); } |
79bf2bb33 [PATCH] tick-mana... |
43 44 45 46 47 |
/** * tick_is_oneshot_available - check for a oneshot capable event device */ int tick_is_oneshot_available(void) { |
909ea9646 core: Replace __g... |
48 |
struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev); |
79bf2bb33 [PATCH] tick-mana... |
49 |
|
3a142a067 clockevents: Prev... |
50 51 52 53 54 |
if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT)) return 0; if (!(dev->features & CLOCK_EVT_FEAT_C3STOP)) return 1; return tick_broadcast_oneshot_available(); |
79bf2bb33 [PATCH] tick-mana... |
55 |
} |
906568c9c [PATCH] tick-mana... |
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
/* * Periodic tick */ static void tick_periodic(int cpu) { if (tick_do_timer_cpu == cpu) { write_seqlock(&xtime_lock); /* Keep track of the next tick event */ tick_next_period = ktime_add(tick_next_period, tick_period); do_timer(1); write_sequnlock(&xtime_lock); } update_process_times(user_mode(get_irq_regs())); profile_tick(CPU_PROFILING); } /* * Event handler for periodic ticks */ void tick_handle_periodic(struct clock_event_device *dev) { int cpu = smp_processor_id(); |
3494c1667 [TICK] tick-commo... |
81 |
ktime_t next; |
906568c9c [PATCH] tick-mana... |
82 83 84 85 86 87 88 89 90 |
tick_periodic(cpu); if (dev->mode != CLOCK_EVT_MODE_ONESHOT) return; /* * Setup the next period for devices, which do not have * periodic mode: */ |
3494c1667 [TICK] tick-commo... |
91 |
next = ktime_add(dev->next_event, tick_period); |
906568c9c [PATCH] tick-mana... |
92 |
for (;;) { |
d1748302f clockevents: Make... |
93 |
if (!clockevents_program_event(dev, next, false)) |
906568c9c [PATCH] tick-mana... |
94 |
return; |
74a03b69d clockevents: prev... |
95 96 97 98 99 100 101 102 103 104 105 |
/* * Have to be careful here. If we're in oneshot mode, * before we call tick_periodic() in a loop, we need * to be sure we're using a real hardware clocksource. * Otherwise we could get trapped in an infinite * loop, as the tick_periodic() increments jiffies, * when then will increment time, posibly causing * the loop to trigger again and again. */ if (timekeeping_valid_for_hres()) tick_periodic(cpu); |
3494c1667 [TICK] tick-commo... |
106 |
next = ktime_add(next, tick_period); |
906568c9c [PATCH] tick-mana... |
107 108 109 110 111 112 |
} } /* * Setup the device for a periodic tick */ |
f8381cba0 [PATCH] tick-mana... |
113 |
void tick_setup_periodic(struct clock_event_device *dev, int broadcast) |
906568c9c [PATCH] tick-mana... |
114 |
{ |
f8381cba0 [PATCH] tick-mana... |
115 116 117 118 119 |
tick_set_periodic_handler(dev, broadcast); /* Broadcast setup ? */ if (!tick_device_is_functional(dev)) return; |
906568c9c [PATCH] tick-mana... |
120 |
|
27ce4cb4a clockevents: prev... |
121 122 |
if ((dev->features & CLOCK_EVT_FEAT_PERIODIC) && !tick_broadcast_oneshot_active()) { |
906568c9c [PATCH] tick-mana... |
123 124 125 126 127 128 129 130 131 132 133 134 135 |
clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC); } else { unsigned long seq; ktime_t next; do { seq = read_seqbegin(&xtime_lock); next = tick_next_period; } while (read_seqretry(&xtime_lock, seq)); clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); for (;;) { |
d1748302f clockevents: Make... |
136 |
if (!clockevents_program_event(dev, next, false)) |
906568c9c [PATCH] tick-mana... |
137 138 139 140 141 142 143 144 145 146 147 |
return; next = ktime_add(next, tick_period); } } } /* * Setup the tick device */ static void tick_setup_device(struct tick_device *td, struct clock_event_device *newdev, int cpu, |
0de26520c cpumask: make irq... |
148 |
const struct cpumask *cpumask) |
906568c9c [PATCH] tick-mana... |
149 150 151 152 153 154 155 156 157 158 159 160 |
{ ktime_t next_event; void (*handler)(struct clock_event_device *) = NULL; /* * First device setup ? */ if (!td->evtdev) { /* * If no cpu took the do_timer update, assign it to * this cpu: */ |
6441402b1 clockevents: prev... |
161 |
if (tick_do_timer_cpu == TICK_DO_TIMER_BOOT) { |
906568c9c [PATCH] tick-mana... |
162 163 164 165 166 167 168 169 170 171 172 173 |
tick_do_timer_cpu = cpu; tick_next_period = ktime_get(); tick_period = ktime_set(0, NSEC_PER_SEC / HZ); } /* * Startup in periodic mode first. */ td->mode = TICKDEV_MODE_PERIODIC; } else { handler = td->evtdev->event_handler; next_event = td->evtdev->next_event; |
7c1e76897 clockevents: prev... |
174 |
td->evtdev->event_handler = clockevents_handle_noop; |
906568c9c [PATCH] tick-mana... |
175 176 177 178 179 180 181 182 |
} td->evtdev = newdev; /* * When the device is not per cpu, pin the interrupt to the * current cpu: */ |
320ab2b0b cpumask: convert ... |
183 |
if (!cpumask_equal(newdev->cpumask, cpumask)) |
0de26520c cpumask: make irq... |
184 |
irq_set_affinity(newdev->irq, cpumask); |
906568c9c [PATCH] tick-mana... |
185 |
|
f8381cba0 [PATCH] tick-mana... |
186 187 188 189 190 191 192 193 |
/* * When global broadcasting is active, check if the current * device is registered as a placeholder for broadcast mode. * This allows us to handle this x86 misfeature in a generic * way. */ if (tick_device_uses_broadcast(newdev, cpu)) return; |
906568c9c [PATCH] tick-mana... |
194 195 |
if (td->mode == TICKDEV_MODE_PERIODIC) tick_setup_periodic(newdev, 0); |
79bf2bb33 [PATCH] tick-mana... |
196 197 |
else tick_setup_oneshot(newdev, handler, next_event); |
906568c9c [PATCH] tick-mana... |
198 199 200 201 202 203 204 205 206 207 208 |
} /* * Check, if the new registered device should be used. */ static int tick_check_new_device(struct clock_event_device *newdev) { struct clock_event_device *curdev; struct tick_device *td; int cpu, ret = NOTIFY_OK; unsigned long flags; |
906568c9c [PATCH] tick-mana... |
209 |
|
b5f91da0a clockevents: Conv... |
210 |
raw_spin_lock_irqsave(&tick_device_lock, flags); |
906568c9c [PATCH] tick-mana... |
211 212 |
cpu = smp_processor_id(); |
320ab2b0b cpumask: convert ... |
213 |
if (!cpumask_test_cpu(cpu, newdev->cpumask)) |
4a93232da clock events: all... |
214 |
goto out_bc; |
906568c9c [PATCH] tick-mana... |
215 216 217 |
td = &per_cpu(tick_cpu_device, cpu); curdev = td->evtdev; |
906568c9c [PATCH] tick-mana... |
218 219 |
/* cpu local device ? */ |
320ab2b0b cpumask: convert ... |
220 |
if (!cpumask_equal(newdev->cpumask, cpumask_of(cpu))) { |
906568c9c [PATCH] tick-mana... |
221 222 223 224 225 226 227 228 229 230 231 232 |
/* * If the cpu affinity of the device interrupt can not * be set, ignore it. */ if (!irq_can_set_affinity(newdev->irq)) goto out_bc; /* * If we have a cpu local device already, do not replace it * by a non cpu local device */ |
320ab2b0b cpumask: convert ... |
233 |
if (curdev && cpumask_equal(curdev->cpumask, cpumask_of(cpu))) |
906568c9c [PATCH] tick-mana... |
234 235 236 237 238 239 240 241 242 |
goto out_bc; } /* * If we have an active device, then check the rating and the oneshot * feature. */ if (curdev) { /* |
79bf2bb33 [PATCH] tick-mana... |
243 244 245 246 247 248 |
* Prefer one shot capable devices ! */ if ((curdev->features & CLOCK_EVT_FEAT_ONESHOT) && !(newdev->features & CLOCK_EVT_FEAT_ONESHOT)) goto out_bc; /* |
906568c9c [PATCH] tick-mana... |
249 250 251 |
* Check the rating */ if (curdev->rating >= newdev->rating) |
f8381cba0 [PATCH] tick-mana... |
252 |
goto out_bc; |
906568c9c [PATCH] tick-mana... |
253 254 255 256 |
} /* * Replace the eventually existing device by the new |
f8381cba0 [PATCH] tick-mana... |
257 258 |
* device. If the current device is the broadcast device, do * not give it back to the clockevents layer ! |
906568c9c [PATCH] tick-mana... |
259 |
*/ |
f8381cba0 [PATCH] tick-mana... |
260 |
if (tick_is_broadcast_device(curdev)) { |
2344abbcb clockevents: make... |
261 |
clockevents_shutdown(curdev); |
f8381cba0 [PATCH] tick-mana... |
262 263 |
curdev = NULL; } |
906568c9c [PATCH] tick-mana... |
264 |
clockevents_exchange_device(curdev, newdev); |
6b954823c cpumask: convert ... |
265 |
tick_setup_device(td, newdev, cpu, cpumask_of(cpu)); |
79bf2bb33 [PATCH] tick-mana... |
266 267 |
if (newdev->features & CLOCK_EVT_FEAT_ONESHOT) tick_oneshot_notify(); |
906568c9c [PATCH] tick-mana... |
268 |
|
b5f91da0a clockevents: Conv... |
269 |
raw_spin_unlock_irqrestore(&tick_device_lock, flags); |
f8381cba0 [PATCH] tick-mana... |
270 271 272 273 274 275 276 277 |
return NOTIFY_STOP; out_bc: /* * Can the new device be used as a broadcast device ? */ if (tick_check_broadcast_device(newdev)) ret = NOTIFY_STOP; |
4a93232da clock events: all... |
278 |
|
b5f91da0a clockevents: Conv... |
279 |
raw_spin_unlock_irqrestore(&tick_device_lock, flags); |
f8381cba0 [PATCH] tick-mana... |
280 |
|
906568c9c [PATCH] tick-mana... |
281 282 283 284 |
return ret; } /* |
94df7de02 hrtimers: allow t... |
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
* Transfer the do_timer job away from a dying cpu. * * Called with interrupts disabled. */ static void tick_handover_do_timer(int *cpup) { if (*cpup == tick_do_timer_cpu) { int cpu = cpumask_first(cpu_online_mask); tick_do_timer_cpu = (cpu < nr_cpu_ids) ? cpu : TICK_DO_TIMER_NONE; } } /* |
906568c9c [PATCH] tick-mana... |
300 301 302 303 304 305 306 307 308 309 310 |
* Shutdown an event device on a given cpu: * * This is called on a life CPU, when a CPU is dead. So we cannot * access the hardware device itself. * We just set the mode and remove it from the lists. */ static void tick_shutdown(unsigned int *cpup) { struct tick_device *td = &per_cpu(tick_cpu_device, *cpup); struct clock_event_device *dev = td->evtdev; unsigned long flags; |
b5f91da0a clockevents: Conv... |
311 |
raw_spin_lock_irqsave(&tick_device_lock, flags); |
906568c9c [PATCH] tick-mana... |
312 313 314 315 316 317 318 319 320 321 |
td->mode = TICKDEV_MODE_PERIODIC; if (dev) { /* * Prevent that the clock events layer tries to call * the set mode function! */ dev->mode = CLOCK_EVT_MODE_UNUSED; clockevents_exchange_device(dev, NULL); td->evtdev = NULL; } |
b5f91da0a clockevents: Conv... |
322 |
raw_spin_unlock_irqrestore(&tick_device_lock, flags); |
906568c9c [PATCH] tick-mana... |
323 |
} |
cd05a1f81 [PATCH] clockeven... |
324 |
static void tick_suspend(void) |
6321dd60c [PATCH] Save/rest... |
325 326 327 |
{ struct tick_device *td = &__get_cpu_var(tick_cpu_device); unsigned long flags; |
b5f91da0a clockevents: Conv... |
328 |
raw_spin_lock_irqsave(&tick_device_lock, flags); |
2344abbcb clockevents: make... |
329 |
clockevents_shutdown(td->evtdev); |
b5f91da0a clockevents: Conv... |
330 |
raw_spin_unlock_irqrestore(&tick_device_lock, flags); |
6321dd60c [PATCH] Save/rest... |
331 |
} |
cd05a1f81 [PATCH] clockeven... |
332 |
static void tick_resume(void) |
6321dd60c [PATCH] Save/rest... |
333 334 335 |
{ struct tick_device *td = &__get_cpu_var(tick_cpu_device); unsigned long flags; |
18de5bc4c clockevents: fix ... |
336 |
int broadcast = tick_resume_broadcast(); |
6321dd60c [PATCH] Save/rest... |
337 |
|
b5f91da0a clockevents: Conv... |
338 |
raw_spin_lock_irqsave(&tick_device_lock, flags); |
18de5bc4c clockevents: fix ... |
339 340 341 342 343 344 345 346 |
clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_RESUME); if (!broadcast) { if (td->mode == TICKDEV_MODE_PERIODIC) tick_setup_periodic(td->evtdev, 0); else tick_resume_oneshot(); } |
b5f91da0a clockevents: Conv... |
347 |
raw_spin_unlock_irqrestore(&tick_device_lock, flags); |
6321dd60c [PATCH] Save/rest... |
348 |
} |
906568c9c [PATCH] tick-mana... |
349 350 351 352 353 354 355 356 357 358 |
/* * Notification about clock event devices */ static int tick_notify(struct notifier_block *nb, unsigned long reason, void *dev) { switch (reason) { case CLOCK_EVT_NOTIFY_ADD: return tick_check_new_device(dev); |
f8381cba0 [PATCH] tick-mana... |
359 360 |
case CLOCK_EVT_NOTIFY_BROADCAST_ON: case CLOCK_EVT_NOTIFY_BROADCAST_OFF: |
1595f452f clockevents: intr... |
361 |
case CLOCK_EVT_NOTIFY_BROADCAST_FORCE: |
f8381cba0 [PATCH] tick-mana... |
362 363 |
tick_broadcast_on_off(reason, dev); break; |
79bf2bb33 [PATCH] tick-mana... |
364 365 366 367 |
case CLOCK_EVT_NOTIFY_BROADCAST_ENTER: case CLOCK_EVT_NOTIFY_BROADCAST_EXIT: tick_broadcast_oneshot_control(reason); break; |
94df7de02 hrtimers: allow t... |
368 369 370 |
case CLOCK_EVT_NOTIFY_CPU_DYING: tick_handover_do_timer(dev); break; |
906568c9c [PATCH] tick-mana... |
371 |
case CLOCK_EVT_NOTIFY_CPU_DEAD: |
79bf2bb33 [PATCH] tick-mana... |
372 |
tick_shutdown_broadcast_oneshot(dev); |
f8381cba0 [PATCH] tick-mana... |
373 |
tick_shutdown_broadcast(dev); |
906568c9c [PATCH] tick-mana... |
374 375 |
tick_shutdown(dev); break; |
6321dd60c [PATCH] Save/rest... |
376 |
case CLOCK_EVT_NOTIFY_SUSPEND: |
cd05a1f81 [PATCH] clockeven... |
377 |
tick_suspend(); |
6321dd60c [PATCH] Save/rest... |
378 379 380 381 |
tick_suspend_broadcast(); break; case CLOCK_EVT_NOTIFY_RESUME: |
18de5bc4c clockevents: fix ... |
382 |
tick_resume(); |
6321dd60c [PATCH] Save/rest... |
383 |
break; |
906568c9c [PATCH] tick-mana... |
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 |
default: break; } return NOTIFY_OK; } static struct notifier_block tick_notifier = { .notifier_call = tick_notify, }; /** * tick_init - initialize the tick control * * Register the notifier with the clockevents framework */ void __init tick_init(void) { clockevents_register_notifier(&tick_notifier); } |