Skip to content

Commit 5b404fd

Browse files
paulmckrcuFrederic Weisbecker
authored andcommitted
rcu: Add RCU CPU stall notifier
It is sometimes helpful to have a way for the subsystem causing the stall to dump its state when an RCU CPU stall occurs. This commit therefore bases rcu_stall_chain_notifier_register() and rcu_stall_chain_notifier_unregister() on atomic notifiers in order to provide this functionality. Signed-off-by: Paul E. McKenney <paulmck@kernel.org> Cc: Steven Rostedt <rostedt@goodmis.org> Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
1 parent 243d5ab commit 5b404fd

File tree

4 files changed

+101
-2
lines changed

4 files changed

+101
-2
lines changed

include/linux/rcu_notifier.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/* SPDX-License-Identifier: GPL-2.0+ */
2+
/*
3+
* Read-Copy Update notifiers, initially RCU CPU stall notifier.
4+
* Separate from rcupdate.h to avoid #include loops.
5+
*
6+
* Copyright (C) 2023 Paul E. McKenney.
7+
*/
8+
9+
#ifndef __LINUX_RCU_NOTIFIER_H
10+
#define __LINUX_RCU_NOTIFIER_H
11+
12+
// Actions for RCU CPU stall notifier calls.
13+
#define RCU_STALL_NOTIFY_NORM 1
14+
#define RCU_STALL_NOTIFY_EXP 2
15+
16+
#ifdef CONFIG_RCU_STALL_COMMON
17+
18+
#include <linux/notifier.h>
19+
#include <linux/types.h>
20+
21+
int rcu_stall_chain_notifier_register(struct notifier_block *n);
22+
int rcu_stall_chain_notifier_unregister(struct notifier_block *n);
23+
24+
#else // #ifdef CONFIG_RCU_STALL_COMMON
25+
26+
// No RCU CPU stall warnings in Tiny RCU.
27+
static inline int rcu_stall_chain_notifier_register(struct notifier_block *n) { return -EEXIST; }
28+
static inline int rcu_stall_chain_notifier_unregister(struct notifier_block *n) { return -ENOENT; }
29+
30+
#endif // #else // #ifdef CONFIG_RCU_STALL_COMMON
31+
32+
#endif /* __LINUX_RCU_NOTIFIER_H */

kernel/rcu/rcu.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,4 +654,10 @@ static inline bool rcu_cpu_beenfullyonline(int cpu) { return true; }
654654
bool rcu_cpu_beenfullyonline(int cpu);
655655
#endif
656656

657+
#ifdef CONFIG_RCU_STALL_COMMON
658+
int rcu_stall_notifier_call_chain(unsigned long val, void *v);
659+
#else // #ifdef CONFIG_RCU_STALL_COMMON
660+
static inline int rcu_stall_notifier_call_chain(unsigned long val, void *v) { return NOTIFY_DONE; }
661+
#endif // #else // #ifdef CONFIG_RCU_STALL_COMMON
662+
657663
#endif /* __LINUX_RCU_H */

kernel/rcu/tree_exp.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,10 +621,14 @@ static void synchronize_rcu_expedited_wait(void)
621621
}
622622

