diff options
author | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2014-02-19 13:51:42 -0500 |
---|---|---|
committer | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2014-05-13 14:18:18 -0400 |
commit | ad0dc7f94dbf417b1c7d42e1f0b250f045b27f8f (patch) | |
tree | 7ab989cc1da5014778a7bd24ec94184104e5b517 /kernel/rcu | |
parent | c9eaa447e77efe77b7fa4c953bd62de8297fd6c5 (diff) |
rcutorture: Add forward-progress checking for writer
The rcutorture output currently does not distinguish between stalls in
the RCU implementation and stalls in the rcu_torture_writer() kthreads.
This commit therefore adds some diagnostics to help distinguish between
these two conditions, at least for the non-SRCU implementations. (SRCU
does not provide evidence of update-side forward progress by design.)
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Diffstat (limited to 'kernel/rcu')
-rw-r--r-- | kernel/rcu/rcutorture.c | 37 | ||||
-rw-r--r-- | kernel/rcu/tree.c | 33 |
2 files changed, 70 insertions, 0 deletions
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index bd30bc61bc05..0d739e3797e3 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c | |||
@@ -138,6 +138,15 @@ static long n_barrier_attempts; | |||
138 | static long n_barrier_successes; | 138 | static long n_barrier_successes; |
139 | static struct list_head rcu_torture_removed; | 139 | static struct list_head rcu_torture_removed; |
140 | 140 | ||
141 | static int rcu_torture_writer_state; | ||
142 | #define RTWS_FIXED_DELAY 0 | ||
143 | #define RTWS_DELAY 1 | ||
144 | #define RTWS_REPLACE 2 | ||
145 | #define RTWS_DEF_FREE 3 | ||
146 | #define RTWS_EXP_SYNC 4 | ||
147 | #define RTWS_STUTTER 5 | ||
148 | #define RTWS_STOPPING 6 | ||
149 | |||
141 | #if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE) | 150 | #if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE) |
142 | #define RCUTORTURE_RUNNABLE_INIT 1 | 151 | #define RCUTORTURE_RUNNABLE_INIT 1 |
143 | #else | 152 | #else |
@@ -214,6 +223,7 @@ rcu_torture_free(struct rcu_torture *p) | |||
214 | */ | 223 | */ |
215 | 224 | ||
216 | struct rcu_torture_ops { | 225 | struct rcu_torture_ops { |
226 | int ttype; | ||
217 | void (*init)(void); | 227 | void (*init)(void); |
218 | int (*readlock)(void); | 228 | int (*readlock)(void); |
219 | void (*read_delay)(struct torture_random_state *rrsp); | 229 | void (*read_delay)(struct torture_random_state *rrsp); |
@@ -312,6 +322,7 @@ static void rcu_sync_torture_init(void) | |||
312 | } | 322 | } |
313 | 323 | ||
314 | static struct rcu_torture_ops rcu_ops = { | 324 | static struct rcu_torture_ops rcu_ops = { |
325 | .ttype = RCU_FLAVOR, | ||
315 | .init = rcu_sync_torture_init, | 326 | .init = rcu_sync_torture_init, |
316 | .readlock = rcu_torture_read_lock, | 327 | .readlock = rcu_torture_read_lock, |
317 | .read_delay = rcu_read_delay, | 328 | .read_delay = rcu_read_delay, |
@@ -355,6 +366,7 @@ static void rcu_bh_torture_deferred_free(struct rcu_torture *p) | |||
355 | } | 366 | } |
356 | 367 | ||
357 | static struct rcu_torture_ops rcu_bh_ops = { | 368 | static struct rcu_torture_ops rcu_bh_ops = { |
369 | .ttype = RCU_BH_FLAVOR, | ||
358 | .init = rcu_sync_torture_init, | 370 | .init = rcu_sync_torture_init, |
359 | .readlock = rcu_bh_torture_read_lock, | 371 | .readlock = rcu_bh_torture_read_lock, |
360 | .read_delay = rcu_read_delay, /* just reuse rcu's version. */ | 372 | .read_delay = rcu_read_delay, /* just reuse rcu's version. */ |
@@ -397,6 +409,7 @@ call_rcu_busted(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) | |||
397 | } | 409 | } |
398 | 410 | ||
399 | static struct rcu_torture_ops rcu_busted_ops = { | 411 | static struct rcu_torture_ops rcu_busted_ops = { |
412 | .ttype = INVALID_RCU_FLAVOR, | ||
400 | .init = rcu_sync_torture_init, | 413 | .init = rcu_sync_torture_init, |
401 | .readlock = rcu_torture_read_lock, | 414 | .readlock = rcu_torture_read_lock, |
402 | .read_delay = rcu_read_delay, /* just reuse rcu's version. */ | 415 | .read_delay = rcu_read_delay, /* just reuse rcu's version. */ |
@@ -492,6 +505,7 @@ static void srcu_torture_synchronize_expedited(void) | |||
492 | } | 505 | } |
493 | 506 | ||
494 | static struct rcu_torture_ops srcu_ops = { | 507 | static struct rcu_torture_ops srcu_ops = { |
508 | .ttype = SRCU_FLAVOR, | ||
495 | .init = rcu_sync_torture_init, | 509 | .init = rcu_sync_torture_init, |
496 | .readlock = srcu_torture_read_lock, | 510 | .readlock = srcu_torture_read_lock, |
497 | .read_delay = srcu_read_delay, | 511 | .read_delay = srcu_read_delay, |
@@ -527,6 +541,7 @@ static void rcu_sched_torture_deferred_free(struct rcu_torture *p) | |||
527 | } | 541 | } |
528 | 542 | ||
529 | static struct rcu_torture_ops sched_ops = { | 543 | static struct rcu_torture_ops sched_ops = { |
544 | .ttype = RCU_SCHED_FLAVOR, | ||
530 | .init = rcu_sync_torture_init, | 545 | .init = rcu_sync_torture_init, |
531 | .readlock = sched_torture_read_lock, | 546 | .readlock = sched_torture_read_lock, |
532 | .read_delay = rcu_read_delay, /* just reuse rcu's version. */ | 547 | .read_delay = rcu_read_delay, /* just reuse rcu's version. */ |
@@ -699,12 +714,15 @@ rcu_torture_writer(void *arg) | |||
699 | set_user_nice(current, MAX_NICE); | 714 | set_user_nice(current, MAX_NICE); |
700 | 715 | ||
701 | do { | 716 | do { |
717 | rcu_torture_writer_state = RTWS_FIXED_DELAY; | ||
702 | schedule_timeout_uninterruptible(1); | 718 | schedule_timeout_uninterruptible(1); |
703 | rp = rcu_torture_alloc(); | 719 | rp = rcu_torture_alloc(); |
704 | if (rp == NULL) | 720 | if (rp == NULL) |
705 | continue; | 721 | continue; |
706 | rp->rtort_pipe_count = 0; | 722 | rp->rtort_pipe_count = 0; |
723 | rcu_torture_writer_state = RTWS_DELAY; | ||
707 | udelay(torture_random(&rand) & 0x3ff); | 724 | udelay(torture_random(&rand) & 0x3ff); |
725 | rcu_torture_writer_state = RTWS_REPLACE; | ||
708 | old_rp = rcu_dereference_check(rcu_torture_current, | 726 | old_rp = rcu_dereference_check(rcu_torture_current, |
709 | current == writer_task); | 727 | current == writer_task); |
710 | rp->rtort_mbtest = 1; | 728 | rp->rtort_mbtest = 1; |
@@ -721,8 +739,10 @@ rcu_torture_writer(void *arg) | |||
721 | else | 739 | else |
722 | exp = gp_exp; | 740 | exp = gp_exp; |
723 | if (!exp) { | 741 | if (!exp) { |
742 | rcu_torture_writer_state = RTWS_DEF_FREE; | ||
724 | cur_ops->deferred_free(old_rp); | 743 | cur_ops->deferred_free(old_rp); |
725 | } else { | 744 | } else { |
745 | rcu_torture_writer_state = RTWS_EXP_SYNC; | ||
726 | cur_ops->exp_sync(); | 746 | cur_ops->exp_sync(); |
727 | list_add(&old_rp->rtort_free, | 747 | list_add(&old_rp->rtort_free, |
728 | &rcu_torture_removed); | 748 | &rcu_torture_removed); |
@@ -743,8 +763,10 @@ rcu_torture_writer(void *arg) | |||
743 | } | 763 | } |
744 | } | 764 | } |
745 | rcutorture_record_progress(++rcu_torture_current_version); | 765 | rcutorture_record_progress(++rcu_torture_current_version); |
766 | rcu_torture_writer_state = RTWS_STUTTER; | ||
746 | stutter_wait("rcu_torture_writer"); | 767 | stutter_wait("rcu_torture_writer"); |
747 | } while (!torture_must_stop()); | 768 | } while (!torture_must_stop()); |
769 | rcu_torture_writer_state = RTWS_STOPPING; | ||
748 | torture_kthread_stopping("rcu_torture_writer"); | 770 | torture_kthread_stopping("rcu_torture_writer"); |
749 | return 0; | 771 | return 0; |
750 | } | 772 | } |
@@ -937,6 +959,7 @@ rcu_torture_printk(char *page) | |||
937 | int i; | 959 | int i; |
938 | long pipesummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 }; | 960 | long pipesummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 }; |
939 | long batchsummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 }; | 961 | long batchsummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 }; |
962 | static unsigned long rtcv_snap = ULONG_MAX; | ||
940 | 963 | ||
941 | for_each_possible_cpu(cpu) { | 964 | for_each_possible_cpu(cpu) { |
942 | for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) { | 965 | for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) { |
@@ -997,6 +1020,20 @@ rcu_torture_printk(char *page) | |||
997 | page += sprintf(page, "\n"); | 1020 | page += sprintf(page, "\n"); |
998 | if (cur_ops->stats) | 1021 | if (cur_ops->stats) |
999 | cur_ops->stats(page); | 1022 | cur_ops->stats(page); |
1023 | if (rtcv_snap == rcu_torture_current_version && | ||
1024 | rcu_torture_current != NULL) { | ||
1025 | int __maybe_unused flags; | ||
1026 | unsigned long __maybe_unused gpnum; | ||
1027 | unsigned long __maybe_unused completed; | ||
1028 | |||
1029 | rcutorture_get_gp_data(cur_ops->ttype, | ||
1030 | &flags, &gpnum, &completed); | ||
1031 | page += sprintf(page, | ||
1032 | "??? Writer stall state %d g%lu c%lu f%#x\n", | ||
1033 | rcu_torture_writer_state, | ||
1034 | gpnum, completed, flags); | ||
1035 | } | ||
1036 | rtcv_snap = rcu_torture_current_version; | ||
1000 | } | 1037 | } |
1001 | 1038 | ||
1002 | /* | 1039 | /* |
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 0c47e300210a..3d15b5a82ae8 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c | |||
@@ -294,6 +294,39 @@ void rcutorture_record_test_transition(void) | |||
294 | EXPORT_SYMBOL_GPL(rcutorture_record_test_transition); | 294 | EXPORT_SYMBOL_GPL(rcutorture_record_test_transition); |
295 | 295 | ||
296 | /* | 296 | /* |
297 | * Send along grace-period-related data for rcutorture diagnostics. | ||
298 | */ | ||
299 | void rcutorture_get_gp_data(enum rcutorture_type test_type, int *flags, | ||
300 | unsigned long *gpnum, unsigned long *completed) | ||
301 | { | ||
302 | struct rcu_state *rsp = NULL; | ||
303 | |||
304 | switch (test_type) { | ||
305 | case RCU_FLAVOR: | ||
306 | rsp = rcu_state; | ||
307 | break; | ||
308 | case RCU_BH_FLAVOR: | ||
309 | rsp = &rcu_bh_state; | ||
310 | break; | ||
311 | case RCU_SCHED_FLAVOR: | ||
312 | rsp = &rcu_sched_state; | ||
313 | break; | ||
314 | default: | ||
315 | break; | ||
316 | } | ||
317 | if (rsp != NULL) { | ||
318 | *flags = ACCESS_ONCE(rsp->gp_flags); | ||
319 | *gpnum = ACCESS_ONCE(rsp->gpnum); | ||
320 | *completed = ACCESS_ONCE(rsp->completed); | ||
321 | return; | ||
322 | } | ||
323 | *flags = 0; | ||
324 | *gpnum = 0; | ||
325 | *completed = 0; | ||
326 | } | ||
327 | EXPORT_SYMBOL_GPL(rcutorture_get_gp_data); | ||
328 | |||
329 | /* | ||
297 | * Record the number of writer passes through the current rcutorture test. | 330 | * Record the number of writer passes through the current rcutorture test. |
298 | * This is also used to correlate debugfs tracing stats with the rcutorture | 331 | * This is also used to correlate debugfs tracing stats with the rcutorture |
299 | * messages. | 332 | * messages. |