18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/cpumask.h> 38c2ecf20Sopenharmony_ci#include <linux/fs.h> 48c2ecf20Sopenharmony_ci#include <linux/init.h> 58c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 68c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h> 78c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 88c2ecf20Sopenharmony_ci#include <linux/sched.h> 98c2ecf20Sopenharmony_ci#include <linux/sched/stat.h> 108c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/time.h> 138c2ecf20Sopenharmony_ci#include <linux/irqnr.h> 148c2ecf20Sopenharmony_ci#include <linux/sched/cputime.h> 158c2ecf20Sopenharmony_ci#include <linux/tick.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#ifndef arch_irq_stat_cpu 188c2ecf20Sopenharmony_ci#define arch_irq_stat_cpu(cpu) 0 198c2ecf20Sopenharmony_ci#endif 208c2ecf20Sopenharmony_ci#ifndef arch_irq_stat 218c2ecf20Sopenharmony_ci#define arch_irq_stat() 0 228c2ecf20Sopenharmony_ci#endif 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#ifdef arch_idle_time 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciu64 get_idle_time(struct kernel_cpustat *kcs, int cpu) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci u64 idle; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci idle = kcs->cpustat[CPUTIME_IDLE]; 318c2ecf20Sopenharmony_ci if (cpu_online(cpu) && !nr_iowait_cpu(cpu)) 328c2ecf20Sopenharmony_ci idle += arch_idle_time(cpu); 338c2ecf20Sopenharmony_ci return idle; 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic u64 get_iowait_time(struct kernel_cpustat *kcs, int cpu) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci u64 iowait; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci iowait = kcs->cpustat[CPUTIME_IOWAIT]; 418c2ecf20Sopenharmony_ci if (cpu_online(cpu) && nr_iowait_cpu(cpu)) 428c2ecf20Sopenharmony_ci iowait += arch_idle_time(cpu); 438c2ecf20Sopenharmony_ci return iowait; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#else 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ciu64 get_idle_time(struct kernel_cpustat *kcs, int cpu) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci u64 idle, idle_usecs = -1ULL; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (cpu_online(cpu)) 538c2ecf20Sopenharmony_ci idle_usecs = get_cpu_idle_time_us(cpu, NULL); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (idle_usecs == -1ULL) 568c2ecf20Sopenharmony_ci /* !NO_HZ or cpu offline so we can rely on cpustat.idle */ 578c2ecf20Sopenharmony_ci idle = kcs->cpustat[CPUTIME_IDLE]; 588c2ecf20Sopenharmony_ci else 598c2ecf20Sopenharmony_ci idle = idle_usecs * NSEC_PER_USEC; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return idle; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic u64 get_iowait_time(struct kernel_cpustat *kcs, int cpu) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci u64 iowait, iowait_usecs = -1ULL; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (cpu_online(cpu)) 698c2ecf20Sopenharmony_ci iowait_usecs = get_cpu_iowait_time_us(cpu, NULL); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (iowait_usecs == -1ULL) 728c2ecf20Sopenharmony_ci /* !NO_HZ or cpu offline so we can rely on cpustat.iowait */ 738c2ecf20Sopenharmony_ci iowait = kcs->cpustat[CPUTIME_IOWAIT]; 748c2ecf20Sopenharmony_ci else 758c2ecf20Sopenharmony_ci iowait = iowait_usecs * NSEC_PER_USEC; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return iowait; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#endif 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic void show_irq_gap(struct seq_file *p, unsigned int gap) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci static const char zeros[] = " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci while (gap > 0) { 878c2ecf20Sopenharmony_ci unsigned int inc; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci inc = min_t(unsigned int, gap, ARRAY_SIZE(zeros) / 2); 908c2ecf20Sopenharmony_ci seq_write(p, zeros, 2 * inc); 918c2ecf20Sopenharmony_ci gap -= inc; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic void show_all_irqs(struct seq_file *p) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci unsigned int i, next = 0; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci for_each_active_irq(i) { 1008c2ecf20Sopenharmony_ci show_irq_gap(p, i - next); 1018c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", kstat_irqs_usr(i)); 1028c2ecf20Sopenharmony_ci next = i + 1; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci show_irq_gap(p, nr_irqs - next); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int show_stat(struct seq_file *p, void *v) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci int i, j; 1108c2ecf20Sopenharmony_ci u64 user, nice, system, idle, iowait, irq, softirq, steal; 1118c2ecf20Sopenharmony_ci u64 guest, guest_nice; 1128c2ecf20Sopenharmony_ci u64 sum = 0; 1138c2ecf20Sopenharmony_ci u64 sum_softirq = 0; 1148c2ecf20Sopenharmony_ci unsigned int per_softirq_sums[NR_SOFTIRQS] = {0}; 1158c2ecf20Sopenharmony_ci struct timespec64 boottime; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci user = nice = system = idle = iowait = 1188c2ecf20Sopenharmony_ci irq = softirq = steal = 0; 1198c2ecf20Sopenharmony_ci guest = guest_nice = 0; 1208c2ecf20Sopenharmony_ci getboottime64(&boottime); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 1238c2ecf20Sopenharmony_ci struct kernel_cpustat kcpustat; 1248c2ecf20Sopenharmony_ci u64 *cpustat = kcpustat.cpustat; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci kcpustat_cpu_fetch(&kcpustat, i); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci user += cpustat[CPUTIME_USER]; 1298c2ecf20Sopenharmony_ci nice += cpustat[CPUTIME_NICE]; 1308c2ecf20Sopenharmony_ci system += cpustat[CPUTIME_SYSTEM]; 1318c2ecf20Sopenharmony_ci idle += get_idle_time(&kcpustat, i); 1328c2ecf20Sopenharmony_ci iowait += get_iowait_time(&kcpustat, i); 1338c2ecf20Sopenharmony_ci irq += cpustat[CPUTIME_IRQ]; 1348c2ecf20Sopenharmony_ci softirq += cpustat[CPUTIME_SOFTIRQ]; 1358c2ecf20Sopenharmony_ci steal += cpustat[CPUTIME_STEAL]; 1368c2ecf20Sopenharmony_ci guest += cpustat[CPUTIME_GUEST]; 1378c2ecf20Sopenharmony_ci guest_nice += cpustat[CPUTIME_GUEST_NICE]; 1388c2ecf20Sopenharmony_ci sum += kstat_cpu_irqs_sum(i); 1398c2ecf20Sopenharmony_ci sum += arch_irq_stat_cpu(i); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci for (j = 0; j < NR_SOFTIRQS; j++) { 1428c2ecf20Sopenharmony_ci unsigned int softirq_stat = kstat_softirqs_cpu(j, i); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci per_softirq_sums[j] += softirq_stat; 1458c2ecf20Sopenharmony_ci sum_softirq += softirq_stat; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci sum += arch_irq_stat(); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, "cpu ", nsec_to_clock_t(user)); 1518c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice)); 1528c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(system)); 1538c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(idle)); 1548c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(iowait)); 1558c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(irq)); 1568c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(softirq)); 1578c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(steal)); 1588c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest)); 1598c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest_nice)); 1608c2ecf20Sopenharmony_ci seq_putc(p, '\n'); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci for_each_online_cpu(i) { 1638c2ecf20Sopenharmony_ci struct kernel_cpustat kcpustat; 1648c2ecf20Sopenharmony_ci u64 *cpustat = kcpustat.cpustat; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci kcpustat_cpu_fetch(&kcpustat, i); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* Copy values here to work around gcc-2.95.3, gcc-2.96 */ 1698c2ecf20Sopenharmony_ci user = cpustat[CPUTIME_USER]; 1708c2ecf20Sopenharmony_ci nice = cpustat[CPUTIME_NICE]; 1718c2ecf20Sopenharmony_ci system = cpustat[CPUTIME_SYSTEM]; 1728c2ecf20Sopenharmony_ci idle = get_idle_time(&kcpustat, i); 1738c2ecf20Sopenharmony_ci iowait = get_iowait_time(&kcpustat, i); 1748c2ecf20Sopenharmony_ci irq = cpustat[CPUTIME_IRQ]; 1758c2ecf20Sopenharmony_ci softirq = cpustat[CPUTIME_SOFTIRQ]; 1768c2ecf20Sopenharmony_ci steal = cpustat[CPUTIME_STEAL]; 1778c2ecf20Sopenharmony_ci guest = cpustat[CPUTIME_GUEST]; 1788c2ecf20Sopenharmony_ci guest_nice = cpustat[CPUTIME_GUEST_NICE]; 1798c2ecf20Sopenharmony_ci seq_printf(p, "cpu%d", i); 1808c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(user)); 1818c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice)); 1828c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(system)); 1838c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(idle)); 1848c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(iowait)); 1858c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(irq)); 1868c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(softirq)); 1878c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(steal)); 1888c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest)); 1898c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest_nice)); 1908c2ecf20Sopenharmony_ci seq_putc(p, '\n'); 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, "intr ", (unsigned long long)sum); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci show_all_irqs(p); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci seq_printf(p, 1978c2ecf20Sopenharmony_ci "\nctxt %llu\n" 1988c2ecf20Sopenharmony_ci "btime %llu\n" 1998c2ecf20Sopenharmony_ci "processes %lu\n" 2008c2ecf20Sopenharmony_ci "procs_running %lu\n" 2018c2ecf20Sopenharmony_ci "procs_blocked %lu\n", 2028c2ecf20Sopenharmony_ci nr_context_switches(), 2038c2ecf20Sopenharmony_ci (unsigned long long)boottime.tv_sec, 2048c2ecf20Sopenharmony_ci total_forks, 2058c2ecf20Sopenharmony_ci nr_running(), 2068c2ecf20Sopenharmony_ci nr_iowait()); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, "softirq ", (unsigned long long)sum_softirq); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci for (i = 0; i < NR_SOFTIRQS; i++) 2118c2ecf20Sopenharmony_ci seq_put_decimal_ull(p, " ", per_softirq_sums[i]); 2128c2ecf20Sopenharmony_ci seq_putc(p, '\n'); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int stat_open(struct inode *inode, struct file *file) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci unsigned int size = 1024 + 128 * num_online_cpus(); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* minimum size to display an interrupt count : 2 bytes */ 2228c2ecf20Sopenharmony_ci size += 2 * nr_irqs; 2238c2ecf20Sopenharmony_ci return single_open_size(file, show_stat, NULL, size); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic const struct proc_ops stat_proc_ops = { 2278c2ecf20Sopenharmony_ci .proc_flags = PROC_ENTRY_PERMANENT, 2288c2ecf20Sopenharmony_ci .proc_open = stat_open, 2298c2ecf20Sopenharmony_ci .proc_read_iter = seq_read_iter, 2308c2ecf20Sopenharmony_ci .proc_lseek = seq_lseek, 2318c2ecf20Sopenharmony_ci .proc_release = single_release, 2328c2ecf20Sopenharmony_ci}; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int __init proc_stat_init(void) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci proc_create("stat", 0, NULL, &stat_proc_ops); 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_cifs_initcall(proc_stat_init); 240