623623
for (;;) {
624+
unsigned long j;
625+
624626
if (synchronize_rcu_expedited_wait_once(jiffies_stall))
625627
return;
626628
if (rcu_stall_is_suppressed())
627629
continue;
630+
j = jiffies;
631+
rcu_stall_notifier_call_chain(RCU_STALL_NOTIFY_EXP, (void *)(j - jiffies_start));
628632
trace_rcu_stall_warning(rcu_state.name, TPS("ExpeditedStall"));
629633
pr_err("INFO: %s detected expedited stalls on CPUs/tasks: {",
630634
rcu_state.name);
@@ -647,7 +651,7 @@ static void synchronize_rcu_expedited_wait(void)
647651
}
648652
}
649653
pr_cont(" } %lu jiffies s: %lu root: %#lx/%c\n",
650-
jiffies - jiffies_start, rcu_state.expedited_sequence,
654+
j - jiffies_start, rcu_state.expedited_sequence,
651655
data_race(rnp_root->expmask),
652656
".T"[!!data_race(rnp_root->exp_tasks)]);
653657
if (ndetected) {

kernel/rcu/tree_stall.h

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
#include <linux/kvm_para.h>
11+
#include <linux/rcu_notifier.h>
1112

1213
//////////////////////////////////////////////////////////////////////////////
1314
//
@@ -770,6 +771,7 @@ static void check_cpu_stall(struct rcu_data *rdp)
770771
if (kvm_check_and_clear_guest_paused())
771772
return;
772773

774+
rcu_stall_notifier_call_chain(RCU_STALL_NOTIFY_NORM, (void *)j - gps);
773775
if (self_detected) {
774776
/* We haven't checked in, so go dump stack. */
775777
print_cpu_stall(gps);
@@ -790,7 +792,7 @@ static void check_cpu_stall(struct rcu_data *rdp)
790792

791793
//////////////////////////////////////////////////////////////////////////////
792794
//
793-
// RCU forward-progress mechanisms, including of callback invocation.
795+
// RCU forward-progress mechanisms, including for callback invocation.
794796

795797

796798
/*
@@ -1042,3 +1044,58 @@ static int __init rcu_sysrq_init(void)
10421044
return 0;
10431045
}
10441046
early_initcall(rcu_sysrq_init);
1047+
1048+
1049+
//////////////////////////////////////////////////////////////////////////////
1050+
//
1051+
// RCU CPU stall-warning notifiers
1052+
1053+
static ATOMIC_NOTIFIER_HEAD(rcu_cpu_stall_notifier_list);
1054+
1055+
/**
1056+
* rcu_stall_chain_notifier_register - Add an RCU CPU stall notifier
1057+
* @n: Entry to add.
1058+
*
1059+
* Adds an RCU CPU stall notifier to an atomic notifier chain.
1060+
* The @action passed to a notifier will be @RCU_STALL_NOTIFY_NORM or
1061+
* friends. The @data will be the duration of the stalled grace period,
1062+
* in jiffies, coerced to a void* pointer.
1063+
*
1064+
* Returns 0 on success, %-EEXIST on error.
1065+
*/
1066+
int rcu_stall_chain_notifier_register(struct notifier_block *n)
1067+
{
1068+
return atomic_notifier_chain_register(&rcu_cpu_stall_notifier_list, n);
1069+
}
1070+
EXPORT_SYMBOL_GPL(rcu_stall_chain_notifier_register);
1071+
1072+
/**
1073+
* rcu_stall_chain_notifier_unregister - Remove an RCU CPU stall notifier
1074+
* @n: Entry to add.
1075+
*
1076+
* Removes an RCU CPU stall notifier from an atomic notifier chain.
1077+
*
1078+
* Returns zero on success, %-ENOENT on failure.
1079+
*/
1080+
int rcu_stall_chain_notifier_unregister(struct notifier_block *n)
1081+
{
1082+
return atomic_notifier_chain_unregister(&rcu_cpu_stall_notifier_list, n);
1083+
}
1084+
EXPORT_SYMBOL_GPL(rcu_stall_chain_notifier_unregister);
1085+
1086+
/*
1087+
* rcu_stall_notifier_call_chain - Call functions in an RCU CPU stall notifier chain
1088+
* @val: Value passed unmodified to notifier function
1089+
* @v: Pointer passed unmodified to notifier function
1090+
*
1091+
* Calls each function in the RCU CPU stall notifier chain in turn, which
1092+
* is an atomic call chain. See atomic_notifier_call_chain() for more
1093+
* information.
1094+
*
1095+
* This is for use within RCU, hence the omission of the extra asterisk
1096+
* to indicate a non-kerneldoc format header comment.
1097+
*/
1098+
int rcu_stall_notifier_call_chain(unsigned long val, void *v)
1099+
{
1100+
return atomic_notifier_call_chain(&rcu_cpu_stall_notifier_list, val, v);
1101+
}

0 commit comments

Comments
 (0)