Skip to content

Commit cb3cb67

Browse files
author
Ingo Molnar
committed
Merge branch 'WIP.core/rcu' into core/rcu, to pick up two x86/entry dependencies
Signed-off-by: Ingo Molnar <[email protected]>
2 parents 806f04e + 07325d4 commit cb3cb67

File tree

4 files changed

+104
-32
lines changed

4 files changed

+104
-32
lines changed

include/linux/hardirq.h

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,28 @@
22
#ifndef LINUX_HARDIRQ_H
33
#define LINUX_HARDIRQ_H
44

5+
#include <linux/context_tracking_state.h>
56
#include <linux/preempt.h>
67
#include <linux/lockdep.h>
78
#include <linux/ftrace_irq.h>
89
#include <linux/vtime.h>
910
#include <asm/hardirq.h>
1011

11-
1212
extern void synchronize_irq(unsigned int irq);
1313
extern bool synchronize_hardirq(unsigned int irq);
1414

15-
#if defined(CONFIG_TINY_RCU)
16-
17-
static inline void rcu_nmi_enter(void)
18-
{
19-
}
15+
#ifdef CONFIG_NO_HZ_FULL
16+
void __rcu_irq_enter_check_tick(void);
17+
#else
18+
static inline void __rcu_irq_enter_check_tick(void) { }
19+
#endif
2020

21-
static inline void rcu_nmi_exit(void)
21+
static __always_inline void rcu_irq_enter_check_tick(void)
2222
{
23+
if (context_tracking_enabled())
24+
__rcu_irq_enter_check_tick();
2325
}
2426

25-
#else
26-
extern void rcu_nmi_enter(void);
27-
extern void rcu_nmi_exit(void);
28-
#endif
29-
3027
/*
3128
* It is safe to do non-atomic ops on ->hardirq_context,
3229
* because NMI handlers may not preempt and the ops are
@@ -65,6 +62,14 @@ extern void irq_exit(void);
6562
#define arch_nmi_exit() do { } while (0)
6663
#endif
6764

65+
#ifdef CONFIG_TINY_RCU
66+
static inline void rcu_nmi_enter(void) { }
67+
static inline void rcu_nmi_exit(void) { }
68+
#else
69+
extern void rcu_nmi_enter(void);
70+
extern void rcu_nmi_exit(void);
71+
#endif
72+
6873
/*
6974
* NMI vs Tracing
7075
* --------------

include/linux/rcutiny.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ static inline void rcu_irq_exit_irqson(void) { }
7272
static inline void rcu_irq_enter_irqson(void) { }
7373
static inline void rcu_irq_exit(void) { }
7474
static inline void rcu_irq_exit_preempt(void) { }
75+
static inline void rcu_irq_exit_check_preempt(void) { }
7576
static inline void exit_rcu(void) { }
7677
static inline bool rcu_preempt_need_deferred_qs(struct task_struct *t)
7778
{

include/linux/rcutree.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ void rcu_irq_exit_preempt(void);
5151
void rcu_irq_enter_irqson(void);
5252
void rcu_irq_exit_irqson(void);
5353

54+
#ifdef CONFIG_PROVE_RCU
55+
void rcu_irq_exit_check_preempt(void);
56+
#else
57+
static inline void rcu_irq_exit_check_preempt(void) { }
58+
#endif
59+
5460
void exit_rcu(void);
5561

5662
void rcu_scheduler_starting(void);

kernel/rcu/tree.c

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,24 @@ void rcu_irq_exit_preempt(void)
778778
"RCU in extended quiescent state!");
779779
}
780780

781+
#ifdef CONFIG_PROVE_RCU
782+
/**
783+
* rcu_irq_exit_check_preempt - Validate that scheduling is possible
784+
*/
785+
void rcu_irq_exit_check_preempt(void)
786+
{
787+
lockdep_assert_irqs_disabled();
788+
789+
RCU_LOCKDEP_WARN(__this_cpu_read(rcu_data.dynticks_nesting) <= 0,
790+
"RCU dynticks_nesting counter underflow/zero!");
791+
RCU_LOCKDEP_WARN(__this_cpu_read(rcu_data.dynticks_nmi_nesting) !=
792+
DYNTICK_IRQ_NONIDLE,
793+
"Bad RCU dynticks_nmi_nesting counter\n");
794+
RCU_LOCKDEP_WARN(rcu_dynticks_curr_cpu_in_eqs(),
795+
"RCU in extended quiescent state!");
796+
}
797+
#endif /* #ifdef CONFIG_PROVE_RCU */
798+
781799
/*
782800
* Wrapper for rcu_irq_exit() where interrupts are enabled.
783801
*
@@ -861,6 +879,67 @@ void noinstr rcu_user_exit(void)
861879
{
862880
rcu_eqs_exit(1);
863881
}
882+
883+
/**
884+
* __rcu_irq_enter_check_tick - Enable scheduler tick on CPU if RCU needs it.
885+
*
886+
* The scheduler tick is not normally enabled when CPUs enter the kernel
887+
* from nohz_full userspace execution. After all, nohz_full userspace
888+
* execution is an RCU quiescent state and the time executing in the kernel
889+
* is quite short. Except of course when it isn't. And it is not hard to
890+
* cause a large system to spend tens of seconds or even minutes looping
891+
* in the kernel, which can cause a number of problems, include RCU CPU
892+
* stall warnings.
893+
*
894+
* Therefore, if a nohz_full CPU fails to report a quiescent state
895+
* in a timely manner, the RCU grace-period kthread sets that CPU's
896+
* ->rcu_urgent_qs flag with the expectation that the next interrupt or
897+
* exception will invoke this function, which will turn on the scheduler
898+
* tick, which will enable RCU to detect that CPU's quiescent states,
899+
* for example, due to cond_resched() calls in CONFIG_PREEMPT=n kernels.
900+
* The tick will be disabled once a quiescent state is reported for
901+
* this CPU.
902+
*
903+
* Of course, in carefully tuned systems, there might never be an
904+
* interrupt or exception. In that case, the RCU grace-period kthread
905+
* will eventually cause one to happen. However, in less carefully
906+
* controlled environments, this function allows RCU to get what it
907+
* needs without creating otherwise useless interruptions.
908+
*/
909+
void __rcu_irq_enter_check_tick(void)
910+
{
911+
struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
912+
913+
// Enabling the tick is unsafe in NMI handlers.
914+
if (WARN_ON_ONCE(in_nmi()))
915+
return;
916+
917+
RCU_LOCKDEP_WARN(rcu_dynticks_curr_cpu_in_eqs(),
918+
"Illegal rcu_irq_enter_check_tick() from extended quiescent state");
919+
920+
if (!tick_nohz_full_cpu(rdp->cpu) ||
921+
!READ_ONCE(rdp->rcu_urgent_qs) ||
922+
READ_ONCE(rdp->rcu_forced_tick)) {
923+
// RCU doesn't need nohz_full help from this CPU, or it is
924+
// already getting that help.
925+
return;
926+
}
927+
928+
// We get here only when not in an extended quiescent state and
929+
// from interrupts (as opposed to NMIs). Therefore, (1) RCU is
930+
// already watching and (2) The fact that we are in an interrupt
931+
// handler and that the rcu_node lock is an irq-disabled lock
932+
// prevents self-deadlock. So we can safely recheck under the lock.
933+
// Note that the nohz_full state currently cannot change.
934+
raw_spin_lock_rcu_node(rdp->mynode);
935+
if (rdp->rcu_urgent_qs && !rdp->rcu_forced_tick) {
936+
// A nohz_full CPU is in the kernel and RCU needs a
937+
// quiescent state. Turn on the tick!
938+
WRITE_ONCE(rdp->rcu_forced_tick, true);
939+
tick_dep_set_cpu(rdp->cpu, TICK_DEP_BIT_RCU);
940+
}
941+
raw_spin_unlock_rcu_node(rdp->mynode);
942+
}
864943
#endif /* CONFIG_NO_HZ_FULL */
865944

