diff options
Diffstat (limited to 'arch/powerpc/kernel/time.c')
-rw-r--r-- | arch/powerpc/kernel/time.c | 317 |
1 files changed, 154 insertions, 163 deletions
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 8533b3b83f5d..010406958d97 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c | |||
@@ -53,7 +53,7 @@ | |||
53 | #include <linux/posix-timers.h> | 53 | #include <linux/posix-timers.h> |
54 | #include <linux/irq.h> | 54 | #include <linux/irq.h> |
55 | #include <linux/delay.h> | 55 | #include <linux/delay.h> |
56 | #include <linux/perf_event.h> | 56 | #include <linux/irq_work.h> |
57 | #include <asm/trace.h> | 57 | #include <asm/trace.h> |
58 | 58 | ||
59 | #include <asm/io.h> | 59 | #include <asm/io.h> |
@@ -161,10 +161,9 @@ extern struct timezone sys_tz; | |||
161 | static long timezone_offset; | 161 | static long timezone_offset; |
162 | 162 | ||
163 | unsigned long ppc_proc_freq; | 163 | unsigned long ppc_proc_freq; |
164 | EXPORT_SYMBOL(ppc_proc_freq); | 164 | EXPORT_SYMBOL_GPL(ppc_proc_freq); |
165 | unsigned long ppc_tb_freq; | 165 | unsigned long ppc_tb_freq; |
166 | 166 | EXPORT_SYMBOL_GPL(ppc_tb_freq); | |
167 | static DEFINE_PER_CPU(u64, last_jiffy); | ||
168 | 167 | ||
169 | #ifdef CONFIG_VIRT_CPU_ACCOUNTING | 168 | #ifdef CONFIG_VIRT_CPU_ACCOUNTING |
170 | /* | 169 | /* |
@@ -185,6 +184,8 @@ DEFINE_PER_CPU(unsigned long, cputime_scaled_last_delta); | |||
185 | 184 | ||
186 | cputime_t cputime_one_jiffy; | 185 | cputime_t cputime_one_jiffy; |
187 | 186 | ||
187 | void (*dtl_consumer)(struct dtl_entry *, u64); | ||
188 | |||
188 | static void calc_cputime_factors(void) | 189 | static void calc_cputime_factors(void) |
189 | { | 190 | { |
190 | struct div_result res; | 191 | struct div_result res; |
@@ -200,62 +201,153 @@ static void calc_cputime_factors(void) | |||
200 | } | 201 | } |
201 | 202 | ||
202 | /* | 203 | /* |
203 | * Read the PURR on systems that have it, otherwise the timebase. | 204 | * Read the SPURR on systems that have it, otherwise the PURR, |
205 | * or if that doesn't exist return the timebase value passed in. | ||
204 | */ | 206 | */ |
205 | static u64 read_purr(void) | 207 | static u64 read_spurr(u64 tb) |
206 | { | 208 | { |
209 | if (cpu_has_feature(CPU_FTR_SPURR)) | ||
210 | return mfspr(SPRN_SPURR); | ||
207 | if (cpu_has_feature(CPU_FTR_PURR)) | 211 | if (cpu_has_feature(CPU_FTR_PURR)) |
208 | return mfspr(SPRN_PURR); | 212 | return mfspr(SPRN_PURR); |
209 | return mftb(); | 213 | return tb; |
210 | } | 214 | } |
211 | 215 | ||
216 | #ifdef CONFIG_PPC_SPLPAR | ||
217 | |||
212 | /* | 218 | /* |
213 | * Read the SPURR on systems that have it, otherwise the purr | 219 | * Scan the dispatch trace log and count up the stolen time. |
220 | * Should be called with interrupts disabled. | ||
214 | */ | 221 | */ |
215 | static u64 read_spurr(u64 purr) | 222 | static u64 scan_dispatch_log(u64 stop_tb) |
216 | { | 223 | { |
217 | /* | 224 | u64 i = local_paca->dtl_ridx; |
218 | * cpus without PURR won't have a SPURR | 225 | struct dtl_entry *dtl = local_paca->dtl_curr; |
219 | * We already know the former when we use this, so tell gcc | 226 | struct dtl_entry *dtl_end = local_paca->dispatch_log_end; |
220 | */ | 227 | struct lppaca *vpa = local_paca->lppaca_ptr; |
221 | if (cpu_has_feature(CPU_FTR_PURR) && cpu_has_feature(CPU_FTR_SPURR)) | 228 | u64 tb_delta; |
222 | return mfspr(SPRN_SPURR); | 229 | u64 stolen = 0; |
223 | return purr; | 230 | u64 dtb; |
231 | |||
232 | if (i == vpa->dtl_idx) | ||
233 | return 0; | ||
234 | while (i < vpa->dtl_idx) { | ||
235 | if (dtl_consumer) | ||
236 | dtl_consumer(dtl, i); | ||
237 | dtb = dtl->timebase; | ||
238 | tb_delta = dtl->enqueue_to_dispatch_time + | ||
239 | dtl->ready_to_enqueue_time; | ||
240 | barrier(); | ||
241 | if (i + N_DISPATCH_LOG < vpa->dtl_idx) { | ||
242 | /* buffer has overflowed */ | ||
243 | i = vpa->dtl_idx - N_DISPATCH_LOG; | ||
244 | dtl = local_paca->dispatch_log + (i % N_DISPATCH_LOG); | ||
245 | continue; | ||
246 | } | ||
247 | if (dtb > stop_tb) | ||
248 | break; | ||
249 | stolen += tb_delta; | ||
250 | ++i; | ||
251 | ++dtl; | ||
252 | if (dtl == dtl_end) | ||
253 | dtl = local_paca->dispatch_log; | ||
254 | } | ||
255 | local_paca->dtl_ridx = i; | ||
256 | local_paca->dtl_curr = dtl; | ||
257 | return stolen; | ||
224 | } | 258 | } |
225 | 259 | ||
226 | /* | 260 | /* |
261 | * Accumulate stolen time by scanning the dispatch trace log. | ||
262 | * Called on entry from user mode. | ||
263 | */ | ||
264 | void accumulate_stolen_time(void) | ||
265 | { | ||
266 | u64 sst, ust; | ||
267 | |||
268 | sst = scan_dispatch_log(get_paca()->starttime_user); | ||
269 | ust = scan_dispatch_log(get_paca()->starttime); | ||
270 | get_paca()->system_time -= sst; | ||
271 | get_paca()->user_time -= ust; | ||
272 | get_paca()->stolen_time += ust + sst; | ||
273 | } | ||
274 | |||
275 | static inline u64 calculate_stolen_time(u64 stop_tb) | ||
276 | { | ||
277 | u64 stolen = 0; | ||
278 | |||
279 | if (get_paca()->dtl_ridx != get_paca()->lppaca_ptr->dtl_idx) { | ||
280 | stolen = scan_dispatch_log(stop_tb); | ||
281 | get_paca()->system_time -= stolen; | ||
282 | } | ||
283 | |||
284 | stolen += get_paca()->stolen_time; | ||
285 | get_paca()->stolen_time = 0; | ||
286 | return stolen; | ||
287 | } | ||
288 | |||
289 | #else /* CONFIG_PPC_SPLPAR */ | ||
290 | static inline u64 calculate_stolen_time(u64 stop_tb) | ||
291 | { | ||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | #endif /* CONFIG_PPC_SPLPAR */ | ||
296 | |||
297 | /* | ||
227 | * Account time for a transition between system, hard irq | 298 | * Account time for a transition between system, hard irq |
228 | * or soft irq state. | 299 | * or soft irq state. |
229 | */ | 300 | */ |
230 | void account_system_vtime(struct task_struct *tsk) | 301 | void account_system_vtime(struct task_struct *tsk) |
231 | { | 302 | { |
232 | u64 now, nowscaled, delta, deltascaled, sys_time; | 303 | u64 now, nowscaled, delta, deltascaled; |
233 | unsigned long flags; | 304 | unsigned long flags; |
305 | u64 stolen, udelta, sys_scaled, user_scaled; | ||
234 | 306 | ||
235 | local_irq_save(flags); | 307 | local_irq_save(flags); |
236 | now = read_purr(); | 308 | now = mftb(); |
237 | nowscaled = read_spurr(now); | 309 | nowscaled = read_spurr(now); |
238 | delta = now - get_paca()->startpurr; | 310 | get_paca()->system_time += now - get_paca()->starttime; |
311 | get_paca()->starttime = now; | ||
239 | deltascaled = nowscaled - get_paca()->startspurr; | 312 | deltascaled = nowscaled - get_paca()->startspurr; |
240 | get_paca()->startpurr = now; | ||
241 | get_paca()->startspurr = nowscaled; | 313 | get_paca()->startspurr = nowscaled; |
242 | if (!in_interrupt()) { | 314 | |
243 | /* deltascaled includes both user and system time. | 315 | stolen = calculate_stolen_time(now); |
244 | * Hence scale it based on the purr ratio to estimate | 316 | |
245 | * the system time */ | 317 | delta = get_paca()->system_time; |
246 | sys_time = get_paca()->system_time; | 318 | get_paca()->system_time = 0; |
247 | if (get_paca()->user_time) | 319 | udelta = get_paca()->user_time - get_paca()->utime_sspurr; |
248 | deltascaled = deltascaled * sys_time / | 320 | get_paca()->utime_sspurr = get_paca()->user_time; |
249 | (sys_time + get_paca()->user_time); | 321 | |
250 | delta += sys_time; | 322 | /* |
251 | get_paca()->system_time = 0; | 323 | * Because we don't read the SPURR on every kernel entry/exit, |
324 | * deltascaled includes both user and system SPURR ticks. | ||
325 | * Apportion these ticks to system SPURR ticks and user | ||
326 | * SPURR ticks in the same ratio as the system time (delta) | ||
327 | * and user time (udelta) values obtained from the timebase | ||
328 | * over the same interval. The system ticks get accounted here; | ||
329 | * the user ticks get saved up in paca->user_time_scaled to be | ||
330 | * used by account_process_tick. | ||
331 | */ | ||
332 | sys_scaled = delta; | ||
333 | user_scaled = udelta; | ||
334 | if (deltascaled != delta + udelta) { | ||
335 | if (udelta) { | ||
336 | sys_scaled = deltascaled * delta / (delta + udelta); | ||
337 | user_scaled = deltascaled - sys_scaled; | ||
338 | } else { | ||
339 | sys_scaled = deltascaled; | ||
340 | } | ||
341 | } | ||
342 | get_paca()->user_time_scaled += user_scaled; | ||
343 | |||
344 | if (in_irq() || idle_task(smp_processor_id()) != tsk) { | ||
345 | account_system_time(tsk, 0, delta, sys_scaled); | ||
346 | if (stolen) | ||
347 | account_steal_time(stolen); | ||
348 | } else { | ||
349 | account_idle_time(delta + stolen); | ||
252 | } | 350 | } |
253 | if (in_irq() || idle_task(smp_processor_id()) != tsk) | ||
254 | account_system_time(tsk, 0, delta, deltascaled); | ||
255 | else | ||
256 | account_idle_time(delta); | ||
257 | __get_cpu_var(cputime_last_delta) = delta; | ||
258 | __get_cpu_var(cputime_scaled_last_delta) = deltascaled; | ||
259 | local_irq_restore(flags); | 351 | local_irq_restore(flags); |
260 | } | 352 | } |
261 | EXPORT_SYMBOL_GPL(account_system_vtime); | 353 | EXPORT_SYMBOL_GPL(account_system_vtime); |
@@ -265,125 +357,26 @@ EXPORT_SYMBOL_GPL(account_system_vtime); | |||
265 | * by the exception entry and exit code to the generic process | 357 | * by the exception entry and exit code to the generic process |
266 | * user and system time records. | 358 | * user and system time records. |
267 | * Must be called with interrupts disabled. | 359 | * Must be called with interrupts disabled. |
360 | * Assumes that account_system_vtime() has been called recently | ||
361 | * (i.e. since the last entry from usermode) so that | ||
362 | * get_paca()->user_time_scaled is up to date. | ||
268 | */ | 363 | */ |
269 | void account_process_tick(struct task_struct *tsk, int user_tick) | 364 | void account_process_tick(struct task_struct *tsk, int user_tick) |
270 | { | 365 | { |
271 | cputime_t utime, utimescaled; | 366 | cputime_t utime, utimescaled; |
272 | 367 | ||
273 | utime = get_paca()->user_time; | 368 | utime = get_paca()->user_time; |
369 | utimescaled = get_paca()->user_time_scaled; | ||
274 | get_paca()->user_time = 0; | 370 | get_paca()->user_time = 0; |
275 | utimescaled = cputime_to_scaled(utime); | 371 | get_paca()->user_time_scaled = 0; |
372 | get_paca()->utime_sspurr = 0; | ||
276 | account_user_time(tsk, utime, utimescaled); | 373 | account_user_time(tsk, utime, utimescaled); |
277 | } | 374 | } |
278 | 375 | ||
279 | /* | ||
280 | * Stuff for accounting stolen time. | ||
281 | */ | ||
282 | struct cpu_purr_data { | ||
283 | int initialized; /* thread is running */ | ||
284 | u64 tb; /* last TB value read */ | ||
285 | u64 purr; /* last PURR value read */ | ||
286 | u64 spurr; /* last SPURR value read */ | ||
287 | }; | ||
288 | |||
289 | /* | ||
290 | * Each entry in the cpu_purr_data array is manipulated only by its | ||
291 | * "owner" cpu -- usually in the timer interrupt but also occasionally | ||
292 | * in process context for cpu online. As long as cpus do not touch | ||
293 | * each others' cpu_purr_data, disabling local interrupts is | ||
294 | * sufficient to serialize accesses. | ||
295 | */ | ||
296 | static DEFINE_PER_CPU(struct cpu_purr_data, cpu_purr_data); | ||
297 | |||
298 | static void snapshot_tb_and_purr(void *data) | ||
299 | { | ||
300 | unsigned long flags; | ||
301 | struct cpu_purr_data *p = &__get_cpu_var(cpu_purr_data); | ||
302 | |||
303 | local_irq_save(flags); | ||
304 | p->tb = get_tb_or_rtc(); | ||
305 | p->purr = mfspr(SPRN_PURR); | ||
306 | wmb(); | ||
307 | p->initialized = 1; | ||
308 | local_irq_restore(flags); | ||
309 | } | ||
310 | |||
311 | /* | ||
312 | * Called during boot when all cpus have come up. | ||
313 | */ | ||
314 | void snapshot_timebases(void) | ||
315 | { | ||
316 | if (!cpu_has_feature(CPU_FTR_PURR)) | ||
317 | return; | ||
318 | on_each_cpu(snapshot_tb_and_purr, NULL, 1); | ||
319 | } | ||
320 | |||
321 | /* | ||
322 | * Must be called with interrupts disabled. | ||
323 | */ | ||
324 | void calculate_steal_time(void) | ||
325 | { | ||
326 | u64 tb, purr; | ||
327 | s64 stolen; | ||
328 | struct cpu_purr_data *pme; | ||
329 | |||
330 | pme = &__get_cpu_var(cpu_purr_data); | ||
331 | if (!pme->initialized) | ||
332 | return; /* !CPU_FTR_PURR or early in early boot */ | ||
333 | tb = mftb(); | ||
334 | purr = mfspr(SPRN_PURR); | ||
335 | stolen = (tb - pme->tb) - (purr - pme->purr); | ||
336 | if (stolen > 0) { | ||
337 | if (idle_task(smp_processor_id()) != current) | ||
338 | account_steal_time(stolen); | ||
339 | else | ||
340 | account_idle_time(stolen); | ||
341 | } | ||
342 | pme->tb = tb; | ||
343 | pme->purr = purr; | ||
344 | } | ||
345 | |||
346 | #ifdef CONFIG_PPC_SPLPAR | ||
347 | /* | ||
348 | * Must be called before the cpu is added to the online map when | ||
349 | * a cpu is being brought up at runtime. | ||
350 | */ | ||
351 | static void snapshot_purr(void) | ||
352 | { | ||
353 | struct cpu_purr_data *pme; | ||
354 | unsigned long flags; | ||
355 | |||
356 | if (!cpu_has_feature(CPU_FTR_PURR)) | ||
357 | return; | ||
358 | local_irq_save(flags); | ||
359 | pme = &__get_cpu_var(cpu_purr_data); | ||
360 | pme->tb = mftb(); | ||
361 | pme->purr = mfspr(SPRN_PURR); | ||
362 | pme->initialized = 1; | ||
363 | local_irq_restore(flags); | ||
364 | } | ||
365 | |||
366 | #endif /* CONFIG_PPC_SPLPAR */ | ||
367 | |||
368 | #else /* ! CONFIG_VIRT_CPU_ACCOUNTING */ | 376 | #else /* ! CONFIG_VIRT_CPU_ACCOUNTING */ |
369 | #define calc_cputime_factors() | 377 | #define calc_cputime_factors() |
370 | #define calculate_steal_time() do { } while (0) | ||
371 | #endif | 378 | #endif |
372 | 379 | ||
373 | #if !(defined(CONFIG_VIRT_CPU_ACCOUNTING) && defined(CONFIG_PPC_SPLPAR)) | ||
374 | #define snapshot_purr() do { } while (0) | ||
375 | #endif | ||
376 | |||
377 | /* | ||
378 | * Called when a cpu comes up after the system has finished booting, | ||
379 | * i.e. as a result of a hotplug cpu action. | ||
380 | */ | ||
381 | void snapshot_timebase(void) | ||
382 | { | ||
383 | __get_cpu_var(last_jiffy) = get_tb_or_rtc(); | ||
384 | snapshot_purr(); | ||
385 | } | ||
386 | |||
387 | void __delay(unsigned long loops) | 380 | void __delay(unsigned long loops) |
388 | { | 381 | { |
389 | unsigned long start; | 382 | unsigned long start; |
@@ -493,60 +486,60 @@ void __init iSeries_time_init_early(void) | |||
493 | } | 486 | } |
494 | #endif /* CONFIG_PPC_ISERIES */ | 487 | #endif /* CONFIG_PPC_ISERIES */ |
495 | 488 | ||
496 | #ifdef CONFIG_PERF_EVENTS | 489 | #ifdef CONFIG_IRQ_WORK |
497 | 490 | ||
498 | /* | 491 | /* |
499 | * 64-bit uses a byte in the PACA, 32-bit uses a per-cpu variable... | 492 | * 64-bit uses a byte in the PACA, 32-bit uses a per-cpu variable... |
500 | */ | 493 | */ |
501 | #ifdef CONFIG_PPC64 | 494 | #ifdef CONFIG_PPC64 |
502 | static inline unsigned long test_perf_event_pending(void) | 495 | static inline unsigned long test_irq_work_pending(void) |
503 | { | 496 | { |
504 | unsigned long x; | 497 | unsigned long x; |
505 | 498 | ||
506 | asm volatile("lbz %0,%1(13)" | 499 | asm volatile("lbz %0,%1(13)" |
507 | : "=r" (x) | 500 | : "=r" (x) |
508 | : "i" (offsetof(struct paca_struct, perf_event_pending))); | 501 | : "i" (offsetof(struct paca_struct, irq_work_pending))); |
509 | return x; | 502 | return x; |
510 | } | 503 | } |
511 | 504 | ||
512 | static inline void set_perf_event_pending_flag(void) | 505 | static inline void set_irq_work_pending_flag(void) |
513 | { | 506 | { |
514 | asm volatile("stb %0,%1(13)" : : | 507 | asm volatile("stb %0,%1(13)" : : |
515 | "r" (1), | 508 | "r" (1), |
516 | "i" (offsetof(struct paca_struct, perf_event_pending))); | 509 | "i" (offsetof(struct paca_struct, irq_work_pending))); |
517 | } | 510 | } |
518 | 511 | ||
519 | static inline void clear_perf_event_pending(void) | 512 | static inline void clear_irq_work_pending(void) |
520 | { | 513 | { |
521 | asm volatile("stb %0,%1(13)" : : | 514 | asm volatile("stb %0,%1(13)" : : |
522 | "r" (0), | 515 | "r" (0), |
523 | "i" (offsetof(struct paca_struct, perf_event_pending))); | 516 | "i" (offsetof(struct paca_struct, irq_work_pending))); |
524 | } | 517 | } |
525 | 518 | ||
526 | #else /* 32-bit */ | 519 | #else /* 32-bit */ |
527 | 520 | ||
528 | DEFINE_PER_CPU(u8, perf_event_pending); | 521 | DEFINE_PER_CPU(u8, irq_work_pending); |
529 | 522 | ||
530 | #define set_perf_event_pending_flag() __get_cpu_var(perf_event_pending) = 1 | 523 | #define set_irq_work_pending_flag() __get_cpu_var(irq_work_pending) = 1 |
531 | #define test_perf_event_pending() __get_cpu_var(perf_event_pending) | 524 | #define test_irq_work_pending() __get_cpu_var(irq_work_pending) |
532 | #define clear_perf_event_pending() __get_cpu_var(perf_event_pending) = 0 | 525 | #define clear_irq_work_pending() __get_cpu_var(irq_work_pending) = 0 |
533 | 526 | ||
534 | #endif /* 32 vs 64 bit */ | 527 | #endif /* 32 vs 64 bit */ |
535 | 528 | ||
536 | void set_perf_event_pending(void) | 529 | void set_irq_work_pending(void) |
537 | { | 530 | { |
538 | preempt_disable(); | 531 | preempt_disable(); |
539 | set_perf_event_pending_flag(); | 532 | set_irq_work_pending_flag(); |
540 | set_dec(1); | 533 | set_dec(1); |
541 | preempt_enable(); | 534 | preempt_enable(); |
542 | } | 535 | } |
543 | 536 | ||
544 | #else /* CONFIG_PERF_EVENTS */ | 537 | #else /* CONFIG_IRQ_WORK */ |
545 | 538 | ||
546 | #define test_perf_event_pending() 0 | 539 | #define test_irq_work_pending() 0 |
547 | #define clear_perf_event_pending() | 540 | #define clear_irq_work_pending() |
548 | 541 | ||
549 | #endif /* CONFIG_PERF_EVENTS */ | 542 | #endif /* CONFIG_IRQ_WORK */ |
550 | 543 | ||
551 | /* | 544 | /* |
552 | * For iSeries shared processors, we have to let the hypervisor | 545 | * For iSeries shared processors, we have to let the hypervisor |
@@ -585,11 +578,9 @@ void timer_interrupt(struct pt_regs * regs) | |||
585 | old_regs = set_irq_regs(regs); | 578 | old_regs = set_irq_regs(regs); |
586 | irq_enter(); | 579 | irq_enter(); |
587 | 580 | ||
588 | calculate_steal_time(); | 581 | if (test_irq_work_pending()) { |
589 | 582 | clear_irq_work_pending(); | |
590 | if (test_perf_event_pending()) { | 583 | irq_work_run(); |
591 | clear_perf_event_pending(); | ||
592 | perf_event_do_pending(); | ||
593 | } | 584 | } |
594 | 585 | ||
595 | #ifdef CONFIG_PPC_ISERIES | 586 | #ifdef CONFIG_PPC_ISERIES |