aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/rcu/tree.c
diff options
context:
space:
mode:
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>2018-04-12 14:50:41 -0400
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>2018-05-15 13:30:37 -0400
commit360e0da67eab610b0efd53cbab3e1535095e7aa4 (patch)
tree306fce1544d093da439658e662fb691a02b7d5a6 /kernel/rcu/tree.c
parent41e80595abfc608eb0fe5148bcaed1ed78d7a6b7 (diff)
rcu: Add funnel locking to rcu_start_this_gp()
The rcu_start_this_gp() function had a simple form of funnel locking that used only the leaves and root of the rcu_node tree, which is fine for systems with only a few hundred CPUs, but sub-optimal for systems having thousands of CPUs. This commit therefore adds full-tree funnel locking. This variant of funnel locking is unusual in the following ways: 1. The leaf-level rcu_node structure's ->lock is held throughout. Other funnel-locking implementations drop the leaf-level lock before progressing to the next level of the tree. 2. Funnel locking can be started at the root, which is convenient for code that already holds the root rcu_node structure's ->lock. Other funnel-locking implementations start at the leaves. 3. If an rcu_node structure other than the initial one believes that a grace period is in progress, it is not necessary to go further up the tree. This is because grace-period cleanup scans the full tree, so that marking the need for a subsequent grace period anywhere in the tree suffices -- but only if a grace period is currently in progress. 4. It is possible that the RCU grace-period kthread has not yet started, and this case must be handled appropriately. However, the general approach of using a tree to control lock contention is still in place. Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Tested-by: Nicholas Piggin <npiggin@gmail.com>
Diffstat (limited to 'kernel/rcu/tree.c')
-rw-r--r--kernel/rcu/tree.c92
1 files changed, 35 insertions, 57 deletions
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index 94519c7d552f..d3c769502929 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -1682,74 +1682,52 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp,
1682{ 1682{
1683 bool ret = false; 1683 bool ret = false;
1684 struct rcu_state *rsp = rdp->rsp; 1684 struct rcu_state *rsp = rdp->rsp;
1685 struct rcu_node *rnp_root = rcu_get_root(rsp); 1685 struct rcu_node *rnp_root;
1686
1687 raw_lockdep_assert_held_rcu_node(rnp);
1688
1689 /* If the specified GP is already known needed, return to caller. */
1690 trace_rcu_this_gp(rnp, rdp, c, TPS("Startleaf"));
1691 if (need_future_gp_element(rnp, c)) {
1692 trace_rcu_this_gp(rnp, rdp, c, TPS("Prestartleaf"));
1693 goto out;
1694 }
1695 1686
1696 /* 1687 /*
1697 * If this rcu_node structure believes that a grace period is in 1688 * Use funnel locking to either acquire the root rcu_node
1698 * progress, then we must wait for the one following, which is in 1689 * structure's lock or bail out if the need for this grace period
1699 * "c". Because our request will be noticed at the end of the 1690 * has already been recorded -- or has already started. If there
1700 * current grace period, we don't need to explicitly start one. 1691 * is already a grace period in progress in a non-leaf node, no
1692 * recording is needed because the end of the grace period will
1693 * scan the leaf rcu_node structures. Note that rnp->lock must
1694 * not be released.
1701 */ 1695 */
1702 if (rnp->gpnum != rnp->completed) { 1696 raw_lockdep_assert_held_rcu_node(rnp);
1703 need_future_gp_element(rnp, c) = true; 1697 trace_rcu_this_gp(rnp, rdp, c, TPS("Startleaf"));
1704 trace_rcu_this_gp(rnp, rdp, c, TPS("Startedleaf")); 1698 for (rnp_root = rnp; 1; rnp_root = rnp_root->parent) {
1705 goto out; 1699 if (rnp_root != rnp)
1700 raw_spin_lock_rcu_node(rnp_root);
1701 if (need_future_gp_element(rnp_root, c) ||
1702 ULONG_CMP_GE(rnp_root->gpnum, c) ||
1703 (rnp != rnp_root &&
1704 rnp_root->gpnum != rnp_root->completed)) {
1705 trace_rcu_this_gp(rnp_root, rdp, c, TPS("Prestarted"));
1706 goto unlock_out;
1707 }
1708 need_future_gp_element(rnp_root, c) = true;
1709 if (rnp_root != rnp && rnp_root->parent != NULL)
1710 raw_spin_unlock_rcu_node(rnp_root);
1711 if (!rnp_root->parent)
1712 break; /* At root, and perhaps also leaf. */
1706 } 1713 }
1707 1714
1708 /* 1715 /* If GP already in progress, just leave, otherwise start one. */
1709 * There might be no grace period in progress. If we don't already 1716 if (rnp_root->gpnum != rnp_root->completed) {
1710 * hold it, acquire the root rcu_node structure's lock in order to 1717 trace_rcu_this_gp(rnp_root, rdp, c, TPS("Startedleafroot"));
1711 * start one (if needed).
1712 */
1713 if (rnp != rnp_root)
1714 raw_spin_lock_rcu_node(rnp_root);
1715
1716 /*
1717 * Get a new grace-period number. If there really is no grace
1718 * period in progress, it will be smaller than the one we obtained
1719 * earlier. Adjust callbacks as needed.
1720 */
1721 c = rcu_cbs_completed(rsp, rnp_root);
1722 if (!rcu_is_nocb_cpu(rdp->cpu))
1723 (void)rcu_segcblist_accelerate(&rdp->cblist, c);
1724
1725 /*
1726 * If the needed for the required grace period is already
1727 * recorded, trace and leave.
1728 */
1729 if (need_future_gp_element(rnp_root, c)) {
1730 trace_rcu_this_gp(rnp, rdp, c, TPS("Prestartedroot"));
1731 goto unlock_out; 1718 goto unlock_out;
1732 } 1719 }
1733 1720 trace_rcu_this_gp(rnp_root, rdp, c, TPS("Startedroot"));
1734 /* Record the need for the future grace period. */ 1721 WRITE_ONCE(rsp->gp_flags, rsp->gp_flags | RCU_GP_FLAG_INIT);
1735 need_future_gp_element(rnp_root, c) = true; 1722 if (!rsp->gp_kthread) {
1736 1723 trace_rcu_this_gp(rnp_root, rdp, c, TPS("NoGPkthread"));
1737 /* If a grace period is not already in progress, start one. */ 1724 goto unlock_out;
1738 if (rnp_root->gpnum != rnp_root->completed) {
1739 trace_rcu_this_gp(rnp, rdp, c, TPS("Startedleafroot"));
1740 } else {
1741 trace_rcu_this_gp(rnp, rdp, c, TPS("Startedroot"));
1742 if (!rsp->gp_kthread)
1743 goto unlock_out; /* No grace-period kthread yet! */
1744 WRITE_ONCE(rsp->gp_flags, rsp->gp_flags | RCU_GP_FLAG_INIT);
1745 trace_rcu_grace_period(rsp->name, READ_ONCE(rsp->gpnum),
1746 TPS("newreq"));
1747 ret = true; /* Caller must wake GP kthread. */
1748 } 1725 }
1726 trace_rcu_grace_period(rsp->name, READ_ONCE(rsp->gpnum), TPS("newreq"));
1727 ret = true; /* Caller must wake GP kthread. */
1749unlock_out: 1728unlock_out:
1750 if (rnp != rnp_root) 1729 if (rnp != rnp_root)
1751 raw_spin_unlock_rcu_node(rnp_root); 1730 raw_spin_unlock_rcu_node(rnp_root);
1752out:
1753 return ret; 1731 return ret;
1754} 1732}
1755 1733