10264c8c9SSteven Rostedt (VMware) /* SPDX-License-Identifier: GPL-2.0 */ 20264c8c9SSteven Rostedt (VMware) #ifndef _LINUX_TRACE_RECURSION_H 30264c8c9SSteven Rostedt (VMware) #define _LINUX_TRACE_RECURSION_H 40264c8c9SSteven Rostedt (VMware) 50264c8c9SSteven Rostedt (VMware) #include <linux/interrupt.h> 60264c8c9SSteven Rostedt (VMware) #include <linux/sched.h> 70264c8c9SSteven Rostedt (VMware) 80264c8c9SSteven Rostedt (VMware) #ifdef CONFIG_TRACING 90264c8c9SSteven Rostedt (VMware) 100264c8c9SSteven Rostedt (VMware) /* Only current can touch trace_recursion */ 110264c8c9SSteven Rostedt (VMware) 120264c8c9SSteven Rostedt (VMware) /* 130264c8c9SSteven Rostedt (VMware) * For function tracing recursion: 140264c8c9SSteven Rostedt (VMware) * The order of these bits are important. 150264c8c9SSteven Rostedt (VMware) * 160264c8c9SSteven Rostedt (VMware) * When function tracing occurs, the following steps are made: 170264c8c9SSteven Rostedt (VMware) * If arch does not support a ftrace feature: 180264c8c9SSteven Rostedt (VMware) * call internal function (uses INTERNAL bits) which calls... 190264c8c9SSteven Rostedt (VMware) * The function callback, which can use the FTRACE bits to 200264c8c9SSteven Rostedt (VMware) * check for recursion. 210264c8c9SSteven Rostedt (VMware) */ 220264c8c9SSteven Rostedt (VMware) enum { 230264c8c9SSteven Rostedt (VMware) /* Function recursion bits */ 240264c8c9SSteven Rostedt (VMware) TRACE_FTRACE_BIT, 250264c8c9SSteven Rostedt (VMware) TRACE_FTRACE_NMI_BIT, 260264c8c9SSteven Rostedt (VMware) TRACE_FTRACE_IRQ_BIT, 270264c8c9SSteven Rostedt (VMware) TRACE_FTRACE_SIRQ_BIT, 28ed65df63SSteven Rostedt (VMware) TRACE_FTRACE_TRANSITION_BIT, 290264c8c9SSteven Rostedt (VMware) 30ed65df63SSteven Rostedt (VMware) /* Internal use recursion bits */ 310264c8c9SSteven Rostedt (VMware) TRACE_INTERNAL_BIT, 320264c8c9SSteven Rostedt (VMware) TRACE_INTERNAL_NMI_BIT, 330264c8c9SSteven Rostedt (VMware) TRACE_INTERNAL_IRQ_BIT, 340264c8c9SSteven Rostedt (VMware) TRACE_INTERNAL_SIRQ_BIT, 35ed65df63SSteven Rostedt (VMware) TRACE_INTERNAL_TRANSITION_BIT, 360264c8c9SSteven Rostedt (VMware) 370264c8c9SSteven Rostedt (VMware) TRACE_BRANCH_BIT, 380264c8c9SSteven Rostedt (VMware) /* 390264c8c9SSteven Rostedt (VMware) * Abuse of the trace_recursion. 400264c8c9SSteven Rostedt (VMware) * As we need a way to maintain state if we are tracing the function 410264c8c9SSteven Rostedt (VMware) * graph in irq because we want to trace a particular function that 420264c8c9SSteven Rostedt (VMware) * was called in irq context but we have irq tracing off. Since this 430264c8c9SSteven Rostedt (VMware) * can only be modified by current, we can reuse trace_recursion. 440264c8c9SSteven Rostedt (VMware) */ 450264c8c9SSteven Rostedt (VMware) TRACE_IRQ_BIT, 460264c8c9SSteven Rostedt (VMware) 470264c8c9SSteven Rostedt (VMware) /* Set if the function is in the set_graph_function file */ 480264c8c9SSteven Rostedt (VMware) TRACE_GRAPH_BIT, 490264c8c9SSteven Rostedt (VMware) 500264c8c9SSteven Rostedt (VMware) /* 510264c8c9SSteven Rostedt (VMware) * In the very unlikely case that an interrupt came in 520264c8c9SSteven Rostedt (VMware) * at a start of graph tracing, and we want to trace 530264c8c9SSteven Rostedt (VMware) * the function in that interrupt, the depth can be greater 540264c8c9SSteven Rostedt (VMware) * than zero, because of the preempted start of a previous 550264c8c9SSteven Rostedt (VMware) * trace. In an even more unlikely case, depth could be 2 560264c8c9SSteven Rostedt (VMware) * if a softirq interrupted the start of graph tracing, 570264c8c9SSteven Rostedt (VMware) * followed by an interrupt preempting a start of graph 580264c8c9SSteven Rostedt (VMware) * tracing in the softirq, and depth can even be 3 590264c8c9SSteven Rostedt (VMware) * if an NMI came in at the start of an interrupt function 600264c8c9SSteven Rostedt (VMware) * that preempted a softirq start of a function that 610264c8c9SSteven Rostedt (VMware) * preempted normal context!!!! Luckily, it can't be 620264c8c9SSteven Rostedt (VMware) * greater than 3, so the next two bits are a mask 630264c8c9SSteven Rostedt (VMware) * of what the depth is when we set TRACE_GRAPH_BIT 640264c8c9SSteven Rostedt (VMware) */ 650264c8c9SSteven Rostedt (VMware) 660264c8c9SSteven Rostedt (VMware) TRACE_GRAPH_DEPTH_START_BIT, 670264c8c9SSteven Rostedt (VMware) TRACE_GRAPH_DEPTH_END_BIT, 680264c8c9SSteven Rostedt (VMware) 690264c8c9SSteven Rostedt (VMware) /* 700264c8c9SSteven Rostedt (VMware) * To implement set_graph_notrace, if this bit is set, we ignore 710264c8c9SSteven Rostedt (VMware) * function graph tracing of called functions, until the return 720264c8c9SSteven Rostedt (VMware) * function is called to clear it. 730264c8c9SSteven Rostedt (VMware) */ 740264c8c9SSteven Rostedt (VMware) TRACE_GRAPH_NOTRACE_BIT, 750264c8c9SSteven Rostedt (VMware) 76773c1670SSteven Rostedt (VMware) /* Used to prevent recursion recording from recursing. */ 77773c1670SSteven Rostedt (VMware) TRACE_RECORD_RECURSION_BIT, 780264c8c9SSteven Rostedt (VMware) }; 790264c8c9SSteven Rostedt (VMware) 800264c8c9SSteven Rostedt (VMware) #define trace_recursion_set(bit) do { (current)->trace_recursion |= (1<<(bit)); } while (0) 810264c8c9SSteven Rostedt (VMware) #define trace_recursion_clear(bit) do { (current)->trace_recursion &= ~(1<<(bit)); } while (0) 820264c8c9SSteven Rostedt (VMware) #define trace_recursion_test(bit) ((current)->trace_recursion & (1<<(bit))) 830264c8c9SSteven Rostedt (VMware) 840264c8c9SSteven Rostedt (VMware) #define trace_recursion_depth() \ 850264c8c9SSteven Rostedt (VMware) (((current)->trace_recursion >> TRACE_GRAPH_DEPTH_START_BIT) & 3) 860264c8c9SSteven Rostedt (VMware) #define trace_recursion_set_depth(depth) \ 870264c8c9SSteven Rostedt (VMware) do { \ 880264c8c9SSteven Rostedt (VMware) current->trace_recursion &= \ 890264c8c9SSteven Rostedt (VMware) ~(3 << TRACE_GRAPH_DEPTH_START_BIT); \ 900264c8c9SSteven Rostedt (VMware) current->trace_recursion |= \ 910264c8c9SSteven Rostedt (VMware) ((depth) & 3) << TRACE_GRAPH_DEPTH_START_BIT; \ 920264c8c9SSteven Rostedt (VMware) } while (0) 930264c8c9SSteven Rostedt (VMware) 940264c8c9SSteven Rostedt (VMware) #define TRACE_CONTEXT_BITS 4 950264c8c9SSteven Rostedt (VMware) 960264c8c9SSteven Rostedt (VMware) #define TRACE_FTRACE_START TRACE_FTRACE_BIT 970264c8c9SSteven Rostedt (VMware) 980264c8c9SSteven Rostedt (VMware) #define TRACE_LIST_START TRACE_INTERNAL_BIT 990264c8c9SSteven Rostedt (VMware) 100ed65df63SSteven Rostedt (VMware) #define TRACE_CONTEXT_MASK ((1 << (TRACE_LIST_START + TRACE_CONTEXT_BITS)) - 1) 1010264c8c9SSteven Rostedt (VMware) 102da5afbebSSteven Rostedt (VMware) /* 103da5afbebSSteven Rostedt (VMware) * Used for setting context 104da5afbebSSteven Rostedt (VMware) * NMI = 0 105da5afbebSSteven Rostedt (VMware) * IRQ = 1 106da5afbebSSteven Rostedt (VMware) * SOFTIRQ = 2 107da5afbebSSteven Rostedt (VMware) * NORMAL = 3 108da5afbebSSteven Rostedt (VMware) */ 109da5afbebSSteven Rostedt (VMware) enum { 110da5afbebSSteven Rostedt (VMware) TRACE_CTX_NMI, 111da5afbebSSteven Rostedt (VMware) TRACE_CTX_IRQ, 112da5afbebSSteven Rostedt (VMware) TRACE_CTX_SOFTIRQ, 113da5afbebSSteven Rostedt (VMware) TRACE_CTX_NORMAL, 114ed65df63SSteven Rostedt (VMware) TRACE_CTX_TRANSITION, 115da5afbebSSteven Rostedt (VMware) }; 116da5afbebSSteven Rostedt (VMware) 1170264c8c9SSteven Rostedt (VMware) static __always_inline int trace_get_context_bit(void) 1180264c8c9SSteven Rostedt (VMware) { 11991ebe8bcSSteven Rostedt (VMware) unsigned char bit = interrupt_context_level(); 1200264c8c9SSteven Rostedt (VMware) 1219b84fadcSSteven Rostedt (VMware) return TRACE_CTX_NORMAL - bit; 1220264c8c9SSteven Rostedt (VMware) } 1230264c8c9SSteven Rostedt (VMware) 124773c1670SSteven Rostedt (VMware) #ifdef CONFIG_FTRACE_RECORD_RECURSION 125773c1670SSteven Rostedt (VMware) extern void ftrace_record_recursion(unsigned long ip, unsigned long parent_ip); 126773c1670SSteven Rostedt (VMware) # define do_ftrace_record_recursion(ip, pip) \ 127773c1670SSteven Rostedt (VMware) do { \ 128773c1670SSteven Rostedt (VMware) if (!trace_recursion_test(TRACE_RECORD_RECURSION_BIT)) { \ 129773c1670SSteven Rostedt (VMware) trace_recursion_set(TRACE_RECORD_RECURSION_BIT); \ 130773c1670SSteven Rostedt (VMware) ftrace_record_recursion(ip, pip); \ 131773c1670SSteven Rostedt (VMware) trace_recursion_clear(TRACE_RECORD_RECURSION_BIT); \ 132773c1670SSteven Rostedt (VMware) } \ 133773c1670SSteven Rostedt (VMware) } while (0) 134773c1670SSteven Rostedt (VMware) #else 135773c1670SSteven Rostedt (VMware) # define do_ftrace_record_recursion(ip, pip) do { } while (0) 136773c1670SSteven Rostedt (VMware) #endif 137773c1670SSteven Rostedt (VMware) 138*d099dbfdSPeter Zijlstra #ifdef CONFIG_ARCH_WANTS_NO_INSTR 139*d099dbfdSPeter Zijlstra # define trace_warn_on_no_rcu(ip) \ 140*d099dbfdSPeter Zijlstra ({ \ 141*d099dbfdSPeter Zijlstra bool __ret = !rcu_is_watching(); \ 142*d099dbfdSPeter Zijlstra if (__ret && !trace_recursion_test(TRACE_RECORD_RECURSION_BIT)) { \ 143*d099dbfdSPeter Zijlstra trace_recursion_set(TRACE_RECORD_RECURSION_BIT); \ 144*d099dbfdSPeter Zijlstra WARN_ONCE(true, "RCU not on for: %pS\n", (void *)ip); \ 145*d099dbfdSPeter Zijlstra trace_recursion_clear(TRACE_RECORD_RECURSION_BIT); \ 146*d099dbfdSPeter Zijlstra } \ 147*d099dbfdSPeter Zijlstra __ret; \ 148*d099dbfdSPeter Zijlstra }) 149*d099dbfdSPeter Zijlstra #else 150*d099dbfdSPeter Zijlstra # define trace_warn_on_no_rcu(ip) false 151*d099dbfdSPeter Zijlstra #endif 152*d099dbfdSPeter Zijlstra 153ce5e4803S王贇 /* 154ce5e4803S王贇 * Preemption is promised to be disabled when return bit >= 0. 155ce5e4803S王贇 */ 156773c1670SSteven Rostedt (VMware) static __always_inline int trace_test_and_set_recursion(unsigned long ip, unsigned long pip, 157ed65df63SSteven Rostedt (VMware) int start) 1580264c8c9SSteven Rostedt (VMware) { 1597b68621fSSteven Rostedt (VMware) unsigned int val = READ_ONCE(current->trace_recursion); 1600264c8c9SSteven Rostedt (VMware) int bit; 1610264c8c9SSteven Rostedt (VMware) 162*d099dbfdSPeter Zijlstra if (trace_warn_on_no_rcu(ip)) 163*d099dbfdSPeter Zijlstra return -1; 164*d099dbfdSPeter Zijlstra 1650264c8c9SSteven Rostedt (VMware) bit = trace_get_context_bit() + start; 1660264c8c9SSteven Rostedt (VMware) if (unlikely(val & (1 << bit))) { 1670264c8c9SSteven Rostedt (VMware) /* 168bce5c81cSSteven Rostedt (VMware) * If an interrupt occurs during a trace, and another trace 169bce5c81cSSteven Rostedt (VMware) * happens in that interrupt but before the preempt_count is 170bce5c81cSSteven Rostedt (VMware) * updated to reflect the new interrupt context, then this 171bce5c81cSSteven Rostedt (VMware) * will think a recursion occurred, and the event will be dropped. 172bce5c81cSSteven Rostedt (VMware) * Let a single instance happen via the TRANSITION_BIT to 173bce5c81cSSteven Rostedt (VMware) * not drop those events. 1740264c8c9SSteven Rostedt (VMware) */ 175ed65df63SSteven Rostedt (VMware) bit = TRACE_CTX_TRANSITION + start; 1767b68621fSSteven Rostedt (VMware) if (val & (1 << bit)) { 177773c1670SSteven Rostedt (VMware) do_ftrace_record_recursion(ip, pip); 1780264c8c9SSteven Rostedt (VMware) return -1; 179773c1670SSteven Rostedt (VMware) } 1807b68621fSSteven Rostedt (VMware) } 1810264c8c9SSteven Rostedt (VMware) 1820264c8c9SSteven Rostedt (VMware) val |= 1 << bit; 1830264c8c9SSteven Rostedt (VMware) current->trace_recursion = val; 1840264c8c9SSteven Rostedt (VMware) barrier(); 1850264c8c9SSteven Rostedt (VMware) 186ce5e4803S王贇 preempt_disable_notrace(); 187ce5e4803S王贇 188ed65df63SSteven Rostedt (VMware) return bit; 1890264c8c9SSteven Rostedt (VMware) } 1900264c8c9SSteven Rostedt (VMware) 191ce5e4803S王贇 /* 192ce5e4803S王贇 * Preemption will be enabled (if it was previously enabled). 193ce5e4803S王贇 */ 1940264c8c9SSteven Rostedt (VMware) static __always_inline void trace_clear_recursion(int bit) 1950264c8c9SSteven Rostedt (VMware) { 196ce5e4803S王贇 preempt_enable_notrace(); 1970264c8c9SSteven Rostedt (VMware) barrier(); 1987b68621fSSteven Rostedt (VMware) trace_recursion_clear(bit); 1990264c8c9SSteven Rostedt (VMware) } 2000264c8c9SSteven Rostedt (VMware) 2016e4eb9cbSSteven Rostedt (VMware) /** 2026e4eb9cbSSteven Rostedt (VMware) * ftrace_test_recursion_trylock - tests for recursion in same context 2036e4eb9cbSSteven Rostedt (VMware) * 2046e4eb9cbSSteven Rostedt (VMware) * Use this for ftrace callbacks. This will detect if the function 2056e4eb9cbSSteven Rostedt (VMware) * tracing recursed in the same context (normal vs interrupt), 2066e4eb9cbSSteven Rostedt (VMware) * 2076e4eb9cbSSteven Rostedt (VMware) * Returns: -1 if a recursion happened. 208ce5e4803S王贇 * >= 0 if no recursion. 2096e4eb9cbSSteven Rostedt (VMware) */ 210773c1670SSteven Rostedt (VMware) static __always_inline int ftrace_test_recursion_trylock(unsigned long ip, 211773c1670SSteven Rostedt (VMware) unsigned long parent_ip) 2126e4eb9cbSSteven Rostedt (VMware) { 213ed65df63SSteven Rostedt (VMware) return trace_test_and_set_recursion(ip, parent_ip, TRACE_FTRACE_START); 2146e4eb9cbSSteven Rostedt (VMware) } 2156e4eb9cbSSteven Rostedt (VMware) 2166e4eb9cbSSteven Rostedt (VMware) /** 2176e4eb9cbSSteven Rostedt (VMware) * ftrace_test_recursion_unlock - called when function callback is complete 2186e4eb9cbSSteven Rostedt (VMware) * @bit: The return of a successful ftrace_test_recursion_trylock() 2196e4eb9cbSSteven Rostedt (VMware) * 2206e4eb9cbSSteven Rostedt (VMware) * This is used at the end of a ftrace callback. 2216e4eb9cbSSteven Rostedt (VMware) */ 2226e4eb9cbSSteven Rostedt (VMware) static __always_inline void ftrace_test_recursion_unlock(int bit) 2236e4eb9cbSSteven Rostedt (VMware) { 2246e4eb9cbSSteven Rostedt (VMware) trace_clear_recursion(bit); 2256e4eb9cbSSteven Rostedt (VMware) } 2266e4eb9cbSSteven Rostedt (VMware) 2270264c8c9SSteven Rostedt (VMware) #endif /* CONFIG_TRACING */ 2280264c8c9SSteven Rostedt (VMware) #endif /* _LINUX_TRACE_RECURSION_H */ 229