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, 28*ed65df63SSteven Rostedt (VMware) TRACE_FTRACE_TRANSITION_BIT, 290264c8c9SSteven Rostedt (VMware) 30*ed65df63SSteven 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, 35*ed65df63SSteven 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) 100*ed65df63SSteven 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, 114*ed65df63SSteven 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) { 119da5afbebSSteven Rostedt (VMware) unsigned long pc = preempt_count(); 1200264c8c9SSteven Rostedt (VMware) 121da5afbebSSteven Rostedt (VMware) if (!(pc & (NMI_MASK | HARDIRQ_MASK | SOFTIRQ_OFFSET))) 122da5afbebSSteven Rostedt (VMware) return TRACE_CTX_NORMAL; 1230264c8c9SSteven Rostedt (VMware) else 124da5afbebSSteven Rostedt (VMware) return pc & NMI_MASK ? TRACE_CTX_NMI : 125da5afbebSSteven Rostedt (VMware) pc & HARDIRQ_MASK ? TRACE_CTX_IRQ : TRACE_CTX_SOFTIRQ; 1260264c8c9SSteven Rostedt (VMware) } 1270264c8c9SSteven Rostedt (VMware) 128773c1670SSteven Rostedt (VMware) #ifdef CONFIG_FTRACE_RECORD_RECURSION 129773c1670SSteven Rostedt (VMware) extern void ftrace_record_recursion(unsigned long ip, unsigned long parent_ip); 130773c1670SSteven Rostedt (VMware) # define do_ftrace_record_recursion(ip, pip) \ 131773c1670SSteven Rostedt (VMware) do { \ 132773c1670SSteven Rostedt (VMware) if (!trace_recursion_test(TRACE_RECORD_RECURSION_BIT)) { \ 133773c1670SSteven Rostedt (VMware) trace_recursion_set(TRACE_RECORD_RECURSION_BIT); \ 134773c1670SSteven Rostedt (VMware) ftrace_record_recursion(ip, pip); \ 135773c1670SSteven Rostedt (VMware) trace_recursion_clear(TRACE_RECORD_RECURSION_BIT); \ 136773c1670SSteven Rostedt (VMware) } \ 137773c1670SSteven Rostedt (VMware) } while (0) 138773c1670SSteven Rostedt (VMware) #else 139773c1670SSteven Rostedt (VMware) # define do_ftrace_record_recursion(ip, pip) do { } while (0) 140773c1670SSteven Rostedt (VMware) #endif 141773c1670SSteven Rostedt (VMware) 142773c1670SSteven Rostedt (VMware) static __always_inline int trace_test_and_set_recursion(unsigned long ip, unsigned long pip, 143*ed65df63SSteven Rostedt (VMware) int start) 1440264c8c9SSteven Rostedt (VMware) { 1457b68621fSSteven Rostedt (VMware) unsigned int val = READ_ONCE(current->trace_recursion); 1460264c8c9SSteven Rostedt (VMware) int bit; 1470264c8c9SSteven Rostedt (VMware) 1480264c8c9SSteven Rostedt (VMware) bit = trace_get_context_bit() + start; 1490264c8c9SSteven Rostedt (VMware) if (unlikely(val & (1 << bit))) { 1500264c8c9SSteven Rostedt (VMware) /* 1510264c8c9SSteven Rostedt (VMware) * It could be that preempt_count has not been updated during 1520264c8c9SSteven Rostedt (VMware) * a switch between contexts. Allow for a single recursion. 1530264c8c9SSteven Rostedt (VMware) */ 154*ed65df63SSteven Rostedt (VMware) bit = TRACE_CTX_TRANSITION + start; 1557b68621fSSteven Rostedt (VMware) if (val & (1 << bit)) { 156773c1670SSteven Rostedt (VMware) do_ftrace_record_recursion(ip, pip); 1570264c8c9SSteven Rostedt (VMware) return -1; 158773c1670SSteven Rostedt (VMware) } 1597b68621fSSteven Rostedt (VMware) } 1600264c8c9SSteven Rostedt (VMware) 1610264c8c9SSteven Rostedt (VMware) val |= 1 << bit; 1620264c8c9SSteven Rostedt (VMware) current->trace_recursion = val; 1630264c8c9SSteven Rostedt (VMware) barrier(); 1640264c8c9SSteven Rostedt (VMware) 165*ed65df63SSteven Rostedt (VMware) return bit; 1660264c8c9SSteven Rostedt (VMware) } 1670264c8c9SSteven Rostedt (VMware) 1680264c8c9SSteven Rostedt (VMware) static __always_inline void trace_clear_recursion(int bit) 1690264c8c9SSteven Rostedt (VMware) { 1700264c8c9SSteven Rostedt (VMware) barrier(); 1717b68621fSSteven Rostedt (VMware) trace_recursion_clear(bit); 1720264c8c9SSteven Rostedt (VMware) } 1730264c8c9SSteven Rostedt (VMware) 1746e4eb9cbSSteven Rostedt (VMware) /** 1756e4eb9cbSSteven Rostedt (VMware) * ftrace_test_recursion_trylock - tests for recursion in same context 1766e4eb9cbSSteven Rostedt (VMware) * 1776e4eb9cbSSteven Rostedt (VMware) * Use this for ftrace callbacks. This will detect if the function 1786e4eb9cbSSteven Rostedt (VMware) * tracing recursed in the same context (normal vs interrupt), 1796e4eb9cbSSteven Rostedt (VMware) * 1806e4eb9cbSSteven Rostedt (VMware) * Returns: -1 if a recursion happened. 1816e4eb9cbSSteven Rostedt (VMware) * >= 0 if no recursion 1826e4eb9cbSSteven Rostedt (VMware) */ 183773c1670SSteven Rostedt (VMware) static __always_inline int ftrace_test_recursion_trylock(unsigned long ip, 184773c1670SSteven Rostedt (VMware) unsigned long parent_ip) 1856e4eb9cbSSteven Rostedt (VMware) { 186*ed65df63SSteven Rostedt (VMware) return trace_test_and_set_recursion(ip, parent_ip, TRACE_FTRACE_START); 1876e4eb9cbSSteven Rostedt (VMware) } 1886e4eb9cbSSteven Rostedt (VMware) 1896e4eb9cbSSteven Rostedt (VMware) /** 1906e4eb9cbSSteven Rostedt (VMware) * ftrace_test_recursion_unlock - called when function callback is complete 1916e4eb9cbSSteven Rostedt (VMware) * @bit: The return of a successful ftrace_test_recursion_trylock() 1926e4eb9cbSSteven Rostedt (VMware) * 1936e4eb9cbSSteven Rostedt (VMware) * This is used at the end of a ftrace callback. 1946e4eb9cbSSteven Rostedt (VMware) */ 1956e4eb9cbSSteven Rostedt (VMware) static __always_inline void ftrace_test_recursion_unlock(int bit) 1966e4eb9cbSSteven Rostedt (VMware) { 1976e4eb9cbSSteven Rostedt (VMware) trace_clear_recursion(bit); 1986e4eb9cbSSteven Rostedt (VMware) } 1996e4eb9cbSSteven Rostedt (VMware) 2000264c8c9SSteven Rostedt (VMware) #endif /* CONFIG_TRACING */ 2010264c8c9SSteven Rostedt (VMware) #endif /* _LINUX_TRACE_RECURSION_H */ 202