xref: /linux/fs/proc/stat.c (revision 6b3f7af57881f6d6250c6dcc4d910fe8e855a607)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/cpumask.h>
3 #include <linux/fs.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>
17 
18 #ifndef arch_irq_stat_cpu
19 #define arch_irq_stat_cpu(cpu) 0
20 #endif
21 
22 static void show_irq_gap(struct seq_file *p, unsigned int gap)
23 {
24 	static const char zeros[] = " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
25 
26 	while (gap > 0) {
27 		unsigned int inc;
28 
29 		inc = min_t(unsigned int, gap, ARRAY_SIZE(zeros) / 2);
30 		seq_write(p, zeros, 2 * inc);
31 		gap -= inc;
32 	}
33 }
34 
35 static void show_all_irqs(struct seq_file *p)
36 {
37 	unsigned int i, next = 0;
38 
39 	for_each_active_irq(i) {
40 		show_irq_gap(p, i - next);
41 		seq_put_decimal_ull(p, " ", kstat_irqs_usr(i));
42 		next = i + 1;
43 	}
44 	show_irq_gap(p, irq_get_nr_irqs() - next);
45 }
46 
47 static int show_stat(struct seq_file *p, void *v)
48 {
49 	int i, j;
50 	u64 user, nice, system, idle, iowait, irq, softirq, steal;
51 	u64 guest, guest_nice;
52 	u64 sum = 0;
53 	u64 sum_softirq = 0;
54 	unsigned int per_softirq_sums[NR_SOFTIRQS] = {0};
55 	struct timespec64 boottime;
56 
57 	user = nice = system = idle = iowait =
58 		irq = softirq = steal = 0;
59 	guest = guest_nice = 0;
60 	getboottime64(&boottime);
61 	/* shift boot timestamp according to the timens offset */
62 	timens_sub_boottime(&boottime);
63 
64 	for_each_possible_cpu(i) {
65 		struct kernel_cpustat kcpustat;
66 		u64 *cpustat = kcpustat.cpustat;
67 
68 		kcpustat_cpu_fetch(&kcpustat, i);
69 
70 		user		+= cpustat[CPUTIME_USER];
71 		nice		+= cpustat[CPUTIME_NICE];
72 		system		+= cpustat[CPUTIME_SYSTEM];
73 		idle		+= cpustat[CPUTIME_IDLE];
74 		iowait		+= cpustat[CPUTIME_IOWAIT];
75 		irq		+= cpustat[CPUTIME_IRQ];
76 		softirq		+= cpustat[CPUTIME_SOFTIRQ];
77 		steal		+= cpustat[CPUTIME_STEAL];
78 		guest		+= cpustat[CPUTIME_GUEST];
79 		guest_nice	+= cpustat[CPUTIME_GUEST_NICE];
80 		sum		+= kstat_cpu_irqs_sum(i);
81 		sum		+= arch_irq_stat_cpu(i);
82 
83 		for (j = 0; j < NR_SOFTIRQS; j++) {
84 			unsigned int softirq_stat = kstat_softirqs_cpu(j, i);
85 
86 			per_softirq_sums[j] += softirq_stat;
87 			sum_softirq += softirq_stat;
88 		}
89 	}
90 
91 	seq_put_decimal_ull(p, "cpu  ", nsec_to_clock_t(user));
92 	seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice));
93 	seq_put_decimal_ull(p, " ", nsec_to_clock_t(system));
94 	seq_put_decimal_ull(p, " ", nsec_to_clock_t(idle));
95 	seq_put_decimal_ull(p, " ", nsec_to_clock_t(iowait));
96 	seq_put_decimal_ull(p, " ", nsec_to_clock_t(irq));
97 	seq_put_decimal_ull(p, " ", nsec_to_clock_t(softirq));
98 	seq_put_decimal_ull(p, " ", nsec_to_clock_t(steal));
99 	seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest));
100 	seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest_nice));
101 	seq_putc(p, '\n');
102 
103 	for_each_online_cpu(i) {
104 		struct kernel_cpustat kcpustat;
105 		u64 *cpustat = kcpustat.cpustat;
106 
107 		kcpustat_cpu_fetch(&kcpustat, i);
108 
109 		/* Copy values here to work around gcc-2.95.3, gcc-2.96 */
110 		user		= cpustat[CPUTIME_USER];
111 		nice		= cpustat[CPUTIME_NICE];
112 		system		= cpustat[CPUTIME_SYSTEM];
113 		idle		= cpustat[CPUTIME_IDLE];
114 		iowait		= cpustat[CPUTIME_IOWAIT];
115 		irq		= cpustat[CPUTIME_IRQ];
116 		softirq		= cpustat[CPUTIME_SOFTIRQ];
117 		steal		= cpustat[CPUTIME_STEAL];
118 		guest		= cpustat[CPUTIME_GUEST];
119 		guest_nice	= cpustat[CPUTIME_GUEST_NICE];
120 		seq_printf(p, "cpu%d", i);
121 		seq_put_decimal_ull(p, " ", nsec_to_clock_t(user));
122 		seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice));
123 		seq_put_decimal_ull(p, " ", nsec_to_clock_t(system));
124 		seq_put_decimal_ull(p, " ", nsec_to_clock_t(idle));
125 		seq_put_decimal_ull(p, " ", nsec_to_clock_t(iowait));
126 		seq_put_decimal_ull(p, " ", nsec_to_clock_t(irq));
127 		seq_put_decimal_ull(p, " ", nsec_to_clock_t(softirq));
128 		seq_put_decimal_ull(p, " ", nsec_to_clock_t(steal));
129 		seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest));
130 		seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest_nice));
131 		seq_putc(p, '\n');
132 	}
133 	seq_put_decimal_ull(p, "intr ", (unsigned long long)sum);
134 
135 	show_all_irqs(p);
136 
137 	seq_printf(p,
138 		"\nctxt %llu\n"
139 		"btime %llu\n"
140 		"processes %lu\n"
141 		"procs_running %u\n"
142 		"procs_blocked %u\n",
143 		nr_context_switches(),
144 		(unsigned long long)boottime.tv_sec,
145 		total_forks,
146 		nr_running(),
147 		nr_iowait());
148 
149 	seq_put_decimal_ull(p, "softirq ", (unsigned long long)sum_softirq);
150 
151 	for (i = 0; i < NR_SOFTIRQS; i++)
152 		seq_put_decimal_ull(p, " ", per_softirq_sums[i]);
153 	seq_putc(p, '\n');
154 
155 	return 0;
156 }
157 
158 static int stat_open(struct inode *inode, struct file *file)
159 {
160 	unsigned int size = 1024 + 128 * num_online_cpus();
161 
162 	/* minimum size to display an interrupt count : 2 bytes */
163 	size += 2 * irq_get_nr_irqs();
164 	return single_open_size(file, show_stat, NULL, size);
165 }
166 
167 static const struct proc_ops stat_proc_ops = {
168 	.proc_flags	= PROC_ENTRY_PERMANENT,
169 	.proc_open	= stat_open,
170 	.proc_read_iter	= seq_read_iter,
171 	.proc_lseek	= seq_lseek,
172 	.proc_release	= single_release,
173 };
174 
175 static int __init proc_stat_init(void)
176 {
177 	proc_create("stat", 0, NULL, &stat_proc_ops);
178 	return 0;
179 }
180 fs_initcall(proc_stat_init);
181