diff options
author | Paul E. McKenney <paulmck@linux.ibm.com> | 2019-06-25 16:32:51 -0400 |
---|---|---|
committer | Paul E. McKenney <paulmck@linux.ibm.com> | 2019-08-13 17:38:24 -0400 |
commit | f7a81b12d6af42a9d09be1e5f041169f04b0b67a (patch) | |
tree | 6bac3cfa8d0b88bd7b8091a88c20fc09694774ed | |
parent | 6aacd88d1721e12b013ae4ccf4f17609bd5091f3 (diff) |
rcu/nocb: Print no-CBs diagnostics when rcutorture writer unduly delayed
This commit causes locking, sleeping, and callback state to be printed
for no-CBs CPUs when the rcutorture writer is delayed sufficiently for
rcutorture to complain.
Signed-off-by: Paul E. McKenney <paulmck@linux.ibm.com>
-rw-r--r-- | kernel/rcu/rcutorture.c | 1 | ||||
-rw-r--r-- | kernel/rcu/tree.h | 7 | ||||
-rw-r--r-- | kernel/rcu/tree_plugin.h | 82 | ||||
-rw-r--r-- | kernel/rcu/tree_stall.h | 5 |
4 files changed, 94 insertions, 1 deletions
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index b22947324423..3c9feca1eab1 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c | |||
@@ -2176,6 +2176,7 @@ rcu_torture_cleanup(void) | |||
2176 | return; | 2176 | return; |
2177 | } | 2177 | } |
2178 | 2178 | ||
2179 | show_rcu_gp_kthreads(); | ||
2179 | rcu_torture_barrier_cleanup(); | 2180 | rcu_torture_barrier_cleanup(); |
2180 | torture_stop_kthread(rcu_torture_fwd_prog, fwd_prog_task); | 2181 | torture_stop_kthread(rcu_torture_fwd_prog, fwd_prog_task); |
2181 | torture_stop_kthread(rcu_torture_stall, stall_task); | 2182 | torture_stop_kthread(rcu_torture_stall, stall_task); |
diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index e4df86db8137..c612f306fe89 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h | |||
@@ -212,7 +212,11 @@ struct rcu_data { | |||
212 | /* The following fields are used by GP kthread, hence own cacheline. */ | 212 | /* The following fields are used by GP kthread, hence own cacheline. */ |
213 | raw_spinlock_t nocb_gp_lock ____cacheline_internodealigned_in_smp; | 213 | raw_spinlock_t nocb_gp_lock ____cacheline_internodealigned_in_smp; |
214 | struct timer_list nocb_bypass_timer; /* Force nocb_bypass flush. */ | 214 | struct timer_list nocb_bypass_timer; /* Force nocb_bypass flush. */ |
215 | bool nocb_gp_sleep; /* Is the nocb GP thread asleep? */ | 215 | u8 nocb_gp_sleep; /* Is the nocb GP thread asleep? */ |
216 | u8 nocb_gp_bypass; /* Found a bypass on last scan? */ | ||
217 | u8 nocb_gp_gp; /* GP to wait for on last scan? */ | ||
218 | unsigned long nocb_gp_seq; /* If so, ->gp_seq to wait for. */ | ||
219 | unsigned long nocb_gp_loops; /* # passes through wait code. */ | ||
216 | struct swait_queue_head nocb_gp_wq; /* For nocb kthreads to sleep on. */ | 220 | struct swait_queue_head nocb_gp_wq; /* For nocb kthreads to sleep on. */ |
217 | bool nocb_cb_sleep; /* Is the nocb CB thread asleep? */ | 221 | bool nocb_cb_sleep; /* Is the nocb CB thread asleep? */ |
218 | struct task_struct *nocb_cb_kthread; | 222 | struct task_struct *nocb_cb_kthread; |
@@ -438,6 +442,7 @@ static void do_nocb_deferred_wakeup(struct rcu_data *rdp); | |||
438 | static void rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp); | 442 | static void rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp); |
439 | static void rcu_spawn_cpu_nocb_kthread(int cpu); | 443 | static void rcu_spawn_cpu_nocb_kthread(int cpu); |
440 | static void __init rcu_spawn_nocb_kthreads(void); | 444 | static void __init rcu_spawn_nocb_kthreads(void); |
445 | static void show_rcu_nocb_state(struct rcu_data *rdp); | ||
441 | static void rcu_nocb_lock(struct rcu_data *rdp); | 446 | static void rcu_nocb_lock(struct rcu_data *rdp); |
442 | static void rcu_nocb_unlock(struct rcu_data *rdp); | 447 | static void rcu_nocb_unlock(struct rcu_data *rdp); |
443 | static void rcu_nocb_unlock_irqrestore(struct rcu_data *rdp, | 448 | static void rcu_nocb_unlock_irqrestore(struct rcu_data *rdp, |
diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 97c730753a6d..25a53742ca68 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h | |||
@@ -2021,6 +2021,9 @@ static void nocb_gp_wait(struct rcu_data *my_rdp) | |||
2021 | rcu_gp_kthread_wake(); | 2021 | rcu_gp_kthread_wake(); |
2022 | } | 2022 | } |
2023 | 2023 | ||
2024 | my_rdp->nocb_gp_bypass = bypass; | ||
2025 | my_rdp->nocb_gp_gp = needwait_gp; | ||
2026 | my_rdp->nocb_gp_seq = needwait_gp ? wait_gp_seq : 0; | ||
2024 | if (bypass && !rcu_nocb_poll) { | 2027 | if (bypass && !rcu_nocb_poll) { |
2025 | // At least one child with non-empty ->nocb_bypass, so set | 2028 | // At least one child with non-empty ->nocb_bypass, so set |
2026 | // timer in order to avoid stranding its callbacks. | 2029 | // timer in order to avoid stranding its callbacks. |
@@ -2055,6 +2058,7 @@ static void nocb_gp_wait(struct rcu_data *my_rdp) | |||
2055 | WRITE_ONCE(my_rdp->nocb_gp_sleep, true); | 2058 | WRITE_ONCE(my_rdp->nocb_gp_sleep, true); |
2056 | raw_spin_unlock_irqrestore(&my_rdp->nocb_gp_lock, flags); | 2059 | raw_spin_unlock_irqrestore(&my_rdp->nocb_gp_lock, flags); |
2057 | } | 2060 | } |
2061 | my_rdp->nocb_gp_seq = -1; | ||
2058 | WARN_ON(signal_pending(current)); | 2062 | WARN_ON(signal_pending(current)); |
2059 | } | 2063 | } |
2060 | 2064 | ||
@@ -2071,6 +2075,7 @@ static int rcu_nocb_gp_kthread(void *arg) | |||
2071 | struct rcu_data *rdp = arg; | 2075 | struct rcu_data *rdp = arg; |
2072 | 2076 | ||
2073 | for (;;) { | 2077 | for (;;) { |
2078 | WRITE_ONCE(rdp->nocb_gp_loops, rdp->nocb_gp_loops + 1); | ||
2074 | nocb_gp_wait(rdp); | 2079 | nocb_gp_wait(rdp); |
2075 | cond_resched_tasks_rcu_qs(); | 2080 | cond_resched_tasks_rcu_qs(); |
2076 | } | 2081 | } |
@@ -2362,6 +2367,79 @@ void rcu_bind_current_to_nocb(void) | |||
2362 | } | 2367 | } |
2363 | EXPORT_SYMBOL_GPL(rcu_bind_current_to_nocb); | 2368 | EXPORT_SYMBOL_GPL(rcu_bind_current_to_nocb); |
2364 | 2369 | ||
2370 | /* | ||
2371 | * Dump out nocb grace-period kthread state for the specified rcu_data | ||
2372 | * structure. | ||
2373 | */ | ||
2374 | static void show_rcu_nocb_gp_state(struct rcu_data *rdp) | ||
2375 | { | ||
2376 | struct rcu_node *rnp = rdp->mynode; | ||
2377 | |||
2378 | pr_info("nocb GP %d %c%c%c%c%c%c %c[%c%c] %c%c:%ld rnp %d:%d %lu\n", | ||
2379 | rdp->cpu, | ||
2380 | "kK"[!!rdp->nocb_gp_kthread], | ||
2381 | "lL"[raw_spin_is_locked(&rdp->nocb_gp_lock)], | ||
2382 | "dD"[!!rdp->nocb_defer_wakeup], | ||
2383 | "tT"[timer_pending(&rdp->nocb_timer)], | ||
2384 | "bB"[timer_pending(&rdp->nocb_bypass_timer)], | ||
2385 | "sS"[!!rdp->nocb_gp_sleep], | ||
2386 | ".W"[swait_active(&rdp->nocb_gp_wq)], | ||
2387 | ".W"[swait_active(&rnp->nocb_gp_wq[0])], | ||
2388 | ".W"[swait_active(&rnp->nocb_gp_wq[1])], | ||
2389 | ".B"[!!rdp->nocb_gp_bypass], | ||
2390 | ".G"[!!rdp->nocb_gp_gp], | ||
2391 | (long)rdp->nocb_gp_seq, | ||
2392 | rnp->grplo, rnp->grphi, READ_ONCE(rdp->nocb_gp_loops)); | ||
2393 | } | ||
2394 | |||
2395 | /* Dump out nocb kthread state for the specified rcu_data structure. */ | ||
2396 | static void show_rcu_nocb_state(struct rcu_data *rdp) | ||
2397 | { | ||
2398 | struct rcu_segcblist *rsclp = &rdp->cblist; | ||
2399 | bool waslocked; | ||
2400 | bool wastimer; | ||
2401 | bool wassleep; | ||
2402 | |||
2403 | if (rdp->nocb_gp_rdp == rdp) | ||
2404 | show_rcu_nocb_gp_state(rdp); | ||
2405 | |||
2406 | pr_info(" CB %d->%d %c%c%c%c%c%c F%ld L%ld C%d %c%c%c%c%c q%ld\n", | ||
2407 | rdp->cpu, rdp->nocb_gp_rdp->cpu, | ||
2408 | "kK"[!!rdp->nocb_cb_kthread], | ||
2409 | "bB"[raw_spin_is_locked(&rdp->nocb_bypass_lock)], | ||
2410 | "cC"[!!atomic_read(&rdp->nocb_lock_contended)], | ||
2411 | "lL"[raw_spin_is_locked(&rdp->nocb_lock)], | ||
2412 | "sS"[!!rdp->nocb_cb_sleep], | ||
2413 | ".W"[swait_active(&rdp->nocb_cb_wq)], | ||
2414 | jiffies - rdp->nocb_bypass_first, | ||
2415 | jiffies - rdp->nocb_nobypass_last, | ||
2416 | rdp->nocb_nobypass_count, | ||
2417 | ".D"[rcu_segcblist_ready_cbs(rsclp)], | ||
2418 | ".W"[!rcu_segcblist_restempty(rsclp, RCU_DONE_TAIL)], | ||
2419 | ".R"[!rcu_segcblist_restempty(rsclp, RCU_WAIT_TAIL)], | ||
2420 | ".N"[!rcu_segcblist_restempty(rsclp, RCU_NEXT_READY_TAIL)], | ||
2421 | ".B"[!!rcu_cblist_n_cbs(&rdp->nocb_bypass)], | ||
2422 | rcu_segcblist_n_cbs(&rdp->cblist)); | ||
2423 | |||
2424 | /* It is OK for GP kthreads to have GP state. */ | ||
2425 | if (rdp->nocb_gp_rdp == rdp) | ||
2426 | return; | ||
2427 | |||
2428 | waslocked = raw_spin_is_locked(&rdp->nocb_gp_lock); | ||
2429 | wastimer = timer_pending(&rdp->nocb_timer); | ||
2430 | wassleep = swait_active(&rdp->nocb_gp_wq); | ||
2431 | if (!rdp->nocb_defer_wakeup && !rdp->nocb_gp_sleep && | ||
2432 | !waslocked && !wastimer && !wassleep) | ||
2433 | return; /* Nothing untowards. */ | ||
2434 | |||
2435 | pr_info(" !!! %c%c%c%c %c\n", | ||
2436 | "lL"[waslocked], | ||
2437 | "dD"[!!rdp->nocb_defer_wakeup], | ||
2438 | "tT"[wastimer], | ||
2439 | "sS"[!!rdp->nocb_gp_sleep], | ||
2440 | ".W"[wassleep]); | ||
2441 | } | ||
2442 | |||
2365 | #else /* #ifdef CONFIG_RCU_NOCB_CPU */ | 2443 | #else /* #ifdef CONFIG_RCU_NOCB_CPU */ |
2366 | 2444 | ||
2367 | /* No ->nocb_lock to acquire. */ | 2445 | /* No ->nocb_lock to acquire. */ |
@@ -2439,6 +2517,10 @@ static void __init rcu_spawn_nocb_kthreads(void) | |||
2439 | { | 2517 | { |
2440 | } | 2518 | } |
2441 | 2519 | ||
2520 | static void show_rcu_nocb_state(struct rcu_data *rdp) | ||
2521 | { | ||
2522 | } | ||
2523 | |||
2442 | #endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */ | 2524 | #endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */ |
2443 | 2525 | ||
2444 | /* | 2526 | /* |
diff --git a/kernel/rcu/tree_stall.h b/kernel/rcu/tree_stall.h index 0627a66699a6..841ab43f3e60 100644 --- a/kernel/rcu/tree_stall.h +++ b/kernel/rcu/tree_stall.h | |||
@@ -589,6 +589,11 @@ void show_rcu_gp_kthreads(void) | |||
589 | cpu, (long)rdp->gp_seq_needed); | 589 | cpu, (long)rdp->gp_seq_needed); |
590 | } | 590 | } |
591 | } | 591 | } |
592 | for_each_possible_cpu(cpu) { | ||
593 | rdp = per_cpu_ptr(&rcu_data, cpu); | ||
594 | if (rcu_segcblist_is_offloaded(&rdp->cblist)) | ||
595 | show_rcu_nocb_state(rdp); | ||
596 | } | ||
592 | /* sched_show_task(rcu_state.gp_kthread); */ | 597 | /* sched_show_task(rcu_state.gp_kthread); */ |
593 | } | 598 | } |
594 | EXPORT_SYMBOL_GPL(show_rcu_gp_kthreads); | 599 | EXPORT_SYMBOL_GPL(show_rcu_gp_kthreads); |