| /* | 
 |  * 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(); | 
 | } |