blob: 22817e4945cf39b9c77915ac805dadaef3d46b74 [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2019 MediaTek Inc.
4 */
5
6#include <linux/cpu.h>
7#include <linux/cpumask.h>
8/* #include <linux/fs.h> */
9#include <linux/init.h>
10#include <linux/version.h>
11#include <linux/interrupt.h>
12#include <linux/kernel_stat.h>
13/* #include <linux/proc_fs.h> */
14#include <linux/sched.h>
15/* #include <linux/seq_file.h> */
16/* #include <linux/slab.h> */
17#include <linux/time.h>
18#include <linux/irqnr.h>
19#include <linux/vmalloc.h>
20/* #include <asm/cputime.h> */
21/* #include <asm-generic/cputime.h> */
22#include <linux/tick.h>
23#include <linux/jiffies.h>
24
25#include <asm/page.h>
26#include <linux/slab.h>
27
28#include "stat.h"
29#include "met_drv.h"
30#include "trace.h"
31
32#define MS_STAT_FMT "%5lu.%06lu"
33#define FMTLX7 ",%llx,%llx,%llx,%llx,%llx,%llx,%llx\n"
34#define FMTLX10 ",%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx\n"
35
36
37static DEFINE_PER_CPU(int, cpu_status);
38
39
40/* void ms_st(unsigned long long timestamp, unsigned char cnt, unsigned int *value) */
41noinline void ms_st(unsigned long long timestamp, unsigned char cnt, u64 *value)
42{
43 unsigned long nano_rem = do_div(timestamp, 1000000000);
44
45 switch (cnt) {
46 case 10:
47 MET_TRACE(MS_STAT_FMT FMTLX10, (unsigned long)(timestamp), (nano_rem/1000),
48 value[0], value[1], value[2], value[3], value[4],
49 value[5], value[6], value[7], value[8], value[9]);
50 break;
51 case 7:
52 MET_TRACE(MS_STAT_FMT FMTLX7, (unsigned long)(timestamp), (nano_rem/1000),
53 value[0], value[1], value[2], value[3], value[4],
54 value[5], value[6]);
55 break;
56 }
57}
58
59static void met_stat_start(void)
60{
61 int cpu = raw_smp_processor_id();
62
63 if (get_ctrl_flags() & 1)
64 met_stat.mode = 0;
65
66 per_cpu(cpu_status, cpu) = MET_CPU_ONLINE;
67}
68
69static void met_stat_stop(void)
70{
71}
72
73static int do_stat(void)
74{
75 return met_stat.mode;
76}
77
78u64 met_usecs_to_cputime64(u64 n)
79{
80#if (NSEC_PER_SEC % HZ) == 0
81 /* Common case, HZ = 100, 128, 200, 250, 256, 500, 512, 1000 etc. */
82 return div_u64(n, NSEC_PER_SEC / HZ);
83#elif (HZ % 512) == 0
84 /* overflow after 292 years if HZ = 1024 */
85 return div_u64(n * HZ / 512, NSEC_PER_SEC / 512);
86#else
87 /*
88 * Generic case - optimized for cases where HZ is a multiple of 3.
89 * overflow after 64.99 years, exact for HZ = 60, 72, 90, 120 etc.
90 */
91 return div_u64(n * 9, (9ull * NSEC_PER_SEC + HZ / 2) / HZ);
92#endif
93}
94
95static u64 get_idle_time(int cpu)
96{
97 u64 idle, idle_time = get_cpu_idle_time_us(cpu, NULL);
98
99 if (idle_time == -1ULL) {
100 /* !NO_HZ so we can rely on cpustat.idle */
101 idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE];
102 } else
103 idle = met_usecs_to_cputime64(idle_time);
104
105 return idle;
106}
107
108static u64 get_iowait_time(int cpu)
109{
110 u64 iowait, iowait_time = get_cpu_iowait_time_us(cpu, NULL);
111
112 if (iowait_time == -1ULL) {
113 /* !NO_HZ so we can rely on cpustat.iowait */
114 iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT];
115 } else
116 iowait = met_usecs_to_cputime64(iowait_time);
117
118 return iowait;
119}
120
121
122static unsigned int stat_os_polling(u64 *value, int i)
123{
124 int j = -1;
125
126 /* Copy values here to work around gcc-2.95.3, gcc-2.96 */
127 value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_USER]); /* user */
128 value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_NICE]); /* nice */
129 value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]); /* system */
130 value[++j] = jiffies_64_to_clock_t(get_idle_time(i)); /* idle */
131 value[++j] = jiffies_64_to_clock_t(get_iowait_time(i)); /* iowait */
132 value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_IRQ]); /* irq */
133 value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]); /* softirq */
134 value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_STEAL]); /* steal */
135 value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_GUEST]); /* guest */
136 value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE]); /* guest_nice */
137
138 return j + 1;
139}
140
141static void met_stat_polling(unsigned long long stamp, int cpu)
142{
143 unsigned char count;
144 u64 value[10] = {0};
145
146 if (per_cpu(cpu_status, cpu) != MET_CPU_ONLINE)
147 return;
148
149 /* return; */
150 if (do_stat()) {
151 count = stat_os_polling(value, cpu);
152 if (count)
153 ms_st(stamp, count, value);
154 }
155}
156
157static const char header[] =
158 "met-info [000] 0.0: met_st_header: user,nice,system,idle,iowait,irq,softirq,steal,guest,guest_nice\n";
159
160static const char help[] = " --stat monitor stat\n";
161
162
163static int met_stat_print_help(char *buf, int len)
164{
165 return snprintf(buf, PAGE_SIZE, help);
166}
167
168static int met_stat_print_header(char *buf, int len)
169{
170 return snprintf(buf, PAGE_SIZE, header);
171}
172
173static void met_stat_cpu_state_notify(long cpu, unsigned long action)
174{
175 per_cpu(cpu_status, cpu) = action;
176}
177
178
179struct metdevice met_stat = {
180 .name = "stat",
181 .type = MET_TYPE_PMU,
182 .cpu_related = 1,
183 .start = met_stat_start,
184 .stop = met_stat_stop,
185 .polling_interval = 30,
186 .timed_polling = met_stat_polling,
187 .print_help = met_stat_print_help,
188 .print_header = met_stat_print_header,
189 .cpu_state_notify = met_stat_cpu_state_notify,
190};