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