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