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>
This commit is contained in:
parent
243d5ab344
commit
5b404fdaba
4 changed files with 101 additions and 2 deletions
32
include/linux/rcu_notifier.h
Normal file
32
include/linux/rcu_notifier.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/* 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; }
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#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)
|
||||||
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)
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// 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)
|
||||||
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);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue