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