1*bce29ac9SDaniel Bristot de Oliveira // SPDX-License-Identifier: GPL-2.0 2*bce29ac9SDaniel Bristot de Oliveira /* 3*bce29ac9SDaniel Bristot de Oliveira * OS Noise Tracer: computes the OS Noise suffered by a running thread. 4*bce29ac9SDaniel Bristot de Oliveira * 5*bce29ac9SDaniel Bristot de Oliveira * Based on "hwlat_detector" tracer by: 6*bce29ac9SDaniel Bristot de Oliveira * Copyright (C) 2008-2009 Jon Masters, Red Hat, Inc. <jcm@redhat.com> 7*bce29ac9SDaniel Bristot de Oliveira * Copyright (C) 2013-2016 Steven Rostedt, Red Hat, Inc. <srostedt@redhat.com> 8*bce29ac9SDaniel Bristot de Oliveira * With feedback from Clark Williams <williams@redhat.com> 9*bce29ac9SDaniel Bristot de Oliveira * 10*bce29ac9SDaniel Bristot de Oliveira * And also based on the rtsl tracer presented on: 11*bce29ac9SDaniel Bristot de Oliveira * DE OLIVEIRA, Daniel Bristot, et al. Demystifying the real-time linux 12*bce29ac9SDaniel Bristot de Oliveira * scheduling latency. In: 32nd Euromicro Conference on Real-Time Systems 13*bce29ac9SDaniel Bristot de Oliveira * (ECRTS 2020). Schloss Dagstuhl-Leibniz-Zentrum fur Informatik, 2020. 14*bce29ac9SDaniel Bristot de Oliveira * 15*bce29ac9SDaniel Bristot de Oliveira * Copyright (C) 2021 Daniel Bristot de Oliveira, Red Hat, Inc. <bristot@redhat.com> 16*bce29ac9SDaniel Bristot de Oliveira */ 17*bce29ac9SDaniel Bristot de Oliveira 18*bce29ac9SDaniel Bristot de Oliveira #include <linux/kthread.h> 19*bce29ac9SDaniel Bristot de Oliveira #include <linux/tracefs.h> 20*bce29ac9SDaniel Bristot de Oliveira #include <linux/uaccess.h> 21*bce29ac9SDaniel Bristot de Oliveira #include <linux/cpumask.h> 22*bce29ac9SDaniel Bristot de Oliveira #include <linux/delay.h> 23*bce29ac9SDaniel Bristot de Oliveira #include <linux/sched/clock.h> 24*bce29ac9SDaniel Bristot de Oliveira #include <linux/sched.h> 25*bce29ac9SDaniel Bristot de Oliveira #include "trace.h" 26*bce29ac9SDaniel Bristot de Oliveira 27*bce29ac9SDaniel Bristot de Oliveira #ifdef CONFIG_X86_LOCAL_APIC 28*bce29ac9SDaniel Bristot de Oliveira #include <asm/trace/irq_vectors.h> 29*bce29ac9SDaniel Bristot de Oliveira #undef TRACE_INCLUDE_PATH 30*bce29ac9SDaniel Bristot de Oliveira #undef TRACE_INCLUDE_FILE 31*bce29ac9SDaniel Bristot de Oliveira #endif /* CONFIG_X86_LOCAL_APIC */ 32*bce29ac9SDaniel Bristot de Oliveira 33*bce29ac9SDaniel Bristot de Oliveira #include <trace/events/irq.h> 34*bce29ac9SDaniel Bristot de Oliveira #include <trace/events/sched.h> 35*bce29ac9SDaniel Bristot de Oliveira 36*bce29ac9SDaniel Bristot de Oliveira #define CREATE_TRACE_POINTS 37*bce29ac9SDaniel Bristot de Oliveira #include <trace/events/osnoise.h> 38*bce29ac9SDaniel Bristot de Oliveira 39*bce29ac9SDaniel Bristot de Oliveira static struct trace_array *osnoise_trace; 40*bce29ac9SDaniel Bristot de Oliveira 41*bce29ac9SDaniel Bristot de Oliveira /* 42*bce29ac9SDaniel Bristot de Oliveira * Default values. 43*bce29ac9SDaniel Bristot de Oliveira */ 44*bce29ac9SDaniel Bristot de Oliveira #define BANNER "osnoise: " 45*bce29ac9SDaniel Bristot de Oliveira #define DEFAULT_SAMPLE_PERIOD 1000000 /* 1s */ 46*bce29ac9SDaniel Bristot de Oliveira #define DEFAULT_SAMPLE_RUNTIME 1000000 /* 1s */ 47*bce29ac9SDaniel Bristot de Oliveira 48*bce29ac9SDaniel Bristot de Oliveira /* 49*bce29ac9SDaniel Bristot de Oliveira * NMI runtime info. 50*bce29ac9SDaniel Bristot de Oliveira */ 51*bce29ac9SDaniel Bristot de Oliveira struct osn_nmi { 52*bce29ac9SDaniel Bristot de Oliveira u64 count; 53*bce29ac9SDaniel Bristot de Oliveira u64 delta_start; 54*bce29ac9SDaniel Bristot de Oliveira }; 55*bce29ac9SDaniel Bristot de Oliveira 56*bce29ac9SDaniel Bristot de Oliveira /* 57*bce29ac9SDaniel Bristot de Oliveira * IRQ runtime info. 58*bce29ac9SDaniel Bristot de Oliveira */ 59*bce29ac9SDaniel Bristot de Oliveira struct osn_irq { 60*bce29ac9SDaniel Bristot de Oliveira u64 count; 61*bce29ac9SDaniel Bristot de Oliveira u64 arrival_time; 62*bce29ac9SDaniel Bristot de Oliveira u64 delta_start; 63*bce29ac9SDaniel Bristot de Oliveira }; 64*bce29ac9SDaniel Bristot de Oliveira 65*bce29ac9SDaniel Bristot de Oliveira /* 66*bce29ac9SDaniel Bristot de Oliveira * sofirq runtime info. 67*bce29ac9SDaniel Bristot de Oliveira */ 68*bce29ac9SDaniel Bristot de Oliveira struct osn_softirq { 69*bce29ac9SDaniel Bristot de Oliveira u64 count; 70*bce29ac9SDaniel Bristot de Oliveira u64 arrival_time; 71*bce29ac9SDaniel Bristot de Oliveira u64 delta_start; 72*bce29ac9SDaniel Bristot de Oliveira }; 73*bce29ac9SDaniel Bristot de Oliveira 74*bce29ac9SDaniel Bristot de Oliveira /* 75*bce29ac9SDaniel Bristot de Oliveira * thread runtime info. 76*bce29ac9SDaniel Bristot de Oliveira */ 77*bce29ac9SDaniel Bristot de Oliveira struct osn_thread { 78*bce29ac9SDaniel Bristot de Oliveira u64 count; 79*bce29ac9SDaniel Bristot de Oliveira u64 arrival_time; 80*bce29ac9SDaniel Bristot de Oliveira u64 delta_start; 81*bce29ac9SDaniel Bristot de Oliveira }; 82*bce29ac9SDaniel Bristot de Oliveira 83*bce29ac9SDaniel Bristot de Oliveira /* 84*bce29ac9SDaniel Bristot de Oliveira * Runtime information: this structure saves the runtime information used by 85*bce29ac9SDaniel Bristot de Oliveira * one sampling thread. 86*bce29ac9SDaniel Bristot de Oliveira */ 87*bce29ac9SDaniel Bristot de Oliveira struct osnoise_variables { 88*bce29ac9SDaniel Bristot de Oliveira struct task_struct *kthread; 89*bce29ac9SDaniel Bristot de Oliveira bool sampling; 90*bce29ac9SDaniel Bristot de Oliveira pid_t pid; 91*bce29ac9SDaniel Bristot de Oliveira struct osn_nmi nmi; 92*bce29ac9SDaniel Bristot de Oliveira struct osn_irq irq; 93*bce29ac9SDaniel Bristot de Oliveira struct osn_softirq softirq; 94*bce29ac9SDaniel Bristot de Oliveira struct osn_thread thread; 95*bce29ac9SDaniel Bristot de Oliveira local_t int_counter; 96*bce29ac9SDaniel Bristot de Oliveira }; 97*bce29ac9SDaniel Bristot de Oliveira 98*bce29ac9SDaniel Bristot de Oliveira /* 99*bce29ac9SDaniel Bristot de Oliveira * Per-cpu runtime information. 100*bce29ac9SDaniel Bristot de Oliveira */ 101*bce29ac9SDaniel Bristot de Oliveira DEFINE_PER_CPU(struct osnoise_variables, per_cpu_osnoise_var); 102*bce29ac9SDaniel Bristot de Oliveira 103*bce29ac9SDaniel Bristot de Oliveira /* 104*bce29ac9SDaniel Bristot de Oliveira * this_cpu_osn_var - Return the per-cpu osnoise_variables on its relative CPU 105*bce29ac9SDaniel Bristot de Oliveira */ 106*bce29ac9SDaniel Bristot de Oliveira static inline struct osnoise_variables *this_cpu_osn_var(void) 107*bce29ac9SDaniel Bristot de Oliveira { 108*bce29ac9SDaniel Bristot de Oliveira return this_cpu_ptr(&per_cpu_osnoise_var); 109*bce29ac9SDaniel Bristot de Oliveira } 110*bce29ac9SDaniel Bristot de Oliveira 111*bce29ac9SDaniel Bristot de Oliveira /* 112*bce29ac9SDaniel Bristot de Oliveira * osn_var_reset - Reset the values of the given osnoise_variables 113*bce29ac9SDaniel Bristot de Oliveira */ 114*bce29ac9SDaniel Bristot de Oliveira static inline void osn_var_reset(struct osnoise_variables *osn_var) 115*bce29ac9SDaniel Bristot de Oliveira { 116*bce29ac9SDaniel Bristot de Oliveira /* 117*bce29ac9SDaniel Bristot de Oliveira * So far, all the values are initialized as 0, so 118*bce29ac9SDaniel Bristot de Oliveira * zeroing the structure is perfect. 119*bce29ac9SDaniel Bristot de Oliveira */ 120*bce29ac9SDaniel Bristot de Oliveira memset(osn_var, 0, sizeof(*osn_var)); 121*bce29ac9SDaniel Bristot de Oliveira } 122*bce29ac9SDaniel Bristot de Oliveira 123*bce29ac9SDaniel Bristot de Oliveira /* 124*bce29ac9SDaniel Bristot de Oliveira * osn_var_reset_all - Reset the value of all per-cpu osnoise_variables 125*bce29ac9SDaniel Bristot de Oliveira */ 126*bce29ac9SDaniel Bristot de Oliveira static inline void osn_var_reset_all(void) 127*bce29ac9SDaniel Bristot de Oliveira { 128*bce29ac9SDaniel Bristot de Oliveira struct osnoise_variables *osn_var; 129*bce29ac9SDaniel Bristot de Oliveira int cpu; 130*bce29ac9SDaniel Bristot de Oliveira 131*bce29ac9SDaniel Bristot de Oliveira for_each_cpu(cpu, cpu_online_mask) { 132*bce29ac9SDaniel Bristot de Oliveira osn_var = per_cpu_ptr(&per_cpu_osnoise_var, cpu); 133*bce29ac9SDaniel Bristot de Oliveira osn_var_reset(osn_var); 134*bce29ac9SDaniel Bristot de Oliveira } 135*bce29ac9SDaniel Bristot de Oliveira } 136*bce29ac9SDaniel Bristot de Oliveira 137*bce29ac9SDaniel Bristot de Oliveira /* 138*bce29ac9SDaniel Bristot de Oliveira * Tells NMIs to call back to the osnoise tracer to record timestamps. 139*bce29ac9SDaniel Bristot de Oliveira */ 140*bce29ac9SDaniel Bristot de Oliveira bool trace_osnoise_callback_enabled; 141*bce29ac9SDaniel Bristot de Oliveira 142*bce29ac9SDaniel Bristot de Oliveira /* 143*bce29ac9SDaniel Bristot de Oliveira * osnoise sample structure definition. Used to store the statistics of a 144*bce29ac9SDaniel Bristot de Oliveira * sample run. 145*bce29ac9SDaniel Bristot de Oliveira */ 146*bce29ac9SDaniel Bristot de Oliveira struct osnoise_sample { 147*bce29ac9SDaniel Bristot de Oliveira u64 runtime; /* runtime */ 148*bce29ac9SDaniel Bristot de Oliveira u64 noise; /* noise */ 149*bce29ac9SDaniel Bristot de Oliveira u64 max_sample; /* max single noise sample */ 150*bce29ac9SDaniel Bristot de Oliveira int hw_count; /* # HW (incl. hypervisor) interference */ 151*bce29ac9SDaniel Bristot de Oliveira int nmi_count; /* # NMIs during this sample */ 152*bce29ac9SDaniel Bristot de Oliveira int irq_count; /* # IRQs during this sample */ 153*bce29ac9SDaniel Bristot de Oliveira int softirq_count; /* # softirqs during this sample */ 154*bce29ac9SDaniel Bristot de Oliveira int thread_count; /* # threads during this sample */ 155*bce29ac9SDaniel Bristot de Oliveira }; 156*bce29ac9SDaniel Bristot de Oliveira 157*bce29ac9SDaniel Bristot de Oliveira /* 158*bce29ac9SDaniel Bristot de Oliveira * Protect the interface. 159*bce29ac9SDaniel Bristot de Oliveira */ 160*bce29ac9SDaniel Bristot de Oliveira struct mutex interface_lock; 161*bce29ac9SDaniel Bristot de Oliveira 162*bce29ac9SDaniel Bristot de Oliveira /* 163*bce29ac9SDaniel Bristot de Oliveira * Tracer data. 164*bce29ac9SDaniel Bristot de Oliveira */ 165*bce29ac9SDaniel Bristot de Oliveira static struct osnoise_data { 166*bce29ac9SDaniel Bristot de Oliveira u64 sample_period; /* total sampling period */ 167*bce29ac9SDaniel Bristot de Oliveira u64 sample_runtime; /* active sampling portion of period */ 168*bce29ac9SDaniel Bristot de Oliveira u64 stop_tracing; /* stop trace in the inside operation (loop) */ 169*bce29ac9SDaniel Bristot de Oliveira u64 stop_tracing_total; /* stop trace in the outside operation (report) */ 170*bce29ac9SDaniel Bristot de Oliveira bool tainted; /* infor users and developers about a problem */ 171*bce29ac9SDaniel Bristot de Oliveira } osnoise_data = { 172*bce29ac9SDaniel Bristot de Oliveira .sample_period = DEFAULT_SAMPLE_PERIOD, 173*bce29ac9SDaniel Bristot de Oliveira .sample_runtime = DEFAULT_SAMPLE_RUNTIME, 174*bce29ac9SDaniel Bristot de Oliveira .stop_tracing = 0, 175*bce29ac9SDaniel Bristot de Oliveira .stop_tracing_total = 0, 176*bce29ac9SDaniel Bristot de Oliveira }; 177*bce29ac9SDaniel Bristot de Oliveira 178*bce29ac9SDaniel Bristot de Oliveira /* 179*bce29ac9SDaniel Bristot de Oliveira * Boolean variable used to inform that the tracer is currently sampling. 180*bce29ac9SDaniel Bristot de Oliveira */ 181*bce29ac9SDaniel Bristot de Oliveira static bool osnoise_busy; 182*bce29ac9SDaniel Bristot de Oliveira 183*bce29ac9SDaniel Bristot de Oliveira /* 184*bce29ac9SDaniel Bristot de Oliveira * Print the osnoise header info. 185*bce29ac9SDaniel Bristot de Oliveira */ 186*bce29ac9SDaniel Bristot de Oliveira static void print_osnoise_headers(struct seq_file *s) 187*bce29ac9SDaniel Bristot de Oliveira { 188*bce29ac9SDaniel Bristot de Oliveira if (osnoise_data.tainted) 189*bce29ac9SDaniel Bristot de Oliveira seq_puts(s, "# osnoise is tainted!\n"); 190*bce29ac9SDaniel Bristot de Oliveira 191*bce29ac9SDaniel Bristot de Oliveira seq_puts(s, "# _-----=> irqs-off\n"); 192*bce29ac9SDaniel Bristot de Oliveira seq_puts(s, "# / _----=> need-resched\n"); 193*bce29ac9SDaniel Bristot de Oliveira seq_puts(s, "# | / _---=> hardirq/softirq\n"); 194*bce29ac9SDaniel Bristot de Oliveira seq_puts(s, "# || / _--=> preempt-depth "); 195*bce29ac9SDaniel Bristot de Oliveira seq_puts(s, " MAX\n"); 196*bce29ac9SDaniel Bristot de Oliveira 197*bce29ac9SDaniel Bristot de Oliveira seq_puts(s, "# || / "); 198*bce29ac9SDaniel Bristot de Oliveira seq_puts(s, " SINGLE Interference counters:\n"); 199*bce29ac9SDaniel Bristot de Oliveira 200*bce29ac9SDaniel Bristot de Oliveira seq_puts(s, "# |||| RUNTIME "); 201*bce29ac9SDaniel Bristot de Oliveira seq_puts(s, " NOISE %% OF CPU NOISE +-----------------------------+\n"); 202*bce29ac9SDaniel Bristot de Oliveira 203*bce29ac9SDaniel Bristot de Oliveira seq_puts(s, "# TASK-PID CPU# |||| TIMESTAMP IN US "); 204*bce29ac9SDaniel Bristot de Oliveira seq_puts(s, " IN US AVAILABLE IN US HW NMI IRQ SIRQ THREAD\n"); 205*bce29ac9SDaniel Bristot de Oliveira 206*bce29ac9SDaniel Bristot de Oliveira seq_puts(s, "# | | | |||| | | "); 207*bce29ac9SDaniel Bristot de Oliveira seq_puts(s, " | | | | | | | |\n"); 208*bce29ac9SDaniel Bristot de Oliveira } 209*bce29ac9SDaniel Bristot de Oliveira 210*bce29ac9SDaniel Bristot de Oliveira /* 211*bce29ac9SDaniel Bristot de Oliveira * osnoise_taint - report an osnoise error. 212*bce29ac9SDaniel Bristot de Oliveira */ 213*bce29ac9SDaniel Bristot de Oliveira #define osnoise_taint(msg) ({ \ 214*bce29ac9SDaniel Bristot de Oliveira struct trace_array *tr = osnoise_trace; \ 215*bce29ac9SDaniel Bristot de Oliveira \ 216*bce29ac9SDaniel Bristot de Oliveira trace_array_printk_buf(tr->array_buffer.buffer, _THIS_IP_, msg); \ 217*bce29ac9SDaniel Bristot de Oliveira osnoise_data.tainted = true; \ 218*bce29ac9SDaniel Bristot de Oliveira }) 219*bce29ac9SDaniel Bristot de Oliveira 220*bce29ac9SDaniel Bristot de Oliveira /* 221*bce29ac9SDaniel Bristot de Oliveira * Record an osnoise_sample into the tracer buffer. 222*bce29ac9SDaniel Bristot de Oliveira */ 223*bce29ac9SDaniel Bristot de Oliveira static void trace_osnoise_sample(struct osnoise_sample *sample) 224*bce29ac9SDaniel Bristot de Oliveira { 225*bce29ac9SDaniel Bristot de Oliveira struct trace_array *tr = osnoise_trace; 226*bce29ac9SDaniel Bristot de Oliveira struct trace_buffer *buffer = tr->array_buffer.buffer; 227*bce29ac9SDaniel Bristot de Oliveira struct trace_event_call *call = &event_osnoise; 228*bce29ac9SDaniel Bristot de Oliveira struct ring_buffer_event *event; 229*bce29ac9SDaniel Bristot de Oliveira struct osnoise_entry *entry; 230*bce29ac9SDaniel Bristot de Oliveira 231*bce29ac9SDaniel Bristot de Oliveira event = trace_buffer_lock_reserve(buffer, TRACE_OSNOISE, sizeof(*entry), 232*bce29ac9SDaniel Bristot de Oliveira tracing_gen_ctx()); 233*bce29ac9SDaniel Bristot de Oliveira if (!event) 234*bce29ac9SDaniel Bristot de Oliveira return; 235*bce29ac9SDaniel Bristot de Oliveira entry = ring_buffer_event_data(event); 236*bce29ac9SDaniel Bristot de Oliveira entry->runtime = sample->runtime; 237*bce29ac9SDaniel Bristot de Oliveira entry->noise = sample->noise; 238*bce29ac9SDaniel Bristot de Oliveira entry->max_sample = sample->max_sample; 239*bce29ac9SDaniel Bristot de Oliveira entry->hw_count = sample->hw_count; 240*bce29ac9SDaniel Bristot de Oliveira entry->nmi_count = sample->nmi_count; 241*bce29ac9SDaniel Bristot de Oliveira entry->irq_count = sample->irq_count; 242*bce29ac9SDaniel Bristot de Oliveira entry->softirq_count = sample->softirq_count; 243*bce29ac9SDaniel Bristot de Oliveira entry->thread_count = sample->thread_count; 244*bce29ac9SDaniel Bristot de Oliveira 245*bce29ac9SDaniel Bristot de Oliveira if (!call_filter_check_discard(call, entry, buffer, event)) 246*bce29ac9SDaniel Bristot de Oliveira trace_buffer_unlock_commit_nostack(buffer, event); 247*bce29ac9SDaniel Bristot de Oliveira } 248*bce29ac9SDaniel Bristot de Oliveira 249*bce29ac9SDaniel Bristot de Oliveira /* 250*bce29ac9SDaniel Bristot de Oliveira * Macros to encapsulate the time capturing infrastructure. 251*bce29ac9SDaniel Bristot de Oliveira */ 252*bce29ac9SDaniel Bristot de Oliveira #define time_get() trace_clock_local() 253*bce29ac9SDaniel Bristot de Oliveira #define time_to_us(x) div_u64(x, 1000) 254*bce29ac9SDaniel Bristot de Oliveira #define time_sub(a, b) ((a) - (b)) 255*bce29ac9SDaniel Bristot de Oliveira 256*bce29ac9SDaniel Bristot de Oliveira /* 257*bce29ac9SDaniel Bristot de Oliveira * cond_move_irq_delta_start - Forward the delta_start of a running IRQ 258*bce29ac9SDaniel Bristot de Oliveira * 259*bce29ac9SDaniel Bristot de Oliveira * If an IRQ is preempted by an NMI, its delta_start is pushed forward 260*bce29ac9SDaniel Bristot de Oliveira * to discount the NMI interference. 261*bce29ac9SDaniel Bristot de Oliveira * 262*bce29ac9SDaniel Bristot de Oliveira * See get_int_safe_duration(). 263*bce29ac9SDaniel Bristot de Oliveira */ 264*bce29ac9SDaniel Bristot de Oliveira static inline void 265*bce29ac9SDaniel Bristot de Oliveira cond_move_irq_delta_start(struct osnoise_variables *osn_var, u64 duration) 266*bce29ac9SDaniel Bristot de Oliveira { 267*bce29ac9SDaniel Bristot de Oliveira if (osn_var->irq.delta_start) 268*bce29ac9SDaniel Bristot de Oliveira osn_var->irq.delta_start += duration; 269*bce29ac9SDaniel Bristot de Oliveira } 270*bce29ac9SDaniel Bristot de Oliveira 271*bce29ac9SDaniel Bristot de Oliveira #ifndef CONFIG_PREEMPT_RT 272*bce29ac9SDaniel Bristot de Oliveira /* 273*bce29ac9SDaniel Bristot de Oliveira * cond_move_softirq_delta_start - Forward the delta_start of a running softirq. 274*bce29ac9SDaniel Bristot de Oliveira * 275*bce29ac9SDaniel Bristot de Oliveira * If a softirq is preempted by an IRQ or NMI, its delta_start is pushed 276*bce29ac9SDaniel Bristot de Oliveira * forward to discount the interference. 277*bce29ac9SDaniel Bristot de Oliveira * 278*bce29ac9SDaniel Bristot de Oliveira * See get_int_safe_duration(). 279*bce29ac9SDaniel Bristot de Oliveira */ 280*bce29ac9SDaniel Bristot de Oliveira static inline void 281*bce29ac9SDaniel Bristot de Oliveira cond_move_softirq_delta_start(struct osnoise_variables *osn_var, u64 duration) 282*bce29ac9SDaniel Bristot de Oliveira { 283*bce29ac9SDaniel Bristot de Oliveira if (osn_var->softirq.delta_start) 284*bce29ac9SDaniel Bristot de Oliveira osn_var->softirq.delta_start += duration; 285*bce29ac9SDaniel Bristot de Oliveira } 286*bce29ac9SDaniel Bristot de Oliveira #else /* CONFIG_PREEMPT_RT */ 287*bce29ac9SDaniel Bristot de Oliveira #define cond_move_softirq_delta_start(osn_var, duration) do {} while (0) 288*bce29ac9SDaniel Bristot de Oliveira #endif 289*bce29ac9SDaniel Bristot de Oliveira 290*bce29ac9SDaniel Bristot de Oliveira /* 291*bce29ac9SDaniel Bristot de Oliveira * cond_move_thread_delta_start - Forward the delta_start of a running thread 292*bce29ac9SDaniel Bristot de Oliveira * 293*bce29ac9SDaniel Bristot de Oliveira * If a noisy thread is preempted by an softirq, IRQ or NMI, its delta_start 294*bce29ac9SDaniel Bristot de Oliveira * is pushed forward to discount the interference. 295*bce29ac9SDaniel Bristot de Oliveira * 296*bce29ac9SDaniel Bristot de Oliveira * See get_int_safe_duration(). 297*bce29ac9SDaniel Bristot de Oliveira */ 298*bce29ac9SDaniel Bristot de Oliveira static inline void 299*bce29ac9SDaniel Bristot de Oliveira cond_move_thread_delta_start(struct osnoise_variables *osn_var, u64 duration) 300*bce29ac9SDaniel Bristot de Oliveira { 301*bce29ac9SDaniel Bristot de Oliveira if (osn_var->thread.delta_start) 302*bce29ac9SDaniel Bristot de Oliveira osn_var->thread.delta_start += duration; 303*bce29ac9SDaniel Bristot de Oliveira } 304*bce29ac9SDaniel Bristot de Oliveira 305*bce29ac9SDaniel Bristot de Oliveira /* 306*bce29ac9SDaniel Bristot de Oliveira * get_int_safe_duration - Get the duration of a window 307*bce29ac9SDaniel Bristot de Oliveira * 308*bce29ac9SDaniel Bristot de Oliveira * The irq, softirq and thread varaibles need to have its duration without 309*bce29ac9SDaniel Bristot de Oliveira * the interference from higher priority interrupts. Instead of keeping a 310*bce29ac9SDaniel Bristot de Oliveira * variable to discount the interrupt interference from these variables, the 311*bce29ac9SDaniel Bristot de Oliveira * starting time of these variables are pushed forward with the interrupt's 312*bce29ac9SDaniel Bristot de Oliveira * duration. In this way, a single variable is used to: 313*bce29ac9SDaniel Bristot de Oliveira * 314*bce29ac9SDaniel Bristot de Oliveira * - Know if a given window is being measured. 315*bce29ac9SDaniel Bristot de Oliveira * - Account its duration. 316*bce29ac9SDaniel Bristot de Oliveira * - Discount the interference. 317*bce29ac9SDaniel Bristot de Oliveira * 318*bce29ac9SDaniel Bristot de Oliveira * To avoid getting inconsistent values, e.g.,: 319*bce29ac9SDaniel Bristot de Oliveira * 320*bce29ac9SDaniel Bristot de Oliveira * now = time_get() 321*bce29ac9SDaniel Bristot de Oliveira * ---> interrupt! 322*bce29ac9SDaniel Bristot de Oliveira * delta_start -= int duration; 323*bce29ac9SDaniel Bristot de Oliveira * <--- 324*bce29ac9SDaniel Bristot de Oliveira * duration = now - delta_start; 325*bce29ac9SDaniel Bristot de Oliveira * 326*bce29ac9SDaniel Bristot de Oliveira * result: negative duration if the variable duration before the 327*bce29ac9SDaniel Bristot de Oliveira * interrupt was smaller than the interrupt execution. 328*bce29ac9SDaniel Bristot de Oliveira * 329*bce29ac9SDaniel Bristot de Oliveira * A counter of interrupts is used. If the counter increased, try 330*bce29ac9SDaniel Bristot de Oliveira * to capture an interference safe duration. 331*bce29ac9SDaniel Bristot de Oliveira */ 332*bce29ac9SDaniel Bristot de Oliveira static inline s64 333*bce29ac9SDaniel Bristot de Oliveira get_int_safe_duration(struct osnoise_variables *osn_var, u64 *delta_start) 334*bce29ac9SDaniel Bristot de Oliveira { 335*bce29ac9SDaniel Bristot de Oliveira u64 int_counter, now; 336*bce29ac9SDaniel Bristot de Oliveira s64 duration; 337*bce29ac9SDaniel Bristot de Oliveira 338*bce29ac9SDaniel Bristot de Oliveira do { 339*bce29ac9SDaniel Bristot de Oliveira int_counter = local_read(&osn_var->int_counter); 340*bce29ac9SDaniel Bristot de Oliveira /* synchronize with interrupts */ 341*bce29ac9SDaniel Bristot de Oliveira barrier(); 342*bce29ac9SDaniel Bristot de Oliveira 343*bce29ac9SDaniel Bristot de Oliveira now = time_get(); 344*bce29ac9SDaniel Bristot de Oliveira duration = (now - *delta_start); 345*bce29ac9SDaniel Bristot de Oliveira 346*bce29ac9SDaniel Bristot de Oliveira /* synchronize with interrupts */ 347*bce29ac9SDaniel Bristot de Oliveira barrier(); 348*bce29ac9SDaniel Bristot de Oliveira } while (int_counter != local_read(&osn_var->int_counter)); 349*bce29ac9SDaniel Bristot de Oliveira 350*bce29ac9SDaniel Bristot de Oliveira /* 351*bce29ac9SDaniel Bristot de Oliveira * This is an evidence of race conditions that cause 352*bce29ac9SDaniel Bristot de Oliveira * a value to be "discounted" too much. 353*bce29ac9SDaniel Bristot de Oliveira */ 354*bce29ac9SDaniel Bristot de Oliveira if (duration < 0) 355*bce29ac9SDaniel Bristot de Oliveira osnoise_taint("Negative duration!\n"); 356*bce29ac9SDaniel Bristot de Oliveira 357*bce29ac9SDaniel Bristot de Oliveira *delta_start = 0; 358*bce29ac9SDaniel Bristot de Oliveira 359*bce29ac9SDaniel Bristot de Oliveira return duration; 360*bce29ac9SDaniel Bristot de Oliveira } 361*bce29ac9SDaniel Bristot de Oliveira 362*bce29ac9SDaniel Bristot de Oliveira /* 363*bce29ac9SDaniel Bristot de Oliveira * 364*bce29ac9SDaniel Bristot de Oliveira * set_int_safe_time - Save the current time on *time, aware of interference 365*bce29ac9SDaniel Bristot de Oliveira * 366*bce29ac9SDaniel Bristot de Oliveira * Get the time, taking into consideration a possible interference from 367*bce29ac9SDaniel Bristot de Oliveira * higher priority interrupts. 368*bce29ac9SDaniel Bristot de Oliveira * 369*bce29ac9SDaniel Bristot de Oliveira * See get_int_safe_duration() for an explanation. 370*bce29ac9SDaniel Bristot de Oliveira */ 371*bce29ac9SDaniel Bristot de Oliveira static u64 372*bce29ac9SDaniel Bristot de Oliveira set_int_safe_time(struct osnoise_variables *osn_var, u64 *time) 373*bce29ac9SDaniel Bristot de Oliveira { 374*bce29ac9SDaniel Bristot de Oliveira u64 int_counter; 375*bce29ac9SDaniel Bristot de Oliveira 376*bce29ac9SDaniel Bristot de Oliveira do { 377*bce29ac9SDaniel Bristot de Oliveira int_counter = local_read(&osn_var->int_counter); 378*bce29ac9SDaniel Bristot de Oliveira /* synchronize with interrupts */ 379*bce29ac9SDaniel Bristot de Oliveira barrier(); 380*bce29ac9SDaniel Bristot de Oliveira 381*bce29ac9SDaniel Bristot de Oliveira *time = time_get(); 382*bce29ac9SDaniel Bristot de Oliveira 383*bce29ac9SDaniel Bristot de Oliveira /* synchronize with interrupts */ 384*bce29ac9SDaniel Bristot de Oliveira barrier(); 385*bce29ac9SDaniel Bristot de Oliveira } while (int_counter != local_read(&osn_var->int_counter)); 386*bce29ac9SDaniel Bristot de Oliveira 387*bce29ac9SDaniel Bristot de Oliveira return int_counter; 388*bce29ac9SDaniel Bristot de Oliveira } 389*bce29ac9SDaniel Bristot de Oliveira 390*bce29ac9SDaniel Bristot de Oliveira /* 391*bce29ac9SDaniel Bristot de Oliveira * trace_osnoise_callback - NMI entry/exit callback 392*bce29ac9SDaniel Bristot de Oliveira * 393*bce29ac9SDaniel Bristot de Oliveira * This function is called at the entry and exit NMI code. The bool enter 394*bce29ac9SDaniel Bristot de Oliveira * distinguishes between either case. This function is used to note a NMI 395*bce29ac9SDaniel Bristot de Oliveira * occurrence, compute the noise caused by the NMI, and to remove the noise 396*bce29ac9SDaniel Bristot de Oliveira * it is potentially causing on other interference variables. 397*bce29ac9SDaniel Bristot de Oliveira */ 398*bce29ac9SDaniel Bristot de Oliveira void trace_osnoise_callback(bool enter) 399*bce29ac9SDaniel Bristot de Oliveira { 400*bce29ac9SDaniel Bristot de Oliveira struct osnoise_variables *osn_var = this_cpu_osn_var(); 401*bce29ac9SDaniel Bristot de Oliveira u64 duration; 402*bce29ac9SDaniel Bristot de Oliveira 403*bce29ac9SDaniel Bristot de Oliveira if (!osn_var->sampling) 404*bce29ac9SDaniel Bristot de Oliveira return; 405*bce29ac9SDaniel Bristot de Oliveira 406*bce29ac9SDaniel Bristot de Oliveira /* 407*bce29ac9SDaniel Bristot de Oliveira * Currently trace_clock_local() calls sched_clock() and the 408*bce29ac9SDaniel Bristot de Oliveira * generic version is not NMI safe. 409*bce29ac9SDaniel Bristot de Oliveira */ 410*bce29ac9SDaniel Bristot de Oliveira if (!IS_ENABLED(CONFIG_GENERIC_SCHED_CLOCK)) { 411*bce29ac9SDaniel Bristot de Oliveira if (enter) { 412*bce29ac9SDaniel Bristot de Oliveira osn_var->nmi.delta_start = time_get(); 413*bce29ac9SDaniel Bristot de Oliveira local_inc(&osn_var->int_counter); 414*bce29ac9SDaniel Bristot de Oliveira } else { 415*bce29ac9SDaniel Bristot de Oliveira duration = time_get() - osn_var->nmi.delta_start; 416*bce29ac9SDaniel Bristot de Oliveira 417*bce29ac9SDaniel Bristot de Oliveira trace_nmi_noise(osn_var->nmi.delta_start, duration); 418*bce29ac9SDaniel Bristot de Oliveira 419*bce29ac9SDaniel Bristot de Oliveira cond_move_irq_delta_start(osn_var, duration); 420*bce29ac9SDaniel Bristot de Oliveira cond_move_softirq_delta_start(osn_var, duration); 421*bce29ac9SDaniel Bristot de Oliveira cond_move_thread_delta_start(osn_var, duration); 422*bce29ac9SDaniel Bristot de Oliveira } 423*bce29ac9SDaniel Bristot de Oliveira } 424*bce29ac9SDaniel Bristot de Oliveira 425*bce29ac9SDaniel Bristot de Oliveira if (enter) 426*bce29ac9SDaniel Bristot de Oliveira osn_var->nmi.count++; 427*bce29ac9SDaniel Bristot de Oliveira } 428*bce29ac9SDaniel Bristot de Oliveira 429*bce29ac9SDaniel Bristot de Oliveira /* 430*bce29ac9SDaniel Bristot de Oliveira * osnoise_trace_irq_entry - Note the starting of an IRQ 431*bce29ac9SDaniel Bristot de Oliveira * 432*bce29ac9SDaniel Bristot de Oliveira * Save the starting time of an IRQ. As IRQs are non-preemptive to other IRQs, 433*bce29ac9SDaniel Bristot de Oliveira * it is safe to use a single variable (ons_var->irq) to save the statistics. 434*bce29ac9SDaniel Bristot de Oliveira * The arrival_time is used to report... the arrival time. The delta_start 435*bce29ac9SDaniel Bristot de Oliveira * is used to compute the duration at the IRQ exit handler. See 436*bce29ac9SDaniel Bristot de Oliveira * cond_move_irq_delta_start(). 437*bce29ac9SDaniel Bristot de Oliveira */ 438*bce29ac9SDaniel Bristot de Oliveira void osnoise_trace_irq_entry(int id) 439*bce29ac9SDaniel Bristot de Oliveira { 440*bce29ac9SDaniel Bristot de Oliveira struct osnoise_variables *osn_var = this_cpu_osn_var(); 441*bce29ac9SDaniel Bristot de Oliveira 442*bce29ac9SDaniel Bristot de Oliveira if (!osn_var->sampling) 443*bce29ac9SDaniel Bristot de Oliveira return; 444*bce29ac9SDaniel Bristot de Oliveira /* 445*bce29ac9SDaniel Bristot de Oliveira * This value will be used in the report, but not to compute 446*bce29ac9SDaniel Bristot de Oliveira * the execution time, so it is safe to get it unsafe. 447*bce29ac9SDaniel Bristot de Oliveira */ 448*bce29ac9SDaniel Bristot de Oliveira osn_var->irq.arrival_time = time_get(); 449*bce29ac9SDaniel Bristot de Oliveira set_int_safe_time(osn_var, &osn_var->irq.delta_start); 450*bce29ac9SDaniel Bristot de Oliveira osn_var->irq.count++; 451*bce29ac9SDaniel Bristot de Oliveira 452*bce29ac9SDaniel Bristot de Oliveira local_inc(&osn_var->int_counter); 453*bce29ac9SDaniel Bristot de Oliveira } 454*bce29ac9SDaniel Bristot de Oliveira 455*bce29ac9SDaniel Bristot de Oliveira /* 456*bce29ac9SDaniel Bristot de Oliveira * osnoise_irq_exit - Note the end of an IRQ, sava data and trace 457*bce29ac9SDaniel Bristot de Oliveira * 458*bce29ac9SDaniel Bristot de Oliveira * Computes the duration of the IRQ noise, and trace it. Also discounts the 459*bce29ac9SDaniel Bristot de Oliveira * interference from other sources of noise could be currently being accounted. 460*bce29ac9SDaniel Bristot de Oliveira */ 461*bce29ac9SDaniel Bristot de Oliveira void osnoise_trace_irq_exit(int id, const char *desc) 462*bce29ac9SDaniel Bristot de Oliveira { 463*bce29ac9SDaniel Bristot de Oliveira struct osnoise_variables *osn_var = this_cpu_osn_var(); 464*bce29ac9SDaniel Bristot de Oliveira int duration; 465*bce29ac9SDaniel Bristot de Oliveira 466*bce29ac9SDaniel Bristot de Oliveira if (!osn_var->sampling) 467*bce29ac9SDaniel Bristot de Oliveira return; 468*bce29ac9SDaniel Bristot de Oliveira 469*bce29ac9SDaniel Bristot de Oliveira duration = get_int_safe_duration(osn_var, &osn_var->irq.delta_start); 470*bce29ac9SDaniel Bristot de Oliveira trace_irq_noise(id, desc, osn_var->irq.arrival_time, duration); 471*bce29ac9SDaniel Bristot de Oliveira osn_var->irq.arrival_time = 0; 472*bce29ac9SDaniel Bristot de Oliveira cond_move_softirq_delta_start(osn_var, duration); 473*bce29ac9SDaniel Bristot de Oliveira cond_move_thread_delta_start(osn_var, duration); 474*bce29ac9SDaniel Bristot de Oliveira } 475*bce29ac9SDaniel Bristot de Oliveira 476*bce29ac9SDaniel Bristot de Oliveira /* 477*bce29ac9SDaniel Bristot de Oliveira * trace_irqentry_callback - Callback to the irq:irq_entry traceevent 478*bce29ac9SDaniel Bristot de Oliveira * 479*bce29ac9SDaniel Bristot de Oliveira * Used to note the starting of an IRQ occurece. 480*bce29ac9SDaniel Bristot de Oliveira */ 481*bce29ac9SDaniel Bristot de Oliveira static void trace_irqentry_callback(void *data, int irq, 482*bce29ac9SDaniel Bristot de Oliveira struct irqaction *action) 483*bce29ac9SDaniel Bristot de Oliveira { 484*bce29ac9SDaniel Bristot de Oliveira osnoise_trace_irq_entry(irq); 485*bce29ac9SDaniel Bristot de Oliveira } 486*bce29ac9SDaniel Bristot de Oliveira 487*bce29ac9SDaniel Bristot de Oliveira /* 488*bce29ac9SDaniel Bristot de Oliveira * trace_irqexit_callback - Callback to the irq:irq_exit traceevent 489*bce29ac9SDaniel Bristot de Oliveira * 490*bce29ac9SDaniel Bristot de Oliveira * Used to note the end of an IRQ occurece. 491*bce29ac9SDaniel Bristot de Oliveira */ 492*bce29ac9SDaniel Bristot de Oliveira static void trace_irqexit_callback(void *data, int irq, 493*bce29ac9SDaniel Bristot de Oliveira struct irqaction *action, int ret) 494*bce29ac9SDaniel Bristot de Oliveira { 495*bce29ac9SDaniel Bristot de Oliveira osnoise_trace_irq_exit(irq, action->name); 496*bce29ac9SDaniel Bristot de Oliveira } 497*bce29ac9SDaniel Bristot de Oliveira 498*bce29ac9SDaniel Bristot de Oliveira /* 499*bce29ac9SDaniel Bristot de Oliveira * arch specific register function. 500*bce29ac9SDaniel Bristot de Oliveira */ 501*bce29ac9SDaniel Bristot de Oliveira int __weak osnoise_arch_register(void) 502*bce29ac9SDaniel Bristot de Oliveira { 503*bce29ac9SDaniel Bristot de Oliveira return 0; 504*bce29ac9SDaniel Bristot de Oliveira } 505*bce29ac9SDaniel Bristot de Oliveira 506*bce29ac9SDaniel Bristot de Oliveira /* 507*bce29ac9SDaniel Bristot de Oliveira * arch specific unregister function. 508*bce29ac9SDaniel Bristot de Oliveira */ 509*bce29ac9SDaniel Bristot de Oliveira void __weak osnoise_arch_unregister(void) 510*bce29ac9SDaniel Bristot de Oliveira { 511*bce29ac9SDaniel Bristot de Oliveira return; 512*bce29ac9SDaniel Bristot de Oliveira } 513*bce29ac9SDaniel Bristot de Oliveira 514*bce29ac9SDaniel Bristot de Oliveira /* 515*bce29ac9SDaniel Bristot de Oliveira * hook_irq_events - Hook IRQ handling events 516*bce29ac9SDaniel Bristot de Oliveira * 517*bce29ac9SDaniel Bristot de Oliveira * This function hooks the IRQ related callbacks to the respective trace 518*bce29ac9SDaniel Bristot de Oliveira * events. 519*bce29ac9SDaniel Bristot de Oliveira */ 520*bce29ac9SDaniel Bristot de Oliveira int hook_irq_events(void) 521*bce29ac9SDaniel Bristot de Oliveira { 522*bce29ac9SDaniel Bristot de Oliveira int ret; 523*bce29ac9SDaniel Bristot de Oliveira 524*bce29ac9SDaniel Bristot de Oliveira ret = register_trace_irq_handler_entry(trace_irqentry_callback, NULL); 525*bce29ac9SDaniel Bristot de Oliveira if (ret) 526*bce29ac9SDaniel Bristot de Oliveira goto out_err; 527*bce29ac9SDaniel Bristot de Oliveira 528*bce29ac9SDaniel Bristot de Oliveira ret = register_trace_irq_handler_exit(trace_irqexit_callback, NULL); 529*bce29ac9SDaniel Bristot de Oliveira if (ret) 530*bce29ac9SDaniel Bristot de Oliveira goto out_unregister_entry; 531*bce29ac9SDaniel Bristot de Oliveira 532*bce29ac9SDaniel Bristot de Oliveira ret = osnoise_arch_register(); 533*bce29ac9SDaniel Bristot de Oliveira if (ret) 534*bce29ac9SDaniel Bristot de Oliveira goto out_irq_exit; 535*bce29ac9SDaniel Bristot de Oliveira 536*bce29ac9SDaniel Bristot de Oliveira return 0; 537*bce29ac9SDaniel Bristot de Oliveira 538*bce29ac9SDaniel Bristot de Oliveira out_irq_exit: 539*bce29ac9SDaniel Bristot de Oliveira unregister_trace_irq_handler_exit(trace_irqexit_callback, NULL); 540*bce29ac9SDaniel Bristot de Oliveira out_unregister_entry: 541*bce29ac9SDaniel Bristot de Oliveira unregister_trace_irq_handler_entry(trace_irqentry_callback, NULL); 542*bce29ac9SDaniel Bristot de Oliveira out_err: 543*bce29ac9SDaniel Bristot de Oliveira return -EINVAL; 544*bce29ac9SDaniel Bristot de Oliveira } 545*bce29ac9SDaniel Bristot de Oliveira 546*bce29ac9SDaniel Bristot de Oliveira /* 547*bce29ac9SDaniel Bristot de Oliveira * unhook_irq_events - Unhook IRQ handling events 548*bce29ac9SDaniel Bristot de Oliveira * 549*bce29ac9SDaniel Bristot de Oliveira * This function unhooks the IRQ related callbacks to the respective trace 550*bce29ac9SDaniel Bristot de Oliveira * events. 551*bce29ac9SDaniel Bristot de Oliveira */ 552*bce29ac9SDaniel Bristot de Oliveira void unhook_irq_events(void) 553*bce29ac9SDaniel Bristot de Oliveira { 554*bce29ac9SDaniel Bristot de Oliveira osnoise_arch_unregister(); 555*bce29ac9SDaniel Bristot de Oliveira unregister_trace_irq_handler_exit(trace_irqexit_callback, NULL); 556*bce29ac9SDaniel Bristot de Oliveira unregister_trace_irq_handler_entry(trace_irqentry_callback, NULL); 557*bce29ac9SDaniel Bristot de Oliveira } 558*bce29ac9SDaniel Bristot de Oliveira 559*bce29ac9SDaniel Bristot de Oliveira #ifndef CONFIG_PREEMPT_RT 560*bce29ac9SDaniel Bristot de Oliveira /* 561*bce29ac9SDaniel Bristot de Oliveira * trace_softirq_entry_callback - Note the starting of a softirq 562*bce29ac9SDaniel Bristot de Oliveira * 563*bce29ac9SDaniel Bristot de Oliveira * Save the starting time of a softirq. As softirqs are non-preemptive to 564*bce29ac9SDaniel Bristot de Oliveira * other softirqs, it is safe to use a single variable (ons_var->softirq) 565*bce29ac9SDaniel Bristot de Oliveira * to save the statistics. The arrival_time is used to report... the 566*bce29ac9SDaniel Bristot de Oliveira * arrival time. The delta_start is used to compute the duration at the 567*bce29ac9SDaniel Bristot de Oliveira * softirq exit handler. See cond_move_softirq_delta_start(). 568*bce29ac9SDaniel Bristot de Oliveira */ 569*bce29ac9SDaniel Bristot de Oliveira void trace_softirq_entry_callback(void *data, unsigned int vec_nr) 570*bce29ac9SDaniel Bristot de Oliveira { 571*bce29ac9SDaniel Bristot de Oliveira struct osnoise_variables *osn_var = this_cpu_osn_var(); 572*bce29ac9SDaniel Bristot de Oliveira 573*bce29ac9SDaniel Bristot de Oliveira if (!osn_var->sampling) 574*bce29ac9SDaniel Bristot de Oliveira return; 575*bce29ac9SDaniel Bristot de Oliveira /* 576*bce29ac9SDaniel Bristot de Oliveira * This value will be used in the report, but not to compute 577*bce29ac9SDaniel Bristot de Oliveira * the execution time, so it is safe to get it unsafe. 578*bce29ac9SDaniel Bristot de Oliveira */ 579*bce29ac9SDaniel Bristot de Oliveira osn_var->softirq.arrival_time = time_get(); 580*bce29ac9SDaniel Bristot de Oliveira set_int_safe_time(osn_var, &osn_var->softirq.delta_start); 581*bce29ac9SDaniel Bristot de Oliveira osn_var->softirq.count++; 582*bce29ac9SDaniel Bristot de Oliveira 583*bce29ac9SDaniel Bristot de Oliveira local_inc(&osn_var->int_counter); 584*bce29ac9SDaniel Bristot de Oliveira } 585*bce29ac9SDaniel Bristot de Oliveira 586*bce29ac9SDaniel Bristot de Oliveira /* 587*bce29ac9SDaniel Bristot de Oliveira * trace_softirq_exit_callback - Note the end of an softirq 588*bce29ac9SDaniel Bristot de Oliveira * 589*bce29ac9SDaniel Bristot de Oliveira * Computes the duration of the softirq noise, and trace it. Also discounts the 590*bce29ac9SDaniel Bristot de Oliveira * interference from other sources of noise could be currently being accounted. 591*bce29ac9SDaniel Bristot de Oliveira */ 592*bce29ac9SDaniel Bristot de Oliveira void trace_softirq_exit_callback(void *data, unsigned int vec_nr) 593*bce29ac9SDaniel Bristot de Oliveira { 594*bce29ac9SDaniel Bristot de Oliveira struct osnoise_variables *osn_var = this_cpu_osn_var(); 595*bce29ac9SDaniel Bristot de Oliveira int duration; 596*bce29ac9SDaniel Bristot de Oliveira 597*bce29ac9SDaniel Bristot de Oliveira if (!osn_var->sampling) 598*bce29ac9SDaniel Bristot de Oliveira return; 599*bce29ac9SDaniel Bristot de Oliveira 600*bce29ac9SDaniel Bristot de Oliveira duration = get_int_safe_duration(osn_var, &osn_var->softirq.delta_start); 601*bce29ac9SDaniel Bristot de Oliveira trace_softirq_noise(vec_nr, osn_var->softirq.arrival_time, duration); 602*bce29ac9SDaniel Bristot de Oliveira cond_move_thread_delta_start(osn_var, duration); 603*bce29ac9SDaniel Bristot de Oliveira osn_var->softirq.arrival_time = 0; 604*bce29ac9SDaniel Bristot de Oliveira } 605*bce29ac9SDaniel Bristot de Oliveira 606*bce29ac9SDaniel Bristot de Oliveira /* 607*bce29ac9SDaniel Bristot de Oliveira * hook_softirq_events - Hook softirq handling events 608*bce29ac9SDaniel Bristot de Oliveira * 609*bce29ac9SDaniel Bristot de Oliveira * This function hooks the softirq related callbacks to the respective trace 610*bce29ac9SDaniel Bristot de Oliveira * events. 611*bce29ac9SDaniel Bristot de Oliveira */ 612*bce29ac9SDaniel Bristot de Oliveira static int hook_softirq_events(void) 613*bce29ac9SDaniel Bristot de Oliveira { 614*bce29ac9SDaniel Bristot de Oliveira int ret; 615*bce29ac9SDaniel Bristot de Oliveira 616*bce29ac9SDaniel Bristot de Oliveira ret = register_trace_softirq_entry(trace_softirq_entry_callback, NULL); 617*bce29ac9SDaniel Bristot de Oliveira if (ret) 618*bce29ac9SDaniel Bristot de Oliveira goto out_err; 619*bce29ac9SDaniel Bristot de Oliveira 620*bce29ac9SDaniel Bristot de Oliveira ret = register_trace_softirq_exit(trace_softirq_exit_callback, NULL); 621*bce29ac9SDaniel Bristot de Oliveira if (ret) 622*bce29ac9SDaniel Bristot de Oliveira goto out_unreg_entry; 623*bce29ac9SDaniel Bristot de Oliveira 624*bce29ac9SDaniel Bristot de Oliveira return 0; 625*bce29ac9SDaniel Bristot de Oliveira 626*bce29ac9SDaniel Bristot de Oliveira out_unreg_entry: 627*bce29ac9SDaniel Bristot de Oliveira unregister_trace_softirq_entry(trace_softirq_entry_callback, NULL); 628*bce29ac9SDaniel Bristot de Oliveira out_err: 629*bce29ac9SDaniel Bristot de Oliveira return -EINVAL; 630*bce29ac9SDaniel Bristot de Oliveira } 631*bce29ac9SDaniel Bristot de Oliveira 632*bce29ac9SDaniel Bristot de Oliveira /* 633*bce29ac9SDaniel Bristot de Oliveira * unhook_softirq_events - Unhook softirq handling events 634*bce29ac9SDaniel Bristot de Oliveira * 635*bce29ac9SDaniel Bristot de Oliveira * This function hooks the softirq related callbacks to the respective trace 636*bce29ac9SDaniel Bristot de Oliveira * events. 637*bce29ac9SDaniel Bristot de Oliveira */ 638*bce29ac9SDaniel Bristot de Oliveira static void unhook_softirq_events(void) 639*bce29ac9SDaniel Bristot de Oliveira { 640*bce29ac9SDaniel Bristot de Oliveira unregister_trace_softirq_entry(trace_softirq_entry_callback, NULL); 641*bce29ac9SDaniel Bristot de Oliveira unregister_trace_softirq_exit(trace_softirq_exit_callback, NULL); 642*bce29ac9SDaniel Bristot de Oliveira } 643*bce29ac9SDaniel Bristot de Oliveira #else /* CONFIG_PREEMPT_RT */ 644*bce29ac9SDaniel Bristot de Oliveira /* 645*bce29ac9SDaniel Bristot de Oliveira * softirq are threads on the PREEMPT_RT mode. 646*bce29ac9SDaniel Bristot de Oliveira */ 647*bce29ac9SDaniel Bristot de Oliveira static int hook_softirq_events(void) 648*bce29ac9SDaniel Bristot de Oliveira { 649*bce29ac9SDaniel Bristot de Oliveira return 0; 650*bce29ac9SDaniel Bristot de Oliveira } 651*bce29ac9SDaniel Bristot de Oliveira static void unhook_softirq_events(void) 652*bce29ac9SDaniel Bristot de Oliveira { 653*bce29ac9SDaniel Bristot de Oliveira } 654*bce29ac9SDaniel Bristot de Oliveira #endif 655*bce29ac9SDaniel Bristot de Oliveira 656*bce29ac9SDaniel Bristot de Oliveira /* 657*bce29ac9SDaniel Bristot de Oliveira * thread_entry - Record the starting of a thread noise window 658*bce29ac9SDaniel Bristot de Oliveira * 659*bce29ac9SDaniel Bristot de Oliveira * It saves the context switch time for a noisy thread, and increments 660*bce29ac9SDaniel Bristot de Oliveira * the interference counters. 661*bce29ac9SDaniel Bristot de Oliveira */ 662*bce29ac9SDaniel Bristot de Oliveira static void 663*bce29ac9SDaniel Bristot de Oliveira thread_entry(struct osnoise_variables *osn_var, struct task_struct *t) 664*bce29ac9SDaniel Bristot de Oliveira { 665*bce29ac9SDaniel Bristot de Oliveira if (!osn_var->sampling) 666*bce29ac9SDaniel Bristot de Oliveira return; 667*bce29ac9SDaniel Bristot de Oliveira /* 668*bce29ac9SDaniel Bristot de Oliveira * The arrival time will be used in the report, but not to compute 669*bce29ac9SDaniel Bristot de Oliveira * the execution time, so it is safe to get it unsafe. 670*bce29ac9SDaniel Bristot de Oliveira */ 671*bce29ac9SDaniel Bristot de Oliveira osn_var->thread.arrival_time = time_get(); 672*bce29ac9SDaniel Bristot de Oliveira 673*bce29ac9SDaniel Bristot de Oliveira set_int_safe_time(osn_var, &osn_var->thread.delta_start); 674*bce29ac9SDaniel Bristot de Oliveira 675*bce29ac9SDaniel Bristot de Oliveira osn_var->thread.count++; 676*bce29ac9SDaniel Bristot de Oliveira local_inc(&osn_var->int_counter); 677*bce29ac9SDaniel Bristot de Oliveira } 678*bce29ac9SDaniel Bristot de Oliveira 679*bce29ac9SDaniel Bristot de Oliveira /* 680*bce29ac9SDaniel Bristot de Oliveira * thread_exit - Report the end of a thread noise window 681*bce29ac9SDaniel Bristot de Oliveira * 682*bce29ac9SDaniel Bristot de Oliveira * It computes the total noise from a thread, tracing if needed. 683*bce29ac9SDaniel Bristot de Oliveira */ 684*bce29ac9SDaniel Bristot de Oliveira static void 685*bce29ac9SDaniel Bristot de Oliveira thread_exit(struct osnoise_variables *osn_var, struct task_struct *t) 686*bce29ac9SDaniel Bristot de Oliveira { 687*bce29ac9SDaniel Bristot de Oliveira int duration; 688*bce29ac9SDaniel Bristot de Oliveira 689*bce29ac9SDaniel Bristot de Oliveira if (!osn_var->sampling) 690*bce29ac9SDaniel Bristot de Oliveira return; 691*bce29ac9SDaniel Bristot de Oliveira 692*bce29ac9SDaniel Bristot de Oliveira duration = get_int_safe_duration(osn_var, &osn_var->thread.delta_start); 693*bce29ac9SDaniel Bristot de Oliveira 694*bce29ac9SDaniel Bristot de Oliveira trace_thread_noise(t, osn_var->thread.arrival_time, duration); 695*bce29ac9SDaniel Bristot de Oliveira 696*bce29ac9SDaniel Bristot de Oliveira osn_var->thread.arrival_time = 0; 697*bce29ac9SDaniel Bristot de Oliveira } 698*bce29ac9SDaniel Bristot de Oliveira 699*bce29ac9SDaniel Bristot de Oliveira /* 700*bce29ac9SDaniel Bristot de Oliveira * trace_sched_switch - sched:sched_switch trace event handler 701*bce29ac9SDaniel Bristot de Oliveira * 702*bce29ac9SDaniel Bristot de Oliveira * This function is hooked to the sched:sched_switch trace event, and it is 703*bce29ac9SDaniel Bristot de Oliveira * used to record the beginning and to report the end of a thread noise window. 704*bce29ac9SDaniel Bristot de Oliveira */ 705*bce29ac9SDaniel Bristot de Oliveira void 706*bce29ac9SDaniel Bristot de Oliveira trace_sched_switch_callback(void *data, bool preempt, struct task_struct *p, 707*bce29ac9SDaniel Bristot de Oliveira struct task_struct *n) 708*bce29ac9SDaniel Bristot de Oliveira { 709*bce29ac9SDaniel Bristot de Oliveira struct osnoise_variables *osn_var = this_cpu_osn_var(); 710*bce29ac9SDaniel Bristot de Oliveira 711*bce29ac9SDaniel Bristot de Oliveira if (p->pid != osn_var->pid) 712*bce29ac9SDaniel Bristot de Oliveira thread_exit(osn_var, p); 713*bce29ac9SDaniel Bristot de Oliveira 714*bce29ac9SDaniel Bristot de Oliveira if (n->pid != osn_var->pid) 715*bce29ac9SDaniel Bristot de Oliveira thread_entry(osn_var, n); 716*bce29ac9SDaniel Bristot de Oliveira } 717*bce29ac9SDaniel Bristot de Oliveira 718*bce29ac9SDaniel Bristot de Oliveira /* 719*bce29ac9SDaniel Bristot de Oliveira * hook_thread_events - Hook the insturmentation for thread noise 720*bce29ac9SDaniel Bristot de Oliveira * 721*bce29ac9SDaniel Bristot de Oliveira * Hook the osnoise tracer callbacks to handle the noise from other 722*bce29ac9SDaniel Bristot de Oliveira * threads on the necessary kernel events. 723*bce29ac9SDaniel Bristot de Oliveira */ 724*bce29ac9SDaniel Bristot de Oliveira int hook_thread_events(void) 725*bce29ac9SDaniel Bristot de Oliveira { 726*bce29ac9SDaniel Bristot de Oliveira int ret; 727*bce29ac9SDaniel Bristot de Oliveira 728*bce29ac9SDaniel Bristot de Oliveira ret = register_trace_sched_switch(trace_sched_switch_callback, NULL); 729*bce29ac9SDaniel Bristot de Oliveira if (ret) 730*bce29ac9SDaniel Bristot de Oliveira return -EINVAL; 731*bce29ac9SDaniel Bristot de Oliveira 732*bce29ac9SDaniel Bristot de Oliveira return 0; 733*bce29ac9SDaniel Bristot de Oliveira } 734*bce29ac9SDaniel Bristot de Oliveira 735*bce29ac9SDaniel Bristot de Oliveira /* 736*bce29ac9SDaniel Bristot de Oliveira * unhook_thread_events - *nhook the insturmentation for thread noise 737*bce29ac9SDaniel Bristot de Oliveira * 738*bce29ac9SDaniel Bristot de Oliveira * Unook the osnoise tracer callbacks to handle the noise from other 739*bce29ac9SDaniel Bristot de Oliveira * threads on the necessary kernel events. 740*bce29ac9SDaniel Bristot de Oliveira */ 741*bce29ac9SDaniel Bristot de Oliveira void unhook_thread_events(void) 742*bce29ac9SDaniel Bristot de Oliveira { 743*bce29ac9SDaniel Bristot de Oliveira unregister_trace_sched_switch(trace_sched_switch_callback, NULL); 744*bce29ac9SDaniel Bristot de Oliveira } 745*bce29ac9SDaniel Bristot de Oliveira 746*bce29ac9SDaniel Bristot de Oliveira /* 747*bce29ac9SDaniel Bristot de Oliveira * save_osn_sample_stats - Save the osnoise_sample statistics 748*bce29ac9SDaniel Bristot de Oliveira * 749*bce29ac9SDaniel Bristot de Oliveira * Save the osnoise_sample statistics before the sampling phase. These 750*bce29ac9SDaniel Bristot de Oliveira * values will be used later to compute the diff betwneen the statistics 751*bce29ac9SDaniel Bristot de Oliveira * before and after the osnoise sampling. 752*bce29ac9SDaniel Bristot de Oliveira */ 753*bce29ac9SDaniel Bristot de Oliveira void save_osn_sample_stats(struct osnoise_variables *osn_var, struct osnoise_sample *s) 754*bce29ac9SDaniel Bristot de Oliveira { 755*bce29ac9SDaniel Bristot de Oliveira s->nmi_count = osn_var->nmi.count; 756*bce29ac9SDaniel Bristot de Oliveira s->irq_count = osn_var->irq.count; 757*bce29ac9SDaniel Bristot de Oliveira s->softirq_count = osn_var->softirq.count; 758*bce29ac9SDaniel Bristot de Oliveira s->thread_count = osn_var->thread.count; 759*bce29ac9SDaniel Bristot de Oliveira } 760*bce29ac9SDaniel Bristot de Oliveira 761*bce29ac9SDaniel Bristot de Oliveira /* 762*bce29ac9SDaniel Bristot de Oliveira * diff_osn_sample_stats - Compute the osnoise_sample statistics 763*bce29ac9SDaniel Bristot de Oliveira * 764*bce29ac9SDaniel Bristot de Oliveira * After a sample period, compute the difference on the osnoise_sample 765*bce29ac9SDaniel Bristot de Oliveira * statistics. The struct osnoise_sample *s contains the statistics saved via 766*bce29ac9SDaniel Bristot de Oliveira * save_osn_sample_stats() before the osnoise sampling. 767*bce29ac9SDaniel Bristot de Oliveira */ 768*bce29ac9SDaniel Bristot de Oliveira void diff_osn_sample_stats(struct osnoise_variables *osn_var, struct osnoise_sample *s) 769*bce29ac9SDaniel Bristot de Oliveira { 770*bce29ac9SDaniel Bristot de Oliveira s->nmi_count = osn_var->nmi.count - s->nmi_count; 771*bce29ac9SDaniel Bristot de Oliveira s->irq_count = osn_var->irq.count - s->irq_count; 772*bce29ac9SDaniel Bristot de Oliveira s->softirq_count = osn_var->softirq.count - s->softirq_count; 773*bce29ac9SDaniel Bristot de Oliveira s->thread_count = osn_var->thread.count - s->thread_count; 774*bce29ac9SDaniel Bristot de Oliveira } 775*bce29ac9SDaniel Bristot de Oliveira 776*bce29ac9SDaniel Bristot de Oliveira /* 777*bce29ac9SDaniel Bristot de Oliveira * osnoise_stop_tracing - Stop tracing and the tracer. 778*bce29ac9SDaniel Bristot de Oliveira */ 779*bce29ac9SDaniel Bristot de Oliveira static void osnoise_stop_tracing(void) 780*bce29ac9SDaniel Bristot de Oliveira { 781*bce29ac9SDaniel Bristot de Oliveira struct trace_array *tr = osnoise_trace; 782*bce29ac9SDaniel Bristot de Oliveira tracer_tracing_off(tr); 783*bce29ac9SDaniel Bristot de Oliveira } 784*bce29ac9SDaniel Bristot de Oliveira 785*bce29ac9SDaniel Bristot de Oliveira /* 786*bce29ac9SDaniel Bristot de Oliveira * run_osnoise - Sample the time and look for osnoise 787*bce29ac9SDaniel Bristot de Oliveira * 788*bce29ac9SDaniel Bristot de Oliveira * Used to capture the time, looking for potential osnoise latency repeatedly. 789*bce29ac9SDaniel Bristot de Oliveira * Different from hwlat_detector, it is called with preemption and interrupts 790*bce29ac9SDaniel Bristot de Oliveira * enabled. This allows irqs, softirqs and threads to run, interfering on the 791*bce29ac9SDaniel Bristot de Oliveira * osnoise sampling thread, as they would do with a regular thread. 792*bce29ac9SDaniel Bristot de Oliveira */ 793*bce29ac9SDaniel Bristot de Oliveira static int run_osnoise(void) 794*bce29ac9SDaniel Bristot de Oliveira { 795*bce29ac9SDaniel Bristot de Oliveira struct osnoise_variables *osn_var = this_cpu_osn_var(); 796*bce29ac9SDaniel Bristot de Oliveira u64 noise = 0, sum_noise = 0, max_noise = 0; 797*bce29ac9SDaniel Bristot de Oliveira struct trace_array *tr = osnoise_trace; 798*bce29ac9SDaniel Bristot de Oliveira u64 start, sample, last_sample; 799*bce29ac9SDaniel Bristot de Oliveira u64 last_int_count, int_count; 800*bce29ac9SDaniel Bristot de Oliveira s64 total, last_total = 0; 801*bce29ac9SDaniel Bristot de Oliveira struct osnoise_sample s; 802*bce29ac9SDaniel Bristot de Oliveira unsigned int threshold; 803*bce29ac9SDaniel Bristot de Oliveira int hw_count = 0; 804*bce29ac9SDaniel Bristot de Oliveira u64 runtime, stop_in; 805*bce29ac9SDaniel Bristot de Oliveira int ret = -1; 806*bce29ac9SDaniel Bristot de Oliveira 807*bce29ac9SDaniel Bristot de Oliveira /* 808*bce29ac9SDaniel Bristot de Oliveira * Considers the current thread as the workload. 809*bce29ac9SDaniel Bristot de Oliveira */ 810*bce29ac9SDaniel Bristot de Oliveira osn_var->pid = current->pid; 811*bce29ac9SDaniel Bristot de Oliveira 812*bce29ac9SDaniel Bristot de Oliveira /* 813*bce29ac9SDaniel Bristot de Oliveira * Save the current stats for the diff 814*bce29ac9SDaniel Bristot de Oliveira */ 815*bce29ac9SDaniel Bristot de Oliveira save_osn_sample_stats(osn_var, &s); 816*bce29ac9SDaniel Bristot de Oliveira 817*bce29ac9SDaniel Bristot de Oliveira /* 818*bce29ac9SDaniel Bristot de Oliveira * if threshold is 0, use the default value of 5 us. 819*bce29ac9SDaniel Bristot de Oliveira */ 820*bce29ac9SDaniel Bristot de Oliveira threshold = tracing_thresh ? : 5000; 821*bce29ac9SDaniel Bristot de Oliveira 822*bce29ac9SDaniel Bristot de Oliveira /* 823*bce29ac9SDaniel Bristot de Oliveira * Make sure NMIs see sampling first 824*bce29ac9SDaniel Bristot de Oliveira */ 825*bce29ac9SDaniel Bristot de Oliveira osn_var->sampling = true; 826*bce29ac9SDaniel Bristot de Oliveira barrier(); 827*bce29ac9SDaniel Bristot de Oliveira 828*bce29ac9SDaniel Bristot de Oliveira /* 829*bce29ac9SDaniel Bristot de Oliveira * Transform the *_us config to nanoseconds to avoid the 830*bce29ac9SDaniel Bristot de Oliveira * division on the main loop. 831*bce29ac9SDaniel Bristot de Oliveira */ 832*bce29ac9SDaniel Bristot de Oliveira runtime = osnoise_data.sample_runtime * NSEC_PER_USEC; 833*bce29ac9SDaniel Bristot de Oliveira stop_in = osnoise_data.stop_tracing * NSEC_PER_USEC; 834*bce29ac9SDaniel Bristot de Oliveira 835*bce29ac9SDaniel Bristot de Oliveira /* 836*bce29ac9SDaniel Bristot de Oliveira * Start timestemp 837*bce29ac9SDaniel Bristot de Oliveira */ 838*bce29ac9SDaniel Bristot de Oliveira start = time_get(); 839*bce29ac9SDaniel Bristot de Oliveira 840*bce29ac9SDaniel Bristot de Oliveira /* 841*bce29ac9SDaniel Bristot de Oliveira * "previous" loop. 842*bce29ac9SDaniel Bristot de Oliveira */ 843*bce29ac9SDaniel Bristot de Oliveira last_int_count = set_int_safe_time(osn_var, &last_sample); 844*bce29ac9SDaniel Bristot de Oliveira 845*bce29ac9SDaniel Bristot de Oliveira do { 846*bce29ac9SDaniel Bristot de Oliveira /* 847*bce29ac9SDaniel Bristot de Oliveira * Get sample! 848*bce29ac9SDaniel Bristot de Oliveira */ 849*bce29ac9SDaniel Bristot de Oliveira int_count = set_int_safe_time(osn_var, &sample); 850*bce29ac9SDaniel Bristot de Oliveira 851*bce29ac9SDaniel Bristot de Oliveira noise = time_sub(sample, last_sample); 852*bce29ac9SDaniel Bristot de Oliveira 853*bce29ac9SDaniel Bristot de Oliveira /* 854*bce29ac9SDaniel Bristot de Oliveira * This shouldn't happen. 855*bce29ac9SDaniel Bristot de Oliveira */ 856*bce29ac9SDaniel Bristot de Oliveira if (noise < 0) { 857*bce29ac9SDaniel Bristot de Oliveira osnoise_taint("negative noise!"); 858*bce29ac9SDaniel Bristot de Oliveira goto out; 859*bce29ac9SDaniel Bristot de Oliveira } 860*bce29ac9SDaniel Bristot de Oliveira 861*bce29ac9SDaniel Bristot de Oliveira /* 862*bce29ac9SDaniel Bristot de Oliveira * Sample runtime. 863*bce29ac9SDaniel Bristot de Oliveira */ 864*bce29ac9SDaniel Bristot de Oliveira total = time_sub(sample, start); 865*bce29ac9SDaniel Bristot de Oliveira 866*bce29ac9SDaniel Bristot de Oliveira /* 867*bce29ac9SDaniel Bristot de Oliveira * Check for possible overflows. 868*bce29ac9SDaniel Bristot de Oliveira */ 869*bce29ac9SDaniel Bristot de Oliveira if (total < last_total) { 870*bce29ac9SDaniel Bristot de Oliveira osnoise_taint("total overflow!"); 871*bce29ac9SDaniel Bristot de Oliveira break; 872*bce29ac9SDaniel Bristot de Oliveira } 873*bce29ac9SDaniel Bristot de Oliveira 874*bce29ac9SDaniel Bristot de Oliveira last_total = total; 875*bce29ac9SDaniel Bristot de Oliveira 876*bce29ac9SDaniel Bristot de Oliveira if (noise >= threshold) { 877*bce29ac9SDaniel Bristot de Oliveira int interference = int_count - last_int_count; 878*bce29ac9SDaniel Bristot de Oliveira 879*bce29ac9SDaniel Bristot de Oliveira if (noise > max_noise) 880*bce29ac9SDaniel Bristot de Oliveira max_noise = noise; 881*bce29ac9SDaniel Bristot de Oliveira 882*bce29ac9SDaniel Bristot de Oliveira if (!interference) 883*bce29ac9SDaniel Bristot de Oliveira hw_count++; 884*bce29ac9SDaniel Bristot de Oliveira 885*bce29ac9SDaniel Bristot de Oliveira sum_noise += noise; 886*bce29ac9SDaniel Bristot de Oliveira 887*bce29ac9SDaniel Bristot de Oliveira trace_sample_threshold(last_sample, noise, interference); 888*bce29ac9SDaniel Bristot de Oliveira 889*bce29ac9SDaniel Bristot de Oliveira if (osnoise_data.stop_tracing) 890*bce29ac9SDaniel Bristot de Oliveira if (noise > stop_in) 891*bce29ac9SDaniel Bristot de Oliveira osnoise_stop_tracing(); 892*bce29ac9SDaniel Bristot de Oliveira } 893*bce29ac9SDaniel Bristot de Oliveira 894*bce29ac9SDaniel Bristot de Oliveira /* 895*bce29ac9SDaniel Bristot de Oliveira * For the non-preemptive kernel config: let threads runs, if 896*bce29ac9SDaniel Bristot de Oliveira * they so wish. 897*bce29ac9SDaniel Bristot de Oliveira */ 898*bce29ac9SDaniel Bristot de Oliveira cond_resched(); 899*bce29ac9SDaniel Bristot de Oliveira 900*bce29ac9SDaniel Bristot de Oliveira last_sample = sample; 901*bce29ac9SDaniel Bristot de Oliveira last_int_count = int_count; 902*bce29ac9SDaniel Bristot de Oliveira 903*bce29ac9SDaniel Bristot de Oliveira } while (total < runtime && !kthread_should_stop()); 904*bce29ac9SDaniel Bristot de Oliveira 905*bce29ac9SDaniel Bristot de Oliveira /* 906*bce29ac9SDaniel Bristot de Oliveira * Finish the above in the view for interrupts. 907*bce29ac9SDaniel Bristot de Oliveira */ 908*bce29ac9SDaniel Bristot de Oliveira barrier(); 909*bce29ac9SDaniel Bristot de Oliveira 910*bce29ac9SDaniel Bristot de Oliveira osn_var->sampling = false; 911*bce29ac9SDaniel Bristot de Oliveira 912*bce29ac9SDaniel Bristot de Oliveira /* 913*bce29ac9SDaniel Bristot de Oliveira * Make sure sampling data is no longer updated. 914*bce29ac9SDaniel Bristot de Oliveira */ 915*bce29ac9SDaniel Bristot de Oliveira barrier(); 916*bce29ac9SDaniel Bristot de Oliveira 917*bce29ac9SDaniel Bristot de Oliveira /* 918*bce29ac9SDaniel Bristot de Oliveira * Save noise info. 919*bce29ac9SDaniel Bristot de Oliveira */ 920*bce29ac9SDaniel Bristot de Oliveira s.noise = time_to_us(sum_noise); 921*bce29ac9SDaniel Bristot de Oliveira s.runtime = time_to_us(total); 922*bce29ac9SDaniel Bristot de Oliveira s.max_sample = time_to_us(max_noise); 923*bce29ac9SDaniel Bristot de Oliveira s.hw_count = hw_count; 924*bce29ac9SDaniel Bristot de Oliveira 925*bce29ac9SDaniel Bristot de Oliveira /* Save interference stats info */ 926*bce29ac9SDaniel Bristot de Oliveira diff_osn_sample_stats(osn_var, &s); 927*bce29ac9SDaniel Bristot de Oliveira 928*bce29ac9SDaniel Bristot de Oliveira trace_osnoise_sample(&s); 929*bce29ac9SDaniel Bristot de Oliveira 930*bce29ac9SDaniel Bristot de Oliveira /* Keep a running maximum ever recorded osnoise "latency" */ 931*bce29ac9SDaniel Bristot de Oliveira if (max_noise > tr->max_latency) { 932*bce29ac9SDaniel Bristot de Oliveira tr->max_latency = max_noise; 933*bce29ac9SDaniel Bristot de Oliveira latency_fsnotify(tr); 934*bce29ac9SDaniel Bristot de Oliveira } 935*bce29ac9SDaniel Bristot de Oliveira 936*bce29ac9SDaniel Bristot de Oliveira if (osnoise_data.stop_tracing_total) 937*bce29ac9SDaniel Bristot de Oliveira if (s.noise > osnoise_data.stop_tracing_total) 938*bce29ac9SDaniel Bristot de Oliveira osnoise_stop_tracing(); 939*bce29ac9SDaniel Bristot de Oliveira 940*bce29ac9SDaniel Bristot de Oliveira return 0; 941*bce29ac9SDaniel Bristot de Oliveira out: 942*bce29ac9SDaniel Bristot de Oliveira return ret; 943*bce29ac9SDaniel Bristot de Oliveira } 944*bce29ac9SDaniel Bristot de Oliveira 945*bce29ac9SDaniel Bristot de Oliveira static struct cpumask osnoise_cpumask; 946*bce29ac9SDaniel Bristot de Oliveira static struct cpumask save_cpumask; 947*bce29ac9SDaniel Bristot de Oliveira 948*bce29ac9SDaniel Bristot de Oliveira /* 949*bce29ac9SDaniel Bristot de Oliveira * osnoise_main - The osnoise detection kernel thread 950*bce29ac9SDaniel Bristot de Oliveira * 951*bce29ac9SDaniel Bristot de Oliveira * Calls run_osnoise() function to measure the osnoise for the configured runtime, 952*bce29ac9SDaniel Bristot de Oliveira * every period. 953*bce29ac9SDaniel Bristot de Oliveira */ 954*bce29ac9SDaniel Bristot de Oliveira static int osnoise_main(void *data) 955*bce29ac9SDaniel Bristot de Oliveira { 956*bce29ac9SDaniel Bristot de Oliveira s64 interval; 957*bce29ac9SDaniel Bristot de Oliveira 958*bce29ac9SDaniel Bristot de Oliveira while (!kthread_should_stop()) { 959*bce29ac9SDaniel Bristot de Oliveira 960*bce29ac9SDaniel Bristot de Oliveira run_osnoise(); 961*bce29ac9SDaniel Bristot de Oliveira 962*bce29ac9SDaniel Bristot de Oliveira mutex_lock(&interface_lock); 963*bce29ac9SDaniel Bristot de Oliveira interval = osnoise_data.sample_period - osnoise_data.sample_runtime; 964*bce29ac9SDaniel Bristot de Oliveira mutex_unlock(&interface_lock); 965*bce29ac9SDaniel Bristot de Oliveira 966*bce29ac9SDaniel Bristot de Oliveira do_div(interval, USEC_PER_MSEC); 967*bce29ac9SDaniel Bristot de Oliveira 968*bce29ac9SDaniel Bristot de Oliveira /* 969*bce29ac9SDaniel Bristot de Oliveira * differently from hwlat_detector, the osnoise tracer can run 970*bce29ac9SDaniel Bristot de Oliveira * without a pause because preemption is on. 971*bce29ac9SDaniel Bristot de Oliveira */ 972*bce29ac9SDaniel Bristot de Oliveira if (interval < 1) 973*bce29ac9SDaniel Bristot de Oliveira continue; 974*bce29ac9SDaniel Bristot de Oliveira 975*bce29ac9SDaniel Bristot de Oliveira if (msleep_interruptible(interval)) 976*bce29ac9SDaniel Bristot de Oliveira break; 977*bce29ac9SDaniel Bristot de Oliveira } 978*bce29ac9SDaniel Bristot de Oliveira 979*bce29ac9SDaniel Bristot de Oliveira return 0; 980*bce29ac9SDaniel Bristot de Oliveira } 981*bce29ac9SDaniel Bristot de Oliveira 982*bce29ac9SDaniel Bristot de Oliveira /* 983*bce29ac9SDaniel Bristot de Oliveira * stop_per_cpu_kthread - stop per-cpu threads 984*bce29ac9SDaniel Bristot de Oliveira * 985*bce29ac9SDaniel Bristot de Oliveira * Stop the osnoise sampling htread. Use this on unload and at system 986*bce29ac9SDaniel Bristot de Oliveira * shutdown. 987*bce29ac9SDaniel Bristot de Oliveira */ 988*bce29ac9SDaniel Bristot de Oliveira static void stop_per_cpu_kthreads(void) 989*bce29ac9SDaniel Bristot de Oliveira { 990*bce29ac9SDaniel Bristot de Oliveira struct task_struct *kthread; 991*bce29ac9SDaniel Bristot de Oliveira int cpu; 992*bce29ac9SDaniel Bristot de Oliveira 993*bce29ac9SDaniel Bristot de Oliveira for_each_online_cpu(cpu) { 994*bce29ac9SDaniel Bristot de Oliveira kthread = per_cpu(per_cpu_osnoise_var, cpu).kthread; 995*bce29ac9SDaniel Bristot de Oliveira if (kthread) 996*bce29ac9SDaniel Bristot de Oliveira kthread_stop(kthread); 997*bce29ac9SDaniel Bristot de Oliveira per_cpu(per_cpu_osnoise_var, cpu).kthread = NULL; 998*bce29ac9SDaniel Bristot de Oliveira } 999*bce29ac9SDaniel Bristot de Oliveira } 1000*bce29ac9SDaniel Bristot de Oliveira 1001*bce29ac9SDaniel Bristot de Oliveira /* 1002*bce29ac9SDaniel Bristot de Oliveira * start_per_cpu_kthread - Kick off per-cpu osnoise sampling kthreads 1003*bce29ac9SDaniel Bristot de Oliveira * 1004*bce29ac9SDaniel Bristot de Oliveira * This starts the kernel thread that will look for osnoise on many 1005*bce29ac9SDaniel Bristot de Oliveira * cpus. 1006*bce29ac9SDaniel Bristot de Oliveira */ 1007*bce29ac9SDaniel Bristot de Oliveira static int start_per_cpu_kthreads(struct trace_array *tr) 1008*bce29ac9SDaniel Bristot de Oliveira { 1009*bce29ac9SDaniel Bristot de Oliveira struct cpumask *current_mask = &save_cpumask; 1010*bce29ac9SDaniel Bristot de Oliveira struct task_struct *kthread; 1011*bce29ac9SDaniel Bristot de Oliveira char comm[24]; 1012*bce29ac9SDaniel Bristot de Oliveira int cpu; 1013*bce29ac9SDaniel Bristot de Oliveira 1014*bce29ac9SDaniel Bristot de Oliveira get_online_cpus(); 1015*bce29ac9SDaniel Bristot de Oliveira /* 1016*bce29ac9SDaniel Bristot de Oliveira * Run only on CPUs in which trace and osnoise are allowed to run. 1017*bce29ac9SDaniel Bristot de Oliveira */ 1018*bce29ac9SDaniel Bristot de Oliveira cpumask_and(current_mask, tr->tracing_cpumask, &osnoise_cpumask); 1019*bce29ac9SDaniel Bristot de Oliveira /* 1020*bce29ac9SDaniel Bristot de Oliveira * And the CPU is online. 1021*bce29ac9SDaniel Bristot de Oliveira */ 1022*bce29ac9SDaniel Bristot de Oliveira cpumask_and(current_mask, cpu_online_mask, current_mask); 1023*bce29ac9SDaniel Bristot de Oliveira put_online_cpus(); 1024*bce29ac9SDaniel Bristot de Oliveira 1025*bce29ac9SDaniel Bristot de Oliveira for_each_online_cpu(cpu) 1026*bce29ac9SDaniel Bristot de Oliveira per_cpu(per_cpu_osnoise_var, cpu).kthread = NULL; 1027*bce29ac9SDaniel Bristot de Oliveira 1028*bce29ac9SDaniel Bristot de Oliveira for_each_cpu(cpu, current_mask) { 1029*bce29ac9SDaniel Bristot de Oliveira snprintf(comm, 24, "osnoise/%d", cpu); 1030*bce29ac9SDaniel Bristot de Oliveira 1031*bce29ac9SDaniel Bristot de Oliveira kthread = kthread_create_on_cpu(osnoise_main, NULL, cpu, comm); 1032*bce29ac9SDaniel Bristot de Oliveira 1033*bce29ac9SDaniel Bristot de Oliveira if (IS_ERR(kthread)) { 1034*bce29ac9SDaniel Bristot de Oliveira pr_err(BANNER "could not start sampling thread\n"); 1035*bce29ac9SDaniel Bristot de Oliveira stop_per_cpu_kthreads(); 1036*bce29ac9SDaniel Bristot de Oliveira return -ENOMEM; 1037*bce29ac9SDaniel Bristot de Oliveira } 1038*bce29ac9SDaniel Bristot de Oliveira 1039*bce29ac9SDaniel Bristot de Oliveira per_cpu(per_cpu_osnoise_var, cpu).kthread = kthread; 1040*bce29ac9SDaniel Bristot de Oliveira wake_up_process(kthread); 1041*bce29ac9SDaniel Bristot de Oliveira } 1042*bce29ac9SDaniel Bristot de Oliveira 1043*bce29ac9SDaniel Bristot de Oliveira return 0; 1044*bce29ac9SDaniel Bristot de Oliveira } 1045*bce29ac9SDaniel Bristot de Oliveira 1046*bce29ac9SDaniel Bristot de Oliveira /* 1047*bce29ac9SDaniel Bristot de Oliveira * osnoise_cpus_read - Read function for reading the "cpus" file 1048*bce29ac9SDaniel Bristot de Oliveira * @filp: The active open file structure 1049*bce29ac9SDaniel Bristot de Oliveira * @ubuf: The userspace provided buffer to read value into 1050*bce29ac9SDaniel Bristot de Oliveira * @cnt: The maximum number of bytes to read 1051*bce29ac9SDaniel Bristot de Oliveira * @ppos: The current "file" position 1052*bce29ac9SDaniel Bristot de Oliveira * 1053*bce29ac9SDaniel Bristot de Oliveira * Prints the "cpus" output into the user-provided buffer. 1054*bce29ac9SDaniel Bristot de Oliveira */ 1055*bce29ac9SDaniel Bristot de Oliveira static ssize_t 1056*bce29ac9SDaniel Bristot de Oliveira osnoise_cpus_read(struct file *filp, char __user *ubuf, size_t count, 1057*bce29ac9SDaniel Bristot de Oliveira loff_t *ppos) 1058*bce29ac9SDaniel Bristot de Oliveira { 1059*bce29ac9SDaniel Bristot de Oliveira char *mask_str; 1060*bce29ac9SDaniel Bristot de Oliveira int len; 1061*bce29ac9SDaniel Bristot de Oliveira 1062*bce29ac9SDaniel Bristot de Oliveira mutex_lock(&interface_lock); 1063*bce29ac9SDaniel Bristot de Oliveira 1064*bce29ac9SDaniel Bristot de Oliveira len = snprintf(NULL, 0, "%*pbl\n", cpumask_pr_args(&osnoise_cpumask)) + 1; 1065*bce29ac9SDaniel Bristot de Oliveira mask_str = kmalloc(len, GFP_KERNEL); 1066*bce29ac9SDaniel Bristot de Oliveira if (!mask_str) { 1067*bce29ac9SDaniel Bristot de Oliveira count = -ENOMEM; 1068*bce29ac9SDaniel Bristot de Oliveira goto out_unlock; 1069*bce29ac9SDaniel Bristot de Oliveira } 1070*bce29ac9SDaniel Bristot de Oliveira 1071*bce29ac9SDaniel Bristot de Oliveira len = snprintf(mask_str, len, "%*pbl\n", cpumask_pr_args(&osnoise_cpumask)); 1072*bce29ac9SDaniel Bristot de Oliveira if (len >= count) { 1073*bce29ac9SDaniel Bristot de Oliveira count = -EINVAL; 1074*bce29ac9SDaniel Bristot de Oliveira goto out_free; 1075*bce29ac9SDaniel Bristot de Oliveira } 1076*bce29ac9SDaniel Bristot de Oliveira 1077*bce29ac9SDaniel Bristot de Oliveira count = simple_read_from_buffer(ubuf, count, ppos, mask_str, len); 1078*bce29ac9SDaniel Bristot de Oliveira 1079*bce29ac9SDaniel Bristot de Oliveira out_free: 1080*bce29ac9SDaniel Bristot de Oliveira kfree(mask_str); 1081*bce29ac9SDaniel Bristot de Oliveira out_unlock: 1082*bce29ac9SDaniel Bristot de Oliveira mutex_unlock(&interface_lock); 1083*bce29ac9SDaniel Bristot de Oliveira 1084*bce29ac9SDaniel Bristot de Oliveira return count; 1085*bce29ac9SDaniel Bristot de Oliveira } 1086*bce29ac9SDaniel Bristot de Oliveira 1087*bce29ac9SDaniel Bristot de Oliveira static void osnoise_tracer_start(struct trace_array *tr); 1088*bce29ac9SDaniel Bristot de Oliveira static void osnoise_tracer_stop(struct trace_array *tr); 1089*bce29ac9SDaniel Bristot de Oliveira 1090*bce29ac9SDaniel Bristot de Oliveira /* 1091*bce29ac9SDaniel Bristot de Oliveira * osnoise_cpus_write - Write function for "cpus" entry 1092*bce29ac9SDaniel Bristot de Oliveira * @filp: The active open file structure 1093*bce29ac9SDaniel Bristot de Oliveira * @ubuf: The user buffer that contains the value to write 1094*bce29ac9SDaniel Bristot de Oliveira * @cnt: The maximum number of bytes to write to "file" 1095*bce29ac9SDaniel Bristot de Oliveira * @ppos: The current position in @file 1096*bce29ac9SDaniel Bristot de Oliveira * 1097*bce29ac9SDaniel Bristot de Oliveira * This function provides a write implementation for the "cpus" 1098*bce29ac9SDaniel Bristot de Oliveira * interface to the osnoise trace. By default, it lists all CPUs, 1099*bce29ac9SDaniel Bristot de Oliveira * in this way, allowing osnoise threads to run on any online CPU 1100*bce29ac9SDaniel Bristot de Oliveira * of the system. It serves to restrict the execution of osnoise to the 1101*bce29ac9SDaniel Bristot de Oliveira * set of CPUs writing via this interface. Note that osnoise also 1102*bce29ac9SDaniel Bristot de Oliveira * respects the "tracing_cpumask." Hence, osnoise threads will run only 1103*bce29ac9SDaniel Bristot de Oliveira * on the set of CPUs allowed here AND on "tracing_cpumask." Why not 1104*bce29ac9SDaniel Bristot de Oliveira * have just "tracing_cpumask?" Because the user might be interested 1105*bce29ac9SDaniel Bristot de Oliveira * in tracing what is running on other CPUs. For instance, one might 1106*bce29ac9SDaniel Bristot de Oliveira * run osnoise in one HT CPU while observing what is running on the 1107*bce29ac9SDaniel Bristot de Oliveira * sibling HT CPU. 1108*bce29ac9SDaniel Bristot de Oliveira */ 1109*bce29ac9SDaniel Bristot de Oliveira static ssize_t 1110*bce29ac9SDaniel Bristot de Oliveira osnoise_cpus_write(struct file *filp, const char __user *ubuf, size_t count, 1111*bce29ac9SDaniel Bristot de Oliveira loff_t *ppos) 1112*bce29ac9SDaniel Bristot de Oliveira { 1113*bce29ac9SDaniel Bristot de Oliveira struct trace_array *tr = osnoise_trace; 1114*bce29ac9SDaniel Bristot de Oliveira cpumask_var_t osnoise_cpumask_new; 1115*bce29ac9SDaniel Bristot de Oliveira int running, err; 1116*bce29ac9SDaniel Bristot de Oliveira char buf[256]; 1117*bce29ac9SDaniel Bristot de Oliveira 1118*bce29ac9SDaniel Bristot de Oliveira if (count >= 256) 1119*bce29ac9SDaniel Bristot de Oliveira return -EINVAL; 1120*bce29ac9SDaniel Bristot de Oliveira 1121*bce29ac9SDaniel Bristot de Oliveira if (copy_from_user(buf, ubuf, count)) 1122*bce29ac9SDaniel Bristot de Oliveira return -EFAULT; 1123*bce29ac9SDaniel Bristot de Oliveira 1124*bce29ac9SDaniel Bristot de Oliveira if (!zalloc_cpumask_var(&osnoise_cpumask_new, GFP_KERNEL)) 1125*bce29ac9SDaniel Bristot de Oliveira return -ENOMEM; 1126*bce29ac9SDaniel Bristot de Oliveira 1127*bce29ac9SDaniel Bristot de Oliveira err = cpulist_parse(buf, osnoise_cpumask_new); 1128*bce29ac9SDaniel Bristot de Oliveira if (err) 1129*bce29ac9SDaniel Bristot de Oliveira goto err_free; 1130*bce29ac9SDaniel Bristot de Oliveira 1131*bce29ac9SDaniel Bristot de Oliveira /* 1132*bce29ac9SDaniel Bristot de Oliveira * trace_types_lock is taken to avoid concurrency on start/stop 1133*bce29ac9SDaniel Bristot de Oliveira * and osnoise_busy. 1134*bce29ac9SDaniel Bristot de Oliveira */ 1135*bce29ac9SDaniel Bristot de Oliveira mutex_lock(&trace_types_lock); 1136*bce29ac9SDaniel Bristot de Oliveira running = osnoise_busy; 1137*bce29ac9SDaniel Bristot de Oliveira if (running) 1138*bce29ac9SDaniel Bristot de Oliveira osnoise_tracer_stop(tr); 1139*bce29ac9SDaniel Bristot de Oliveira 1140*bce29ac9SDaniel Bristot de Oliveira mutex_lock(&interface_lock); 1141*bce29ac9SDaniel Bristot de Oliveira cpumask_copy(&osnoise_cpumask, osnoise_cpumask_new); 1142*bce29ac9SDaniel Bristot de Oliveira mutex_unlock(&interface_lock); 1143*bce29ac9SDaniel Bristot de Oliveira 1144*bce29ac9SDaniel Bristot de Oliveira if (running) 1145*bce29ac9SDaniel Bristot de Oliveira osnoise_tracer_start(tr); 1146*bce29ac9SDaniel Bristot de Oliveira mutex_unlock(&trace_types_lock); 1147*bce29ac9SDaniel Bristot de Oliveira 1148*bce29ac9SDaniel Bristot de Oliveira free_cpumask_var(osnoise_cpumask_new); 1149*bce29ac9SDaniel Bristot de Oliveira return count; 1150*bce29ac9SDaniel Bristot de Oliveira 1151*bce29ac9SDaniel Bristot de Oliveira err_free: 1152*bce29ac9SDaniel Bristot de Oliveira free_cpumask_var(osnoise_cpumask_new); 1153*bce29ac9SDaniel Bristot de Oliveira 1154*bce29ac9SDaniel Bristot de Oliveira return err; 1155*bce29ac9SDaniel Bristot de Oliveira } 1156*bce29ac9SDaniel Bristot de Oliveira 1157*bce29ac9SDaniel Bristot de Oliveira /* 1158*bce29ac9SDaniel Bristot de Oliveira * osnoise/runtime_us: cannot be greater than the period. 1159*bce29ac9SDaniel Bristot de Oliveira */ 1160*bce29ac9SDaniel Bristot de Oliveira static struct trace_min_max_param osnoise_runtime = { 1161*bce29ac9SDaniel Bristot de Oliveira .lock = &interface_lock, 1162*bce29ac9SDaniel Bristot de Oliveira .val = &osnoise_data.sample_runtime, 1163*bce29ac9SDaniel Bristot de Oliveira .max = &osnoise_data.sample_period, 1164*bce29ac9SDaniel Bristot de Oliveira .min = NULL, 1165*bce29ac9SDaniel Bristot de Oliveira }; 1166*bce29ac9SDaniel Bristot de Oliveira 1167*bce29ac9SDaniel Bristot de Oliveira /* 1168*bce29ac9SDaniel Bristot de Oliveira * osnoise/period_us: cannot be smaller than the runtime. 1169*bce29ac9SDaniel Bristot de Oliveira */ 1170*bce29ac9SDaniel Bristot de Oliveira static struct trace_min_max_param osnoise_period = { 1171*bce29ac9SDaniel Bristot de Oliveira .lock = &interface_lock, 1172*bce29ac9SDaniel Bristot de Oliveira .val = &osnoise_data.sample_period, 1173*bce29ac9SDaniel Bristot de Oliveira .max = NULL, 1174*bce29ac9SDaniel Bristot de Oliveira .min = &osnoise_data.sample_runtime, 1175*bce29ac9SDaniel Bristot de Oliveira }; 1176*bce29ac9SDaniel Bristot de Oliveira 1177*bce29ac9SDaniel Bristot de Oliveira /* 1178*bce29ac9SDaniel Bristot de Oliveira * osnoise/stop_tracing_us: no limit. 1179*bce29ac9SDaniel Bristot de Oliveira */ 1180*bce29ac9SDaniel Bristot de Oliveira static struct trace_min_max_param osnoise_stop_tracing_in = { 1181*bce29ac9SDaniel Bristot de Oliveira .lock = &interface_lock, 1182*bce29ac9SDaniel Bristot de Oliveira .val = &osnoise_data.stop_tracing, 1183*bce29ac9SDaniel Bristot de Oliveira .max = NULL, 1184*bce29ac9SDaniel Bristot de Oliveira .min = NULL, 1185*bce29ac9SDaniel Bristot de Oliveira }; 1186*bce29ac9SDaniel Bristot de Oliveira 1187*bce29ac9SDaniel Bristot de Oliveira /* 1188*bce29ac9SDaniel Bristot de Oliveira * osnoise/stop_tracing_total_us: no limit. 1189*bce29ac9SDaniel Bristot de Oliveira */ 1190*bce29ac9SDaniel Bristot de Oliveira static struct trace_min_max_param osnoise_stop_tracing_total = { 1191*bce29ac9SDaniel Bristot de Oliveira .lock = &interface_lock, 1192*bce29ac9SDaniel Bristot de Oliveira .val = &osnoise_data.stop_tracing_total, 1193*bce29ac9SDaniel Bristot de Oliveira .max = NULL, 1194*bce29ac9SDaniel Bristot de Oliveira .min = NULL, 1195*bce29ac9SDaniel Bristot de Oliveira }; 1196*bce29ac9SDaniel Bristot de Oliveira 1197*bce29ac9SDaniel Bristot de Oliveira static const struct file_operations cpus_fops = { 1198*bce29ac9SDaniel Bristot de Oliveira .open = tracing_open_generic, 1199*bce29ac9SDaniel Bristot de Oliveira .read = osnoise_cpus_read, 1200*bce29ac9SDaniel Bristot de Oliveira .write = osnoise_cpus_write, 1201*bce29ac9SDaniel Bristot de Oliveira .llseek = generic_file_llseek, 1202*bce29ac9SDaniel Bristot de Oliveira }; 1203*bce29ac9SDaniel Bristot de Oliveira 1204*bce29ac9SDaniel Bristot de Oliveira /* 1205*bce29ac9SDaniel Bristot de Oliveira * init_tracefs - A function to initialize the tracefs interface files 1206*bce29ac9SDaniel Bristot de Oliveira * 1207*bce29ac9SDaniel Bristot de Oliveira * This function creates entries in tracefs for "osnoise". It creates the 1208*bce29ac9SDaniel Bristot de Oliveira * "osnoise" directory in the tracing directory, and within that 1209*bce29ac9SDaniel Bristot de Oliveira * directory is the count, runtime and period files to change and view 1210*bce29ac9SDaniel Bristot de Oliveira * those values. 1211*bce29ac9SDaniel Bristot de Oliveira */ 1212*bce29ac9SDaniel Bristot de Oliveira static int init_tracefs(void) 1213*bce29ac9SDaniel Bristot de Oliveira { 1214*bce29ac9SDaniel Bristot de Oliveira struct dentry *top_dir; 1215*bce29ac9SDaniel Bristot de Oliveira struct dentry *tmp; 1216*bce29ac9SDaniel Bristot de Oliveira int ret; 1217*bce29ac9SDaniel Bristot de Oliveira 1218*bce29ac9SDaniel Bristot de Oliveira ret = tracing_init_dentry(); 1219*bce29ac9SDaniel Bristot de Oliveira if (ret) 1220*bce29ac9SDaniel Bristot de Oliveira return -ENOMEM; 1221*bce29ac9SDaniel Bristot de Oliveira 1222*bce29ac9SDaniel Bristot de Oliveira top_dir = tracefs_create_dir("osnoise", NULL); 1223*bce29ac9SDaniel Bristot de Oliveira if (!top_dir) 1224*bce29ac9SDaniel Bristot de Oliveira return -ENOMEM; 1225*bce29ac9SDaniel Bristot de Oliveira 1226*bce29ac9SDaniel Bristot de Oliveira tmp = tracefs_create_file("period_us", 0640, top_dir, 1227*bce29ac9SDaniel Bristot de Oliveira &osnoise_period, &trace_min_max_fops); 1228*bce29ac9SDaniel Bristot de Oliveira if (!tmp) 1229*bce29ac9SDaniel Bristot de Oliveira goto err; 1230*bce29ac9SDaniel Bristot de Oliveira 1231*bce29ac9SDaniel Bristot de Oliveira tmp = tracefs_create_file("runtime_us", 0644, top_dir, 1232*bce29ac9SDaniel Bristot de Oliveira &osnoise_runtime, &trace_min_max_fops); 1233*bce29ac9SDaniel Bristot de Oliveira if (!tmp) 1234*bce29ac9SDaniel Bristot de Oliveira goto err; 1235*bce29ac9SDaniel Bristot de Oliveira 1236*bce29ac9SDaniel Bristot de Oliveira tmp = tracefs_create_file("stop_tracing_us", 0640, top_dir, 1237*bce29ac9SDaniel Bristot de Oliveira &osnoise_stop_tracing_in, &trace_min_max_fops); 1238*bce29ac9SDaniel Bristot de Oliveira if (!tmp) 1239*bce29ac9SDaniel Bristot de Oliveira goto err; 1240*bce29ac9SDaniel Bristot de Oliveira 1241*bce29ac9SDaniel Bristot de Oliveira tmp = tracefs_create_file("stop_tracing_total_us", 0640, top_dir, 1242*bce29ac9SDaniel Bristot de Oliveira &osnoise_stop_tracing_total, &trace_min_max_fops); 1243*bce29ac9SDaniel Bristot de Oliveira if (!tmp) 1244*bce29ac9SDaniel Bristot de Oliveira goto err; 1245*bce29ac9SDaniel Bristot de Oliveira 1246*bce29ac9SDaniel Bristot de Oliveira tmp = trace_create_file("cpus", 0644, top_dir, NULL, &cpus_fops); 1247*bce29ac9SDaniel Bristot de Oliveira if (!tmp) 1248*bce29ac9SDaniel Bristot de Oliveira goto err; 1249*bce29ac9SDaniel Bristot de Oliveira 1250*bce29ac9SDaniel Bristot de Oliveira return 0; 1251*bce29ac9SDaniel Bristot de Oliveira 1252*bce29ac9SDaniel Bristot de Oliveira err: 1253*bce29ac9SDaniel Bristot de Oliveira tracefs_remove(top_dir); 1254*bce29ac9SDaniel Bristot de Oliveira return -ENOMEM; 1255*bce29ac9SDaniel Bristot de Oliveira } 1256*bce29ac9SDaniel Bristot de Oliveira 1257*bce29ac9SDaniel Bristot de Oliveira static int osnoise_hook_events(void) 1258*bce29ac9SDaniel Bristot de Oliveira { 1259*bce29ac9SDaniel Bristot de Oliveira int retval; 1260*bce29ac9SDaniel Bristot de Oliveira 1261*bce29ac9SDaniel Bristot de Oliveira /* 1262*bce29ac9SDaniel Bristot de Oliveira * Trace is already hooked, we are re-enabling from 1263*bce29ac9SDaniel Bristot de Oliveira * a stop_tracing_*. 1264*bce29ac9SDaniel Bristot de Oliveira */ 1265*bce29ac9SDaniel Bristot de Oliveira if (trace_osnoise_callback_enabled) 1266*bce29ac9SDaniel Bristot de Oliveira return 0; 1267*bce29ac9SDaniel Bristot de Oliveira 1268*bce29ac9SDaniel Bristot de Oliveira retval = hook_irq_events(); 1269*bce29ac9SDaniel Bristot de Oliveira if (retval) 1270*bce29ac9SDaniel Bristot de Oliveira return -EINVAL; 1271*bce29ac9SDaniel Bristot de Oliveira 1272*bce29ac9SDaniel Bristot de Oliveira retval = hook_softirq_events(); 1273*bce29ac9SDaniel Bristot de Oliveira if (retval) 1274*bce29ac9SDaniel Bristot de Oliveira goto out_unhook_irq; 1275*bce29ac9SDaniel Bristot de Oliveira 1276*bce29ac9SDaniel Bristot de Oliveira retval = hook_thread_events(); 1277*bce29ac9SDaniel Bristot de Oliveira /* 1278*bce29ac9SDaniel Bristot de Oliveira * All fine! 1279*bce29ac9SDaniel Bristot de Oliveira */ 1280*bce29ac9SDaniel Bristot de Oliveira if (!retval) 1281*bce29ac9SDaniel Bristot de Oliveira return 0; 1282*bce29ac9SDaniel Bristot de Oliveira 1283*bce29ac9SDaniel Bristot de Oliveira unhook_softirq_events(); 1284*bce29ac9SDaniel Bristot de Oliveira out_unhook_irq: 1285*bce29ac9SDaniel Bristot de Oliveira unhook_irq_events(); 1286*bce29ac9SDaniel Bristot de Oliveira return -EINVAL; 1287*bce29ac9SDaniel Bristot de Oliveira } 1288*bce29ac9SDaniel Bristot de Oliveira 1289*bce29ac9SDaniel Bristot de Oliveira static void osnoise_tracer_start(struct trace_array *tr) 1290*bce29ac9SDaniel Bristot de Oliveira { 1291*bce29ac9SDaniel Bristot de Oliveira int retval; 1292*bce29ac9SDaniel Bristot de Oliveira 1293*bce29ac9SDaniel Bristot de Oliveira if (osnoise_busy) 1294*bce29ac9SDaniel Bristot de Oliveira return; 1295*bce29ac9SDaniel Bristot de Oliveira 1296*bce29ac9SDaniel Bristot de Oliveira osn_var_reset_all(); 1297*bce29ac9SDaniel Bristot de Oliveira 1298*bce29ac9SDaniel Bristot de Oliveira retval = osnoise_hook_events(); 1299*bce29ac9SDaniel Bristot de Oliveira if (retval) 1300*bce29ac9SDaniel Bristot de Oliveira goto out_err; 1301*bce29ac9SDaniel Bristot de Oliveira /* 1302*bce29ac9SDaniel Bristot de Oliveira * Make sure NMIs see reseted values. 1303*bce29ac9SDaniel Bristot de Oliveira */ 1304*bce29ac9SDaniel Bristot de Oliveira barrier(); 1305*bce29ac9SDaniel Bristot de Oliveira trace_osnoise_callback_enabled = true; 1306*bce29ac9SDaniel Bristot de Oliveira 1307*bce29ac9SDaniel Bristot de Oliveira retval = start_per_cpu_kthreads(tr); 1308*bce29ac9SDaniel Bristot de Oliveira /* 1309*bce29ac9SDaniel Bristot de Oliveira * all fine! 1310*bce29ac9SDaniel Bristot de Oliveira */ 1311*bce29ac9SDaniel Bristot de Oliveira if (!retval) 1312*bce29ac9SDaniel Bristot de Oliveira return; 1313*bce29ac9SDaniel Bristot de Oliveira 1314*bce29ac9SDaniel Bristot de Oliveira out_err: 1315*bce29ac9SDaniel Bristot de Oliveira unhook_irq_events(); 1316*bce29ac9SDaniel Bristot de Oliveira pr_err(BANNER "Error starting osnoise tracer\n"); 1317*bce29ac9SDaniel Bristot de Oliveira } 1318*bce29ac9SDaniel Bristot de Oliveira 1319*bce29ac9SDaniel Bristot de Oliveira static void osnoise_tracer_stop(struct trace_array *tr) 1320*bce29ac9SDaniel Bristot de Oliveira { 1321*bce29ac9SDaniel Bristot de Oliveira if (!osnoise_busy) 1322*bce29ac9SDaniel Bristot de Oliveira return; 1323*bce29ac9SDaniel Bristot de Oliveira 1324*bce29ac9SDaniel Bristot de Oliveira trace_osnoise_callback_enabled = false; 1325*bce29ac9SDaniel Bristot de Oliveira barrier(); 1326*bce29ac9SDaniel Bristot de Oliveira 1327*bce29ac9SDaniel Bristot de Oliveira stop_per_cpu_kthreads(); 1328*bce29ac9SDaniel Bristot de Oliveira 1329*bce29ac9SDaniel Bristot de Oliveira unhook_irq_events(); 1330*bce29ac9SDaniel Bristot de Oliveira unhook_softirq_events(); 1331*bce29ac9SDaniel Bristot de Oliveira unhook_thread_events(); 1332*bce29ac9SDaniel Bristot de Oliveira 1333*bce29ac9SDaniel Bristot de Oliveira osnoise_busy = false; 1334*bce29ac9SDaniel Bristot de Oliveira } 1335*bce29ac9SDaniel Bristot de Oliveira 1336*bce29ac9SDaniel Bristot de Oliveira static int osnoise_tracer_init(struct trace_array *tr) 1337*bce29ac9SDaniel Bristot de Oliveira { 1338*bce29ac9SDaniel Bristot de Oliveira /* Only allow one instance to enable this */ 1339*bce29ac9SDaniel Bristot de Oliveira if (osnoise_busy) 1340*bce29ac9SDaniel Bristot de Oliveira return -EBUSY; 1341*bce29ac9SDaniel Bristot de Oliveira 1342*bce29ac9SDaniel Bristot de Oliveira osnoise_trace = tr; 1343*bce29ac9SDaniel Bristot de Oliveira 1344*bce29ac9SDaniel Bristot de Oliveira tr->max_latency = 0; 1345*bce29ac9SDaniel Bristot de Oliveira 1346*bce29ac9SDaniel Bristot de Oliveira osnoise_tracer_start(tr); 1347*bce29ac9SDaniel Bristot de Oliveira 1348*bce29ac9SDaniel Bristot de Oliveira osnoise_busy = true; 1349*bce29ac9SDaniel Bristot de Oliveira 1350*bce29ac9SDaniel Bristot de Oliveira return 0; 1351*bce29ac9SDaniel Bristot de Oliveira } 1352*bce29ac9SDaniel Bristot de Oliveira 1353*bce29ac9SDaniel Bristot de Oliveira static void osnoise_tracer_reset(struct trace_array *tr) 1354*bce29ac9SDaniel Bristot de Oliveira { 1355*bce29ac9SDaniel Bristot de Oliveira osnoise_tracer_stop(tr); 1356*bce29ac9SDaniel Bristot de Oliveira } 1357*bce29ac9SDaniel Bristot de Oliveira 1358*bce29ac9SDaniel Bristot de Oliveira static struct tracer osnoise_tracer __read_mostly = { 1359*bce29ac9SDaniel Bristot de Oliveira .name = "osnoise", 1360*bce29ac9SDaniel Bristot de Oliveira .init = osnoise_tracer_init, 1361*bce29ac9SDaniel Bristot de Oliveira .reset = osnoise_tracer_reset, 1362*bce29ac9SDaniel Bristot de Oliveira .start = osnoise_tracer_start, 1363*bce29ac9SDaniel Bristot de Oliveira .stop = osnoise_tracer_stop, 1364*bce29ac9SDaniel Bristot de Oliveira .print_header = print_osnoise_headers, 1365*bce29ac9SDaniel Bristot de Oliveira .allow_instances = true, 1366*bce29ac9SDaniel Bristot de Oliveira }; 1367*bce29ac9SDaniel Bristot de Oliveira 1368*bce29ac9SDaniel Bristot de Oliveira __init static int init_osnoise_tracer(void) 1369*bce29ac9SDaniel Bristot de Oliveira { 1370*bce29ac9SDaniel Bristot de Oliveira int ret; 1371*bce29ac9SDaniel Bristot de Oliveira 1372*bce29ac9SDaniel Bristot de Oliveira mutex_init(&interface_lock); 1373*bce29ac9SDaniel Bristot de Oliveira 1374*bce29ac9SDaniel Bristot de Oliveira cpumask_copy(&osnoise_cpumask, cpu_all_mask); 1375*bce29ac9SDaniel Bristot de Oliveira 1376*bce29ac9SDaniel Bristot de Oliveira ret = register_tracer(&osnoise_tracer); 1377*bce29ac9SDaniel Bristot de Oliveira if (ret) 1378*bce29ac9SDaniel Bristot de Oliveira return ret; 1379*bce29ac9SDaniel Bristot de Oliveira 1380*bce29ac9SDaniel Bristot de Oliveira init_tracefs(); 1381*bce29ac9SDaniel Bristot de Oliveira 1382*bce29ac9SDaniel Bristot de Oliveira return 0; 1383*bce29ac9SDaniel Bristot de Oliveira } 1384*bce29ac9SDaniel Bristot de Oliveira late_initcall(init_osnoise_tracer); 1385