aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/rcu
diff options
context:
space:
mode:
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>2014-02-19 13:51:42 -0500
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>2014-05-13 14:18:18 -0400
commitad0dc7f94dbf417b1c7d42e1f0b250f045b27f8f (patch)
tree7ab989cc1da5014778a7bd24ec94184104e5b517 /kernel/rcu
parentc9eaa447e77efe77b7fa4c953bd62de8297fd6c5 (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.c37
-rw-r--r--kernel/rcu/tree.c33
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;
138static long n_barrier_successes; 138static long n_barrier_successes;
139static struct list_head rcu_torture_removed; 139static struct list_head rcu_torture_removed;
140 140
141static 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
216struct rcu_torture_ops { 225struct 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
314static struct rcu_torture_ops rcu_ops = { 324static 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
357static struct rcu_torture_ops rcu_bh_ops = { 368static 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
399static struct rcu_torture_ops rcu_busted_ops = { 411static 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
494static struct rcu_torture_ops srcu_ops = { 507static 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
529static struct rcu_torture_ops sched_ops = { 543static 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)
294EXPORT_SYMBOL_GPL(rcutorture_record_test_transition); 294EXPORT_SYMBOL_GPL(rcutorture_record_test_transition);
295 295
296/* 296/*
297 * Send along grace-period-related data for rcutorture diagnostics.
298 */
299void 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}
327EXPORT_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.