866945
/**
@@ -907,26 +986,7 @@ noinstr void rcu_nmi_enter(void)
907986
incby = 1;
908987
} else if (!in_nmi()) {
909988
instrumentation_begin();
910-
if (tick_nohz_full_cpu(rdp->cpu) &&
911-
rdp->dynticks_nmi_nesting == DYNTICK_IRQ_NONIDLE &&
912-
READ_ONCE(rdp->rcu_urgent_qs) &&
913-
!READ_ONCE(rdp->rcu_forced_tick)) {
914-
// We get here only if we had already exited the
915-
// extended quiescent state and this was an
916-
// interrupt (not an NMI). Therefore, (1) RCU is
917-
// already watching and (2) The fact that we are in
918-
// an interrupt handler and that the rcu_node lock
919-
// is an irq-disabled lock prevents self-deadlock.
920-
// So we can safely recheck under the lock.
921-
raw_spin_lock_rcu_node(rdp->mynode);
922-
if (rdp->rcu_urgent_qs && !rdp->rcu_forced_tick) {
923-
// A nohz_full CPU is in the kernel and RCU
924-
// needs a quiescent state. Turn on the tick!
925-
WRITE_ONCE(rdp->rcu_forced_tick, true);
926-
tick_dep_set_cpu(rdp->cpu, TICK_DEP_BIT_RCU);
927-
}
928-
raw_spin_unlock_rcu_node(rdp->mynode);
929-
}
989+
rcu_irq_enter_check_tick();
930990
instrumentation_end();
931991
}
932992
instrumentation_begin();

0 commit comments

Comments
 (0)