1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/ftrace.h>
3 #include <linux/tracepoint.h>
4 #include <linux/kernel.h>
5 #include <linux/module.h>
6 #include <linux/init.h>
7 #include <linux/rv.h>
8 #include <rv/instrumentation.h>
9 #include <rv/da_monitor.h>
10
11 #define MODULE_NAME "sts"
12
13 #include <trace/events/sched.h>
14 #include <trace/events/irq.h>
15 #include <trace/events/preemptirq.h>
16 #include <rv_trace.h>
17 #include <monitors/sched/sched.h>
18
19 #include "sts.h"
20
21 static struct rv_monitor rv_sts;
22 DECLARE_DA_MON_PER_CPU(sts, unsigned char);
23
24 #ifdef CONFIG_X86_LOCAL_APIC
25 #include <asm/trace/irq_vectors.h>
26
handle_vector_irq_entry(void * data,int vector)27 static void handle_vector_irq_entry(void *data, int vector)
28 {
29 da_handle_event_sts(irq_entry_sts);
30 }
31
attach_vector_irq(void)32 static void attach_vector_irq(void)
33 {
34 rv_attach_trace_probe("sts", local_timer_entry, handle_vector_irq_entry);
35 if (IS_ENABLED(CONFIG_IRQ_WORK))
36 rv_attach_trace_probe("sts", irq_work_entry, handle_vector_irq_entry);
37 if (IS_ENABLED(CONFIG_SMP)) {
38 rv_attach_trace_probe("sts", reschedule_entry, handle_vector_irq_entry);
39 rv_attach_trace_probe("sts", call_function_entry, handle_vector_irq_entry);
40 rv_attach_trace_probe("sts", call_function_single_entry, handle_vector_irq_entry);
41 }
42 }
43
detach_vector_irq(void)44 static void detach_vector_irq(void)
45 {
46 rv_detach_trace_probe("sts", local_timer_entry, handle_vector_irq_entry);
47 if (IS_ENABLED(CONFIG_IRQ_WORK))
48 rv_detach_trace_probe("sts", irq_work_entry, handle_vector_irq_entry);
49 if (IS_ENABLED(CONFIG_SMP)) {
50 rv_detach_trace_probe("sts", reschedule_entry, handle_vector_irq_entry);
51 rv_detach_trace_probe("sts", call_function_entry, handle_vector_irq_entry);
52 rv_detach_trace_probe("sts", call_function_single_entry, handle_vector_irq_entry);
53 }
54 }
55
56 #else
57 /* We assume irq_entry tracepoints are sufficient on other architectures */
attach_vector_irq(void)58 static void attach_vector_irq(void) { }
detach_vector_irq(void)59 static void detach_vector_irq(void) { }
60 #endif
61
handle_irq_disable(void * data,unsigned long ip,unsigned long parent_ip)62 static void handle_irq_disable(void *data, unsigned long ip, unsigned long parent_ip)
63 {
64 da_handle_event_sts(irq_disable_sts);
65 }
66
handle_irq_enable(void * data,unsigned long ip,unsigned long parent_ip)67 static void handle_irq_enable(void *data, unsigned long ip, unsigned long parent_ip)
68 {
69 da_handle_event_sts(irq_enable_sts);
70 }
71
handle_irq_entry(void * data,int irq,struct irqaction * action)72 static void handle_irq_entry(void *data, int irq, struct irqaction *action)
73 {
74 da_handle_event_sts(irq_entry_sts);
75 }
76
handle_sched_switch(void * data,bool preempt,struct task_struct * prev,struct task_struct * next,unsigned int prev_state)77 static void handle_sched_switch(void *data, bool preempt,
78 struct task_struct *prev,
79 struct task_struct *next,
80 unsigned int prev_state)
81 {
82 da_handle_event_sts(sched_switch_sts);
83 }
84
handle_schedule_entry(void * data,bool preempt)85 static void handle_schedule_entry(void *data, bool preempt)
86 {
87 da_handle_event_sts(schedule_entry_sts);
88 }
89
handle_schedule_exit(void * data,bool is_switch)90 static void handle_schedule_exit(void *data, bool is_switch)
91 {
92 da_handle_start_event_sts(schedule_exit_sts);
93 }
94
enable_sts(void)95 static int enable_sts(void)
96 {
97 int retval;
98
99 retval = da_monitor_init_sts();
100 if (retval)
101 return retval;
102
103 rv_attach_trace_probe("sts", irq_disable, handle_irq_disable);
104 rv_attach_trace_probe("sts", irq_enable, handle_irq_enable);
105 rv_attach_trace_probe("sts", irq_handler_entry, handle_irq_entry);
106 rv_attach_trace_probe("sts", sched_switch, handle_sched_switch);
107 rv_attach_trace_probe("sts", sched_entry_tp, handle_schedule_entry);
108 rv_attach_trace_probe("sts", sched_exit_tp, handle_schedule_exit);
109 attach_vector_irq();
110
111 return 0;
112 }
113
disable_sts(void)114 static void disable_sts(void)
115 {
116 rv_sts.enabled = 0;
117
118 rv_detach_trace_probe("sts", irq_disable, handle_irq_disable);
119 rv_detach_trace_probe("sts", irq_enable, handle_irq_enable);
120 rv_detach_trace_probe("sts", irq_handler_entry, handle_irq_entry);
121 rv_detach_trace_probe("sts", sched_switch, handle_sched_switch);
122 rv_detach_trace_probe("sts", sched_entry_tp, handle_schedule_entry);
123 rv_detach_trace_probe("sts", sched_exit_tp, handle_schedule_exit);
124 detach_vector_irq();
125
126 da_monitor_destroy_sts();
127 }
128
129 /*
130 * This is the monitor register section.
131 */
132 static struct rv_monitor rv_sts = {
133 .name = "sts",
134 .description = "schedule implies task switch.",
135 .enable = enable_sts,
136 .disable = disable_sts,
137 .reset = da_monitor_reset_all_sts,
138 .enabled = 0,
139 };
140
register_sts(void)141 static int __init register_sts(void)
142 {
143 return rv_register_monitor(&rv_sts, &rv_sched);
144 }
145
unregister_sts(void)146 static void __exit unregister_sts(void)
147 {
148 rv_unregister_monitor(&rv_sts);
149 }
150
151 module_init(register_sts);
152 module_exit(unregister_sts);
153
154 MODULE_LICENSE("GPL");
155 MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
156 MODULE_DESCRIPTION("sts: schedule implies task switch.");
157