1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/cpumask.h>
4 #include <linux/init.h>
5 #include <linux/interrupt.h>
6 #include <linux/kernel_stat.h>
7 #include <linux/proc_fs.h>
8 #include <linux/sched.h>
9 #include <linux/sched/stat.h>
10 #include <linux/seq_file.h>
11 #include <linux/slab.h>
12 #include <linux/time.h>
13 #include <linux/time_namespace.h>
14 #include <linux/irqnr.h>
15 #include <linux/sched/cputime.h>
16 #include <linux/tick.h>
18 #ifndef arch_irq_stat_cpu
19 #define arch_irq_stat_cpu(cpu) 0
22 #define arch_irq_stat() 0
25 u64
get_idle_time(struct kernel_cpustat
*kcs
, int cpu
)
27 u64 idle
, idle_usecs
= -1ULL;
30 idle_usecs
= get_cpu_idle_time_us(cpu
, NULL
);
32 if (idle_usecs
== -1ULL)
33 /* !NO_HZ or cpu offline so we can rely on cpustat.idle */
34 idle
= kcs
->cpustat
[CPUTIME_IDLE
];
36 idle
= idle_usecs
* NSEC_PER_USEC
;
41 static u64
get_iowait_time(struct kernel_cpustat
*kcs
, int cpu
)
43 u64 iowait
, iowait_usecs
= -1ULL;
46 iowait_usecs
= get_cpu_iowait_time_us(cpu
, NULL
);
48 if (iowait_usecs
== -1ULL)
49 /* !NO_HZ or cpu offline so we can rely on cpustat.iowait */
50 iowait
= kcs
->cpustat
[CPUTIME_IOWAIT
];
52 iowait
= iowait_usecs
* NSEC_PER_USEC
;
57 static void show_irq_gap(struct seq_file
*p
, unsigned int gap
)
59 static const char zeros
[] = " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
64 inc
= min_t(unsigned int, gap
, ARRAY_SIZE(zeros
) / 2);
65 seq_write(p
, zeros
, 2 * inc
);
70 static void show_all_irqs(struct seq_file
*p
)
72 unsigned int i
, next
= 0;
74 for_each_active_irq(i
) {
75 show_irq_gap(p
, i
- next
);
76 seq_put_decimal_ull(p
, " ", kstat_irqs_usr(i
));
79 show_irq_gap(p
, irq_get_nr_irqs() - next
);
82 static int show_stat(struct seq_file
*p
, void *v
)
85 u64 user
, nice
, system
, idle
, iowait
, irq
, softirq
, steal
;
86 u64 guest
, guest_nice
;
89 unsigned int per_softirq_sums
[NR_SOFTIRQS
] = {0};
90 struct timespec64 boottime
;
92 user
= nice
= system
= idle
= iowait
=
93 irq
= softirq
= steal
= 0;
94 guest
= guest_nice
= 0;
95 getboottime64(&boottime
);
96 /* shift boot timestamp according to the timens offset */
97 timens_sub_boottime(&boottime
);
99 for_each_possible_cpu(i
) {
100 struct kernel_cpustat kcpustat
;
101 u64
*cpustat
= kcpustat
.cpustat
;
103 kcpustat_cpu_fetch(&kcpustat
, i
);
105 user
+= cpustat
[CPUTIME_USER
];
106 nice
+= cpustat
[CPUTIME_NICE
];
107 system
+= cpustat
[CPUTIME_SYSTEM
];
108 idle
+= get_idle_time(&kcpustat
, i
);
109 iowait
+= get_iowait_time(&kcpustat
, i
);
110 irq
+= cpustat
[CPUTIME_IRQ
];
111 softirq
+= cpustat
[CPUTIME_SOFTIRQ
];
112 steal
+= cpustat
[CPUTIME_STEAL
];
113 guest
+= cpustat
[CPUTIME_GUEST
];
114 guest_nice
+= cpustat
[CPUTIME_GUEST_NICE
];
115 sum
+= kstat_cpu_irqs_sum(i
);
116 sum
+= arch_irq_stat_cpu(i
);
118 for (j
= 0; j
< NR_SOFTIRQS
; j
++) {
119 unsigned int softirq_stat
= kstat_softirqs_cpu(j
, i
);
121 per_softirq_sums
[j
] += softirq_stat
;
122 sum_softirq
+= softirq_stat
;
125 sum
+= arch_irq_stat();
127 seq_put_decimal_ull(p
, "cpu ", nsec_to_clock_t(user
));
128 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(nice
));
129 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(system
));
130 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(idle
));
131 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(iowait
));
132 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(irq
));
133 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(softirq
));
134 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(steal
));
135 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(guest
));
136 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(guest_nice
));
139 for_each_online_cpu(i
) {
140 struct kernel_cpustat kcpustat
;
141 u64
*cpustat
= kcpustat
.cpustat
;
143 kcpustat_cpu_fetch(&kcpustat
, i
);
145 /* Copy values here to work around gcc-2.95.3, gcc-2.96 */
146 user
= cpustat
[CPUTIME_USER
];
147 nice
= cpustat
[CPUTIME_NICE
];
148 system
= cpustat
[CPUTIME_SYSTEM
];
149 idle
= get_idle_time(&kcpustat
, i
);
150 iowait
= get_iowait_time(&kcpustat
, i
);
151 irq
= cpustat
[CPUTIME_IRQ
];
152 softirq
= cpustat
[CPUTIME_SOFTIRQ
];
153 steal
= cpustat
[CPUTIME_STEAL
];
154 guest
= cpustat
[CPUTIME_GUEST
];
155 guest_nice
= cpustat
[CPUTIME_GUEST_NICE
];
156 seq_printf(p
, "cpu%d", i
);
157 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(user
));
158 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(nice
));
159 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(system
));
160 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(idle
));
161 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(iowait
));
162 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(irq
));
163 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(softirq
));
164 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(steal
));
165 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(guest
));
166 seq_put_decimal_ull(p
, " ", nsec_to_clock_t(guest_nice
));
169 seq_put_decimal_ull(p
, "intr ", (unsigned long long)sum
);
178 "procs_blocked %u\n",
179 nr_context_switches(),
180 (unsigned long long)boottime
.tv_sec
,
185 seq_put_decimal_ull(p
, "softirq ", (unsigned long long)sum_softirq
);
187 for (i
= 0; i
< NR_SOFTIRQS
; i
++)
188 seq_put_decimal_ull(p
, " ", per_softirq_sums
[i
]);
194 static int stat_open(struct inode
*inode
, struct file
*file
)
196 unsigned int size
= 1024 + 128 * num_online_cpus();
198 /* minimum size to display an interrupt count : 2 bytes */
199 size
+= 2 * irq_get_nr_irqs();
200 return single_open_size(file
, show_stat
, NULL
, size
);
203 static const struct proc_ops stat_proc_ops
= {
204 .proc_flags
= PROC_ENTRY_PERMANENT
,
205 .proc_open
= stat_open
,
206 .proc_read_iter
= seq_read_iter
,
207 .proc_lseek
= seq_lseek
,
208 .proc_release
= single_release
,
211 static int __init
proc_stat_init(void)
213 proc_create("stat", 0, NULL
, &stat_proc_ops
);
216 fs_initcall(proc_stat_init
);