mirror of
https://github.com/torvalds/linux
synced 2026-07-21 17:50:43 +09:00
Pull NOHZ updates from Thomas Gleixner: - Fix a long standing TOCTOU in get_cpu_sleep_time_us() - Make the CPU offline NOHZ handling more robust by disabling NOHZ on the outgoing CPU early instead of creating unneeded state which needs to be undone. - Unify idle CPU time accounting instead of having two different accounting mechanisms. These two different mechanisms are not really independent, but the different properties can in the worst case cause that gloabl idle time can be observed going backwards. - Consolidate the idle/iowait time retrieval interfaces instead of converting back and forth between them. - Make idle interrupt time accounting more robust. The original code assumes that interrupt time accouting is enabled and therefore stops elapsing idle time while an interrupt is handled in NOHZ dyntick state. That assumption is not correct as interrupt time accounting can be disabled at compile and runtime. - Fix an accounting error between dyntick idle time and dyntick idle steal time. The stolen time is not accounted and therefore idle time becomes inaccurate. The stolen time is now accounted after the fact as there is no way to predict the steal time upfront. * tag 'timers-nohz-2026-06-13' of gitolite.kernel.org:pub/scm/linux/kernel/git/tip/tip: sched/cputime: Handle dyntick-idle steal time correctly sched/cputime: Handle idle irqtime gracefully sched/cputime: Provide get_cpu_[idle|iowait]_time_us() off-case tick/sched: Consolidate idle time fetching APIs tick/sched: Account tickless idle cputime only when tick is stopped tick/sched: Remove unused fields tick/sched: Move dyntick-idle cputime accounting to cputime code tick/sched: Remove nohz disabled special case in cputime fetch tick/sched: Unify idle cputime accounting s390/time: Prepare to stop elapsing in dynticks-idle powerpc/time: Prepare to stop elapsing in dynticks-idle sched/cputime: Correctly support generic vtime idle time sched/cputime: Remove superfluous and error prone kcpustat_field() parameter sched/idle: Handle offlining first in idle loop tick/sched: Fix TOCTOU in nohz idle time fetch
181 lines
5.0 KiB
C
181 lines
5.0 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <linux/cpumask.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/init.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/kernel_stat.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/sched/stat.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/time.h>
|
|
#include <linux/time_namespace.h>
|
|
#include <linux/irqnr.h>
|
|
#include <linux/sched/cputime.h>
|
|
#include <linux/tick.h>
|
|
|
|
#ifndef arch_irq_stat_cpu
|
|
#define arch_irq_stat_cpu(cpu) 0
|
|
#endif
|
|
|
|
static void show_irq_gap(struct seq_file *p, unsigned int gap)
|
|
{
|
|
static const char zeros[] = " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
|
|
|
|
while (gap > 0) {
|
|
unsigned int inc;
|
|
|
|
inc = min_t(unsigned int, gap, ARRAY_SIZE(zeros) / 2);
|
|
seq_write(p, zeros, 2 * inc);
|
|
gap -= inc;
|
|
}
|
|
}
|
|
|
|
static void show_all_irqs(struct seq_file *p)
|
|
{
|
|
unsigned int i, next = 0;
|
|
|
|
for_each_active_irq(i) {
|
|
show_irq_gap(p, i - next);
|
|
seq_put_decimal_ull(p, " ", kstat_irqs_usr(i));
|
|
next = i + 1;
|
|
}
|
|
show_irq_gap(p, irq_get_nr_irqs() - next);
|
|
}
|
|
|
|
static int show_stat(struct seq_file *p, void *v)
|
|
{
|
|
int i, j;
|
|
u64 user, nice, system, idle, iowait, irq, softirq, steal;
|
|
u64 guest, guest_nice;
|
|
u64 sum = 0;
|
|
u64 sum_softirq = 0;
|
|
unsigned int per_softirq_sums[NR_SOFTIRQS] = {0};
|
|
struct timespec64 boottime;
|
|
|
|
user = nice = system = idle = iowait =
|
|
irq = softirq = steal = 0;
|
|
guest = guest_nice = 0;
|
|
getboottime64(&boottime);
|
|
/* shift boot timestamp according to the timens offset */
|
|
timens_sub_boottime(&boottime);
|
|
|
|
for_each_possible_cpu(i) {
|
|
struct kernel_cpustat kcpustat;
|
|
u64 *cpustat = kcpustat.cpustat;
|
|
|
|
kcpustat_cpu_fetch(&kcpustat, i);
|
|
|
|
user += cpustat[CPUTIME_USER];
|
|
nice += cpustat[CPUTIME_NICE];
|
|
system += cpustat[CPUTIME_SYSTEM];
|
|
idle += cpustat[CPUTIME_IDLE];
|
|
iowait += cpustat[CPUTIME_IOWAIT];
|
|
irq += cpustat[CPUTIME_IRQ];
|
|
softirq += cpustat[CPUTIME_SOFTIRQ];
|
|
steal += cpustat[CPUTIME_STEAL];
|
|
guest += cpustat[CPUTIME_GUEST];
|
|
guest_nice += cpustat[CPUTIME_GUEST_NICE];
|
|
sum += kstat_cpu_irqs_sum(i);
|
|
sum += arch_irq_stat_cpu(i);
|
|
|
|
for (j = 0; j < NR_SOFTIRQS; j++) {
|
|
unsigned int softirq_stat = kstat_softirqs_cpu(j, i);
|
|
|
|
per_softirq_sums[j] += softirq_stat;
|
|
sum_softirq += softirq_stat;
|
|
}
|
|
}
|
|
|
|
seq_put_decimal_ull(p, "cpu ", nsec_to_clock_t(user));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(system));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(idle));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(iowait));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(irq));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(softirq));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(steal));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest_nice));
|
|
seq_putc(p, '\n');
|
|
|
|
for_each_online_cpu(i) {
|
|
struct kernel_cpustat kcpustat;
|
|
u64 *cpustat = kcpustat.cpustat;
|
|
|
|
kcpustat_cpu_fetch(&kcpustat, i);
|
|
|
|
/* Copy values here to work around gcc-2.95.3, gcc-2.96 */
|
|
user = cpustat[CPUTIME_USER];
|
|
nice = cpustat[CPUTIME_NICE];
|
|
system = cpustat[CPUTIME_SYSTEM];
|
|
idle = cpustat[CPUTIME_IDLE];
|
|
iowait = cpustat[CPUTIME_IOWAIT];
|
|
irq = cpustat[CPUTIME_IRQ];
|
|
softirq = cpustat[CPUTIME_SOFTIRQ];
|
|
steal = cpustat[CPUTIME_STEAL];
|
|
guest = cpustat[CPUTIME_GUEST];
|
|
guest_nice = cpustat[CPUTIME_GUEST_NICE];
|
|
seq_printf(p, "cpu%d", i);
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(user));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(system));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(idle));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(iowait));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(irq));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(softirq));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(steal));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest));
|
|
seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest_nice));
|
|
seq_putc(p, '\n');
|
|
}
|
|
seq_put_decimal_ull(p, "intr ", (unsigned long long)sum);
|
|
|
|
show_all_irqs(p);
|
|
|
|
seq_printf(p,
|
|
"\nctxt %llu\n"
|
|
"btime %llu\n"
|
|
"processes %lu\n"
|
|
"procs_running %u\n"
|
|
"procs_blocked %u\n",
|
|
nr_context_switches(),
|
|
(unsigned long long)boottime.tv_sec,
|
|
total_forks,
|
|
nr_running(),
|
|
nr_iowait());
|
|
|
|
seq_put_decimal_ull(p, "softirq ", (unsigned long long)sum_softirq);
|
|
|
|
for (i = 0; i < NR_SOFTIRQS; i++)
|
|
seq_put_decimal_ull(p, " ", per_softirq_sums[i]);
|
|
seq_putc(p, '\n');
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stat_open(struct inode *inode, struct file *file)
|
|
{
|
|
unsigned int size = 1024 + 128 * num_online_cpus();
|
|
|
|
/* minimum size to display an interrupt count : 2 bytes */
|
|
size += 2 * irq_get_nr_irqs();
|
|
return single_open_size(file, show_stat, NULL, size);
|
|
}
|
|
|
|
static const struct proc_ops stat_proc_ops = {
|
|
.proc_flags = PROC_ENTRY_PERMANENT,
|
|
.proc_open = stat_open,
|
|
.proc_read_iter = seq_read_iter,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
};
|
|
|
|
static int __init proc_stat_init(void)
|
|
{
|
|
proc_create("stat", 0, NULL, &stat_proc_ops);
|
|
return 0;
|
|
}
|
|
fs_initcall(proc_stat_init);
|