| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * Context tracking: Probe on high level context boundaries such as kernel, | |
| * userspace, guest or idle. | |
| * | |
| * This is used by RCU to remove its dependency on the timer tick while a CPU | |
| * runs in idle, userspace or guest mode. | |
| * | |
| * User/guest tracking started by Frederic Weisbecker: | |
| * | |
| * Copyright (C) 2012 Red Hat, Inc., Frederic Weisbecker | |
| * | |
| * Many thanks to Gilad Ben-Yossef, Paul McKenney, Ingo Molnar, Andrew Morton, | |
| * Steven Rostedt, Peter Zijlstra for suggestions and improvements. | |
| * | |
| * RCU extended quiescent state bits imported from kernel/rcu/tree.c | |
| * where the relevant authorship may be found. | |
| */ | |
| #include <linux/context_tracking.h> | |
| #include <linux/rcupdate.h> | |
| #include <linux/sched.h> | |
| #include <linux/hardirq.h> | |
| #include <linux/export.h> | |
| #include <linux/kprobes.h> | |
| #include <trace/events/rcu.h> | |
| DEFINE_PER_CPU(struct context_tracking, context_tracking) = { | |
| #ifdef CONFIG_CONTEXT_TRACKING_IDLE | |
| .nesting = 1, | |
| .nmi_nesting = CT_NESTING_IRQ_NONIDLE, | |
| #endif | |
| .state = ATOMIC_INIT(CT_RCU_WATCHING), | |
| }; | |
| EXPORT_SYMBOL_GPL(context_tracking); | |
| #ifdef CONFIG_CONTEXT_TRACKING_IDLE | |
| #define TPS(x) tracepoint_string(x) | |
| /* Record the current task on exiting RCU-tasks (dyntick-idle entry). */ | |
| static __always_inline void rcu_task_exit(void) | |
| { | |
| #if defined(CONFIG_TASKS_RCU) && defined(CONFIG_NO_HZ_FULL) | |
| WRITE_ONCE(current->rcu_tasks_idle_cpu, smp_processor_id()); | |
| #endif /* #if defined(CONFIG_TASKS_RCU) && defined(CONFIG_NO_HZ_FULL) */ | |
| } | |
| /* Record no current task on entering RCU-tasks (dyntick-idle exit). */ | |
| static __always_inline void rcu_task_enter(void) | |
| { | |
| #if defined(CONFIG_TASKS_RCU) && defined(CONFIG_NO_HZ_FULL) | |
| WRITE_ONCE(current->rcu_tasks_idle_cpu, -1); | |
| #endif /* #if defined(CONFIG_TASKS_RCU) && defined(CONFIG_NO_HZ_FULL) */ | |
| } | |
| /* | |
| * Record entry into an extended quiescent state. This is only to be | |
| * called when not already in an extended quiescent state, that is, | |
| * RCU is watching prior to the call to this function and is no longer | |
| * watching upon return. | |
| */ | |
| static noinstr void ct_kernel_exit_state(int offset) | |
| { | |
| /* | |
| * CPUs seeing atomic_add_return() must see prior RCU read-side | |
| * critical sections, and we also must force ordering with the | |
| * next idle sojourn. | |
| */ | |
| // RCU is still watching. Better not be in extended quiescent state! | |
| WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && !rcu_is_watching_curr_cpu()); | |
| (void)ct_state_inc(offset); | |
| // RCU is no longer watching. | |
| } | |
| /* | |
| * Record exit from an extended quiescent state. This is only to be | |
| * called from an extended quiescent state, that is, RCU is not watching | |
| * prior to the call to this function and is watching upon return. | |
| */ | |
| static noinstr void ct_kernel_enter_state(int offset) | |
| { | |
| int seq; | |
| /* | |
| * CPUs seeing atomic_add_return() must see prior idle sojourns, | |
| * and we also must force ordering with the next RCU read-side | |
| * critical section. | |
| */ | |
| seq = ct_state_inc(offset); | |
| // RCU is now watching. Better not be in an extended quiescent state! | |
| WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && !(seq & CT_RCU_WATCHING)); | |
| } | |
| /* | |
| * Enter an RCU extended quiescent state, which can be either the | |
| * idle loop or adaptive-tickless usermode execution. | |
| * | |
| * We crowbar the ->nmi_nesting field to zero to allow for | |
| * the possibility of usermode upcalls having messed up our count | |
| * of interrupt nesting level during the prior busy period. | |
| */ | |
| static void noinstr ct_kernel_exit(bool user, int offset) | |
| { | |
| struct context_tracking *ct = this_cpu_ptr(&context_tracking); | |
| WARN_ON_ONCE(ct_nmi_nesting() != CT_NESTING_IRQ_NONIDLE); | |
| WRITE_ONCE(ct->nmi_nesting, 0); | |
| WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && | |
| ct_nesting() == 0); | |
| if (ct_nesting() != 1) { | |
| // RCU will still be watching, so just do accounting and leave. | |
| ct->nesting--; | |
| return; | |
| } | |
| instrumentation_begin(); | |
| lockdep_assert_irqs_disabled(); | |
| trace_rcu_watching(TPS("End"), ct_nesting(), 0, ct_rcu_watching()); | |
| WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && !user && !is_idle_task(current)); | |
| rcu_preempt_deferred_qs(current); | |
| // instrumentation for the noinstr ct_kernel_exit_state() | |
| instrument_atomic_write(&ct->state, sizeof(ct->state)); | |
| instrumentation_end(); | |
| WRITE_ONCE(ct->nesting, 0); /* Avoid irq-access tearing. */ | |
| // RCU is watching here ... | |
| ct_kernel_exit_state(offset); | |
| // ... but is no longer watching here. | |
| rcu_task_exit(); | |
| } | |
| /* | |
| * Exit an RCU extended quiescent state, which can be either the | |
| * idle loop or adaptive-tickless usermode execution. | |
| * | |
| * We crowbar the ->nmi_nesting field to CT_NESTING_IRQ_NONIDLE to | |
| * allow for the possibility of usermode upcalls messing up our count of | |
| * interrupt nesting level during the busy period that is just now starting. | |
| */ | |
| static void noinstr ct_kernel_enter(bool user, int offset) | |
| { | |
| struct context_tracking *ct = this_cpu_ptr(&context_tracking); | |
| long oldval; | |
| WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && !raw_irqs_disabled()); | |
| oldval = ct_nesting(); | |
| WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && oldval < 0); | |
| if (oldval) { | |
| // RCU was already watching, so just do accounting and leave. | |
| ct->nesting++; | |
| return; | |
| } | |
| rcu_task_enter(); | |
| // RCU is not watching here ... | |
| ct_kernel_enter_state(offset); | |
| // ... but is watching here. | |
| instrumentation_begin(); | |
| // instrumentation for the noinstr ct_kernel_enter_state() | |
| instrument_atomic_write(&ct->state, sizeof(ct->state)); | |
| trace_rcu_watching(TPS("Start"), ct_nesting(), 1, ct_rcu_watching()); | |
| WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && !user && !is_idle_task(current)); | |
| WRITE_ONCE(ct->nesting, 1); | |
| WARN_ON_ONCE(ct_nmi_nesting()); | |
| WRITE_ONCE(ct->nmi_nesting, CT_NESTING_IRQ_NONIDLE); | |
| instrumentation_end(); | |
| } | |
| /** | |
| * ct_nmi_exit - inform RCU of exit from NMI context | |
| * | |
| * If we are returning from the outermost NMI handler that interrupted an | |
| * RCU-idle period, update ct->state and ct->nmi_nesting | |
| * to let the RCU grace-period handling know that the CPU is back to | |
| * being RCU-idle. | |
| * | |
| * If you add or remove a call to ct_nmi_exit(), be sure to test | |
| * with CONFIG_RCU_EQS_DEBUG=y. | |
| */ | |
| void noinstr ct_nmi_exit(void) | |
| { | |
| struct context_tracking *ct = this_cpu_ptr(&context_tracking); | |
| instrumentation_begin(); | |
| /* | |
| * Check for ->nmi_nesting underflow and bad CT state. | |
| * (We are exiting an NMI handler, so RCU better be paying attention | |
| * to us!) | |
| */ | |
| WARN_ON_ONCE(ct_nmi_nesting() <= 0); | |
| WARN_ON_ONCE(!rcu_is_watching_curr_cpu()); | |
| /* | |
| * If the nesting level is not 1, the CPU wasn't RCU-idle, so | |
| * leave it in non-RCU-idle state. | |
| */ | |
| if (ct_nmi_nesting() != 1) { | |
| trace_rcu_watching(TPS("--="), ct_nmi_nesting(), ct_nmi_nesting() - 2, | |
| ct_rcu_watching()); | |
| WRITE_ONCE(ct->nmi_nesting, /* No store tearing. */ | |
| ct_nmi_nesting() - 2); | |
| instrumentation_end(); | |
| return; | |
| } | |
| /* This NMI interrupted an RCU-idle CPU, restore RCU-idleness. */ | |
| trace_rcu_watching(TPS("Endirq"), ct_nmi_nesting(), 0, ct_rcu_watching()); | |
| WRITE_ONCE(ct->nmi_nesting, 0); /* Avoid store tearing. */ | |
| // instrumentation for the noinstr ct_kernel_exit_state() | |
| instrument_atomic_write(&ct->state, sizeof(ct->state)); | |
| instrumentation_end(); | |
| // RCU is watching here ... | |
| ct_kernel_exit_state(CT_RCU_WATCHING); | |
| // ... but is no longer watching here. | |
| if (!in_nmi()) | |
| rcu_task_exit(); | |
| } | |
| /** | |
| * ct_nmi_enter - inform RCU of entry to NMI context | |
| * | |
| * If the CPU was idle from RCU's viewpoint, update ct->state and | |
| * ct->nmi_nesting to let the RCU grace-period handling know | |
| * that the CPU is active. This implementation permits nested NMIs, as | |
| * long as the nesting level does not overflow an int. (You will probably | |
| * run out of stack space first.) | |
| * | |
| * If you add or remove a call to ct_nmi_enter(), be sure to test | |
| * with CONFIG_RCU_EQS_DEBUG=y. | |
| */ | |
| void noinstr ct_nmi_enter(void) | |
| { | |
| long incby = 2; | |
| struct context_tracking *ct = this_cpu_ptr(&context_tracking); | |
| /* Complain about underflow. */ | |
| WARN_ON_ONCE(ct_nmi_nesting() < 0); | |
| /* | |
| * If idle from RCU viewpoint, atomically increment CT state | |
| * to mark non-idle and increment ->nmi_nesting by one. | |
| * Otherwise, increment ->nmi_nesting by two. This means | |
| * if ->nmi_nesting is equal to one, we are guaranteed | |
| * to be in the outermost NMI handler that interrupted an RCU-idle | |
| * period (observation due to Andy Lutomirski). | |
| */ | |
| if (!rcu_is_watching_curr_cpu()) { | |
| if (!in_nmi()) | |
| rcu_task_enter(); | |
| // RCU is not watching here ... | |
| ct_kernel_enter_state(CT_RCU_WATCHING); | |
| // ... but is watching here. | |
| instrumentation_begin(); | |
| // instrumentation for the noinstr rcu_is_watching_curr_cpu() | |
| instrument_atomic_read(&ct->state, sizeof(ct->state)); | |
| // instrumentation for the noinstr ct_kernel_enter_state() | |
| instrument_atomic_write(&ct->state, sizeof(ct->state)); | |
| incby = 1; | |
| } else if (!in_nmi()) { | |
| instrumentation_begin(); | |
| rcu_irq_enter_check_tick(); | |
| } else { | |
| instrumentation_begin(); | |
| } | |
| trace_rcu_watching(incby == 1 ? TPS("Startirq") : TPS("++="), | |
| ct_nmi_nesting(), | |
| ct_nmi_nesting() + incby, ct_rcu_watching()); | |
| instrumentation_end(); | |
| WRITE_ONCE(ct->nmi_nesting, /* Prevent store tearing. */ | |
| ct_nmi_nesting() + incby); | |
| barrier(); | |
| } | |
| /** | |
| * ct_idle_enter - inform RCU that current CPU is entering idle | |
| * | |
| * Enter idle mode, in other words, -leave- the mode in which RCU | |
| * read-side critical sections can occur. (Though RCU read-side | |
| * critical sections can occur in irq handlers in idle, a possibility | |
| * handled by irq_enter() and irq_exit().) | |
| * | |
| * If you add or remove a call to ct_idle_enter(), be sure to test with | |
| * CONFIG_RCU_EQS_DEBUG=y. | |
| */ | |
| void noinstr ct_idle_enter(void) | |
| { | |
| WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && !raw_irqs_disabled()); | |
| ct_kernel_exit(false, CT_RCU_WATCHING + CT_STATE_IDLE); | |
| } | |
| EXPORT_SYMBOL_GPL(ct_idle_enter); | |
| /** | |
| * ct_idle_exit - inform RCU that current CPU is leaving idle | |
| * | |
| * Exit idle mode, in other words, -enter- the mode in which RCU | |
| * read-side critical sections can occur. | |
| * | |
| * If you add or remove a call to ct_idle_exit(), be sure to test with | |
| * CONFIG_RCU_EQS_DEBUG=y. | |
| */ | |
| void noinstr ct_idle_exit(void) | |
| { | |
| unsigned long flags; | |
| raw_local_irq_save(flags); | |
| ct_kernel_enter(false, CT_RCU_WATCHING - CT_STATE_IDLE); | |
| raw_local_irq_restore(flags); | |
| } | |
| EXPORT_SYMBOL_GPL(ct_idle_exit); | |
| /** | |
| * ct_irq_enter - inform RCU that current CPU is entering irq away from idle | |
| * | |
| * Enter an interrupt handler, which might possibly result in exiting | |
| * idle mode, in other words, entering the mode in which read-side critical | |
| * sections can occur. The caller must have disabled interrupts. | |
| * | |
| * Note that the Linux kernel is fully capable of entering an interrupt | |
| * handler that it never exits, for example when doing upcalls to user mode! | |
| * This code assumes that the idle loop never does upcalls to user mode. | |
| * If your architecture's idle loop does do upcalls to user mode (or does | |
| * anything else that results in unbalanced calls to the irq_enter() and | |
| * irq_exit() functions), RCU will give you what you deserve, good and hard. | |
| * But very infrequently and irreproducibly. | |
| * | |
| * Use things like work queues to work around this limitation. | |
| * | |
| * You have been warned. | |
| * | |
| * If you add or remove a call to ct_irq_enter(), be sure to test with | |
| * CONFIG_RCU_EQS_DEBUG=y. | |
| */ | |
| noinstr void ct_irq_enter(void) | |
| { | |
| lockdep_assert_irqs_disabled(); | |
| ct_nmi_enter(); | |
| } | |
| /** | |
| * ct_irq_exit - inform RCU that current CPU is exiting irq towards idle | |
| * | |
| * Exit from an interrupt handler, which might possibly result in entering | |
| * idle mode, in other words, leaving the mode in which read-side critical | |
| * sections can occur. The caller must have disabled interrupts. | |
| * | |
| * This code assumes that the idle loop never does anything that might | |
| * result in unbalanced calls to irq_enter() and irq_exit(). If your | |
| * architecture's idle loop violates this assumption, RCU will give you what | |
| * you deserve, good and hard. But very infrequently and irreproducibly. | |
| * | |
| * Use things like work queues to work around this limitation. | |
| * | |
| * You have been warned. | |
| * | |
| * If you add or remove a call to ct_irq_exit(), be sure to test with | |
| * CONFIG_RCU_EQS_DEBUG=y. | |
| */ | |
| noinstr void ct_irq_exit(void) | |
| { | |
| lockdep_assert_irqs_disabled(); | |
| ct_nmi_exit(); | |
| } | |
| /* | |
| * Wrapper for ct_irq_enter() where interrupts are enabled. | |
| * | |
| * If you add or remove a call to ct_irq_enter_irqson(), be sure to test | |
| * with CONFIG_RCU_EQS_DEBUG=y. | |
| */ | |
| void ct_irq_enter_irqson(void) | |
| { | |
| unsigned long flags; | |
| local_irq_save(flags); | |
| ct_irq_enter(); | |
| local_irq_restore(flags); | |
| } | |
| /* | |
| * Wrapper for ct_irq_exit() where interrupts are enabled. | |
| * | |
| * If you add or remove a call to ct_irq_exit_irqson(), be sure to test | |
| * with CONFIG_RCU_EQS_DEBUG=y. | |
| */ | |
| void ct_irq_exit_irqson(void) | |
| { | |
| unsigned long flags; | |
| local_irq_save(flags); | |
| ct_irq_exit(); | |
| local_irq_restore(flags); | |
| } | |
| #else | |
| static __always_inline void ct_kernel_exit(bool user, int offset) { } | |
| static __always_inline void ct_kernel_enter(bool user, int offset) { } | |
| #endif /* #ifdef CONFIG_CONTEXT_TRACKING_IDLE */ | |
| #ifdef CONFIG_CONTEXT_TRACKING_USER | |
| #define CREATE_TRACE_POINTS | |
| #include <trace/events/context_tracking.h> | |
| DEFINE_STATIC_KEY_FALSE_RO(context_tracking_key); | |
| EXPORT_SYMBOL_GPL(context_tracking_key); | |
| static noinstr bool context_tracking_recursion_enter(void) | |
| { | |
| int recursion; | |
| recursion = __this_cpu_inc_return(context_tracking.recursion); | |
| if (recursion == 1) | |
| return true; | |
| WARN_ONCE((recursion < 1), "Invalid context tracking recursion value %d\n", recursion); | |
| __this_cpu_dec(context_tracking.recursion); | |
| return false; | |
| } | |
| static __always_inline void context_tracking_recursion_exit(void) | |
| { | |
| __this_cpu_dec(context_tracking.recursion); | |
| } | |
| /** | |
| * __ct_user_enter - Inform the context tracking that the CPU is going | |
| * to enter user or guest space mode. | |
| * | |
| * @state: userspace context-tracking state to enter. | |
| * | |
| * This function must be called right before we switch from the kernel | |
| * to user or guest space, when it's guaranteed the remaining kernel | |
| * instructions to execute won't use any RCU read side critical section | |
| * because this function sets RCU in extended quiescent state. | |
| */ | |
| void noinstr __ct_user_enter(enum ctx_state state) | |
| { | |
| struct context_tracking *ct = this_cpu_ptr(&context_tracking); | |
| lockdep_assert_irqs_disabled(); | |
| /* Kernel threads aren't supposed to go to userspace */ | |
| WARN_ON_ONCE(!current->mm); | |
| if (!context_tracking_recursion_enter()) | |
| return; | |
| if (__ct_state() != state) { | |
| if (ct->active) { | |
| /* | |
| * At this stage, only low level arch entry code remains and | |
| * then we'll run in userspace. We can assume there won't be | |
| * any RCU read-side critical section until the next call to | |
| * user_exit() or ct_irq_enter(). Let's remove RCU's dependency | |
| * on the tick. | |
| */ | |
| if (state == CT_STATE_USER) { | |
| instrumentation_begin(); | |
| trace_user_enter(0); | |
| vtime_user_enter(current); | |
| instrumentation_end(); | |
| } | |
| /* | |
| * Other than generic entry implementation, we may be past the last | |
| * rescheduling opportunity in the entry code. Trigger a self IPI | |
| * that will fire and reschedule once we resume in user/guest mode. | |
| */ | |
| rcu_irq_work_resched(); | |
| /* | |
| * Enter RCU idle mode right before resuming userspace. No use of RCU | |
| * is permitted between this call and rcu_eqs_exit(). This way the | |
| * CPU doesn't need to maintain the tick for RCU maintenance purposes | |
| * when the CPU runs in userspace. | |
| */ | |
| ct_kernel_exit(true, CT_RCU_WATCHING + state); | |
| /* | |
| * Special case if we only track user <-> kernel transitions for tickless | |
| * cputime accounting but we don't support RCU extended quiescent state. | |
| * In this we case we don't care about any concurrency/ordering. | |
| */ | |
| if (!IS_ENABLED(CONFIG_CONTEXT_TRACKING_IDLE)) | |
| raw_atomic_set(&ct->state, state); | |
| } else { | |
| /* | |
| * Even if context tracking is disabled on this CPU, because it's outside | |
| * the full dynticks mask for example, we still have to keep track of the | |
| * context transitions and states to prevent inconsistency on those of | |
| * other CPUs. | |
| * If a task triggers an exception in userspace, sleep on the exception | |
| * handler and then migrate to another CPU, that new CPU must know where | |
| * the exception returns by the time we call exception_exit(). | |
| * This information can only be provided by the previous CPU when it called | |
| * exception_enter(). | |
| * OTOH we can spare the calls to vtime and RCU when context_tracking.active | |
| * is false because we know that CPU is not tickless. | |
| */ | |
| if (!IS_ENABLED(CONFIG_CONTEXT_TRACKING_IDLE)) { | |
| /* Tracking for vtime only, no concurrent RCU EQS accounting */ | |
| raw_atomic_set(&ct->state, state); | |
| } else { | |
| /* | |
| * Tracking for vtime and RCU EQS. Make sure we don't race | |
| * with NMIs. OTOH we don't care about ordering here since | |
| * RCU only requires CT_RCU_WATCHING increments to be fully | |
| * ordered. | |
| */ | |
| raw_atomic_add(state, &ct->state); | |
| } | |
| } | |
| } | |
| context_tracking_recursion_exit(); | |
| } | |
| EXPORT_SYMBOL_GPL(__ct_user_enter); | |
| /* | |
| * OBSOLETE: | |
| * This function should be noinstr but the below local_irq_restore() is | |
| * unsafe because it involves illegal RCU uses through tracing and lockdep. | |
| * This is unlikely to be fixed as this function is obsolete. The preferred | |
| * way is to call __context_tracking_enter() through user_enter_irqoff() | |
| * or context_tracking_guest_enter(). It should be the arch entry code | |
| * responsibility to call into context tracking with IRQs disabled. | |
| */ | |
| void ct_user_enter(enum ctx_state state) | |
| { | |
| unsigned long flags; | |
| /* | |
| * Some contexts may involve an exception occuring in an irq, | |
| * leading to that nesting: | |
| * ct_irq_enter() rcu_eqs_exit(true) rcu_eqs_enter(true) ct_irq_exit() | |
| * This would mess up the dyntick_nesting count though. And rcu_irq_*() | |
| * helpers are enough to protect RCU uses inside the exception. So | |
| * just return immediately if we detect we are in an IRQ. | |
| */ | |
| if (in_interrupt()) | |
| return; | |
| local_irq_save(flags); | |
| __ct_user_enter(state); | |
| local_irq_restore(flags); | |
| } | |
| NOKPROBE_SYMBOL(ct_user_enter); | |
| EXPORT_SYMBOL_GPL(ct_user_enter); | |
| /** | |
| * user_enter_callable() - Unfortunate ASM callable version of user_enter() for | |
| * archs that didn't manage to check the context tracking | |
| * static key from low level code. | |
| * | |
| * This OBSOLETE function should be noinstr but it unsafely calls | |
| * local_irq_restore(), involving illegal RCU uses through tracing and lockdep. | |
| * This is unlikely to be fixed as this function is obsolete. The preferred | |
| * way is to call user_enter_irqoff(). It should be the arch entry code | |
| * responsibility to call into context tracking with IRQs disabled. | |
| */ | |
| void user_enter_callable(void) | |
| { | |
| user_enter(); | |
| } | |
| NOKPROBE_SYMBOL(user_enter_callable); | |
| /** | |
| * __ct_user_exit - Inform the context tracking that the CPU is | |
| * exiting user or guest mode and entering the kernel. | |
| * | |
| * @state: userspace context-tracking state being exited from. | |
| * | |
| * This function must be called after we entered the kernel from user or | |
| * guest space before any use of RCU read side critical section. This | |
| * potentially include any high level kernel code like syscalls, exceptions, | |
| * signal handling, etc... | |
| * | |
| * This call supports re-entrancy. This way it can be called from any exception | |
| * handler without needing to know if we came from userspace or not. | |
| */ | |
| void noinstr __ct_user_exit(enum ctx_state state) | |
| { | |
| struct context_tracking *ct = this_cpu_ptr(&context_tracking); | |
| if (!context_tracking_recursion_enter()) | |
| return; | |
| if (__ct_state() == state) { | |
| if (ct->active) { | |
| /* | |
| * Exit RCU idle mode while entering the kernel because it can | |
| * run a RCU read side critical section anytime. | |
| */ | |
| ct_kernel_enter(true, CT_RCU_WATCHING - state); | |
| if (state == CT_STATE_USER) { | |
| instrumentation_begin(); | |
| vtime_user_exit(current); | |
| trace_user_exit(0); | |
| instrumentation_end(); | |
| } | |
| /* | |
| * Special case if we only track user <-> kernel transitions for tickless | |
| * cputime accounting but we don't support RCU extended quiescent state. | |
| * In this we case we don't care about any concurrency/ordering. | |
| */ | |
| if (!IS_ENABLED(CONFIG_CONTEXT_TRACKING_IDLE)) | |
| raw_atomic_set(&ct->state, CT_STATE_KERNEL); | |
| } else { | |
| if (!IS_ENABLED(CONFIG_CONTEXT_TRACKING_IDLE)) { | |
| /* Tracking for vtime only, no concurrent RCU EQS accounting */ | |
| raw_atomic_set(&ct->state, CT_STATE_KERNEL); | |
| } else { | |
| /* | |
| * Tracking for vtime and RCU EQS. Make sure we don't race | |
| * with NMIs. OTOH we don't care about ordering here since | |
| * RCU only requires CT_RCU_WATCHING increments to be fully | |
| * ordered. | |
| */ | |
| raw_atomic_sub(state, &ct->state); | |
| } | |
| } | |
| } | |
| context_tracking_recursion_exit(); | |
| } | |
| EXPORT_SYMBOL_GPL(__ct_user_exit); | |
| /* | |
| * OBSOLETE: | |
| * This function should be noinstr but the below local_irq_save() is | |
| * unsafe because it involves illegal RCU uses through tracing and lockdep. | |
| * This is unlikely to be fixed as this function is obsolete. The preferred | |
| * way is to call __context_tracking_exit() through user_exit_irqoff() | |
| * or context_tracking_guest_exit(). It should be the arch entry code | |
| * responsibility to call into context tracking with IRQs disabled. | |
| */ | |
| void ct_user_exit(enum ctx_state state) | |
| { | |
| unsigned long flags; | |
| if (in_interrupt()) | |
| return; | |
| local_irq_save(flags); | |
| __ct_user_exit(state); | |
| local_irq_restore(flags); | |
| } | |
| NOKPROBE_SYMBOL(ct_user_exit); | |
| EXPORT_SYMBOL_GPL(ct_user_exit); | |
| /** | |
| * user_exit_callable() - Unfortunate ASM callable version of user_exit() for | |
| * archs that didn't manage to check the context tracking | |
| * static key from low level code. | |
| * | |
| * This OBSOLETE function should be noinstr but it unsafely calls local_irq_save(), | |
| * involving illegal RCU uses through tracing and lockdep. This is unlikely | |
| * to be fixed as this function is obsolete. The preferred way is to call | |
| * user_exit_irqoff(). It should be the arch entry code responsibility to | |
| * call into context tracking with IRQs disabled. | |
| */ | |
| void user_exit_callable(void) | |
| { | |
| user_exit(); | |
| } | |
| NOKPROBE_SYMBOL(user_exit_callable); | |
| void __init ct_cpu_track_user(int cpu) | |
| { | |
| static __initdata bool initialized = false; | |
| if (!per_cpu(context_tracking.active, cpu)) { | |
| per_cpu(context_tracking.active, cpu) = true; | |
| static_branch_inc(&context_tracking_key); | |
| } | |
| if (initialized) | |
| return; | |
| #ifdef CONFIG_HAVE_TIF_NOHZ | |
| /* | |
| * Set TIF_NOHZ to init/0 and let it propagate to all tasks through fork | |
| * This assumes that init is the only task at this early boot stage. | |
| */ | |
| set_tsk_thread_flag(&init_task, TIF_NOHZ); | |
| #endif | |
| WARN_ON_ONCE(!tasklist_empty()); | |
| initialized = true; | |
| } | |
| #ifdef CONFIG_CONTEXT_TRACKING_USER_FORCE | |
| void __init context_tracking_init(void) | |
| { | |
| int cpu; | |
| for_each_possible_cpu(cpu) | |
| ct_cpu_track_user(cpu); | |
| } | |
| #endif | |
| #endif /* #ifdef CONFIG_CONTEXT_TRACKING_USER */ |