Vince's perf-trinity fuzzer found yet another 'interesting' problem. When we sample the irq_work_exit tracepoint with period==1 (or PERF_SAMPLE_PERIOD) and we add an fasync SIGNAL handler we create an infinite event generation loop: ,-> <IPI> | irq_work_exit() -> | trace_irq_work_exit() -> | ... | __perf_event_overflow() -> (due to fasync) | irq_work_queue() -> (irq_work_list must be empty) '--------- arch_irq_work_raise() Similar things can happen due to regular poll() wakeups if we exceed the ring-buffer wakeup watermark, or have an event_limit. To avoid this, dis-allow sampling this particular tracepoint. In order to achieve this, create a special perf_perm function pointer for each event and call this (when set) on trying to create a tracepoint perf event. [ roasted: use expr... to allow for ',' in your expression ] Reported-by: Vince Weaver <vincent.weaver@maine.edu> Tested-by: Vince Weaver <vincent.weaver@maine.edu> Signed-off-by: Peter Zijlstra <peterz@infradead.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Dave Jones <davej@redhat.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Link: http://lkml.kernel.org/r/20131114152304.GC5364@laptop.programming.kicks-ass.net Signed-off-by: Ingo Molnar <mingo@kernel.org>
		
			
				
	
	
		
			115 lines
		
	
	
	
		
			2.7 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			115 lines
		
	
	
	
		
			2.7 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
#undef TRACE_SYSTEM
 | 
						|
#define TRACE_SYSTEM irq_vectors
 | 
						|
 | 
						|
#if !defined(_TRACE_IRQ_VECTORS_H) || defined(TRACE_HEADER_MULTI_READ)
 | 
						|
#define _TRACE_IRQ_VECTORS_H
 | 
						|
 | 
						|
#include <linux/tracepoint.h>
 | 
						|
 | 
						|
extern void trace_irq_vector_regfunc(void);
 | 
						|
extern void trace_irq_vector_unregfunc(void);
 | 
						|
 | 
						|
DECLARE_EVENT_CLASS(x86_irq_vector,
 | 
						|
 | 
						|
	TP_PROTO(int vector),
 | 
						|
 | 
						|
	TP_ARGS(vector),
 | 
						|
 | 
						|
	TP_STRUCT__entry(
 | 
						|
		__field(		int,	vector	)
 | 
						|
	),
 | 
						|
 | 
						|
	TP_fast_assign(
 | 
						|
		__entry->vector = vector;
 | 
						|
	),
 | 
						|
 | 
						|
	TP_printk("vector=%d", __entry->vector) );
 | 
						|
 | 
						|
#define DEFINE_IRQ_VECTOR_EVENT(name)		\
 | 
						|
DEFINE_EVENT_FN(x86_irq_vector, name##_entry,	\
 | 
						|
	TP_PROTO(int vector),			\
 | 
						|
	TP_ARGS(vector),			\
 | 
						|
	trace_irq_vector_regfunc,		\
 | 
						|
	trace_irq_vector_unregfunc);		\
 | 
						|
DEFINE_EVENT_FN(x86_irq_vector, name##_exit,	\
 | 
						|
	TP_PROTO(int vector),			\
 | 
						|
	TP_ARGS(vector),			\
 | 
						|
	trace_irq_vector_regfunc,		\
 | 
						|
	trace_irq_vector_unregfunc);
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * local_timer - called when entering/exiting a local timer interrupt
 | 
						|
 * vector handler
 | 
						|
 */
 | 
						|
DEFINE_IRQ_VECTOR_EVENT(local_timer);
 | 
						|
 | 
						|
/*
 | 
						|
 * reschedule - called when entering/exiting a reschedule vector handler
 | 
						|
 */
 | 
						|
DEFINE_IRQ_VECTOR_EVENT(reschedule);
 | 
						|
 | 
						|
/*
 | 
						|
 * spurious_apic - called when entering/exiting a spurious apic vector handler
 | 
						|
 */
 | 
						|
DEFINE_IRQ_VECTOR_EVENT(spurious_apic);
 | 
						|
 | 
						|
/*
 | 
						|
 * error_apic - called when entering/exiting an error apic vector handler
 | 
						|
 */
 | 
						|
DEFINE_IRQ_VECTOR_EVENT(error_apic);
 | 
						|
 | 
						|
/*
 | 
						|
 * x86_platform_ipi - called when entering/exiting a x86 platform ipi interrupt
 | 
						|
 * vector handler
 | 
						|
 */
 | 
						|
DEFINE_IRQ_VECTOR_EVENT(x86_platform_ipi);
 | 
						|
 | 
						|
/*
 | 
						|
 * irq_work - called when entering/exiting a irq work interrupt
 | 
						|
 * vector handler
 | 
						|
 */
 | 
						|
DEFINE_IRQ_VECTOR_EVENT(irq_work);
 | 
						|
 | 
						|
/*
 | 
						|
 * We must dis-allow sampling irq_work_exit() because perf event sampling
 | 
						|
 * itself can cause irq_work, which would lead to an infinite loop;
 | 
						|
 *
 | 
						|
 *  1) irq_work_exit happens
 | 
						|
 *  2) generates perf sample
 | 
						|
 *  3) generates irq_work
 | 
						|
 *  4) goto 1
 | 
						|
 */
 | 
						|
TRACE_EVENT_PERF_PERM(irq_work_exit, is_sampling_event(p_event) ? -EPERM : 0);
 | 
						|
 | 
						|
/*
 | 
						|
 * call_function - called when entering/exiting a call function interrupt
 | 
						|
 * vector handler
 | 
						|
 */
 | 
						|
DEFINE_IRQ_VECTOR_EVENT(call_function);
 | 
						|
 | 
						|
/*
 | 
						|
 * call_function_single - called when entering/exiting a call function
 | 
						|
 * single interrupt vector handler
 | 
						|
 */
 | 
						|
DEFINE_IRQ_VECTOR_EVENT(call_function_single);
 | 
						|
 | 
						|
/*
 | 
						|
 * threshold_apic - called when entering/exiting a threshold apic interrupt
 | 
						|
 * vector handler
 | 
						|
 */
 | 
						|
DEFINE_IRQ_VECTOR_EVENT(threshold_apic);
 | 
						|
 | 
						|
/*
 | 
						|
 * thermal_apic - called when entering/exiting a thermal apic interrupt
 | 
						|
 * vector handler
 | 
						|
 */
 | 
						|
DEFINE_IRQ_VECTOR_EVENT(thermal_apic);
 | 
						|
 | 
						|
#undef TRACE_INCLUDE_PATH
 | 
						|
#define TRACE_INCLUDE_PATH .
 | 
						|
#define TRACE_INCLUDE_FILE irq_vectors
 | 
						|
#endif /*  _TRACE_IRQ_VECTORS_H */
 | 
						|
 | 
						|
/* This part must be outside protection */
 | 
						|
#include <trace/define_trace.h>
 |