|
2 | 2 | /* |
3 | 3 | * Simple CPU accounting cgroup controller |
4 | 4 | */ |
| 5 | +#include <linux/sched/clock.h> |
5 | 6 | #include <linux/sched/cputime.h> |
6 | 7 | #include <linux/tsacct_kern.h> |
7 | 8 | #include "sched.h" |
@@ -420,22 +421,155 @@ static inline void irqtime_account_process_tick(struct task_struct *p, int user_ |
420 | 421 | #endif /* !CONFIG_IRQ_TIME_ACCOUNTING */ |
421 | 422 |
|
422 | 423 | #ifdef CONFIG_NO_HZ_COMMON |
423 | | -void kcpustat_dyntick_start(void) |
| 424 | +static void kcpustat_idle_stop(struct kernel_cpustat *kc, u64 now) |
424 | 425 | { |
425 | | - if (!vtime_generic_enabled_this_cpu()) { |
426 | | - vtime_dyntick_start(); |
427 | | - __this_cpu_write(kernel_cpustat.idle_dyntick, 1); |
428 | | - } |
| 426 | + u64 *cpustat = kc->cpustat; |
| 427 | + u64 delta; |
| 428 | + |
| 429 | + if (!kc->idle_elapse) |
| 430 | + return; |
| 431 | + |
| 432 | + delta = now - kc->idle_entrytime; |
| 433 | + |
| 434 | + write_seqcount_begin(&kc->idle_sleeptime_seq); |
| 435 | + if (nr_iowait_cpu(smp_processor_id()) > 0) |
| 436 | + cpustat[CPUTIME_IOWAIT] += delta; |
| 437 | + else |
| 438 | + cpustat[CPUTIME_IDLE] += delta; |
| 439 | + |
| 440 | + kc->idle_entrytime = now; |
| 441 | + kc->idle_elapse = false; |
| 442 | + write_seqcount_end(&kc->idle_sleeptime_seq); |
429 | 443 | } |
430 | 444 |
|
431 | | -void kcpustat_dyntick_stop(void) |
| 445 | +static void kcpustat_idle_start(struct kernel_cpustat *kc, u64 now) |
432 | 446 | { |
| 447 | + write_seqcount_begin(&kc->idle_sleeptime_seq); |
| 448 | + kc->idle_entrytime = now; |
| 449 | + kc->idle_elapse = true; |
| 450 | + write_seqcount_end(&kc->idle_sleeptime_seq); |
| 451 | +} |
| 452 | + |
| 453 | +void kcpustat_dyntick_stop(u64 now) |
| 454 | +{ |
| 455 | + struct kernel_cpustat *kc = kcpustat_this_cpu; |
| 456 | + |
433 | 457 | if (!vtime_generic_enabled_this_cpu()) { |
434 | | - __this_cpu_write(kernel_cpustat.idle_dyntick, 0); |
| 458 | + WARN_ON_ONCE(!kc->idle_dyntick); |
| 459 | + kcpustat_idle_stop(kc, now); |
| 460 | + kc->idle_dyntick = false; |
435 | 461 | vtime_dyntick_stop(); |
436 | 462 | steal_account_process_time(ULONG_MAX); |
437 | 463 | } |
438 | 464 | } |
| 465 | + |
| 466 | +void kcpustat_dyntick_start(u64 now) |
| 467 | +{ |
| 468 | + struct kernel_cpustat *kc = kcpustat_this_cpu; |
| 469 | + |
| 470 | + if (!vtime_generic_enabled_this_cpu()) { |
| 471 | + vtime_dyntick_start(); |
| 472 | + kc->idle_dyntick = true; |
| 473 | + kcpustat_idle_start(kc, now); |
| 474 | + } |
| 475 | +} |
| 476 | + |
| 477 | +void kcpustat_irq_enter(u64 now) |
| 478 | +{ |
| 479 | + struct kernel_cpustat *kc = kcpustat_this_cpu; |
| 480 | + |
| 481 | + if (!vtime_generic_enabled_this_cpu()) |
| 482 | + kcpustat_idle_stop(kc, now); |
| 483 | +} |
| 484 | + |
| 485 | +void kcpustat_irq_exit(u64 now) |
| 486 | +{ |
| 487 | + struct kernel_cpustat *kc = kcpustat_this_cpu; |
| 488 | + |
| 489 | + if (!vtime_generic_enabled_this_cpu()) |
| 490 | + kcpustat_idle_start(kc, now); |
| 491 | +} |
| 492 | + |
| 493 | +static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usage_stat idx, |
| 494 | + bool compute_delta, u64 *last_update_time) |
| 495 | +{ |
| 496 | + struct kernel_cpustat *kc = &kcpustat_cpu(cpu); |
| 497 | + u64 *cpustat = kc->cpustat; |
| 498 | + unsigned int seq; |
| 499 | + ktime_t now; |
| 500 | + u64 idle; |
| 501 | + |
| 502 | + now = ktime_get(); |
| 503 | + if (last_update_time) |
| 504 | + *last_update_time = ktime_to_us(now); |
| 505 | + |
| 506 | + if (vtime_generic_enabled_cpu(cpu)) { |
| 507 | + idle = kcpustat_field(idx, cpu); |
| 508 | + goto to_us; |
| 509 | + } |
| 510 | + |
| 511 | + do { |
| 512 | + seq = read_seqcount_begin(&kc->idle_sleeptime_seq); |
| 513 | + |
| 514 | + idle = cpustat[idx]; |
| 515 | + if (kc->idle_elapse && compute_delta && now > kc->idle_entrytime) |
| 516 | + idle += (now - kc->idle_entrytime); |
| 517 | + } while (read_seqcount_retry(&kc->idle_sleeptime_seq, seq)); |
| 518 | + |
| 519 | +to_us: |
| 520 | + do_div(idle, NSEC_PER_USEC); |
| 521 | + |
| 522 | + return idle; |
| 523 | +} |
| 524 | + |
| 525 | +/** |
| 526 | + * get_cpu_idle_time_us - get the total idle time of a CPU |
| 527 | + * @cpu: CPU number to query |
| 528 | + * @last_update_time: variable to store update time in. Do not update |
| 529 | + * counters if NULL. |
| 530 | + * |
| 531 | + * Return the cumulative idle time (since boot) for a given |
| 532 | + * CPU, in microseconds. Note that this is partially broken due to |
| 533 | + * the counter of iowait tasks that can be remotely updated without |
| 534 | + * any synchronization. Therefore it is possible to observe backward |
| 535 | + * values within two consecutive reads. |
| 536 | + * |
| 537 | + * This time is measured via accounting rather than sampling, |
| 538 | + * and is as accurate as ktime_get() is. |
| 539 | + * |
| 540 | + * Return: -1 if generic vtime is enabled, else total idle time of the @cpu |
| 541 | + */ |
| 542 | +u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time) |
| 543 | +{ |
| 544 | + return get_cpu_sleep_time_us(cpu, CPUTIME_IDLE, |
| 545 | + !nr_iowait_cpu(cpu), last_update_time); |
| 546 | +} |
| 547 | +EXPORT_SYMBOL_GPL(get_cpu_idle_time_us); |
| 548 | + |
| 549 | +/** |
| 550 | + * get_cpu_iowait_time_us - get the total iowait time of a CPU |
| 551 | + * @cpu: CPU number to query |
| 552 | + * @last_update_time: variable to store update time in. Do not update |
| 553 | + * counters if NULL. |
| 554 | + * |
| 555 | + * Return the cumulative iowait time (since boot) for a given |
| 556 | + * CPU, in microseconds. Note this is partially broken due to |
| 557 | + * the counter of iowait tasks that can be remotely updated without |
| 558 | + * any synchronization. Therefore it is possible to observe backward |
| 559 | + * values within two consecutive reads. |
| 560 | + * |
| 561 | + * This time is measured via accounting rather than sampling, |
| 562 | + * and is as accurate as ktime_get() is. |
| 563 | + * |
| 564 | + * Return: -1 if generic vtime is enabled, else total iowait time of @cpu |
| 565 | + */ |
| 566 | +u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time) |
| 567 | +{ |
| 568 | + return get_cpu_sleep_time_us(cpu, CPUTIME_IOWAIT, |
| 569 | + nr_iowait_cpu(cpu), last_update_time); |
| 570 | +} |
| 571 | +EXPORT_SYMBOL_GPL(get_cpu_iowait_time_us); |
| 572 | + |
439 | 573 | #endif /* CONFIG_NO_HZ_COMMON */ |
440 | 574 |
|
441 | 575 | /* |
|
0 commit comments