162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/cpumask.h>
362306a36Sopenharmony_ci#include <linux/fs.h>
462306a36Sopenharmony_ci#include <linux/init.h>
562306a36Sopenharmony_ci#include <linux/interrupt.h>
662306a36Sopenharmony_ci#include <linux/kernel_stat.h>
762306a36Sopenharmony_ci#include <linux/proc_fs.h>
862306a36Sopenharmony_ci#include <linux/sched.h>
962306a36Sopenharmony_ci#include <linux/sched/stat.h>
1062306a36Sopenharmony_ci#include <linux/seq_file.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/time.h>
1362306a36Sopenharmony_ci#include <linux/time_namespace.h>
1462306a36Sopenharmony_ci#include <linux/irqnr.h>
1562306a36Sopenharmony_ci#include <linux/sched/cputime.h>
1662306a36Sopenharmony_ci#include <linux/tick.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#ifndef arch_irq_stat_cpu
1962306a36Sopenharmony_ci#define arch_irq_stat_cpu(cpu) 0
2062306a36Sopenharmony_ci#endif
2162306a36Sopenharmony_ci#ifndef arch_irq_stat
2262306a36Sopenharmony_ci#define arch_irq_stat() 0
2362306a36Sopenharmony_ci#endif
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciu64 get_idle_time(struct kernel_cpustat *kcs, int cpu)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	u64 idle, idle_usecs = -1ULL;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	if (cpu_online(cpu))
3062306a36Sopenharmony_ci		idle_usecs = get_cpu_idle_time_us(cpu, NULL);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (idle_usecs == -1ULL)
3362306a36Sopenharmony_ci		/* !NO_HZ or cpu offline so we can rely on cpustat.idle */
3462306a36Sopenharmony_ci		idle = kcs->cpustat[CPUTIME_IDLE];
3562306a36Sopenharmony_ci	else
3662306a36Sopenharmony_ci		idle = idle_usecs * NSEC_PER_USEC;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	return idle;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic u64 get_iowait_time(struct kernel_cpustat *kcs, int cpu)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	u64 iowait, iowait_usecs = -1ULL;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	if (cpu_online(cpu))
4662306a36Sopenharmony_ci		iowait_usecs = get_cpu_iowait_time_us(cpu, NULL);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (iowait_usecs == -1ULL)
4962306a36Sopenharmony_ci		/* !NO_HZ or cpu offline so we can rely on cpustat.iowait */
5062306a36Sopenharmony_ci		iowait = kcs->cpustat[CPUTIME_IOWAIT];
5162306a36Sopenharmony_ci	else
5262306a36Sopenharmony_ci		iowait = iowait_usecs * NSEC_PER_USEC;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return iowait;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void show_irq_gap(struct seq_file *p, unsigned int gap)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	static const char zeros[] = " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	while (gap > 0) {
6262306a36Sopenharmony_ci		unsigned int inc;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci		inc = min_t(unsigned int, gap, ARRAY_SIZE(zeros) / 2);
6562306a36Sopenharmony_ci		seq_write(p, zeros, 2 * inc);
6662306a36Sopenharmony_ci		gap -= inc;
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic void show_all_irqs(struct seq_file *p)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	unsigned int i, next = 0;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	for_each_active_irq(i) {
7562306a36Sopenharmony_ci		show_irq_gap(p, i - next);
7662306a36Sopenharmony_ci		seq_put_decimal_ull(p, " ", kstat_irqs_usr(i));
7762306a36Sopenharmony_ci		next = i + 1;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci	show_irq_gap(p, nr_irqs - next);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic int show_stat(struct seq_file *p, void *v)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	int i, j;
8562306a36Sopenharmony_ci	u64 user, nice, system, idle, iowait, irq, softirq, steal;
8662306a36Sopenharmony_ci	u64 guest, guest_nice;
8762306a36Sopenharmony_ci	u64 sum = 0;
8862306a36Sopenharmony_ci	u64 sum_softirq = 0;
8962306a36Sopenharmony_ci	unsigned int per_softirq_sums[NR_SOFTIRQS] = {0};
9062306a36Sopenharmony_ci	struct timespec64 boottime;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	user = nice = system = idle = iowait =
9362306a36Sopenharmony_ci		irq = softirq = steal = 0;
9462306a36Sopenharmony_ci	guest = guest_nice = 0;
9562306a36Sopenharmony_ci	getboottime64(&boottime);
9662306a36Sopenharmony_ci	/* shift boot timestamp according to the timens offset */
9762306a36Sopenharmony_ci	timens_sub_boottime(&boottime);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	for_each_possible_cpu(i) {
10062306a36Sopenharmony_ci		struct kernel_cpustat kcpustat;
10162306a36Sopenharmony_ci		u64 *cpustat = kcpustat.cpustat;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		kcpustat_cpu_fetch(&kcpustat, i);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci		user		+= cpustat[CPUTIME_USER];
10662306a36Sopenharmony_ci		nice		+= cpustat[CPUTIME_NICE];
10762306a36Sopenharmony_ci		system		+= cpustat[CPUTIME_SYSTEM];
10862306a36Sopenharmony_ci		idle		+= get_idle_time(&kcpustat, i);
10962306a36Sopenharmony_ci		iowait		+= get_iowait_time(&kcpustat, i);
11062306a36Sopenharmony_ci		irq		+= cpustat[CPUTIME_IRQ];
11162306a36Sopenharmony_ci		softirq		+= cpustat[CPUTIME_SOFTIRQ];
11262306a36Sopenharmony_ci		steal		+= cpustat[CPUTIME_STEAL];
11362306a36Sopenharmony_ci		guest		+= cpustat[CPUTIME_GUEST];
11462306a36Sopenharmony_ci		guest_nice	+= cpustat[CPUTIME_GUEST_NICE];
11562306a36Sopenharmony_ci		sum		+= kstat_cpu_irqs_sum(i);
11662306a36Sopenharmony_ci		sum		+= arch_irq_stat_cpu(i);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci		for (j = 0; j < NR_SOFTIRQS; j++) {
11962306a36Sopenharmony_ci			unsigned int softirq_stat = kstat_softirqs_cpu(j, i);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci			per_softirq_sums[j] += softirq_stat;
12262306a36Sopenharmony_ci			sum_softirq += softirq_stat;
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci	sum += arch_irq_stat();
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	seq_put_decimal_ull(p, "cpu  ", nsec_to_clock_t(user));
12862306a36Sopenharmony_ci	seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice));
12962306a36Sopenharmony_ci	seq_put_decimal_ull(p, " ", nsec_to_clock_t(system));
13062306a36Sopenharmony_ci	seq_put_decimal_ull(p, " ", nsec_to_clock_t(idle));
13162306a36Sopenharmony_ci	seq_put_decimal_ull(p, " ", nsec_to_clock_t(iowait));
13262306a36Sopenharmony_ci	seq_put_decimal_ull(p, " ", nsec_to_clock_t(irq));
13362306a36Sopenharmony_ci	seq_put_decimal_ull(p, " ", nsec_to_clock_t(softirq));
13462306a36Sopenharmony_ci	seq_put_decimal_ull(p, " ", nsec_to_clock_t(steal));
13562306a36Sopenharmony_ci	seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest));
13662306a36Sopenharmony_ci	seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest_nice));
13762306a36Sopenharmony_ci	seq_putc(p, '\n');
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	for_each_online_cpu(i) {
14062306a36Sopenharmony_ci		struct kernel_cpustat kcpustat;
14162306a36Sopenharmony_ci		u64 *cpustat = kcpustat.cpustat;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		kcpustat_cpu_fetch(&kcpustat, i);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		/* Copy values here to work around gcc-2.95.3, gcc-2.96 */
14662306a36Sopenharmony_ci		user		= cpustat[CPUTIME_USER];
14762306a36Sopenharmony_ci		nice		= cpustat[CPUTIME_NICE];
14862306a36Sopenharmony_ci		system		= cpustat[CPUTIME_SYSTEM];
14962306a36Sopenharmony_ci		idle		= get_idle_time(&kcpustat, i);
15062306a36Sopenharmony_ci		iowait		= get_iowait_time(&kcpustat, i);
15162306a36Sopenharmony_ci		irq		= cpustat[CPUTIME_IRQ];
15262306a36Sopenharmony_ci		softirq		= cpustat[CPUTIME_SOFTIRQ];
15362306a36Sopenharmony_ci		steal		= cpustat[CPUTIME_STEAL];
15462306a36Sopenharmony_ci		guest		= cpustat[CPUTIME_GUEST];
15562306a36Sopenharmony_ci		guest_nice	= cpustat[CPUTIME_GUEST_NICE];
15662306a36Sopenharmony_ci		seq_printf(p, "cpu%d", i);
15762306a36Sopenharmony_ci		seq_put_decimal_ull(p, " ", nsec_to_clock_t(user));
15862306a36Sopenharmony_ci		seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice));
15962306a36Sopenharmony_ci		seq_put_decimal_ull(p, " ", nsec_to_clock_t(system));
16062306a36Sopenharmony_ci		seq_put_decimal_ull(p, " ", nsec_to_clock_t(idle));
16162306a36Sopenharmony_ci		seq_put_decimal_ull(p, " ", nsec_to_clock_t(iowait));
16262306a36Sopenharmony_ci		seq_put_decimal_ull(p, " ", nsec_to_clock_t(irq));
16362306a36Sopenharmony_ci		seq_put_decimal_ull(p, " ", nsec_to_clock_t(softirq));
16462306a36Sopenharmony_ci		seq_put_decimal_ull(p, " ", nsec_to_clock_t(steal));
16562306a36Sopenharmony_ci		seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest));
16662306a36Sopenharmony_ci		seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest_nice));
16762306a36Sopenharmony_ci		seq_putc(p, '\n');
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci	seq_put_decimal_ull(p, "intr ", (unsigned long long)sum);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	show_all_irqs(p);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	seq_printf(p,
17462306a36Sopenharmony_ci		"\nctxt %llu\n"
17562306a36Sopenharmony_ci		"btime %llu\n"
17662306a36Sopenharmony_ci		"processes %lu\n"
17762306a36Sopenharmony_ci		"procs_running %u\n"
17862306a36Sopenharmony_ci		"procs_blocked %u\n",
17962306a36Sopenharmony_ci		nr_context_switches(),
18062306a36Sopenharmony_ci		(unsigned long long)boottime.tv_sec,
18162306a36Sopenharmony_ci		total_forks,
18262306a36Sopenharmony_ci		nr_running(),
18362306a36Sopenharmony_ci		nr_iowait());
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	seq_put_decimal_ull(p, "softirq ", (unsigned long long)sum_softirq);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	for (i = 0; i < NR_SOFTIRQS; i++)
18862306a36Sopenharmony_ci		seq_put_decimal_ull(p, " ", per_softirq_sums[i]);
18962306a36Sopenharmony_ci	seq_putc(p, '\n');
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return 0;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic int stat_open(struct inode *inode, struct file *file)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	unsigned int size = 1024 + 128 * num_online_cpus();
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/* minimum size to display an interrupt count : 2 bytes */
19962306a36Sopenharmony_ci	size += 2 * nr_irqs;
20062306a36Sopenharmony_ci	return single_open_size(file, show_stat, NULL, size);
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic const struct proc_ops stat_proc_ops = {
20462306a36Sopenharmony_ci	.proc_flags	= PROC_ENTRY_PERMANENT,
20562306a36Sopenharmony_ci	.proc_open	= stat_open,
20662306a36Sopenharmony_ci	.proc_read_iter	= seq_read_iter,
20762306a36Sopenharmony_ci	.proc_lseek	= seq_lseek,
20862306a36Sopenharmony_ci	.proc_release	= single_release,
20962306a36Sopenharmony_ci};
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic int __init proc_stat_init(void)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	proc_create("stat", 0, NULL, &stat_proc_ops);
21462306a36Sopenharmony_ci	return 0;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_cifs_initcall(proc_stat_init);
217