aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>2015-04-14 15:28:22 -0400
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>2015-05-27 15:59:58 -0400
commitca1d51ed9809a99d71c23a343b3acd3fd4ad8cbe (patch)
tree2e0339669a97ba352d477fb5ba60a98b7f1ba8c4
parent6c7ed42c81a2d9a7e0646240599552040375fa02 (diff)
rcutorture: Test SRCU cleanup code path
The current rcutorture testing does not do any cleanup operations. This works because the srcu_struct is statically allocated, but it does represent a memory leak of the associated dynamically allocated ->per_cpu_ref per-CPU variables. However, rcutorture currently uses a statically allocated srcu_struct, which cannot legally be passed to cleanup_srcu_struct(). Therefore, this commit adds a second form of srcu (called srcud) that dynamically allocates and frees the associated per-CPU variables. This commit also adds a ->cleanup() member to rcu_torture_ops that is invoked at the end of the test, after ->cb_barriers(). This ->cleanup() pointer is NULL for all existing tests, and thus only used for scrud. Finally, the SRCU-P torture-test configuration selects scrud instead of srcu, with SRCU-N continuing to use srcu, thereby testing both static and dynamic srcu_struct structures. Reported-by: "Ahmed, Iftekhar" <ahmedi@onid.oregonstate.edu> Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Reviewed-by: Josh Triplett <josh@joshtriplett.org>
-rw-r--r--kernel/rcu/rcutorture.c77
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/SRCU-P.boot2
2 files changed, 59 insertions, 20 deletions
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
index 90ff8dfc51e5..59e32684c23b 100644
--- a/kernel/rcu/rcutorture.c
+++ b/kernel/rcu/rcutorture.c
@@ -241,6 +241,7 @@ rcu_torture_free(struct rcu_torture *p)
241struct rcu_torture_ops { 241struct rcu_torture_ops {
242 int ttype; 242 int ttype;
243 void (*init)(void); 243 void (*init)(void);
244 void (*cleanup)(void);
244 int (*readlock)(void); 245 int (*readlock)(void);
245 void (*read_delay)(struct torture_random_state *rrsp); 246 void (*read_delay)(struct torture_random_state *rrsp);
246 void (*readunlock)(int idx); 247 void (*readunlock)(int idx);
@@ -477,10 +478,12 @@ static struct rcu_torture_ops rcu_busted_ops = {
477 */ 478 */
478 479
479DEFINE_STATIC_SRCU(srcu_ctl); 480DEFINE_STATIC_SRCU(srcu_ctl);
481static struct srcu_struct srcu_ctld;
482static struct srcu_struct *srcu_ctlp = &srcu_ctl;
480 483
481static int srcu_torture_read_lock(void) __acquires(&srcu_ctl) 484static int srcu_torture_read_lock(void) __acquires(srcu_ctlp)
482{ 485{
483 return srcu_read_lock(&srcu_ctl); 486 return srcu_read_lock(srcu_ctlp);
484} 487}
485 488
486static void srcu_read_delay(struct torture_random_state *rrsp) 489static void srcu_read_delay(struct torture_random_state *rrsp)
@@ -499,49 +502,49 @@ static void srcu_read_delay(struct torture_random_state *rrsp)
499 rcu_read_delay(rrsp); 502 rcu_read_delay(rrsp);
500} 503}
501 504
502static void srcu_torture_read_unlock(int idx) __releases(&srcu_ctl) 505static void srcu_torture_read_unlock(int idx) __releases(srcu_ctlp)
503{ 506{
504 srcu_read_unlock(&srcu_ctl, idx); 507 srcu_read_unlock(srcu_ctlp, idx);
505} 508}
506 509
507static unsigned long srcu_torture_completed(void) 510static unsigned long srcu_torture_completed(void)
508{ 511{
509 return srcu_batches_completed(&srcu_ctl); 512 return srcu_batches_completed(srcu_ctlp);
510} 513}
511 514
512static void srcu_torture_deferred_free(struct rcu_torture *rp) 515static void srcu_torture_deferred_free(struct rcu_torture *rp)
513{ 516{
514 call_srcu(&srcu_ctl, &rp->rtort_rcu, rcu_torture_cb); 517 call_srcu(srcu_ctlp, &rp->rtort_rcu, rcu_torture_cb);
515} 518}
516 519
517static void srcu_torture_synchronize(void) 520static void srcu_torture_synchronize(void)
518{ 521{
519 synchronize_srcu(&srcu_ctl); 522 synchronize_srcu(srcu_ctlp);
520} 523}
521 524
522static void srcu_torture_call(struct rcu_head *head, 525static void srcu_torture_call(struct rcu_head *head,
523 void (*func)(struct rcu_head *head)) 526 void (*func)(struct rcu_head *head))
524{ 527{
525 call_srcu(&srcu_ctl, head, func); 528 call_srcu(srcu_ctlp, head, func);
526} 529}
527 530
528static void srcu_torture_barrier(void) 531static void srcu_torture_barrier(void)
529{ 532{
530 srcu_barrier(&srcu_ctl); 533 srcu_barrier(srcu_ctlp);
531} 534}
532 535
533static void srcu_torture_stats(void) 536static void srcu_torture_stats(void)
534{ 537{
535 int cpu; 538 int cpu;
536 int idx = srcu_ctl.completed & 0x1; 539 int idx = srcu_ctlp->completed & 0x1;
537 540
538 pr_alert("%s%s per-CPU(idx=%d):", 541 pr_alert("%s%s per-CPU(idx=%d):",
539 torture_type, TORTURE_FLAG, idx); 542 torture_type, TORTURE_FLAG, idx);
540 for_each_possible_cpu(cpu) { 543 for_each_possible_cpu(cpu) {
541 long c0, c1; 544 long c0, c1;
542 545
543 c0 = (long)per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[!idx]; 546 c0 = (long)per_cpu_ptr(srcu_ctlp->per_cpu_ref, cpu)->c[!idx];
544 c1 = (long)per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[idx]; 547 c1 = (long)per_cpu_ptr(srcu_ctlp->per_cpu_ref, cpu)->c[idx];
545 pr_cont(" %d(%ld,%ld)", cpu, c0, c1); 548 pr_cont(" %d(%ld,%ld)", cpu, c0, c1);
546 } 549 }
547 pr_cont("\n"); 550 pr_cont("\n");
@@ -549,7 +552,7 @@ static void srcu_torture_stats(void)
549 552
550static void srcu_torture_synchronize_expedited(void) 553static void srcu_torture_synchronize_expedited(void)
551{ 554{
552 synchronize_srcu_expedited(&srcu_ctl); 555 synchronize_srcu_expedited(srcu_ctlp);
553} 556}
554 557
555static struct rcu_torture_ops srcu_ops = { 558static struct rcu_torture_ops srcu_ops = {
@@ -569,6 +572,38 @@ static struct rcu_torture_ops srcu_ops = {
569 .name = "srcu" 572 .name = "srcu"
570}; 573};
571 574
575static void srcu_torture_init(void)
576{
577 rcu_sync_torture_init();
578 WARN_ON(init_srcu_struct(&srcu_ctld));
579 srcu_ctlp = &srcu_ctld;
580}
581
582static void srcu_torture_cleanup(void)
583{
584 cleanup_srcu_struct(&srcu_ctld);
585 srcu_ctlp = &srcu_ctl; /* In case of a later rcutorture run. */
586}
587
588/* As above, but dynamically allocated. */
589static struct rcu_torture_ops srcud_ops = {
590 .ttype = SRCU_FLAVOR,
591 .init = srcu_torture_init,
592 .cleanup = srcu_torture_cleanup,
593 .readlock = srcu_torture_read_lock,
594 .read_delay = srcu_read_delay,
595 .readunlock = srcu_torture_read_unlock,
596 .started = NULL,
597 .completed = srcu_torture_completed,
598 .deferred_free = srcu_torture_deferred_free,
599 .sync = srcu_torture_synchronize,
600 .exp_sync = srcu_torture_synchronize_expedited,
601 .call = srcu_torture_call,
602 .cb_barrier = srcu_torture_barrier,
603 .stats = srcu_torture_stats,
604 .name = "srcud"
605};
606
572/* 607/*
573 * Definitions for sched torture testing. 608 * Definitions for sched torture testing.
574 */ 609 */
@@ -1053,7 +1088,7 @@ static void rcu_torture_timer(unsigned long unused)
1053 p = rcu_dereference_check(rcu_torture_current, 1088 p = rcu_dereference_check(rcu_torture_current,
1054 rcu_read_lock_bh_held() || 1089 rcu_read_lock_bh_held() ||
1055 rcu_read_lock_sched_held() || 1090 rcu_read_lock_sched_held() ||
1056 srcu_read_lock_held(&srcu_ctl)); 1091 srcu_read_lock_held(srcu_ctlp));
1057 if (p == NULL) { 1092 if (p == NULL) {
1058 /* Leave because rcu_torture_writer is not yet underway */ 1093 /* Leave because rcu_torture_writer is not yet underway */
1059 cur_ops->readunlock(idx); 1094 cur_ops->readunlock(idx);
@@ -1127,7 +1162,7 @@ rcu_torture_reader(void *arg)
1127 p = rcu_dereference_check(rcu_torture_current, 1162 p = rcu_dereference_check(rcu_torture_current,
1128 rcu_read_lock_bh_held() || 1163 rcu_read_lock_bh_held() ||
1129 rcu_read_lock_sched_held() || 1164 rcu_read_lock_sched_held() ||
1130 srcu_read_lock_held(&srcu_ctl)); 1165 srcu_read_lock_held(srcu_ctlp));
1131 if (p == NULL) { 1166 if (p == NULL) {
1132 /* Wait for rcu_torture_writer to get underway */ 1167 /* Wait for rcu_torture_writer to get underway */
1133 cur_ops->readunlock(idx); 1168 cur_ops->readunlock(idx);
@@ -1590,10 +1625,14 @@ rcu_torture_cleanup(void)
1590 rcutorture_booster_cleanup(i); 1625 rcutorture_booster_cleanup(i);
1591 } 1626 }
1592 1627
1593 /* Wait for all RCU callbacks to fire. */ 1628 /*
1594 1629 * Wait for all RCU callbacks to fire, then do flavor-specific
1630 * cleanup operations.
1631 */
1595 if (cur_ops->cb_barrier != NULL) 1632 if (cur_ops->cb_barrier != NULL)
1596 cur_ops->cb_barrier(); 1633 cur_ops->cb_barrier();
1634 if (cur_ops->cleanup != NULL)
1635 cur_ops->cleanup();
1597 1636
1598 rcu_torture_stats_print(); /* -After- the stats thread is stopped! */ 1637 rcu_torture_stats_print(); /* -After- the stats thread is stopped! */
1599 1638
@@ -1670,8 +1709,8 @@ rcu_torture_init(void)
1670 int cpu; 1709 int cpu;
1671 int firsterr = 0; 1710 int firsterr = 0;
1672 static struct rcu_torture_ops *torture_ops[] = { 1711 static struct rcu_torture_ops *torture_ops[] = {
1673 &rcu_ops, &rcu_bh_ops, &rcu_busted_ops, &srcu_ops, &sched_ops, 1712 &rcu_ops, &rcu_bh_ops, &rcu_busted_ops, &srcu_ops, &srcud_ops,
1674 RCUTORTURE_TASKS_OPS 1713 &sched_ops, RCUTORTURE_TASKS_OPS
1675 }; 1714 };
1676 1715
1677 if (!torture_init_begin(torture_type, verbose, &torture_runnable)) 1716 if (!torture_init_begin(torture_type, verbose, &torture_runnable))
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-P.boot b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-P.boot
index 238bfe3bd0cc..84a7d51b7481 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-P.boot
+++ b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-P.boot
@@ -1 +1 @@
rcutorture.torture_type=srcu rcutorture.torture_type=srcud