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 10 #define MODULE_NAME "opid" 11 12 #include <trace/events/sched.h> 13 #include <rv_trace.h> 14 #include <monitors/sched/sched.h> 15 16 #define RV_MON_TYPE RV_MON_PER_CPU 17 #include "opid.h" 18 #include <rv/ha_monitor.h> 19 20 static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs_opid env, u64 time_ns) 21 { 22 if (env == irq_off_opid) 23 return irqs_disabled(); 24 else if (env == preempt_off_opid) { 25 /* 26 * If CONFIG_PREEMPTION is enabled, then the tracepoint itself disables 27 * preemption (adding one to the preempt_count). Since we are 28 * interested in the preempt_count at the time the tracepoint was 29 * hit, we consider 1 as still enabled. 30 */ 31 if (IS_ENABLED(CONFIG_PREEMPTION)) 32 return (preempt_count() & PREEMPT_MASK) > 1; 33 return true; 34 } 35 return ENV_INVALID_VALUE; 36 } 37 38 static inline bool ha_verify_guards(struct ha_monitor *ha_mon, 39 enum states curr_state, enum events event, 40 enum states next_state, u64 time_ns) 41 { 42 bool res = true; 43 44 if (curr_state == any_opid && event == sched_need_resched_opid) 45 res = ha_get_env(ha_mon, irq_off_opid, time_ns) == 1ull; 46 else if (curr_state == any_opid && event == sched_waking_opid) 47 res = ha_get_env(ha_mon, irq_off_opid, time_ns) == 1ull && 48 ha_get_env(ha_mon, preempt_off_opid, time_ns) == 1ull; 49 return res; 50 } 51 52 static bool ha_verify_constraint(struct ha_monitor *ha_mon, 53 enum states curr_state, enum events event, 54 enum states next_state, u64 time_ns) 55 { 56 if (!ha_verify_guards(ha_mon, curr_state, event, next_state, time_ns)) 57 return false; 58 59 return true; 60 } 61 62 static void handle_sched_need_resched(void *data, struct task_struct *tsk, int cpu, int tif) 63 { 64 da_handle_start_run_event(sched_need_resched_opid); 65 } 66 67 static void handle_sched_waking(void *data, struct task_struct *p) 68 { 69 da_handle_start_run_event(sched_waking_opid); 70 } 71 72 static int enable_opid(void) 73 { 74 int retval; 75 76 retval = da_monitor_init(); 77 if (retval) 78 return retval; 79 80 rv_attach_trace_probe("opid", sched_set_need_resched_tp, handle_sched_need_resched); 81 rv_attach_trace_probe("opid", sched_waking, handle_sched_waking); 82 83 return 0; 84 } 85 86 static void disable_opid(void) 87 { 88 rv_this.enabled = 0; 89 90 rv_detach_trace_probe("opid", sched_set_need_resched_tp, handle_sched_need_resched); 91 rv_detach_trace_probe("opid", sched_waking, handle_sched_waking); 92 93 da_monitor_destroy(); 94 } 95 96 /* 97 * This is the monitor register section. 98 */ 99 static struct rv_monitor rv_this = { 100 .name = "opid", 101 .description = "operations with preemption and irq disabled.", 102 .enable = enable_opid, 103 .disable = disable_opid, 104 .reset = da_monitor_reset_all, 105 .enabled = 0, 106 }; 107 108 static int __init register_opid(void) 109 { 110 return rv_register_monitor(&rv_this, &rv_sched); 111 } 112 113 static void __exit unregister_opid(void) 114 { 115 rv_unregister_monitor(&rv_this); 116 } 117 118 module_init(register_opid); 119 module_exit(unregister_opid); 120 121 MODULE_LICENSE("GPL"); 122 MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>"); 123 MODULE_DESCRIPTION("opid: operations with preemption and irq disabled."); 124