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