2019-01-12 08:10:57 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
|
|
|
/*
|
|
|
|
* RCU CPU stall warnings for normal RCU grace periods
|
|
|
|
*
|
|
|
|
* Copyright IBM Corporation, 2019
|
|
|
|
*
|
|
|
|
* Author: Paul E. McKenney <paulmck@linux.ibm.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_PROVE_RCU
|
|
|
|
#define RCU_STALL_DELAY_DELTA (5 * HZ)
|
|
|
|
#else
|
|
|
|
#define RCU_STALL_DELAY_DELTA 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int rcu_jiffies_till_stall_check(void)
|
|
|
|
{
|
|
|
|
int till_stall_check = READ_ONCE(rcu_cpu_stall_timeout);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Limit check must be consistent with the Kconfig limits
|
|
|
|
* for CONFIG_RCU_CPU_STALL_TIMEOUT.
|
|
|
|
*/
|
|
|
|
if (till_stall_check < 3) {
|
|
|
|
WRITE_ONCE(rcu_cpu_stall_timeout, 3);
|
|
|
|
till_stall_check = 3;
|
|
|
|
} else if (till_stall_check > 300) {
|
|
|
|
WRITE_ONCE(rcu_cpu_stall_timeout, 300);
|
|
|
|
till_stall_check = 300;
|
|
|
|
}
|
|
|
|
return till_stall_check * HZ + RCU_STALL_DELAY_DELTA;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(rcu_jiffies_till_stall_check);
|
|
|
|
|
|
|
|
void rcu_sysrq_start(void)
|
|
|
|
{
|
|
|
|
if (!rcu_cpu_stall_suppress)
|
|
|
|
rcu_cpu_stall_suppress = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
void rcu_sysrq_end(void)
|
|
|
|
{
|
|
|
|
if (rcu_cpu_stall_suppress == 2)
|
|
|
|
rcu_cpu_stall_suppress = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rcu_panic(struct notifier_block *this, unsigned long ev, void *ptr)
|
|
|
|
{
|
|
|
|
rcu_cpu_stall_suppress = 1;
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct notifier_block rcu_panic_block = {
|
|
|
|
.notifier_call = rcu_panic,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init check_cpu_stall_init(void)
|
|
|
|
{
|
|
|
|
atomic_notifier_chain_register(&panic_notifier_list, &rcu_panic_block);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
early_initcall(check_cpu_stall_init);
|
2019-01-12 08:34:47 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_PREEMPT
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Dump detailed information for all tasks blocking the current RCU
|
|
|
|
* grace period on the specified rcu_node structure.
|
|
|
|
*/
|
|
|
|
static void rcu_print_detail_task_stall_rnp(struct rcu_node *rnp)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
struct task_struct *t;
|
|
|
|
|
|
|
|
raw_spin_lock_irqsave_rcu_node(rnp, flags);
|
|
|
|
if (!rcu_preempt_blocked_readers_cgp(rnp)) {
|
|
|
|
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
t = list_entry(rnp->gp_tasks->prev,
|
|
|
|
struct task_struct, rcu_node_entry);
|
|
|
|
list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry) {
|
|
|
|
/*
|
|
|
|
* We could be printing a lot while holding a spinlock.
|
|
|
|
* Avoid triggering hard lockup.
|
|
|
|
*/
|
|
|
|
touch_nmi_watchdog();
|
|
|
|
sched_show_task(t);
|
|
|
|
}
|
|
|
|
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Dump detailed information for all tasks blocking the current RCU
|
|
|
|
* grace period.
|
|
|
|
*/
|
|
|
|
static void rcu_print_detail_task_stall(void)
|
|
|
|
{
|
|
|
|
struct rcu_node *rnp = rcu_get_root();
|
|
|
|
|
|
|
|
rcu_print_detail_task_stall_rnp(rnp);
|
|
|
|
rcu_for_each_leaf_node(rnp)
|
|
|
|
rcu_print_detail_task_stall_rnp(rnp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rcu_print_task_stall_begin(struct rcu_node *rnp)
|
|
|
|
{
|
|
|
|
pr_err("\tTasks blocked on level-%d rcu_node (CPUs %d-%d):",
|
|
|
|
rnp->level, rnp->grplo, rnp->grphi);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rcu_print_task_stall_end(void)
|
|
|
|
{
|
|
|
|
pr_cont("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan the current list of tasks blocked within RCU read-side critical
|
|
|
|
* sections, printing out the tid of each.
|
|
|
|
*/
|
|
|
|
static int rcu_print_task_stall(struct rcu_node *rnp)
|
|
|
|
{
|
|
|
|
struct task_struct *t;
|
|
|
|
int ndetected = 0;
|
|
|
|
|
|
|
|
if (!rcu_preempt_blocked_readers_cgp(rnp))
|
|
|
|
return 0;
|
|
|
|
rcu_print_task_stall_begin(rnp);
|
|
|
|
t = list_entry(rnp->gp_tasks->prev,
|
|
|
|
struct task_struct, rcu_node_entry);
|
|
|
|
list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry) {
|
|
|
|
pr_cont(" P%d", t->pid);
|
|
|
|
ndetected++;
|
|
|
|
}
|
|
|
|
rcu_print_task_stall_end();
|
|
|
|
return ndetected;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* #ifdef CONFIG_PREEMPT */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Because preemptible RCU does not exist, we never have to check for
|
|
|
|
* tasks blocked within RCU read-side critical sections.
|
|
|
|
*/
|
|
|
|
static void rcu_print_detail_task_stall(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Because preemptible RCU does not exist, we never have to check for
|
|
|
|
* tasks blocked within RCU read-side critical sections.
|
|
|
|
*/
|
|
|
|
static int rcu_print_task_stall(struct rcu_node *rnp)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* #else #ifdef CONFIG_PREEMPT */
|