1b2441318SGreg Kroah-Hartman /* SPDX-License-Identifier: GPL-2.0 */
2e7358b3bSFrederic Weisbecker #ifndef _LINUX_CONTEXT_TRACKING_STATE_H
3e7358b3bSFrederic Weisbecker #define _LINUX_CONTEXT_TRACKING_STATE_H
4e7358b3bSFrederic Weisbecker 
5e7358b3bSFrederic Weisbecker #include <linux/percpu.h>
6e7358b3bSFrederic Weisbecker #include <linux/static_key.h>
76f0e6c15SFrederic Weisbecker #include <linux/context_tracking_irq.h>
8e7358b3bSFrederic Weisbecker 
917147677SFrederic Weisbecker /* Offset to allow distinguishing irq vs. task-based idle entry/exit. */
10e1de4383SValentin Schneider #define CT_NESTING_IRQ_NONIDLE	((LONG_MAX / 2) + 1)
1117147677SFrederic Weisbecker 
1262e2412dSFrederic Weisbecker enum ctx_state {
13d65d411cSValentin Schneider 	CT_STATE_DISABLED	= -1,	/* returned by ct_state() if unknown */
14d65d411cSValentin Schneider 	CT_STATE_KERNEL		= 0,
15d65d411cSValentin Schneider 	CT_STATE_IDLE		= 1,
16d65d411cSValentin Schneider 	CT_STATE_USER		= 2,
17d65d411cSValentin Schneider 	CT_STATE_GUEST		= 3,
18d65d411cSValentin Schneider 	CT_STATE_MAX		= 4,
1962e2412dSFrederic Weisbecker };
2062e2412dSFrederic Weisbecker 
214aa35e0dSValentin Schneider /* Odd value for watching, else even. */
224aa35e0dSValentin Schneider #define CT_RCU_WATCHING CT_STATE_MAX
2317147677SFrederic Weisbecker 
24d65d411cSValentin Schneider #define CT_STATE_MASK (CT_STATE_MAX - 1)
254aa35e0dSValentin Schneider #define CT_RCU_WATCHING_MASK (~CT_STATE_MASK)
2695e04f48SFrederic Weisbecker 
27e7358b3bSFrederic Weisbecker struct context_tracking {
2862e2412dSFrederic Weisbecker #ifdef CONFIG_CONTEXT_TRACKING_USER
29e7358b3bSFrederic Weisbecker 	/*
30e7358b3bSFrederic Weisbecker 	 * When active is false, probes are unset in order
31e7358b3bSFrederic Weisbecker 	 * to minimize overhead: TIF flags are cleared
32e7358b3bSFrederic Weisbecker 	 * and calls to user_enter/exit are ignored. This
33e7358b3bSFrederic Weisbecker 	 * may be further optimized using static keys.
34e7358b3bSFrederic Weisbecker 	 */
35e7358b3bSFrederic Weisbecker 	bool active;
36aed5ed47SFrederic Weisbecker 	int recursion;
3717147677SFrederic Weisbecker #endif
3817147677SFrederic Weisbecker #ifdef CONFIG_CONTEXT_TRACKING
3917147677SFrederic Weisbecker 	atomic_t state;
4062e2412dSFrederic Weisbecker #endif
4162e2412dSFrederic Weisbecker #ifdef CONFIG_CONTEXT_TRACKING_IDLE
42bf664719SValentin Schneider 	long nesting;		/* Track process nesting level. */
43dc5ffaceSValentin Schneider 	long nmi_nesting;	/* Track irq/NMI nesting level. */
4462e2412dSFrederic Weisbecker #endif
45e7358b3bSFrederic Weisbecker };
46e7358b3bSFrederic Weisbecker 
4762e2412dSFrederic Weisbecker #ifdef CONFIG_CONTEXT_TRACKING
4862e2412dSFrederic Weisbecker DECLARE_PER_CPU(struct context_tracking, context_tracking);
49f87d2867SJosh Poimboeuf #endif
5017147677SFrederic Weisbecker 
51f87d2867SJosh Poimboeuf #ifdef CONFIG_CONTEXT_TRACKING_USER
__ct_state(void)5217147677SFrederic Weisbecker static __always_inline int __ct_state(void)
5317147677SFrederic Weisbecker {
540f613bfaSMark Rutland 	return raw_atomic_read(this_cpu_ptr(&context_tracking.state)) & CT_STATE_MASK;
5517147677SFrederic Weisbecker }
5662e2412dSFrederic Weisbecker #endif
5762e2412dSFrederic Weisbecker 
5862e2412dSFrederic Weisbecker #ifdef CONFIG_CONTEXT_TRACKING_IDLE
ct_rcu_watching(void)59a4a7921eSValentin Schneider static __always_inline int ct_rcu_watching(void)
6062e2412dSFrederic Weisbecker {
614aa35e0dSValentin Schneider 	return atomic_read(this_cpu_ptr(&context_tracking.state)) & CT_RCU_WATCHING_MASK;
6262e2412dSFrederic Weisbecker }
6362e2412dSFrederic Weisbecker 
ct_rcu_watching_cpu(int cpu)64a9fde9d1SValentin Schneider static __always_inline int ct_rcu_watching_cpu(int cpu)
6562e2412dSFrederic Weisbecker {
6662e2412dSFrederic Weisbecker 	struct context_tracking *ct = per_cpu_ptr(&context_tracking, cpu);
6762e2412dSFrederic Weisbecker 
684aa35e0dSValentin Schneider 	return atomic_read(&ct->state) & CT_RCU_WATCHING_MASK;
6962e2412dSFrederic Weisbecker }
7062e2412dSFrederic Weisbecker 
ct_rcu_watching_cpu_acquire(int cpu)71125716c3SValentin Schneider static __always_inline int ct_rcu_watching_cpu_acquire(int cpu)
7262e2412dSFrederic Weisbecker {
7362e2412dSFrederic Weisbecker 	struct context_tracking *ct = per_cpu_ptr(&context_tracking, cpu);
7462e2412dSFrederic Weisbecker 
754aa35e0dSValentin Schneider 	return atomic_read_acquire(&ct->state) & CT_RCU_WATCHING_MASK;
7662e2412dSFrederic Weisbecker }
77904e600eSFrederic Weisbecker 
ct_nesting(void)781089c007SValentin Schneider static __always_inline long ct_nesting(void)
79904e600eSFrederic Weisbecker {
80bf664719SValentin Schneider 	return __this_cpu_read(context_tracking.nesting);
81904e600eSFrederic Weisbecker }
82904e600eSFrederic Weisbecker 
ct_nesting_cpu(int cpu)83bca9455dSValentin Schneider static __always_inline long ct_nesting_cpu(int cpu)
84904e600eSFrederic Weisbecker {
85904e600eSFrederic Weisbecker 	struct context_tracking *ct = per_cpu_ptr(&context_tracking, cpu);
86904e600eSFrederic Weisbecker 
87bf664719SValentin Schneider 	return ct->nesting;
88904e600eSFrederic Weisbecker }
8995e04f48SFrederic Weisbecker 
ct_nmi_nesting(void)908375cb26SValentin Schneider static __always_inline long ct_nmi_nesting(void)
9195e04f48SFrederic Weisbecker {
92dc5ffaceSValentin Schneider 	return __this_cpu_read(context_tracking.nmi_nesting);
9395e04f48SFrederic Weisbecker }
9495e04f48SFrederic Weisbecker 
ct_nmi_nesting_cpu(int cpu)952ef2890bSValentin Schneider static __always_inline long ct_nmi_nesting_cpu(int cpu)
9695e04f48SFrederic Weisbecker {
9795e04f48SFrederic Weisbecker 	struct context_tracking *ct = per_cpu_ptr(&context_tracking, cpu);
9895e04f48SFrederic Weisbecker 
99dc5ffaceSValentin Schneider 	return ct->nmi_nesting;
10095e04f48SFrederic Weisbecker }
10162e2412dSFrederic Weisbecker #endif /* #ifdef CONFIG_CONTEXT_TRACKING_IDLE */
10262e2412dSFrederic Weisbecker 
10324a9c541SFrederic Weisbecker #ifdef CONFIG_CONTEXT_TRACKING_USER
10474c57875SFrederic Weisbecker extern struct static_key_false context_tracking_key;
105e7358b3bSFrederic Weisbecker 
context_tracking_enabled(void)1060372007fSThomas Gleixner static __always_inline bool context_tracking_enabled(void)
10758135f57SFrederic Weisbecker {
10874c57875SFrederic Weisbecker 	return static_branch_unlikely(&context_tracking_key);
10958135f57SFrederic Weisbecker }
110d0df09ebSFrederic Weisbecker 
context_tracking_enabled_cpu(int cpu)1110372007fSThomas Gleixner static __always_inline bool context_tracking_enabled_cpu(int cpu)
112097f2541SFrederic Weisbecker {
113023e9debSFrederic Weisbecker 	return context_tracking_enabled() && per_cpu(context_tracking.active, cpu);
114097f2541SFrederic Weisbecker }
115097f2541SFrederic Weisbecker 
context_tracking_enabled_this_cpu(void)116*4040b113SSean Christopherson static __always_inline bool context_tracking_enabled_this_cpu(void)
117d0df09ebSFrederic Weisbecker {
118023e9debSFrederic Weisbecker 	return context_tracking_enabled() && __this_cpu_read(context_tracking.active);
119d0df09ebSFrederic Weisbecker }
120d0df09ebSFrederic Weisbecker 
12117147677SFrederic Weisbecker /**
12217147677SFrederic Weisbecker  * ct_state() - return the current context tracking state if known
12317147677SFrederic Weisbecker  *
12417147677SFrederic Weisbecker  * Returns the current cpu's context tracking state if context tracking
12517147677SFrederic Weisbecker  * is enabled.  If context tracking is disabled, returns
126d65d411cSValentin Schneider  * CT_STATE_DISABLED.  This should be used primarily for debugging.
12717147677SFrederic Weisbecker  */
ct_state(void)12817147677SFrederic Weisbecker static __always_inline int ct_state(void)
12917147677SFrederic Weisbecker {
13017147677SFrederic Weisbecker 	int ret;
13117147677SFrederic Weisbecker 
13217147677SFrederic Weisbecker 	if (!context_tracking_enabled())
133d65d411cSValentin Schneider 		return CT_STATE_DISABLED;
13417147677SFrederic Weisbecker 
13517147677SFrederic Weisbecker 	preempt_disable();
13617147677SFrederic Weisbecker 	ret = __ct_state();
13717147677SFrederic Weisbecker 	preempt_enable();
13817147677SFrederic Weisbecker 
13917147677SFrederic Weisbecker 	return ret;
14017147677SFrederic Weisbecker }
14117147677SFrederic Weisbecker 
142e7358b3bSFrederic Weisbecker #else
context_tracking_enabled(void)143620f8d3bSPeter Zijlstra static __always_inline bool context_tracking_enabled(void) { return false; }
context_tracking_enabled_cpu(int cpu)144620f8d3bSPeter Zijlstra static __always_inline bool context_tracking_enabled_cpu(int cpu) { return false; }
context_tracking_enabled_this_cpu(void)145620f8d3bSPeter Zijlstra static __always_inline bool context_tracking_enabled_this_cpu(void) { return false; }
14624a9c541SFrederic Weisbecker #endif /* CONFIG_CONTEXT_TRACKING_USER */
147e7358b3bSFrederic Weisbecker 
148e7358b3bSFrederic Weisbecker #endif
149