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

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: default avatarPaul E. McKenney <paulmck@kernel.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: default avatarFrederic Weisbecker <frederic@kernel.org>
parent 243d5ab3
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Read-Copy Update notifiers, initially RCU CPU stall notifier.
* Separate from rcupdate.h to avoid #include loops.
*
* Copyright (C) 2023 Paul E. McKenney.
*/
#ifndef __LINUX_RCU_NOTIFIER_H
#define __LINUX_RCU_NOTIFIER_H
// Actions for RCU CPU stall notifier calls.
#define RCU_STALL_NOTIFY_NORM 1
#define RCU_STALL_NOTIFY_EXP 2
#ifdef CONFIG_RCU_STALL_COMMON
#include <linux/notifier.h>
#include <linux/types.h>
int rcu_stall_chain_notifier_register(struct notifier_block *n);
int rcu_stall_chain_notifier_unregister(struct notifier_block *n);
#else // #ifdef CONFIG_RCU_STALL_COMMON
// No RCU CPU stall warnings in Tiny RCU.
static inline int rcu_stall_chain_notifier_register(struct notifier_block *n) { return -EEXIST; }
static inline int rcu_stall_chain_notifier_unregister(struct notifier_block *n) { return -ENOENT; }
#endif // #else // #ifdef CONFIG_RCU_STALL_COMMON
#endif /* __LINUX_RCU_NOTIFIER_H */
...@@ -654,4 +654,10 @@ static inline bool rcu_cpu_beenfullyonline(int cpu) { return true; } ...@@ -654,4 +654,10 @@ static inline bool rcu_cpu_beenfullyonline(int cpu) { return true; }
bool rcu_cpu_beenfullyonline(int cpu); bool rcu_cpu_beenfullyonline(int cpu);
#endif #endif
#ifdef CONFIG_RCU_STALL_COMMON
int rcu_stall_notifier_call_chain(unsigned long val, void *v);
#else // #ifdef CONFIG_RCU_STALL_COMMON
static inline int rcu_stall_notifier_call_chain(unsigned long val, void *v) { return NOTIFY_DONE; }
#endif // #else // #ifdef CONFIG_RCU_STALL_COMMON
#endif /* __LINUX_RCU_H */ #endif /* __LINUX_RCU_H */
...@@ -621,10 +621,14 @@ static void synchronize_rcu_expedited_wait(void) ...@@ -621,10 +621,14 @@ static void synchronize_rcu_expedited_wait(void)
} }
for (;;) { for (;;) {
unsigned long j;
if (synchronize_rcu_expedited_wait_once(jiffies_stall)) if (synchronize_rcu_expedited_wait_once(jiffies_stall))
return; return;
if (rcu_stall_is_suppressed()) if (rcu_stall_is_suppressed())
continue; continue;
j = jiffies;
rcu_stall_notifier_call_chain(RCU_STALL_NOTIFY_EXP, (void *)(j - jiffies_start));
trace_rcu_stall_warning(rcu_state.name, TPS("ExpeditedStall")); trace_rcu_stall_warning(rcu_state.name, TPS("ExpeditedStall"));
pr_err("INFO: %s detected expedited stalls on CPUs/tasks: {", pr_err("INFO: %s detected expedited stalls on CPUs/tasks: {",
rcu_state.name); rcu_state.name);
...@@ -647,7 +651,7 @@ static void synchronize_rcu_expedited_wait(void) ...@@ -647,7 +651,7 @@ static void synchronize_rcu_expedited_wait(void)
} }
} }
pr_cont(" } %lu jiffies s: %lu root: %#lx/%c\n", pr_cont(" } %lu jiffies s: %lu root: %#lx/%c\n",
jiffies - jiffies_start, rcu_state.expedited_sequence, j - jiffies_start, rcu_state.expedited_sequence,
data_race(rnp_root->expmask), data_race(rnp_root->expmask),
".T"[!!data_race(rnp_root->exp_tasks)]); ".T"[!!data_race(rnp_root->exp_tasks)]);
if (ndetected) { if (ndetected) {
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
*/ */
#include <linux/kvm_para.h> #include <linux/kvm_para.h>
#include <linux/rcu_notifier.h>
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// //
...@@ -770,6 +771,7 @@ static void check_cpu_stall(struct rcu_data *rdp) ...@@ -770,6 +771,7 @@ static void check_cpu_stall(struct rcu_data *rdp)
if (kvm_check_and_clear_guest_paused()) if (kvm_check_and_clear_guest_paused())
return; return;
rcu_stall_notifier_call_chain(RCU_STALL_NOTIFY_NORM, (void *)j - gps);
if (self_detected) { if (self_detected) {
/* We haven't checked in, so go dump stack. */ /* We haven't checked in, so go dump stack. */
print_cpu_stall(gps); print_cpu_stall(gps);
...@@ -790,7 +792,7 @@ static void check_cpu_stall(struct rcu_data *rdp) ...@@ -790,7 +792,7 @@ static void check_cpu_stall(struct rcu_data *rdp)
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// //
// RCU forward-progress mechanisms, including of callback invocation. // RCU forward-progress mechanisms, including for callback invocation.
/* /*
...@@ -1042,3 +1044,58 @@ static int __init rcu_sysrq_init(void) ...@@ -1042,3 +1044,58 @@ static int __init rcu_sysrq_init(void)
return 0; return 0;
} }
early_initcall(rcu_sysrq_init); early_initcall(rcu_sysrq_init);
//////////////////////////////////////////////////////////////////////////////
//
// RCU CPU stall-warning notifiers
static ATOMIC_NOTIFIER_HEAD(rcu_cpu_stall_notifier_list);
/**
* rcu_stall_chain_notifier_register - Add an RCU CPU stall notifier
* @n: Entry to add.
*
* Adds an RCU CPU stall notifier to an atomic notifier chain.
* The @action passed to a notifier will be @RCU_STALL_NOTIFY_NORM or
* friends. The @data will be the duration of the stalled grace period,
* in jiffies, coerced to a void* pointer.
*
* Returns 0 on success, %-EEXIST on error.
*/
int rcu_stall_chain_notifier_register(struct notifier_block *n)
{
return atomic_notifier_chain_register(&rcu_cpu_stall_notifier_list, n);
}
EXPORT_SYMBOL_GPL(rcu_stall_chain_notifier_register);
/**
* rcu_stall_chain_notifier_unregister - Remove an RCU CPU stall notifier
* @n: Entry to add.
*
* Removes an RCU CPU stall notifier from an atomic notifier chain.
*
* Returns zero on success, %-ENOENT on failure.
*/
int rcu_stall_chain_notifier_unregister(struct notifier_block *n)
{
return atomic_notifier_chain_unregister(&rcu_cpu_stall_notifier_list, n);
}
EXPORT_SYMBOL_GPL(rcu_stall_chain_notifier_unregister);
/*
* rcu_stall_notifier_call_chain - Call functions in an RCU CPU stall notifier chain
* @val: Value passed unmodified to notifier function
* @v: Pointer passed unmodified to notifier function
*
* Calls each function in the RCU CPU stall notifier chain in turn, which
* is an atomic call chain. See atomic_notifier_call_chain() for more
* information.
*
* This is for use within RCU, hence the omission of the extra asterisk
* to indicate a non-kerneldoc format header comment.
*/
int rcu_stall_notifier_call_chain(unsigned long val, void *v)
{
return atomic_notifier_call_chain(&rcu_cpu_stall_notifier_list, val, v);
}
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