blob: 4f5017109cbe6ea0cde7348250e0e7543646657a [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 <asm/percpu.h> */
7#include <trace/events/sched.h>
8#include <linux/module.h>
9#include <trace/events/irq.h>
10#include <trace/events/power.h>
11
12#include "interface.h"
13#include "met_drv.h"
14#include "cpu_pmu.h"
15#include "switch.h"
16#include "sampler.h"
17#include "met_kernel_symbol.h"
18/* #include "trace.h" */
19
20/*
21 * IRQ_TIRGGER and CPU_IDLE_TRIGGER
22 */
23/* #define IRQ_TRIGGER */
24/* #define CPU_IDLE_TRIGGER */
25
26static DEFINE_PER_CPU(unsigned int, first_log);
27
28#ifdef __aarch64__
29/* #include <asm/compat.h> */
30#include <linux/compat.h>
31#endif
32
33noinline void mt_switch(struct task_struct *prev, struct task_struct *next)
34{
35 int cpu;
36 int prev_state = 0, next_state = 0;
37
38#ifdef __aarch64__
39 prev_state = !(is_compat_thread(task_thread_info(prev)));
40 next_state = !(is_compat_thread(task_thread_info(next)));
41#endif
42
43 cpu = smp_processor_id();
44 if (per_cpu(first_log, cpu)) {
45 MET_TRACE("%d, %d, %d, %d\n", prev->pid, prev_state, next->pid, next_state);
46 per_cpu(first_log, cpu) = 0;
47 }
48 else if (prev_state != next_state)
49 MET_TRACE("%d, %d, %d, %d\n", prev->pid, prev_state, next->pid, next_state);
50}
51
52
53#if 0 /* move to kernel space */
54MET_DEFINE_PROBE(sched_switch,
55 TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next))
56{
57 /* speedup sched_switch callback handle */
58 if (met_switch.mode == 0)
59 return;
60
61 if (met_switch.mode & MT_SWITCH_EVENT_TIMER)
62 met_event_timer_notify();
63
64 if (met_switch.mode & MT_SWITCH_64_32BIT)
65 mt_switch(prev, next);
66
67 if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
68 if (get_pmu_profiling_version() == 1)
69 cpupmu_polling(0, smp_processor_id());
70#ifdef MET_SUPPORT_CPUPMU_V2
71 else if (get_pmu_profiling_version() == 2)
72 cpupmu_polling_v2(0, smp_processor_id());
73#endif
74 }
75}
76#endif
77
78void met_sched_switch(struct task_struct *prev, struct task_struct *next)
79{
80 /* speedup sched_switch callback handle */
81 if (met_switch.mode == 0)
82 return;
83
84 if (met_switch.mode & MT_SWITCH_EVENT_TIMER)
85 met_event_timer_notify();
86
87 if (met_switch.mode & MT_SWITCH_64_32BIT)
88 mt_switch(prev, next);
89
90 /* met_perf_cpupmu_status: 0: stop, others: polling */
91 if ((met_switch.mode & MT_SWITCH_SCHEDSWITCH) && met_perf_cpupmu_status)
92 met_perf_cpupmu_polling(0, smp_processor_id());
93}
94
95#ifdef IRQ_TRIGGER
96MET_DEFINE_PROBE(irq_handler_entry, TP_PROTO(int irq, struct irqaction *action))
97{
98 if (met_switch.mode & MT_SWITCH_EVENT_TIMER) {
99 met_event_timer_notify();
100 return;
101 }
102}
103#endif
104
105#ifdef CPU_IDLE_TRIGGER
106MET_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu_id))
107{
108 if (met_switch.mode & MT_SWITCH_EVENT_TIMER) {
109 met_event_timer_notify();
110 return;
111 }
112}
113#endif
114
115#ifdef MET_ANYTIME
116/*
117 * create related subfs file node
118 */
119
120static ssize_t default_on_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
121{
122 return snprintf(buf, PAGE_SIZE, "1\n");
123}
124
125static struct kobj_attribute default_on_attr = __ATTR(default_on, 0664, default_on_show, NULL);
126static struct kobject *kobj_cpu;
127#endif
128
129static int met_switch_create_subfs(struct kobject *parent)
130{
131 int ret = 0;
132
133 /* register tracepoints */
134#if 0
135 if (MET_REGISTER_TRACE(sched_switch)) {
136 pr_debug("can not register callback of sched_switch\n");
137 return -ENODEV;
138 }
139#else
140 if (met_export_api_symbol->met_reg_switch)
141 ret = met_export_api_symbol->met_reg_switch();
142#endif
143#ifdef CPU_IDLE_TRIGGER
144 if (MET_REGISTER_TRACE(cpu_idle)) {
145 pr_debug("can not register callback of irq_handler_entry\n");
146 return -ENODEV;
147 }
148#endif
149#ifdef IRQ_TRIGGER
150 if (MET_REGISTER_TRACE(irq_handler_entry)) {
151 pr_debug("can not register callback of irq_handler_entry\n");
152 return -ENODEV;
153 }
154#endif
155
156#ifdef MET_ANYTIME
157 /*
158 * to create default_on file node
159 * let user space can know we can support MET default on
160 */
161 kobj_cpu = parent;
162 ret = sysfs_create_file(kobj_cpu, &default_on_attr.attr);
163 if (ret != 0) {
164 pr_debug("Failed to create default_on in sysfs\n");
165 return -1;
166 }
167#endif
168
169 return ret;
170}
171
172
173static void met_switch_delete_subfs(void)
174{
175#ifdef MET_ANYTIME
176 if (kobj_cpu != NULL) {
177 sysfs_remove_file(kobj_cpu, &default_on_attr.attr);
178 kobj_cpu = NULL;
179 }
180#endif
181#ifdef IRQ_TRIGGER
182 MET_UNREGISTER_TRACE(irq_handler_entry);
183#endif
184#ifdef CPU_IDLE_TRIGGER
185 MET_UNREGISTER_TRACE(cpu_idle);
186#endif
187#if 0
188 MET_UNREGISTER_TRACE(sched_switch);
189#else
190 if (met_export_api_symbol->met_unreg_switch)
191 met_export_api_symbol->met_unreg_switch();
192#endif
193
194}
195
196
197static void (*cpu_timed_polling)(unsigned long long stamp, int cpu);
198/* static void (*cpu_tagged_polling)(unsigned long long stamp, int cpu); */
199
200static void met_switch_start(void)
201{
202 int cpu;
203
204 if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
205 cpu_timed_polling = met_cpupmu.timed_polling;
206 /* cpu_tagged_polling = met_cpupmu.tagged_polling; */
207 met_cpupmu.timed_polling = NULL;
208 /* met_cpupmu.tagged_polling = NULL; */
209 }
210
211 for_each_possible_cpu(cpu) {
212 per_cpu(first_log, cpu) = 1;
213 }
214
215}
216
217
218static void met_switch_stop(void)
219{
220 int cpu;
221
222 if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
223 met_cpupmu.timed_polling = cpu_timed_polling;
224 /* met_cpupmu.tagged_polling = cpu_tagged_polling; */
225 }
226
227 for_each_possible_cpu(cpu) {
228 per_cpu(first_log, cpu) = 0;
229 }
230
231}
232
233
234static int met_switch_process_argument(const char *arg, int len)
235{
236 unsigned int value = 0;
237 /*ex: mxitem is 0x0005, max value should be (5-1) + (5-2) = 0x100 + 0x11 = 7 */
238 unsigned int max_value = ((MT_SWITCH_MX_ITEM * 2) - 3);
239
240
241 if (met_parse_num(arg, &value, len) < 0)
242 goto arg_switch_exit;
243
244 if ((value < 1) || (value > max_value))
245 goto arg_switch_exit;
246
247 met_switch.mode = value;
248 return 0;
249
250arg_switch_exit:
251 met_switch.mode = 0;
252 return -EINVAL;
253}
254
255static const char header[] =
256 "met-info [000] 0.0: met_switch_header: prev_pid,prev_state,next_pid,next_state\n";
257
258static const char help[] =
259" --switch=mode mode:0x1 - output CPUPMU whenever sched_switch\n"
260" mode:0x2 - output Aarch 32/64 state whenever state changed (no CPUPMU)\n"
261" mode:0x4 - force output count at tag_start/tag_end\n"
262" mode:0x8 - task switch timer\n"
263" mode:0xF - mode 0x1 + 0x2 + 04 + 08\n";
264
265static int met_switch_print_help(char *buf, int len)
266{
267 return snprintf(buf, PAGE_SIZE, help);
268}
269
270static int met_switch_print_header(char *buf, int len)
271{
272 int ret = 0;
273
274 ret =
275 snprintf(buf, PAGE_SIZE, "met-info [000] 0.0: mp_cpu_switch_base: %d\n",
276 met_switch.mode);
277 if (met_switch.mode & MT_SWITCH_64_32BIT)
278 ret += snprintf(buf + ret, PAGE_SIZE, header);
279
280 return ret;
281}
282
283
284struct metdevice met_switch = {
285 .name = "switch",
286 .type = MET_TYPE_PMU,
287 .create_subfs = met_switch_create_subfs,
288 .delete_subfs = met_switch_delete_subfs,
289 .start = met_switch_start,
290 .stop = met_switch_stop,
291 .process_argument = met_switch_process_argument,
292 .print_help = met_switch_print_help,
293 .print_header = met_switch_print_header,
294};