diff options
author | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2015-04-14 15:28:22 -0400 |
---|---|---|
committer | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2015-05-27 15:59:58 -0400 |
commit | ca1d51ed9809a99d71c23a343b3acd3fd4ad8cbe (patch) | |
tree | 2e0339669a97ba352d477fb5ba60a98b7f1ba8c4 | |
parent | 6c7ed42c81a2d9a7e0646240599552040375fa02 (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.c | 77 | ||||
-rw-r--r-- | tools/testing/selftests/rcutorture/configs/rcu/SRCU-P.boot | 2 |
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) | |||
241 | struct rcu_torture_ops { | 241 | struct 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 | ||
479 | DEFINE_STATIC_SRCU(srcu_ctl); | 480 | DEFINE_STATIC_SRCU(srcu_ctl); |
481 | static struct srcu_struct srcu_ctld; | ||
482 | static struct srcu_struct *srcu_ctlp = &srcu_ctl; | ||
480 | 483 | ||
481 | static int srcu_torture_read_lock(void) __acquires(&srcu_ctl) | 484 | static 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 | ||
486 | static void srcu_read_delay(struct torture_random_state *rrsp) | 489 | static 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 | ||
502 | static void srcu_torture_read_unlock(int idx) __releases(&srcu_ctl) | 505 | static 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 | ||
507 | static unsigned long srcu_torture_completed(void) | 510 | static 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 | ||
512 | static void srcu_torture_deferred_free(struct rcu_torture *rp) | 515 | static 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 | ||
517 | static void srcu_torture_synchronize(void) | 520 | static void srcu_torture_synchronize(void) |
518 | { | 521 | { |
519 | synchronize_srcu(&srcu_ctl); | 522 | synchronize_srcu(srcu_ctlp); |
520 | } | 523 | } |
521 | 524 | ||
522 | static void srcu_torture_call(struct rcu_head *head, | 525 | static 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 | ||
528 | static void srcu_torture_barrier(void) | 531 | static void srcu_torture_barrier(void) |
529 | { | 532 | { |
530 | srcu_barrier(&srcu_ctl); | 533 | srcu_barrier(srcu_ctlp); |
531 | } | 534 | } |
532 | 535 | ||
533 | static void srcu_torture_stats(void) | 536 | static 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 | ||
550 | static void srcu_torture_synchronize_expedited(void) | 553 | static void srcu_torture_synchronize_expedited(void) |
551 | { | 554 | { |
552 | synchronize_srcu_expedited(&srcu_ctl); | 555 | synchronize_srcu_expedited(srcu_ctlp); |
553 | } | 556 | } |
554 | 557 | ||
555 | static struct rcu_torture_ops srcu_ops = { | 558 | static 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 | ||
575 | static 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 | |||
582 | static 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. */ | ||
589 | static 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 | ||