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) * If callback is registered to the "global" list, the list 200264c8c9SSteven Rostedt (VMware) * function is called and recursion checks the GLOBAL bits. 210264c8c9SSteven Rostedt (VMware) * then this function calls... 220264c8c9SSteven Rostedt (VMware) * The function callback, which can use the FTRACE bits to 230264c8c9SSteven Rostedt (VMware) * check for recursion. 240264c8c9SSteven Rostedt (VMware) * 250264c8c9SSteven Rostedt (VMware) * Now if the arch does not support a feature, and it calls 260264c8c9SSteven Rostedt (VMware) * the global list function which calls the ftrace callback 270264c8c9SSteven Rostedt (VMware) * all three of these steps will do a recursion protection. 280264c8c9SSteven Rostedt (VMware) * There's no reason to do one if the previous caller already 290264c8c9SSteven Rostedt (VMware) * did. The recursion that we are protecting against will 300264c8c9SSteven Rostedt (VMware) * go through the same steps again. 310264c8c9SSteven Rostedt (VMware) * 320264c8c9SSteven Rostedt (VMware) * To prevent the multiple recursion checks, if a recursion 330264c8c9SSteven Rostedt (VMware) * bit is set that is higher than the MAX bit of the current 340264c8c9SSteven Rostedt (VMware) * check, then we know that the check was made by the previous 350264c8c9SSteven Rostedt (VMware) * caller, and we can skip the current check. 360264c8c9SSteven Rostedt (VMware) */ 370264c8c9SSteven Rostedt (VMware) enum { 380264c8c9SSteven Rostedt (VMware) /* Function recursion bits */ 390264c8c9SSteven Rostedt (VMware) TRACE_FTRACE_BIT, 400264c8c9SSteven Rostedt (VMware) TRACE_FTRACE_NMI_BIT, 410264c8c9SSteven Rostedt (VMware) TRACE_FTRACE_IRQ_BIT, 420264c8c9SSteven Rostedt (VMware) TRACE_FTRACE_SIRQ_BIT, 430264c8c9SSteven Rostedt (VMware) 440264c8c9SSteven Rostedt (VMware) /* INTERNAL_BITs must be greater than FTRACE_BITs */ 450264c8c9SSteven Rostedt (VMware) TRACE_INTERNAL_BIT, 460264c8c9SSteven Rostedt (VMware) TRACE_INTERNAL_NMI_BIT, 470264c8c9SSteven Rostedt (VMware) TRACE_INTERNAL_IRQ_BIT, 480264c8c9SSteven Rostedt (VMware) TRACE_INTERNAL_SIRQ_BIT, 490264c8c9SSteven Rostedt (VMware) 500264c8c9SSteven Rostedt (VMware) TRACE_BRANCH_BIT, 510264c8c9SSteven Rostedt (VMware) /* 520264c8c9SSteven Rostedt (VMware) * Abuse of the trace_recursion. 530264c8c9SSteven Rostedt (VMware) * As we need a way to maintain state if we are tracing the function 540264c8c9SSteven Rostedt (VMware) * graph in irq because we want to trace a particular function that 550264c8c9SSteven Rostedt (VMware) * was called in irq context but we have irq tracing off. Since this 560264c8c9SSteven Rostedt (VMware) * can only be modified by current, we can reuse trace_recursion. 570264c8c9SSteven Rostedt (VMware) */ 580264c8c9SSteven Rostedt (VMware) TRACE_IRQ_BIT, 590264c8c9SSteven Rostedt (VMware) 600264c8c9SSteven Rostedt (VMware) /* Set if the function is in the set_graph_function file */ 610264c8c9SSteven Rostedt (VMware) TRACE_GRAPH_BIT, 620264c8c9SSteven Rostedt (VMware) 630264c8c9SSteven Rostedt (VMware) /* 640264c8c9SSteven Rostedt (VMware) * In the very unlikely case that an interrupt came in 650264c8c9SSteven Rostedt (VMware) * at a start of graph tracing, and we want to trace 660264c8c9SSteven Rostedt (VMware) * the function in that interrupt, the depth can be greater 670264c8c9SSteven Rostedt (VMware) * than zero, because of the preempted start of a previous 680264c8c9SSteven Rostedt (VMware) * trace. In an even more unlikely case, depth could be 2 690264c8c9SSteven Rostedt (VMware) * if a softirq interrupted the start of graph tracing, 700264c8c9SSteven Rostedt (VMware) * followed by an interrupt preempting a start of graph 710264c8c9SSteven Rostedt (VMware) * tracing in the softirq, and depth can even be 3 720264c8c9SSteven Rostedt (VMware) * if an NMI came in at the start of an interrupt function 730264c8c9SSteven Rostedt (VMware) * that preempted a softirq start of a function that 740264c8c9SSteven Rostedt (VMware) * preempted normal context!!!! Luckily, it can't be 750264c8c9SSteven Rostedt (VMware) * greater than 3, so the next two bits are a mask 760264c8c9SSteven Rostedt (VMware) * of what the depth is when we set TRACE_GRAPH_BIT 770264c8c9SSteven Rostedt (VMware) */ 780264c8c9SSteven Rostedt (VMware) 790264c8c9SSteven Rostedt (VMware) TRACE_GRAPH_DEPTH_START_BIT, 800264c8c9SSteven Rostedt (VMware) TRACE_GRAPH_DEPTH_END_BIT, 810264c8c9SSteven Rostedt (VMware) 820264c8c9SSteven Rostedt (VMware) /* 830264c8c9SSteven Rostedt (VMware) * To implement set_graph_notrace, if this bit is set, we ignore 840264c8c9SSteven Rostedt (VMware) * function graph tracing of called functions, until the return 850264c8c9SSteven Rostedt (VMware) * function is called to clear it. 860264c8c9SSteven Rostedt (VMware) */ 870264c8c9SSteven Rostedt (VMware) TRACE_GRAPH_NOTRACE_BIT, 880264c8c9SSteven Rostedt (VMware) 890264c8c9SSteven Rostedt (VMware) /* 900264c8c9SSteven Rostedt (VMware) * When transitioning between context, the preempt_count() may 910264c8c9SSteven Rostedt (VMware) * not be correct. Allow for a single recursion to cover this case. 920264c8c9SSteven Rostedt (VMware) */ 930264c8c9SSteven Rostedt (VMware) TRACE_TRANSITION_BIT, 940264c8c9SSteven Rostedt (VMware) }; 950264c8c9SSteven Rostedt (VMware) 960264c8c9SSteven Rostedt (VMware) #define trace_recursion_set(bit) do { (current)->trace_recursion |= (1<<(bit)); } while (0) 970264c8c9SSteven Rostedt (VMware) #define trace_recursion_clear(bit) do { (current)->trace_recursion &= ~(1<<(bit)); } while (0) 980264c8c9SSteven Rostedt (VMware) #define trace_recursion_test(bit) ((current)->trace_recursion & (1<<(bit))) 990264c8c9SSteven Rostedt (VMware) 1000264c8c9SSteven Rostedt (VMware) #define trace_recursion_depth() \ 1010264c8c9SSteven Rostedt (VMware) (((current)->trace_recursion >> TRACE_GRAPH_DEPTH_START_BIT) & 3) 1020264c8c9SSteven Rostedt (VMware) #define trace_recursion_set_depth(depth) \ 1030264c8c9SSteven Rostedt (VMware) do { \ 1040264c8c9SSteven Rostedt (VMware) current->trace_recursion &= \ 1050264c8c9SSteven Rostedt (VMware) ~(3 << TRACE_GRAPH_DEPTH_START_BIT); \ 1060264c8c9SSteven Rostedt (VMware) current->trace_recursion |= \ 1070264c8c9SSteven Rostedt (VMware) ((depth) & 3) << TRACE_GRAPH_DEPTH_START_BIT; \ 1080264c8c9SSteven Rostedt (VMware) } while (0) 1090264c8c9SSteven Rostedt (VMware) 1100264c8c9SSteven Rostedt (VMware) #define TRACE_CONTEXT_BITS 4 1110264c8c9SSteven Rostedt (VMware) 1120264c8c9SSteven Rostedt (VMware) #define TRACE_FTRACE_START TRACE_FTRACE_BIT 1130264c8c9SSteven Rostedt (VMware) #define TRACE_FTRACE_MAX ((1 << (TRACE_FTRACE_START + TRACE_CONTEXT_BITS)) - 1) 1140264c8c9SSteven Rostedt (VMware) 1150264c8c9SSteven Rostedt (VMware) #define TRACE_LIST_START TRACE_INTERNAL_BIT 1160264c8c9SSteven Rostedt (VMware) #define TRACE_LIST_MAX ((1 << (TRACE_LIST_START + TRACE_CONTEXT_BITS)) - 1) 1170264c8c9SSteven Rostedt (VMware) 1180264c8c9SSteven Rostedt (VMware) #define TRACE_CONTEXT_MASK TRACE_LIST_MAX 1190264c8c9SSteven Rostedt (VMware) 120*da5afbebSSteven Rostedt (VMware) /* 121*da5afbebSSteven Rostedt (VMware) * Used for setting context 122*da5afbebSSteven Rostedt (VMware) * NMI = 0 123*da5afbebSSteven Rostedt (VMware) * IRQ = 1 124*da5afbebSSteven Rostedt (VMware) * SOFTIRQ = 2 125*da5afbebSSteven Rostedt (VMware) * NORMAL = 3 126*da5afbebSSteven Rostedt (VMware) */ 127*da5afbebSSteven Rostedt (VMware) enum { 128*da5afbebSSteven Rostedt (VMware) TRACE_CTX_NMI, 129*da5afbebSSteven Rostedt (VMware) TRACE_CTX_IRQ, 130*da5afbebSSteven Rostedt (VMware) TRACE_CTX_SOFTIRQ, 131*da5afbebSSteven Rostedt (VMware) TRACE_CTX_NORMAL, 132*da5afbebSSteven Rostedt (VMware) }; 133*da5afbebSSteven Rostedt (VMware) 1340264c8c9SSteven Rostedt (VMware) static __always_inline int trace_get_context_bit(void) 1350264c8c9SSteven Rostedt (VMware) { 136*da5afbebSSteven Rostedt (VMware) unsigned long pc = preempt_count(); 1370264c8c9SSteven Rostedt (VMware) 138*da5afbebSSteven Rostedt (VMware) if (!(pc & (NMI_MASK | HARDIRQ_MASK | SOFTIRQ_OFFSET))) 139*da5afbebSSteven Rostedt (VMware) return TRACE_CTX_NORMAL; 1400264c8c9SSteven Rostedt (VMware) else 141*da5afbebSSteven Rostedt (VMware) return pc & NMI_MASK ? TRACE_CTX_NMI : 142*da5afbebSSteven Rostedt (VMware) pc & HARDIRQ_MASK ? TRACE_CTX_IRQ : TRACE_CTX_SOFTIRQ; 1430264c8c9SSteven Rostedt (VMware) } 1440264c8c9SSteven Rostedt (VMware) 1450264c8c9SSteven Rostedt (VMware) static __always_inline int trace_test_and_set_recursion(int start, int max) 1460264c8c9SSteven Rostedt (VMware) { 1470264c8c9SSteven Rostedt (VMware) unsigned int val = current->trace_recursion; 1480264c8c9SSteven Rostedt (VMware) int bit; 1490264c8c9SSteven Rostedt (VMware) 1500264c8c9SSteven Rostedt (VMware) /* A previous recursion check was made */ 1510264c8c9SSteven Rostedt (VMware) if ((val & TRACE_CONTEXT_MASK) > max) 1520264c8c9SSteven Rostedt (VMware) return 0; 1530264c8c9SSteven Rostedt (VMware) 1540264c8c9SSteven Rostedt (VMware) bit = trace_get_context_bit() + start; 1550264c8c9SSteven Rostedt (VMware) if (unlikely(val & (1 << bit))) { 1560264c8c9SSteven Rostedt (VMware) /* 1570264c8c9SSteven Rostedt (VMware) * It could be that preempt_count has not been updated during 1580264c8c9SSteven Rostedt (VMware) * a switch between contexts. Allow for a single recursion. 1590264c8c9SSteven Rostedt (VMware) */ 1600264c8c9SSteven Rostedt (VMware) bit = TRACE_TRANSITION_BIT; 1610264c8c9SSteven Rostedt (VMware) if (trace_recursion_test(bit)) 1620264c8c9SSteven Rostedt (VMware) return -1; 1630264c8c9SSteven Rostedt (VMware) trace_recursion_set(bit); 1640264c8c9SSteven Rostedt (VMware) barrier(); 1650264c8c9SSteven Rostedt (VMware) return bit + 1; 1660264c8c9SSteven Rostedt (VMware) } 1670264c8c9SSteven Rostedt (VMware) 1680264c8c9SSteven Rostedt (VMware) /* Normal check passed, clear the transition to allow it again */ 1690264c8c9SSteven Rostedt (VMware) trace_recursion_clear(TRACE_TRANSITION_BIT); 1700264c8c9SSteven Rostedt (VMware) 1710264c8c9SSteven Rostedt (VMware) val |= 1 << bit; 1720264c8c9SSteven Rostedt (VMware) current->trace_recursion = val; 1730264c8c9SSteven Rostedt (VMware) barrier(); 1740264c8c9SSteven Rostedt (VMware) 1750264c8c9SSteven Rostedt (VMware) return bit + 1; 1760264c8c9SSteven Rostedt (VMware) } 1770264c8c9SSteven Rostedt (VMware) 1780264c8c9SSteven Rostedt (VMware) static __always_inline void trace_clear_recursion(int bit) 1790264c8c9SSteven Rostedt (VMware) { 1800264c8c9SSteven Rostedt (VMware) unsigned int val = current->trace_recursion; 1810264c8c9SSteven Rostedt (VMware) 1820264c8c9SSteven Rostedt (VMware) if (!bit) 1830264c8c9SSteven Rostedt (VMware) return; 1840264c8c9SSteven Rostedt (VMware) 1850264c8c9SSteven Rostedt (VMware) bit--; 1860264c8c9SSteven Rostedt (VMware) bit = 1 << bit; 1870264c8c9SSteven Rostedt (VMware) val &= ~bit; 1880264c8c9SSteven Rostedt (VMware) 1890264c8c9SSteven Rostedt (VMware) barrier(); 1900264c8c9SSteven Rostedt (VMware) current->trace_recursion = val; 1910264c8c9SSteven Rostedt (VMware) } 1920264c8c9SSteven Rostedt (VMware) 1936e4eb9cbSSteven Rostedt (VMware) /** 1946e4eb9cbSSteven Rostedt (VMware) * ftrace_test_recursion_trylock - tests for recursion in same context 1956e4eb9cbSSteven Rostedt (VMware) * 1966e4eb9cbSSteven Rostedt (VMware) * Use this for ftrace callbacks. This will detect if the function 1976e4eb9cbSSteven Rostedt (VMware) * tracing recursed in the same context (normal vs interrupt), 1986e4eb9cbSSteven Rostedt (VMware) * 1996e4eb9cbSSteven Rostedt (VMware) * Returns: -1 if a recursion happened. 2006e4eb9cbSSteven Rostedt (VMware) * >= 0 if no recursion 2016e4eb9cbSSteven Rostedt (VMware) */ 2026e4eb9cbSSteven Rostedt (VMware) static __always_inline int ftrace_test_recursion_trylock(void) 2036e4eb9cbSSteven Rostedt (VMware) { 2046e4eb9cbSSteven Rostedt (VMware) return trace_test_and_set_recursion(TRACE_FTRACE_START, TRACE_FTRACE_MAX); 2056e4eb9cbSSteven Rostedt (VMware) } 2066e4eb9cbSSteven Rostedt (VMware) 2076e4eb9cbSSteven Rostedt (VMware) /** 2086e4eb9cbSSteven Rostedt (VMware) * ftrace_test_recursion_unlock - called when function callback is complete 2096e4eb9cbSSteven Rostedt (VMware) * @bit: The return of a successful ftrace_test_recursion_trylock() 2106e4eb9cbSSteven Rostedt (VMware) * 2116e4eb9cbSSteven Rostedt (VMware) * This is used at the end of a ftrace callback. 2126e4eb9cbSSteven Rostedt (VMware) */ 2136e4eb9cbSSteven Rostedt (VMware) static __always_inline void ftrace_test_recursion_unlock(int bit) 2146e4eb9cbSSteven Rostedt (VMware) { 2156e4eb9cbSSteven Rostedt (VMware) trace_clear_recursion(bit); 2166e4eb9cbSSteven Rostedt (VMware) } 2176e4eb9cbSSteven Rostedt (VMware) 2180264c8c9SSteven Rostedt (VMware) #endif /* CONFIG_TRACING */ 2190264c8c9SSteven Rostedt (VMware) #endif /* _LINUX_TRACE_RECURSION_H */ 220