| /* |
| * Read-Copy Update mechanism for mutual exclusion |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, you can access it online at |
| * http://www.gnu.org/licenses/gpl-2.0.html. |
| * |
| * Copyright IBM Corporation, 2008 |
| * |
| * Authors: Dipankar Sarma <dipankar@in.ibm.com> |
| * Manfred Spraul <manfred@colorfullife.com> |
| * Paul E. McKenney <paulmck@linux.vnet.ibm.com> Hierarchical version |
| * |
| * Based on the original work by Paul McKenney <paulmck@us.ibm.com> |
| * and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen. |
| * |
| * For detailed explanation of Read-Copy Update mechanism see - |
| * Documentation/RCU |
| * |
| * AKAROS_PORT: was kernel/rcu/tree.c. |
| */ |
| |
| |
| #include <rcu.h> |
| #include <assert.h> |
| #include <smp.h> |
| |
| /* Control rcu_node-tree auto-balancing at boot time. */ |
| bool rcu_fanout_exact; |
| /* Increase (but not decrease) the RCU_FANOUT_LEAF at boot time. */ |
| int rcu_fanout_leaf = RCU_FANOUT_LEAF; |
| int rcu_num_lvls = RCU_NUM_LVLS; |
| /* Number of rcu_nodes at specified level. */ |
| int num_rcu_lvl[] = NUM_RCU_LVL_INIT; |
| int rcu_num_nodes = NUM_RCU_NODES; /* Total # rcu_nodes in use. */ |
| /* Number of cores RCU thinks exist. Set to 0 or nothing to use 'num_cores'. |
| * The extra cores are called 'fake cores' in rcu.c, and are used for testing |
| * the tree. */ |
| int rcu_num_cores; |
| |
| /* in rcu.c */ |
| extern struct rcu_state rcu_state; |
| |
| /** |
| * get_state_synchronize_rcu - Snapshot current RCU state |
| * |
| * Returns a cookie that is used by a later call to cond_synchronize_rcu() |
| * to determine whether or not a full grace period has elapsed in the |
| * meantime. |
| */ |
| unsigned long get_state_synchronize_rcu(void) |
| { |
| /* |
| * Any prior manipulation of RCU-protected data must happen |
| * before the load from ->gpnum. |
| */ |
| mb(); /* ^^^ */ |
| |
| /* |
| * Make sure this load happens before the purportedly |
| * time-consuming work between get_state_synchronize_rcu() |
| * and cond_synchronize_rcu(). |
| */ |
| return smp_load_acquire(&rcu_state.gpnum); |
| } |
| |
| /** |
| * cond_synchronize_rcu - Conditionally wait for an RCU grace period |
| * |
| * @oldstate: return value from earlier call to get_state_synchronize_rcu() |
| * |
| * If a full RCU grace period has elapsed since the earlier call to |
| * get_state_synchronize_rcu(), just return. Otherwise, invoke |
| * synchronize_rcu() to wait for a full grace period. |
| * |
| * Yes, this function does not take counter wrap into account. But |
| * counter wrap is harmless. If the counter wraps, we have waited for |
| * more than 2 billion grace periods (and way more on a 64-bit system!), |
| * so waiting for one additional grace period should be just fine. |
| */ |
| void cond_synchronize_rcu(unsigned long oldstate) |
| { |
| unsigned long newstate; |
| |
| /* |
| * Ensure that this load happens before any RCU-destructive |
| * actions the caller might carry out after we return. |
| */ |
| newstate = smp_load_acquire(&rcu_state.completed); |
| if (ULONG_CMP_GE(oldstate, newstate)) |
| synchronize_rcu(); |
| } |
| |
| |
| /* |
| * Helper function for rcu_init() that initializes one rcu_state structure. |
| */ |
| void rcu_init_one(struct rcu_state *rsp) |
| { |
| int levelspread[RCU_NUM_LVLS]; /* kids/node in each level. */ |
| int cpustride = 1; |
| int i; |
| int j; |
| struct rcu_node *rnp; |
| struct rcu_pcpui *rpi; |
| |
| rendez_init(&rsp->gp_ktask_rv); |
| rsp->gp_ktask_ctl = 0; |
| /* To test wraparound early */ |
| rsp->gpnum = ULONG_MAX - 20; |
| rsp->completed = ULONG_MAX - 20; |
| |
| /* Silence gcc 4.8 false positive about array index out of range. */ |
| if (rcu_num_lvls <= 0 || rcu_num_lvls > RCU_NUM_LVLS) |
| panic("rcu_init_one: rcu_num_lvls out of range"); |
| |
| /* Initialize the level-tracking arrays. */ |
| |
| rsp->level[0] = &rsp->node[0]; |
| for (i = 1; i < rcu_num_lvls; i++) |
| rsp->level[i] = rsp->level[i - 1] + num_rcu_lvl[i - 1]; |
| rcu_init_levelspread(levelspread, num_rcu_lvl); |
| |
| /* Initialize the elements themselves, starting from the leaves. */ |
| |
| for (i = rcu_num_lvls - 1; i >= 0; i--) { |
| cpustride *= levelspread[i]; |
| rnp = rsp->level[i]; |
| for (j = 0; j < num_rcu_lvl[i]; j++, rnp++) { |
| rnp->qsmask = 0; |
| rnp->qsmaskinit = 0; |
| rnp->grplo = j * cpustride; |
| rnp->grphi = (j + 1) * cpustride - 1; |
| if (rnp->grphi >= rcu_num_cores) |
| rnp->grphi = rcu_num_cores - 1; |
| if (i == 0) { |
| rnp->grpnum = 0; |
| rnp->grpmask = 0; |
| rnp->parent = NULL; |
| } else { |
| rnp->grpnum = j % levelspread[i - 1]; |
| rnp->grpmask = 1UL << rnp->grpnum; |
| rnp->parent = rsp->level[i - 1] + |
| j / levelspread[i - 1]; |
| } |
| rnp->level = i; |
| } |
| } |
| |
| rnp = rsp->level[rcu_num_lvls - 1]; |
| for_each_core(i) { |
| while (i > rnp->grphi) |
| rnp++; |
| rpi = _PERCPU_VARPTR(rcu_pcpui, i); |
| rpi->my_node = rnp; |
| rcu_init_pcpui(rsp, rpi, i); |
| } |
| /* Akaros has static rnps, so we can init these once. */ |
| rcu_for_each_node_breadth_first(rsp, rnp) |
| if (rnp->parent) |
| rnp->parent->qsmaskinit |= rnp->grpmask; |
| } |
| |
| /* |
| * Compute the rcu_node tree geometry from kernel parameters. This cannot |
| * replace the definitions in tree.h because those are needed to size |
| * the ->node array in the rcu_state structure. |
| */ |
| void rcu_init_geometry(void) |
| { |
| int i; |
| int rcu_capacity[RCU_NUM_LVLS]; |
| int nr_cpu_ids; |
| |
| if (!rcu_num_cores) |
| rcu_num_cores = num_cores; |
| nr_cpu_ids = rcu_num_cores; |
| /* If the compile-time values are accurate, just leave. */ |
| if (rcu_fanout_leaf == RCU_FANOUT_LEAF && |
| nr_cpu_ids == NR_CPUS) |
| return; |
| printk("RCU: Adjusting geometry for rcu_fanout_leaf=%d, nr_cpu_ids=%d\n", |
| rcu_fanout_leaf, nr_cpu_ids); |
| |
| /* |
| * The boot-time rcu_fanout_leaf parameter must be at least two |
| * and cannot exceed the number of bits in the rcu_node masks. |
| * Complain and fall back to the compile-time values if this |
| * limit is exceeded. |
| */ |
| if (rcu_fanout_leaf < 2 || |
| rcu_fanout_leaf > sizeof(unsigned long) * 8) { |
| rcu_fanout_leaf = RCU_FANOUT_LEAF; |
| warn_on(1); |
| return; |
| } |
| |
| /* |
| * Compute number of nodes that can be handled an rcu_node tree |
| * with the given number of levels. |
| */ |
| rcu_capacity[0] = rcu_fanout_leaf; |
| for (i = 1; i < RCU_NUM_LVLS; i++) |
| rcu_capacity[i] = rcu_capacity[i - 1] * RCU_FANOUT; |
| |
| /* |
| * The tree must be able to accommodate the configured number of CPUs. |
| * If this limit is exceeded, fall back to the compile-time values. |
| */ |
| if (nr_cpu_ids > rcu_capacity[RCU_NUM_LVLS - 1]) { |
| rcu_fanout_leaf = RCU_FANOUT_LEAF; |
| warn_on(1); |
| return; |
| } |
| |
| /* Calculate the number of levels in the tree. */ |
| for (i = 0; nr_cpu_ids > rcu_capacity[i]; i++) { |
| } |
| rcu_num_lvls = i + 1; |
| |
| /* Calculate the number of rcu_nodes at each level of the tree. */ |
| for (i = 0; i < rcu_num_lvls; i++) { |
| int cap = rcu_capacity[(rcu_num_lvls - 1) - i]; |
| num_rcu_lvl[i] = DIV_ROUND_UP(nr_cpu_ids, cap); |
| } |
| |
| /* Calculate the total number of rcu_node structures. */ |
| rcu_num_nodes = 0; |
| for (i = 0; i < rcu_num_lvls; i++) |
| rcu_num_nodes += num_rcu_lvl[i]; |
| } |
| |
| /* |
| * Dump out the structure of the rcu_node combining tree associated |
| * with the rcu_state structure referenced by rsp. |
| */ |
| void rcu_dump_rcu_node_tree(struct rcu_state *rsp) |
| { |
| int level = 0; |
| struct rcu_node *rnp; |
| |
| print_lock(); |
| printk("rcu_node tree layout dump\n"); |
| printk(" "); |
| rcu_for_each_node_breadth_first(rsp, rnp) { |
| if (rnp->level != level) { |
| printk("\n"); |
| printk(" "); |
| level = rnp->level; |
| } |
| printk("%d:%d ^%d ", rnp->grplo, rnp->grphi, rnp->grpnum); |
| } |
| printk("\n"); |
| print_unlock(); |
| } |