Commit e67198cc authored by Frederic Weisbecker's avatar Frederic Weisbecker Committed by Paul E. McKenney

context_tracking: Take idle eqs entrypoints over RCU

The RCU dynticks counter is going to be merged into the context tracking
subsystem. Start with moving the idle extended quiescent states
entrypoints to context tracking. For now those are dumb redirections to
existing RCU calls.

[ paulmck: Apply kernel test robot feedback. ]
Signed-off-by: default avatarFrederic Weisbecker <frederic@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Neeraj Upadhyay <quic_neeraju@quicinc.com>
Cc: Uladzislau Rezki <uladzislau.rezki@sony.com>
Cc: Joel Fernandes <joel@joelfernandes.org>
Cc: Boqun Feng <boqun.feng@gmail.com>
Cc: Nicolas Saenz Julienne <nsaenz@kernel.org>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Xiongfeng Wang <wangxiongfeng2@huawei.com>
Cc: Yu Liao <liaoyu15@huawei.com>
Cc: Phil Auld <pauld@redhat.com>
Cc: Paul Gortmaker<paul.gortmaker@windriver.com>
Cc: Alex Belits <abelits@marvell.com>
Signed-off-by: default avatarPaul E. McKenney <paulmck@kernel.org>
Reviewed-by: default avatarNicolas Saenz Julienne <nsaenzju@redhat.com>
Tested-by: default avatarNicolas Saenz Julienne <nsaenzju@redhat.com>
parent 24a9c541
...@@ -97,8 +97,8 @@ warnings: ...@@ -97,8 +97,8 @@ warnings:
which will include additional debugging information. which will include additional debugging information.
- A low-level kernel issue that either fails to invoke one of the - A low-level kernel issue that either fails to invoke one of the
variants of rcu_user_enter(), rcu_user_exit(), rcu_idle_enter(), variants of rcu_user_enter(), rcu_user_exit(), ct_idle_enter(),
rcu_idle_exit(), rcu_irq_enter(), or rcu_irq_exit() on the one ct_idle_exit(), rcu_irq_enter(), or rcu_irq_exit() on the one
hand, or that invokes one of them too many times on the other. hand, or that invokes one of them too many times on the other.
Historically, the most frequent issue has been an omission Historically, the most frequent issue has been an omission
of either irq_enter() or irq_exit(), which in turn invoke of either irq_enter() or irq_exit(), which in turn invoke
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* Copyright (C) 2012 Freescale Semiconductor, Inc. * Copyright (C) 2012 Freescale Semiconductor, Inc.
*/ */
#include <linux/context_tracking.h>
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/module.h> #include <linux/module.h>
#include <asm/cpuidle.h> #include <asm/cpuidle.h>
...@@ -24,9 +25,9 @@ static int imx6q_enter_wait(struct cpuidle_device *dev, ...@@ -24,9 +25,9 @@ static int imx6q_enter_wait(struct cpuidle_device *dev,
imx6_set_lpm(WAIT_UNCLOCKED); imx6_set_lpm(WAIT_UNCLOCKED);
raw_spin_unlock(&cpuidle_lock); raw_spin_unlock(&cpuidle_lock);
rcu_idle_enter(); ct_idle_enter();
cpu_do_idle(); cpu_do_idle();
rcu_idle_exit(); ct_idle_exit();
raw_spin_lock(&cpuidle_lock); raw_spin_lock(&cpuidle_lock);
if (num_idle_cpus-- == num_online_cpus()) if (num_idle_cpus-- == num_online_cpus())
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/minmax.h> #include <linux/minmax.h>
#include <linux/perf_event.h> #include <linux/perf_event.h>
#include <acpi/processor.h> #include <acpi/processor.h>
#include <linux/context_tracking.h>
/* /*
* Include the apic definitions for x86 to have the APIC timer related defines * Include the apic definitions for x86 to have the APIC timer related defines
...@@ -647,11 +648,11 @@ static int acpi_idle_enter_bm(struct cpuidle_driver *drv, ...@@ -647,11 +648,11 @@ static int acpi_idle_enter_bm(struct cpuidle_driver *drv,
raw_spin_unlock(&c3_lock); raw_spin_unlock(&c3_lock);
} }
rcu_idle_enter(); ct_idle_enter();
acpi_idle_do_entry(cx); acpi_idle_do_entry(cx);
rcu_idle_exit(); ct_idle_exit();
/* Re-enable bus master arbitration */ /* Re-enable bus master arbitration */
if (dis_bm) { if (dis_bm) {
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/tick.h> #include <linux/tick.h>
#include <linux/mmu_context.h> #include <linux/mmu_context.h>
#include <linux/context_tracking.h>
#include <trace/events/power.h> #include <trace/events/power.h>
#include "cpuidle.h" #include "cpuidle.h"
...@@ -150,12 +151,12 @@ static void enter_s2idle_proper(struct cpuidle_driver *drv, ...@@ -150,12 +151,12 @@ static void enter_s2idle_proper(struct cpuidle_driver *drv,
*/ */
stop_critical_timings(); stop_critical_timings();
if (!(target_state->flags & CPUIDLE_FLAG_RCU_IDLE)) if (!(target_state->flags & CPUIDLE_FLAG_RCU_IDLE))
rcu_idle_enter(); ct_idle_enter();
target_state->enter_s2idle(dev, drv, index); target_state->enter_s2idle(dev, drv, index);
if (WARN_ON_ONCE(!irqs_disabled())) if (WARN_ON_ONCE(!irqs_disabled()))
local_irq_disable(); local_irq_disable();
if (!(target_state->flags & CPUIDLE_FLAG_RCU_IDLE)) if (!(target_state->flags & CPUIDLE_FLAG_RCU_IDLE))
rcu_idle_exit(); ct_idle_exit();
tick_unfreeze(); tick_unfreeze();
start_critical_timings(); start_critical_timings();
...@@ -233,10 +234,10 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, ...@@ -233,10 +234,10 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
stop_critical_timings(); stop_critical_timings();
if (!(target_state->flags & CPUIDLE_FLAG_RCU_IDLE)) if (!(target_state->flags & CPUIDLE_FLAG_RCU_IDLE))
rcu_idle_enter(); ct_idle_enter();
entered_state = target_state->enter(dev, drv, index); entered_state = target_state->enter(dev, drv, index);
if (!(target_state->flags & CPUIDLE_FLAG_RCU_IDLE)) if (!(target_state->flags & CPUIDLE_FLAG_RCU_IDLE))
rcu_idle_exit(); ct_idle_exit();
start_critical_timings(); start_critical_timings();
sched_clock_idle_wakeup_event(); sched_clock_idle_wakeup_event();
......
...@@ -119,4 +119,12 @@ extern void context_tracking_init(void); ...@@ -119,4 +119,12 @@ extern void context_tracking_init(void);
static inline void context_tracking_init(void) { } static inline void context_tracking_init(void) { }
#endif /* CONFIG_CONTEXT_TRACKING_USER_FORCE */ #endif /* CONFIG_CONTEXT_TRACKING_USER_FORCE */
#ifdef CONFIG_CONTEXT_TRACKING_IDLE
extern void ct_idle_enter(void);
extern void ct_idle_exit(void);
#else
static inline void ct_idle_enter(void) { }
static inline void ct_idle_exit(void) { }
#endif /* !CONFIG_CONTEXT_TRACKING_IDLE */
#endif #endif
...@@ -128,7 +128,7 @@ static inline void rcu_nocb_flush_deferred_wakeup(void) { } ...@@ -128,7 +128,7 @@ static inline void rcu_nocb_flush_deferred_wakeup(void) { }
* @a: Code that RCU needs to pay attention to. * @a: Code that RCU needs to pay attention to.
* *
* RCU read-side critical sections are forbidden in the inner idle loop, * RCU read-side critical sections are forbidden in the inner idle loop,
* that is, between the rcu_idle_enter() and the rcu_idle_exit() -- RCU * that is, between the ct_idle_enter() and the ct_idle_exit() -- RCU
* will happily ignore any such read-side critical sections. However, * will happily ignore any such read-side critical sections. However,
* things like powertop need tracepoints in the inner idle loop. * things like powertop need tracepoints in the inner idle loop.
* *
......
...@@ -22,6 +22,21 @@ ...@@ -22,6 +22,21 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/kprobes.h> #include <linux/kprobes.h>
#ifdef CONFIG_CONTEXT_TRACKING_IDLE
noinstr void ct_idle_enter(void)
{
rcu_idle_enter();
}
EXPORT_SYMBOL_GPL(ct_idle_enter);
void ct_idle_exit(void)
{
rcu_idle_exit();
}
EXPORT_SYMBOL_GPL(ct_idle_exit);
#endif /* #ifdef CONFIG_CONTEXT_TRACKING_IDLE */
#ifdef CONFIG_CONTEXT_TRACKING_USER #ifdef CONFIG_CONTEXT_TRACKING_USER
#define CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS
......
...@@ -6570,7 +6570,7 @@ void lockdep_rcu_suspicious(const char *file, const int line, const char *s) ...@@ -6570,7 +6570,7 @@ void lockdep_rcu_suspicious(const char *file, const int line, const char *s)
/* /*
* If a CPU is in the RCU-free window in idle (ie: in the section * If a CPU is in the RCU-free window in idle (ie: in the section
* between rcu_idle_enter() and rcu_idle_exit(), then RCU * between ct_idle_enter() and ct_idle_exit(), then RCU
* considers that CPU to be in an "extended quiescent state", * considers that CPU to be in an "extended quiescent state",
* which means that RCU will be completely ignoring that CPU. * which means that RCU will be completely ignoring that CPU.
* Therefore, rcu_read_lock() and friends have absolutely no * Therefore, rcu_read_lock() and friends have absolutely no
......
...@@ -8,6 +8,8 @@ menu "RCU Subsystem" ...@@ -8,6 +8,8 @@ menu "RCU Subsystem"
config TREE_RCU config TREE_RCU
bool bool
default y if SMP default y if SMP
# Dynticks-idle tracking
select CONTEXT_TRACKING_IDLE
help help
This option selects the RCU implementation that is This option selects the RCU implementation that is
designed for very large SMP system with hundreds or designed for very large SMP system with hundreds or
......
...@@ -664,7 +664,6 @@ void noinstr rcu_idle_enter(void) ...@@ -664,7 +664,6 @@ void noinstr rcu_idle_enter(void)
WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && !raw_irqs_disabled()); WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && !raw_irqs_disabled());
rcu_eqs_enter(false); rcu_eqs_enter(false);
} }
EXPORT_SYMBOL_GPL(rcu_idle_enter);
#ifdef CONFIG_NO_HZ_FULL #ifdef CONFIG_NO_HZ_FULL
...@@ -904,7 +903,6 @@ void noinstr rcu_idle_exit(void) ...@@ -904,7 +903,6 @@ void noinstr rcu_idle_exit(void)
rcu_eqs_exit(false); rcu_eqs_exit(false);
raw_local_irq_restore(flags); raw_local_irq_restore(flags);
} }
EXPORT_SYMBOL_GPL(rcu_idle_exit);
#ifdef CONFIG_NO_HZ_FULL #ifdef CONFIG_NO_HZ_FULL
/** /**
......
...@@ -85,7 +85,7 @@ module_param(rcu_normal_after_boot, int, 0444); ...@@ -85,7 +85,7 @@ module_param(rcu_normal_after_boot, int, 0444);
* and while lockdep is disabled. * and while lockdep is disabled.
* *
* Note that if the CPU is in the idle loop from an RCU point of view (ie: * Note that if the CPU is in the idle loop from an RCU point of view (ie:
* that we are in the section between rcu_idle_enter() and rcu_idle_exit()) * that we are in the section between ct_idle_enter() and ct_idle_exit())
* then rcu_read_lock_held() sets ``*ret`` to false even if the CPU did an * then rcu_read_lock_held() sets ``*ret`` to false even if the CPU did an
* rcu_read_lock(). The reason for this is that RCU ignores CPUs that are * rcu_read_lock(). The reason for this is that RCU ignores CPUs that are
* in such a section, considering these as in extended quiescent state, * in such a section, considering these as in extended quiescent state,
......
...@@ -53,14 +53,14 @@ static noinline int __cpuidle cpu_idle_poll(void) ...@@ -53,14 +53,14 @@ static noinline int __cpuidle cpu_idle_poll(void)
{ {
trace_cpu_idle(0, smp_processor_id()); trace_cpu_idle(0, smp_processor_id());
stop_critical_timings(); stop_critical_timings();
rcu_idle_enter(); ct_idle_enter();
local_irq_enable(); local_irq_enable();
while (!tif_need_resched() && while (!tif_need_resched() &&
(cpu_idle_force_poll || tick_check_broadcast_expired())) (cpu_idle_force_poll || tick_check_broadcast_expired()))
cpu_relax(); cpu_relax();
rcu_idle_exit(); ct_idle_exit();
start_critical_timings(); start_critical_timings();
trace_cpu_idle(PWR_EVENT_EXIT, smp_processor_id()); trace_cpu_idle(PWR_EVENT_EXIT, smp_processor_id());
...@@ -98,12 +98,12 @@ void __cpuidle default_idle_call(void) ...@@ -98,12 +98,12 @@ void __cpuidle default_idle_call(void)
* *
* Trace IRQs enable here, then switch off RCU, and have * Trace IRQs enable here, then switch off RCU, and have
* arch_cpu_idle() use raw_local_irq_enable(). Note that * arch_cpu_idle() use raw_local_irq_enable(). Note that
* rcu_idle_enter() relies on lockdep IRQ state, so switch that * ct_idle_enter() relies on lockdep IRQ state, so switch that
* last -- this is very similar to the entry code. * last -- this is very similar to the entry code.
*/ */
trace_hardirqs_on_prepare(); trace_hardirqs_on_prepare();
lockdep_hardirqs_on_prepare(); lockdep_hardirqs_on_prepare();
rcu_idle_enter(); ct_idle_enter();
lockdep_hardirqs_on(_THIS_IP_); lockdep_hardirqs_on(_THIS_IP_);
arch_cpu_idle(); arch_cpu_idle();
...@@ -116,7 +116,7 @@ void __cpuidle default_idle_call(void) ...@@ -116,7 +116,7 @@ void __cpuidle default_idle_call(void)
*/ */
raw_local_irq_disable(); raw_local_irq_disable();
lockdep_hardirqs_off(_THIS_IP_); lockdep_hardirqs_off(_THIS_IP_);
rcu_idle_exit(); ct_idle_exit();
lockdep_hardirqs_on(_THIS_IP_); lockdep_hardirqs_on(_THIS_IP_);
raw_local_irq_enable(); raw_local_irq_enable();
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/cgroup_api.h> #include <linux/cgroup_api.h>
#include <linux/cgroup.h> #include <linux/cgroup.h>
#include <linux/context_tracking.h>
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/cpumask_api.h> #include <linux/cpumask_api.h>
#include <linux/ctype.h> #include <linux/ctype.h>
......
...@@ -76,6 +76,12 @@ config TIME_KUNIT_TEST ...@@ -76,6 +76,12 @@ config TIME_KUNIT_TEST
config CONTEXT_TRACKING config CONTEXT_TRACKING
bool bool
config CONTEXT_TRACKING_IDLE
bool
select CONTEXT_TRACKING
help
Tracks idle state on behalf of RCU.
if GENERIC_CLOCKEVENTS if GENERIC_CLOCKEVENTS
menu "Timers subsystem" menu "Timers subsystem"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment