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