diff options
| -rw-r--r-- | kernel/rcu/rcutorture.c | 254 |
1 files changed, 135 insertions, 119 deletions
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 17f480129a78..bcc33bb8d9a6 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c | |||
| @@ -1650,15 +1650,70 @@ static void rcu_torture_fwd_cb_cr(struct rcu_head *rhp) | |||
| 1650 | spin_unlock(&rcu_fwd_lock); | 1650 | spin_unlock(&rcu_fwd_lock); |
| 1651 | } | 1651 | } |
| 1652 | 1652 | ||
| 1653 | /* Carry out grace-period forward-progress testing. */ | 1653 | /* Carry out need_resched()/cond_resched() forward-progress testing. */ |
| 1654 | static int rcu_torture_fwd_prog(void *args) | 1654 | static void rcu_torture_fwd_prog_nr(int *tested, int *tested_tries) |
| 1655 | { | 1655 | { |
| 1656 | unsigned long cver; | 1656 | unsigned long cver; |
| 1657 | unsigned long dur; | 1657 | unsigned long dur; |
| 1658 | struct fwd_cb_state fcs; | 1658 | struct fwd_cb_state fcs; |
| 1659 | unsigned long gps; | 1659 | unsigned long gps; |
| 1660 | int i; | ||
| 1661 | int idx; | 1660 | int idx; |
| 1661 | int sd; | ||
| 1662 | int sd4; | ||
| 1663 | bool selfpropcb = false; | ||
| 1664 | unsigned long stopat; | ||
| 1665 | static DEFINE_TORTURE_RANDOM(trs); | ||
| 1666 | |||
| 1667 | if (cur_ops->call && cur_ops->sync && cur_ops->cb_barrier) { | ||
| 1668 | init_rcu_head_on_stack(&fcs.rh); | ||
| 1669 | selfpropcb = true; | ||
| 1670 | } | ||
| 1671 | |||
| 1672 | /* Tight loop containing cond_resched(). */ | ||
| 1673 | if (selfpropcb) { | ||
| 1674 | WRITE_ONCE(fcs.stop, 0); | ||
| 1675 | cur_ops->call(&fcs.rh, rcu_torture_fwd_prog_cb); | ||
| 1676 | } | ||
| 1677 | cver = READ_ONCE(rcu_torture_current_version); | ||
| 1678 | gps = cur_ops->get_gp_seq(); | ||
| 1679 | sd = cur_ops->stall_dur() + 1; | ||
| 1680 | sd4 = (sd + fwd_progress_div - 1) / fwd_progress_div; | ||
| 1681 | dur = sd4 + torture_random(&trs) % (sd - sd4); | ||
| 1682 | rcu_fwd_startat = jiffies; | ||
| 1683 | stopat = rcu_fwd_startat + dur; | ||
| 1684 | while (time_before(jiffies, stopat) && !torture_must_stop()) { | ||
| 1685 | idx = cur_ops->readlock(); | ||
| 1686 | udelay(10); | ||
| 1687 | cur_ops->readunlock(idx); | ||
| 1688 | if (!fwd_progress_need_resched || need_resched()) | ||
| 1689 | cond_resched(); | ||
| 1690 | } | ||
| 1691 | (*tested_tries)++; | ||
| 1692 | if (!time_before(jiffies, stopat) && !torture_must_stop()) { | ||
| 1693 | (*tested)++; | ||
| 1694 | cver = READ_ONCE(rcu_torture_current_version) - cver; | ||
| 1695 | gps = rcutorture_seq_diff(cur_ops->get_gp_seq(), gps); | ||
| 1696 | WARN_ON(!cver && gps < 2); | ||
| 1697 | pr_alert("%s: Duration %ld cver %ld gps %ld\n", __func__, dur, cver, gps); | ||
| 1698 | } | ||
| 1699 | if (selfpropcb) { | ||
| 1700 | WRITE_ONCE(fcs.stop, 1); | ||
| 1701 | cur_ops->sync(); /* Wait for running CB to complete. */ | ||
| 1702 | cur_ops->cb_barrier(); /* Wait for queued callbacks. */ | ||
| 1703 | } | ||
| 1704 | |||
| 1705 | if (selfpropcb) { | ||
| 1706 | WARN_ON(READ_ONCE(fcs.stop) != 2); | ||
| 1707 | destroy_rcu_head_on_stack(&fcs.rh); | ||
| 1708 | } | ||
| 1709 | } | ||
| 1710 | |||
| 1711 | /* Carry out call_rcu() forward-progress testing. */ | ||
| 1712 | static void rcu_torture_fwd_prog_cr(void) | ||
| 1713 | { | ||
| 1714 | unsigned long cver; | ||
| 1715 | unsigned long gps; | ||
| 1716 | int i; | ||
| 1662 | int j; | 1717 | int j; |
| 1663 | long n_launders; | 1718 | long n_launders; |
| 1664 | long n_launders_cb_snap; | 1719 | long n_launders_cb_snap; |
| @@ -1667,136 +1722,97 @@ static int rcu_torture_fwd_prog(void *args) | |||
| 1667 | long n_max_gps; | 1722 | long n_max_gps; |
| 1668 | struct rcu_fwd_cb *rfcp; | 1723 | struct rcu_fwd_cb *rfcp; |
| 1669 | struct rcu_fwd_cb *rfcpn; | 1724 | struct rcu_fwd_cb *rfcpn; |
| 1670 | int sd; | ||
| 1671 | int sd4; | ||
| 1672 | bool selfpropcb = false; | ||
| 1673 | unsigned long stopat; | 1725 | unsigned long stopat; |
| 1674 | unsigned long stoppedat; | 1726 | unsigned long stoppedat; |
| 1727 | |||
| 1728 | /* Loop continuously posting RCU callbacks. */ | ||
| 1729 | WRITE_ONCE(rcu_fwd_cb_nodelay, true); | ||
| 1730 | cur_ops->sync(); /* Later readers see above write. */ | ||
| 1731 | rcu_fwd_startat = jiffies; | ||
| 1732 | stopat = rcu_fwd_startat + MAX_FWD_CB_JIFFIES; | ||
| 1733 | n_launders = 0; | ||
| 1734 | n_launders_cb = 0; | ||
| 1735 | n_launders_sa = 0; | ||
| 1736 | n_max_cbs = 0; | ||
| 1737 | n_max_gps = 0; | ||
| 1738 | for (i = 0; i < ARRAY_SIZE(n_launders_hist); i++) | ||
| 1739 | n_launders_hist[i] = 0; | ||
| 1740 | cver = READ_ONCE(rcu_torture_current_version); | ||
| 1741 | gps = cur_ops->get_gp_seq(); | ||
| 1742 | while (time_before(jiffies, stopat) && !torture_must_stop()) { | ||
| 1743 | rfcp = READ_ONCE(rcu_fwd_cb_head); | ||
| 1744 | rfcpn = NULL; | ||
| 1745 | if (rfcp) | ||
| 1746 | rfcpn = READ_ONCE(rfcp->rfc_next); | ||
| 1747 | if (rfcpn) { | ||
| 1748 | if (rfcp->rfc_gps >= MIN_FWD_CB_LAUNDERS && | ||
| 1749 | ++n_max_gps >= MIN_FWD_CBS_LAUNDERED) | ||
| 1750 | break; | ||
| 1751 | rcu_fwd_cb_head = rfcpn; | ||
| 1752 | n_launders++; | ||
| 1753 | n_launders_sa++; | ||
| 1754 | } else { | ||
| 1755 | rfcp = kmalloc(sizeof(*rfcp), GFP_KERNEL); | ||
| 1756 | if (WARN_ON_ONCE(!rfcp)) { | ||
| 1757 | schedule_timeout_interruptible(1); | ||
| 1758 | continue; | ||
| 1759 | } | ||
| 1760 | n_max_cbs++; | ||
| 1761 | n_launders_sa = 0; | ||
| 1762 | rfcp->rfc_gps = 0; | ||
| 1763 | } | ||
| 1764 | cur_ops->call(&rfcp->rh, rcu_torture_fwd_cb_cr); | ||
| 1765 | cond_resched(); | ||
| 1766 | } | ||
| 1767 | stoppedat = jiffies; | ||
| 1768 | n_launders_cb_snap = READ_ONCE(n_launders_cb); | ||
| 1769 | cver = READ_ONCE(rcu_torture_current_version) - cver; | ||
| 1770 | gps = rcutorture_seq_diff(cur_ops->get_gp_seq(), gps); | ||
| 1771 | cur_ops->cb_barrier(); /* Wait for callbacks to be invoked. */ | ||
| 1772 | for (;;) { | ||
| 1773 | rfcp = rcu_fwd_cb_head; | ||
| 1774 | if (!rfcp) | ||
| 1775 | break; | ||
| 1776 | rcu_fwd_cb_head = rfcp->rfc_next; | ||
| 1777 | kfree(rfcp); | ||
| 1778 | } | ||
| 1779 | rcu_fwd_cb_tail = &rcu_fwd_cb_head; | ||
| 1780 | WRITE_ONCE(rcu_fwd_cb_nodelay, false); | ||
| 1781 | if (!torture_must_stop()) { | ||
| 1782 | WARN_ON(n_max_gps < MIN_FWD_CBS_LAUNDERED); | ||
| 1783 | pr_alert("%s Duration %lu barrier: %lu pending %ld n_launders: %ld n_launders_sa: %ld n_max_gps: %ld n_max_cbs: %ld cver %ld gps %ld\n", | ||
| 1784 | __func__, | ||
| 1785 | stoppedat - rcu_fwd_startat, jiffies - stoppedat, | ||
| 1786 | n_launders + n_max_cbs - n_launders_cb_snap, | ||
| 1787 | n_launders, n_launders_sa, | ||
| 1788 | n_max_gps, n_max_cbs, cver, gps); | ||
| 1789 | for (i = ARRAY_SIZE(n_launders_hist) - 1; i > 0; i--) | ||
| 1790 | if (n_launders_hist[i] > 0) | ||
| 1791 | break; | ||
| 1792 | pr_alert("Callback-invocation histogram:"); | ||
| 1793 | for (j = 0; j <= i; j++) | ||
| 1794 | pr_cont(" %ds: %ld", j + 1, n_launders_hist[j]); | ||
| 1795 | pr_cont("\n"); | ||
| 1796 | } | ||
| 1797 | } | ||
| 1798 | |||
| 1799 | /* Carry out grace-period forward-progress testing. */ | ||
| 1800 | static int rcu_torture_fwd_prog(void *args) | ||
| 1801 | { | ||
| 1675 | int tested = 0; | 1802 | int tested = 0; |
| 1676 | int tested_tries = 0; | 1803 | int tested_tries = 0; |
| 1677 | static DEFINE_TORTURE_RANDOM(trs); | ||
| 1678 | 1804 | ||
| 1679 | VERBOSE_TOROUT_STRING("rcu_torture_fwd_progress task started"); | 1805 | VERBOSE_TOROUT_STRING("rcu_torture_fwd_progress task started"); |
| 1680 | if (!IS_ENABLED(CONFIG_SMP) || !IS_ENABLED(CONFIG_RCU_BOOST)) | 1806 | if (!IS_ENABLED(CONFIG_SMP) || !IS_ENABLED(CONFIG_RCU_BOOST)) |
| 1681 | set_user_nice(current, MAX_NICE); | 1807 | set_user_nice(current, MAX_NICE); |
| 1682 | if (cur_ops->call && cur_ops->sync && cur_ops->cb_barrier) { | ||
| 1683 | init_rcu_head_on_stack(&fcs.rh); | ||
| 1684 | selfpropcb = true; | ||
| 1685 | } | ||
| 1686 | do { | 1808 | do { |
| 1687 | schedule_timeout_interruptible(fwd_progress_holdoff * HZ); | 1809 | schedule_timeout_interruptible(fwd_progress_holdoff * HZ); |
| 1688 | 1810 | rcu_torture_fwd_prog_nr(&tested, &tested_tries); | |
| 1689 | /* Tight loop containing cond_resched(). */ | 1811 | rcu_torture_fwd_prog_cr(); |
| 1690 | if (selfpropcb) { | ||
| 1691 | WRITE_ONCE(fcs.stop, 0); | ||
| 1692 | cur_ops->call(&fcs.rh, rcu_torture_fwd_prog_cb); | ||
| 1693 | } | ||
| 1694 | cver = READ_ONCE(rcu_torture_current_version); | ||
| 1695 | gps = cur_ops->get_gp_seq(); | ||
| 1696 | sd = cur_ops->stall_dur() + 1; | ||
| 1697 | sd4 = (sd + fwd_progress_div - 1) / fwd_progress_div; | ||
| 1698 | dur = sd4 + torture_random(&trs) % (sd - sd4); | ||
| 1699 | rcu_fwd_startat = jiffies; | ||
| 1700 | stopat = rcu_fwd_startat + dur; | ||
| 1701 | while (time_before(jiffies, stopat) && !torture_must_stop()) { | ||
| 1702 | idx = cur_ops->readlock(); | ||
| 1703 | udelay(10); | ||
| 1704 | cur_ops->readunlock(idx); | ||
| 1705 | if (!fwd_progress_need_resched || need_resched()) | ||
| 1706 | cond_resched(); | ||
| 1707 | } | ||
| 1708 | tested_tries++; | ||
| 1709 | if (!time_before(jiffies, stopat) && !torture_must_stop()) { | ||
| 1710 | tested++; | ||
| 1711 | cver = READ_ONCE(rcu_torture_current_version) - cver; | ||
| 1712 | gps = rcutorture_seq_diff(cur_ops->get_gp_seq(), gps); | ||
| 1713 | WARN_ON(!cver && gps < 2); | ||
| 1714 | pr_alert("%s: Duration %ld cver %ld gps %ld\n", __func__, dur, cver, gps); | ||
| 1715 | } | ||
| 1716 | if (selfpropcb) { | ||
| 1717 | WRITE_ONCE(fcs.stop, 1); | ||
| 1718 | cur_ops->sync(); /* Wait for running CB to complete. */ | ||
| 1719 | cur_ops->cb_barrier(); /* Wait for queued callbacks. */ | ||
| 1720 | } | ||
| 1721 | |||
| 1722 | /* Loop continuously posting RCU callbacks. */ | ||
| 1723 | WRITE_ONCE(rcu_fwd_cb_nodelay, true); | ||
| 1724 | cur_ops->sync(); /* Later readers see above write. */ | ||
| 1725 | rcu_fwd_startat = jiffies; | ||
| 1726 | stopat = rcu_fwd_startat + MAX_FWD_CB_JIFFIES; | ||
| 1727 | n_launders = 0; | ||
| 1728 | n_launders_cb = 0; | ||
| 1729 | n_launders_sa = 0; | ||
| 1730 | n_max_cbs = 0; | ||
| 1731 | n_max_gps = 0; | ||
| 1732 | for (i = 0; i < ARRAY_SIZE(n_launders_hist); i++) | ||
| 1733 | n_launders_hist[i] = 0; | ||
| 1734 | cver = READ_ONCE(rcu_torture_current_version); | ||
| 1735 | gps = cur_ops->get_gp_seq(); | ||
| 1736 | while (time_before(jiffies, stopat) && !torture_must_stop()) { | ||
| 1737 | rfcp = READ_ONCE(rcu_fwd_cb_head); | ||
| 1738 | rfcpn = NULL; | ||
| 1739 | if (rfcp) | ||
| 1740 | rfcpn = READ_ONCE(rfcp->rfc_next); | ||
| 1741 | if (rfcpn) { | ||
| 1742 | if (rfcp->rfc_gps >= MIN_FWD_CB_LAUNDERS && | ||
| 1743 | ++n_max_gps >= MIN_FWD_CBS_LAUNDERED) | ||
| 1744 | break; | ||
| 1745 | rcu_fwd_cb_head = rfcpn; | ||
| 1746 | n_launders++; | ||
| 1747 | n_launders_sa++; | ||
| 1748 | } else { | ||
| 1749 | rfcp = kmalloc(sizeof(*rfcp), GFP_KERNEL); | ||
| 1750 | if (WARN_ON_ONCE(!rfcp)) { | ||
| 1751 | schedule_timeout_interruptible(1); | ||
| 1752 | continue; | ||
| 1753 | } | ||
| 1754 | n_max_cbs++; | ||
| 1755 | n_launders_sa = 0; | ||
| 1756 | rfcp->rfc_gps = 0; | ||
| 1757 | } | ||
| 1758 | cur_ops->call(&rfcp->rh, rcu_torture_fwd_cb_cr); | ||
| 1759 | cond_resched(); | ||
| 1760 | } | ||
| 1761 | stoppedat = jiffies; | ||
| 1762 | n_launders_cb_snap = READ_ONCE(n_launders_cb); | ||
| 1763 | cver = READ_ONCE(rcu_torture_current_version) - cver; | ||
| 1764 | gps = rcutorture_seq_diff(cur_ops->get_gp_seq(), gps); | ||
| 1765 | cur_ops->cb_barrier(); /* Wait for callbacks to be invoked. */ | ||
| 1766 | for (;;) { | ||
| 1767 | rfcp = rcu_fwd_cb_head; | ||
| 1768 | if (!rfcp) | ||
| 1769 | break; | ||
| 1770 | rcu_fwd_cb_head = rfcp->rfc_next; | ||
| 1771 | kfree(rfcp); | ||
| 1772 | } | ||
| 1773 | rcu_fwd_cb_tail = &rcu_fwd_cb_head; | ||
| 1774 | WRITE_ONCE(rcu_fwd_cb_nodelay, false); | ||
| 1775 | if (!torture_must_stop()) { | ||
| 1776 | WARN_ON(n_max_gps < MIN_FWD_CBS_LAUNDERED); | ||
| 1777 | pr_alert("%s Duration %lu barrier: %lu pending %ld n_launders: %ld n_launders_sa: %ld n_max_gps: %ld n_max_cbs: %ld cver %ld gps %ld\n", | ||
| 1778 | __func__, | ||
| 1779 | stoppedat - rcu_fwd_startat, | ||
| 1780 | jiffies - stoppedat, | ||
| 1781 | n_launders + n_max_cbs - n_launders_cb_snap, | ||
| 1782 | n_launders, n_launders_sa, | ||
| 1783 | n_max_gps, n_max_cbs, cver, gps); | ||
| 1784 | for (i = ARRAY_SIZE(n_launders_hist) - 1; i > 0; i--) | ||
| 1785 | if (n_launders_hist[i] > 0) | ||
| 1786 | break; | ||
| 1787 | pr_alert("Callback-invocation histogram:"); | ||
| 1788 | for (j = 0; j <= i; j++) | ||
| 1789 | pr_cont(" %ds: %ld", j + 1, n_launders_hist[j]); | ||
| 1790 | pr_cont("\n"); | ||
| 1791 | } | ||
| 1792 | 1812 | ||
| 1793 | /* Avoid slow periods, better to test when busy. */ | 1813 | /* Avoid slow periods, better to test when busy. */ |
| 1794 | stutter_wait("rcu_torture_fwd_prog"); | 1814 | stutter_wait("rcu_torture_fwd_prog"); |
| 1795 | } while (!torture_must_stop()); | 1815 | } while (!torture_must_stop()); |
| 1796 | if (selfpropcb) { | ||
| 1797 | WARN_ON(READ_ONCE(fcs.stop) != 2); | ||
| 1798 | destroy_rcu_head_on_stack(&fcs.rh); | ||
| 1799 | } | ||
| 1800 | /* Short runs might not contain a valid forward-progress attempt. */ | 1816 | /* Short runs might not contain a valid forward-progress attempt. */ |
| 1801 | WARN_ON(!tested && tested_tries >= 5); | 1817 | WARN_ON(!tested && tested_tries >= 5); |
| 1802 | pr_alert("%s: tested %d tested_tries %d\n", __func__, tested, tested_tries); | 1818 | pr_alert("%s: tested %d tested_tries %d\n", __func__, tested, tested_tries); |
