Home | History | Annotate | Download | only in sun4vpi
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * Walk the LDOM PRI component nodes and create appropriate topology nodes
     29  */
     30 
     31 #include <sys/types.h>
     32 #include <sys/time.h>
     33 #include <stddef.h>
     34 #include <inttypes.h>
     35 #include <strings.h>
     36 #include <string.h>
     37 #include <libuutil.h>
     38 #include <libnvpair.h>
     39 #include <sys/mdesc.h>
     40 #include <fm/topo_mod.h>
     41 #include <fm/topo_hc.h>
     42 #include "pi_impl.h"
     43 
     44 #define	PI_STR_MIN	"instance_min"
     45 #define	PI_STR_MAX	"instance_max"
     46 
     47 /*
     48  * Allow for custom topo node creation routines based on topo-hc-name.
     49  */
     50 struct pi_enum_functions_s {
     51 	pi_enum_fn_t	*func;
     52 	char		*hc_name;	/* topo-hc-name */
     53 };
     54 typedef struct pi_enum_functions_s pi_enum_functions_t;
     55 
     56 struct pi_methods_s {
     57 	topo_method_t	*meths;
     58 	char		*hc_name;
     59 };
     60 typedef struct pi_methods_s pi_methods_t;
     61 
     62 extern topo_method_t pi_cpu_methods[], pi_mem_methods[];
     63 
     64 /*
     65  * List of custom enumerators for PRI nodes that require them.  The most
     66  * common nodes are listed first.
     67  */
     68 static pi_enum_functions_t pi_enum_fns_builtin[] = {
     69 	{pi_enum_cpu,		STRAND},
     70 	{pi_enum_cpu,		CPU},
     71 	{pi_enum_mem,		DIMM},
     72 	{pi_enum_cpu,		CORE},
     73 	{pi_enum_cpu,		CHIP},
     74 	{pi_enum_hostbridge,	HOSTBRIDGE},
     75 	{pi_enum_pciexrc,	PCIEX_ROOT},
     76 	{pi_enum_niu,		NIU},
     77 	{NULL, NULL}
     78 };
     79 static nvlist_t *pi_enum_fns;
     80 
     81 /* List of methods that will be registered in the nodes. */
     82 static pi_methods_t pi_meths_builtin[] = {
     83 	{pi_cpu_methods,	CHIP},
     84 	{pi_cpu_methods,	CORE},
     85 	{pi_cpu_methods,	STRAND},
     86 	{pi_cpu_methods,	CPU},
     87 	{pi_mem_methods,	DIMM},
     88 	{NULL, NULL}
     89 };
     90 nvlist_t *pi_meths;
     91 
     92 /*
     93  * In order to create a topology node from a PRI MDE node we need to know the
     94  * topology parent node that should be used.  So, after creating a topology
     95  * node from an MDE node, we associate children of the MDE node with the new
     96  * topology node.  Thus, when the children are visited we can know the
     97  * appropriate parent topology node to use.
     98  *
     99  * We take advantage of the libtopo threading model here, which guarantees a
    100  * single thread and a single invocation at a time for an enumerator.  This
    101  * makes using a file-global safe.
    102  */
    103 static uu_list_pool_t	*walker_pool = NULL;
    104 static uu_list_t	*walker_list = NULL;
    105 
    106 struct pi_walkernode_s {
    107 	uu_list_node_t	walker_node;
    108 	tnode_t		*t_parent;	/* Parent topology node */
    109 	mde_cookie_t	mde_node;	/* Child MDE node index */
    110 };
    111 typedef struct pi_walkernode_s pi_walkernode_t;
    112 
    113 
    114 /* The routine called for each node in the PRI while walking the graph */
    115 static int pi_walker_node(md_t *, mde_cookie_t, mde_cookie_t, void *);
    116 
    117 /*
    118  * Create a sub-range for a given PRI node and associate the given topology
    119  * node with the children.
    120  */
    121 static int  pi_walker_node_range(topo_mod_t *, md_t *, tnode_t *, mde_cookie_t);
    122 static int  pi_walker_node_create(topo_mod_t *, md_t *, mde_cookie_t, tnode_t *,
    123     topo_instance_t, tnode_t **);
    124 
    125 /* Routines to handle the list of topology parents and mde_nodes */
    126 static int  pi_walkerlist_compare(const void *, const void *, void *);
    127 static int  pi_walkerlist_create(topo_mod_t *);
    128 static void pi_walkerlist_destroy(topo_mod_t *);
    129 static int  pi_walkerlist_add(topo_mod_t *, tnode_t *, mde_cookie_t);
    130 static int  pi_walkerlist_addtype(topo_mod_t *, nvlist_t *, char *, uint32_t,
    131     uint32_t);
    132 static int  pi_walkerlist_find(topo_mod_t *, mde_cookie_t, tnode_t **);
    133 
    134 
    135 int
    136 pi_walker_init(topo_mod_t *mod)
    137 {
    138 	int			result;
    139 	pi_enum_functions_t	*fp;
    140 	pi_methods_t		*mp;
    141 
    142 	result = topo_mod_nvalloc(mod, &pi_enum_fns, NV_UNIQUE_NAME);
    143 	result |= topo_mod_nvalloc(mod, &pi_meths, NV_UNIQUE_NAME);
    144 	if (result != 0) {
    145 		topo_mod_dprintf(mod, "pi_walker_init failed\n");
    146 		nvlist_free(pi_enum_fns);
    147 		nvlist_free(pi_meths);
    148 		return (-1);
    149 	}
    150 
    151 	/* Add the builtin functions to the list */
    152 	fp = pi_enum_fns_builtin;
    153 	while (fp != NULL && fp->hc_name != NULL) {
    154 		uint64_t	faddr;
    155 
    156 		faddr = (uint64_t)(uintptr_t)*(fp->func);
    157 		result |= nvlist_add_uint64(pi_enum_fns, fp->hc_name, faddr);
    158 		fp++;
    159 	}
    160 
    161 	/* Add the builtin methods to the list */
    162 	mp = pi_meths_builtin;
    163 	while (mp != NULL && mp->hc_name != NULL) {
    164 		uint64_t	maddr;
    165 
    166 		maddr = (uint64_t)(uintptr_t)mp->meths;
    167 		result |= nvlist_add_uint64(pi_meths, mp->hc_name, maddr);
    168 		mp++;
    169 	}
    170 
    171 	if (result != 0) {
    172 		topo_mod_dprintf(mod, "pi_walker_init failed\n");
    173 		nvlist_free(pi_enum_fns);
    174 		nvlist_free(pi_meths);
    175 		return (-1);
    176 	}
    177 
    178 	return (0);
    179 }
    180 
    181 
    182 void
    183 pi_walker_fini(topo_mod_t *mod)
    184 {
    185 	topo_mod_dprintf(mod, "pi_walker_fini: enter\n");
    186 	nvlist_free(pi_enum_fns);
    187 	nvlist_free(pi_meths);
    188 }
    189 
    190 
    191 /*
    192  * Begin to walk the machine description array starting at the given PRI node.
    193  */
    194 int
    195 pi_walker(pi_enum_t *pip, tnode_t *t_parent, const char *hc_name,
    196     mde_cookie_t mde_node, mde_str_cookie_t component_cookie,
    197     mde_str_cookie_t arc_cookie)
    198 {
    199 	int		result;
    200 	hrtime_t	starttime;
    201 	hrtime_t	endtime;
    202 	topo_mod_t	*mod;
    203 
    204 	if (pip == NULL) {
    205 		return (-1);
    206 	}
    207 	mod = pip->mod;
    208 
    209 	starttime = gethrtime();
    210 	topo_mod_dprintf(mod, "walker starting at node_0x%llx\n",
    211 	    mde_node);
    212 
    213 	/*
    214 	 * Create a list to store topology nodes and their associated machine
    215 	 * description index.  This allows the code to know the parent of a
    216 	 * node when creating topology entries.
    217 	 */
    218 	result = pi_walkerlist_create(mod);
    219 	if (result != 0) {
    220 		topo_mod_dprintf(mod, "walker could not create list\n");
    221 		return (result);
    222 	}
    223 
    224 	/* Create a walker node for the parent of the start node */
    225 	result = pi_walkerlist_add(mod, t_parent, mde_node);
    226 	if (result != 0) {
    227 		pi_walkerlist_destroy(mod);
    228 		topo_mod_dprintf(mod, "walker could not add to list\n");
    229 		(void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
    230 		return (result);
    231 	}
    232 
    233 	/*
    234 	 * This is a top-level node.  Make sure we call the top level
    235 	 * enumerator if there is not already a custom enumerator registered.
    236 	 */
    237 	if (! nvlist_exists(pi_enum_fns, hc_name)) {
    238 		uint64_t	faddr;
    239 
    240 		/*
    241 		 * There is no enumerator function registered for this
    242 		 * hc name.  Automatically register the top level node
    243 		 * enumerator function.
    244 		 */
    245 		faddr = (uint64_t)(uintptr_t)pi_enum_top;
    246 		result = nvlist_add_uint64(pi_enum_fns, hc_name, faddr);
    247 		if (result != 0) {
    248 			pi_walkerlist_destroy(mod);
    249 			topo_mod_dprintf(mod,
    250 			    "walker could not register enumerator for type "
    251 			    "%s\n", hc_name);
    252 			(void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
    253 			return (-1);
    254 		}
    255 		topo_mod_dprintf(mod,
    256 		    "walker registered pi_enum_top enumerator for type %s\n",
    257 		    hc_name);
    258 	}
    259 
    260 	/* Walk the machine description list starting at the given node */
    261 	result = md_walk_dag(pip->mdp, mde_node, component_cookie, arc_cookie,
    262 	    pi_walker_node, (void *)pip);
    263 	switch (result) {
    264 		case 0:
    265 			/* Successful completion */
    266 			/* DO NOTHING */
    267 		break;
    268 
    269 		case MDE_WALK_ERROR:
    270 			/*
    271 			 * Store that we have a partial enumeration and return
    272 			 * that we have encountered an error.
    273 			 */
    274 			(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
    275 			result = -1;
    276 		break;
    277 
    278 		default:
    279 			/*
    280 			 * This should not happen.  We want to always produce
    281 			 * as complete a topology as possible, even in the face
    282 			 * of errors, however, so set an error and continue.
    283 			 */
    284 			topo_mod_dprintf(mod,
    285 			    "walker encountered invalid result: %d. "
    286 			    "Continuing\n", result);
    287 			(void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
    288 			result = 0;
    289 		break;
    290 	}
    291 
    292 	/* Destroy the walker list, which is no longer necessary */
    293 	pi_walkerlist_destroy(mod);
    294 
    295 	topo_mod_dprintf(mod, "walker done with node_0x%llx\n", mde_node);
    296 
    297 	endtime = gethrtime();
    298 	topo_mod_dprintf(mod, "walker scan time %lld ms\n",
    299 	    (endtime-starttime)/MICROSEC);
    300 
    301 	return (result);
    302 }
    303 
    304 
    305 /*
    306  * Visited once for each node in the machine description.  Creates a topo
    307  * node for the machine description node and associates it with it's parent,
    308  * by calling an appropriate creation routine for the node type.
    309  *
    310  * Output:
    311  * 	This routine returns MDE_WALK_NEXT, MDE_WALK_DONE or MDE_WALK_ERROR
    312  * only.
    313  */
    314 static int
    315 pi_walker_node(md_t *mdp, mde_cookie_t parent_mde_node, mde_cookie_t mde_node,
    316     void *private)
    317 {
    318 	int		result;
    319 	pi_enum_t	*pip	= (pi_enum_t *)private;
    320 	uint64_t	skip;		/* flag in md to skip this node	*/
    321 	tnode_t		*t_parent;	/* topo parent to this md node	*/
    322 	tnode_t		*t_node;	/* topo parent to this md node	*/
    323 	topo_instance_t	inst;
    324 
    325 	topo_mod_t	*mod;
    326 
    327 	/* Make sure we have our private data */
    328 	if (pip == NULL) {
    329 		return (MDE_WALK_ERROR);
    330 	}
    331 	mod = pip->mod;
    332 
    333 	topo_mod_dprintf(pip->mod,
    334 	    "walker processing node_0x%llx parent node 0x%llx\n",
    335 	    (uint64_t)mde_node, (uint64_t)parent_mde_node);
    336 
    337 	/* Should we skip this node ? */
    338 	skip = pi_skip_node(mod, pip->mdp, mde_node);
    339 	if (skip) {
    340 		/* Skip this node and continue to the next node */
    341 		topo_mod_dprintf(mod, "walker skipping node_0x%llx\n",
    342 		    (uint64_t)mde_node);
    343 		return (MDE_WALK_NEXT);
    344 	}
    345 
    346 	result = pi_get_instance(mod, mdp, mde_node, &inst);
    347 	if (result != 0) {
    348 		/*
    349 		 * No ID available to place this mde node in the topology so
    350 		 * we cannot create a topology node.
    351 		 */
    352 		topo_mod_dprintf(mod, "walker skipping node_0x%llx: "
    353 		    "no instance\n", (uint64_t)mde_node);
    354 		(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
    355 		return (MDE_WALK_NEXT);
    356 	}
    357 
    358 	/*
    359 	 * Find the parent topo node for this machine description node.
    360 	 *
    361 	 * If found, the element will also be removed from the list and the
    362 	 * memory used to keep track of it released.  We will only visit an
    363 	 * MDE node once and so the memory is no longer needed.
    364 	 */
    365 	t_parent = NULL;
    366 	result = pi_walkerlist_find(mod, mde_node, &t_parent);
    367 	if (result != 0 || t_parent == NULL) {
    368 		/*
    369 		 * No parent was found or a NULL parent encountered.  We
    370 		 * cannot create a new topology node without a parent (
    371 		 * even for top level nodes).  We associate children of
    372 		 * this MDE node with a NULL parent to silently skip the
    373 		 * remainder of this MDE branch.
    374 		 */
    375 		topo_mod_dprintf(mod, "no topo parent found for node_0x%llx\n",
    376 		    mde_node);
    377 		result = pi_walker_node_range(mod, mdp, NULL, mde_node);
    378 		(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
    379 
    380 		return (result);
    381 	}
    382 
    383 	/*
    384 	 * We have the mde node instance and parent information.
    385 	 * Attempt to create a topology node for this mde node.
    386 	 */
    387 	t_node = NULL;
    388 	result = pi_walker_node_create(mod, mdp, mde_node, t_parent, inst,
    389 	    &t_node);
    390 	if (result != MDE_WALK_NEXT || t_node == NULL) {
    391 		/*
    392 		 * We have failed to create a new topology node based on
    393 		 * the current MDE node.  We set partial enumeration and
    394 		 * return without associating the children of this MDE
    395 		 * node with a topology parent.  This will propgate the
    396 		 * creation error down this MDE branch.
    397 		 */
    398 		(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
    399 		return (result);
    400 	}
    401 
    402 	/*
    403 	 * Associate the new topology node with any children of this mde node.
    404 	 */
    405 	result = pi_walker_node_range(mod, mdp, t_node, mde_node);
    406 
    407 	topo_mod_dprintf(mod, "walker completed node_0x%llx result = %d\n",
    408 	    (uint64_t)mde_node, result);
    409 
    410 	return (result);
    411 }
    412 
    413 
    414 static int
    415 pi_walker_node_create(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
    416     tnode_t *t_parent, topo_instance_t inst, tnode_t **t_node)
    417 {
    418 	int		result;
    419 	char		*hc_name;
    420 	uint64_t	faddr;
    421 	pi_enum_fn_t	*func;
    422 
    423 	if (t_parent == NULL) {
    424 		/*
    425 		 * A parent topology node is required even for top-level
    426 		 * nodes.
    427 		 */
    428 		return (MDE_WALK_NEXT);
    429 	}
    430 
    431 	/*
    432 	 * Find the topo-hc-name for this node which is used to find
    433 	 * the specific creation function
    434 	 */
    435 	hc_name = pi_get_topo_hc_name(mod, mdp, mde_node);
    436 	if (hc_name == NULL) {
    437 		/* Cannot get the hc-name */
    438 		topo_mod_dprintf(mod,
    439 		    "failed to find hc-name for node_0x%llx\n", mde_node);
    440 		return (MDE_WALK_NEXT);
    441 	}
    442 
    443 	/* Determine the topology node creation routine to use */
    444 	func = pi_enum_generic;
    445 	faddr = 0;
    446 	result = nvlist_lookup_uint64(pi_enum_fns, hc_name, &faddr);
    447 	if (result == 0) {
    448 		/*
    449 		 * A function is registered for this node. Convert the
    450 		 * address to a pointer to function
    451 		 */
    452 		func = (pi_enum_fn_t *)(uintptr_t)faddr;
    453 	}
    454 
    455 	/*
    456 	 * Create a topology node for this mde node by calling the identified
    457 	 * enumeration function
    458 	 */
    459 	*t_node = NULL;
    460 	result = (func)(mod, mdp, mde_node, inst, t_parent, hc_name, t_node);
    461 	if (result != 0) {
    462 		topo_mod_dprintf(mod,
    463 		    "failed to create topo entry for node_0x%llx type %s\n",
    464 		    (uint64_t)mde_node, hc_name);
    465 	}
    466 
    467 	topo_mod_strfree(mod, hc_name);
    468 
    469 	return (MDE_WALK_NEXT);
    470 }
    471 
    472 
    473 /*
    474  * Scan the children of a given MDE node and find all the sets of topo-hc-name
    475  * types and their instance ranges.  From this information we create topology
    476  * node ranges on the given parent so that when the children are visited and a
    477  * topology node is created, the range exists and the creation will succeed.
    478  */
    479 static int
    480 pi_walker_node_range(topo_mod_t *mod, md_t *mdp, tnode_t *t_parent,
    481     mde_cookie_t mde_node)
    482 {
    483 	int		result;
    484 	int		rc;
    485 	int		num_arcs;
    486 	nvlist_t	*typelist;
    487 	nvpair_t	*nvp;
    488 	mde_cookie_t	*arcp;
    489 	size_t		arcsize;
    490 	int		arcidx;
    491 	char		*hc_name;
    492 	nvlist_t	*hc_range;
    493 	topo_instance_t	inst;
    494 	uint32_t	min;
    495 	uint32_t	max;
    496 
    497 	if (t_parent == NULL) {
    498 		topo_mod_dprintf(mod,
    499 		"walker failed to create node range with a NULL parent\n");
    500 		return (MDE_WALK_NEXT);
    501 	}
    502 
    503 	/* Determine how many children the given node has */
    504 	num_arcs = md_get_prop_arcs(mdp, mde_node, MD_STR_FWD, NULL, 0);
    505 	if (num_arcs == 0) {
    506 		/* This node has no children */
    507 		return (MDE_WALK_NEXT);
    508 	}
    509 	topo_mod_dprintf(mod, "node_0x%llx has %d children\n",
    510 	    (uint64_t)mde_node, num_arcs);
    511 
    512 	/* Get the indexes for all the child nodes and put them in an array */
    513 	arcsize	= sizeof (mde_cookie_t) * num_arcs;
    514 	arcp = topo_mod_zalloc(mod, arcsize);
    515 	if (arcp == NULL) {
    516 		topo_mod_dprintf(mod, "out of memory\n");
    517 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
    518 		return (MDE_WALK_ERROR);
    519 	}
    520 	num_arcs = md_get_prop_arcs(mdp, mde_node, MD_STR_FWD, arcp, arcsize);
    521 
    522 	/*
    523 	 * The children of the given node may have multiple types.
    524 	 * Potentially, each child may have a different type and we need to
    525 	 * create a topo node range for each one.
    526 	 *
    527 	 * We loop through the children and collect the type information for
    528 	 * each one and associate the child with the given parent topo node.
    529 	 */
    530 	result = topo_mod_nvalloc(mod, &typelist, NV_UNIQUE_NAME);
    531 	if (result != 0) {
    532 		topo_mod_free(mod, arcp, arcsize);
    533 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
    534 		return (MDE_WALK_ERROR);
    535 	}
    536 
    537 	arcidx = 0;
    538 	for (arcidx = 0; arcidx < num_arcs; arcidx++) {
    539 		/* Should this node be skipped? */
    540 		if (pi_skip_node(mod, mdp, arcp[arcidx])) {
    541 			/* Skip this node */
    542 			topo_mod_dprintf(mod, "skipping node_0x%llx\n",
    543 			    (uint64_t)arcp[arcidx]);
    544 			continue;
    545 		}
    546 
    547 		/* Get the type of this node */
    548 		hc_name = pi_get_topo_hc_name(mod, mdp, arcp[arcidx]);
    549 		rc = pi_get_instance(mod, mdp, arcp[arcidx], &inst);
    550 		if (rc == 0 && hc_name != NULL) {
    551 			/* Increment the count of nodes with this type */
    552 			hc_range = NULL;
    553 			rc = nvlist_lookup_nvlist(typelist, hc_name, &hc_range);
    554 			if (rc != 0) {
    555 				/*
    556 				 * We have not visited this type yet.  Create
    557 				 * a new range based on this nodes instance
    558 				 * information.
    559 				 */
    560 				result = pi_walkerlist_addtype(mod, typelist,
    561 				    hc_name, (uint32_t)inst, (uint32_t)inst);
    562 				if (result != 0) {
    563 					/*
    564 					 * This error can only if there was a
    565 					 * memory failure of some kind.  Stop
    566 					 * the walk or it will just get worse.
    567 					 */
    568 					nvlist_free(typelist);
    569 					topo_mod_strfree(mod, hc_name);
    570 					topo_mod_free(mod, arcp, arcsize);
    571 					(void) topo_mod_seterrno(mod,
    572 					    EMOD_PARTIAL_ENUM);
    573 					return (MDE_WALK_ERROR);
    574 				}
    575 
    576 				/*
    577 				 * We know the list exists now or the above
    578 				 * would have failed.  Just look it up.
    579 				 */
    580 				(void) nvlist_lookup_nvlist(typelist, hc_name,
    581 				    &hc_range);
    582 			}
    583 
    584 			/* Re-calculate the range minimums and maximums */
    585 			(void) nvlist_lookup_uint32(hc_range, PI_STR_MIN, &min);
    586 			(void) nvlist_lookup_uint32(hc_range, PI_STR_MAX, &max);
    587 			min = MIN(min, (uint32_t)inst);
    588 			max = MAX(max, (uint32_t)inst);
    589 			(void) nvlist_add_uint32(hc_range, PI_STR_MIN, min);
    590 			(void) nvlist_add_uint32(hc_range, PI_STR_MAX, max);
    591 
    592 		} else {
    593 			topo_mod_dprintf(mod, "node_0x%llx type %s has no id. "
    594 			    "Excluding from range", (uint64_t)arcp[arcidx],
    595 			    hc_name);
    596 		}
    597 		topo_mod_strfree(mod, hc_name);
    598 
    599 		/*
    600 		 * Associate this node with the given topo parent even if it
    601 		 * has no instance.  We do this so that later an error with
    602 		 * the PRI node will be reported instead of an internal
    603 		 * error about not being able to find the parent of a node
    604 		 */
    605 		rc = pi_walkerlist_add(mod, t_parent, arcp[arcidx]);
    606 		if (rc != 0) {
    607 			topo_mod_dprintf(mod,
    608 			    "could not add node_0x%llx to walker list\n",
    609 			    (uint64_t)arcp[arcidx]);
    610 		}
    611 	}
    612 
    613 	/*
    614 	 * We have associated all the child nodes with the given topo parent
    615 	 * in the walker list.  Now we need to create topo ranges for each
    616 	 * set of child types under the parent.
    617 	 */
    618 	nvp = nvlist_next_nvpair(typelist, NULL);
    619 	while (nvp != NULL) {
    620 		/* Get the type name and count from the list element */
    621 		hc_name = nvpair_name(nvp);
    622 		(void) nvpair_value_nvlist(nvp, &hc_range);
    623 		(void) nvlist_lookup_uint32(hc_range, PI_STR_MIN, &min);
    624 		(void) nvlist_lookup_uint32(hc_range, PI_STR_MAX, &max);
    625 
    626 		/*
    627 		 * We have the number of children with this type.
    628 		 * Create an appropriate range.
    629 		 */
    630 		topo_mod_dprintf(mod,
    631 		    "creating instance range %d to %d of type %s\n",
    632 		    min, max, hc_name);
    633 		rc = topo_node_range_create(mod, t_parent, hc_name,
    634 		    (topo_instance_t)min, (topo_instance_t)max);
    635 		if (rc != 0) {
    636 			topo_mod_dprintf(mod,
    637 			    "failed to created node range %d to %d for "
    638 			    "nodes of type %s\n", min, max, hc_name);
    639 		}
    640 
    641 		/* Check the next node */
    642 		nvp = nvlist_next_nvpair(typelist, nvp);
    643 	}
    644 	topo_mod_free(mod, arcp, arcsize);
    645 	nvlist_free(typelist);
    646 
    647 	return (MDE_WALK_NEXT);
    648 }
    649 
    650 
    651 static int
    652 pi_walkerlist_addtype(topo_mod_t *mod, nvlist_t *typelist, char *hc_name,
    653     uint32_t min, uint32_t max)
    654 {
    655 	int		result;
    656 	nvlist_t	*nvl;
    657 
    658 	result = topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME);
    659 	if (result != 0) {
    660 		return (result);
    661 	}
    662 
    663 	/* Create min and max elements in this list */
    664 	if (nvlist_add_uint32(nvl, PI_STR_MIN, min) != 0 ||
    665 	    nvlist_add_uint32(nvl, PI_STR_MAX, max) != 0 ||
    666 	    nvlist_add_nvlist(typelist, hc_name, nvl) != 0) {
    667 		nvlist_free(nvl);
    668 		return (-1);
    669 	}
    670 	nvlist_free(nvl);
    671 
    672 	return (0);
    673 }
    674 
    675 
    676 /* ARGSUSED */
    677 static int
    678 pi_walkerlist_compare(const void *left, const void *right, void *private)
    679 {
    680 	pi_walkernode_t	*lp = (pi_walkernode_t *)left;
    681 	pi_walkernode_t	*rp = (pi_walkernode_t *)right;
    682 
    683 	if (lp->mde_node > rp->mde_node) {
    684 		return (1);
    685 	}
    686 	if (lp->mde_node < rp->mde_node) {
    687 		return (-1);
    688 	}
    689 	return (0);
    690 }
    691 
    692 
    693 static int
    694 pi_walkerlist_create(topo_mod_t *mod)
    695 {
    696 	/* Initialize the uutil list structure */
    697 	walker_pool = uu_list_pool_create("pi_walker_pool",
    698 	    sizeof (pi_walkernode_t), offsetof(pi_walkernode_t, walker_node),
    699 	    pi_walkerlist_compare, NULL);
    700 	if (walker_pool == NULL) {
    701 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
    702 		return (-1);
    703 	}
    704 	walker_list = uu_list_create(walker_pool, NULL, 0);
    705 	if (walker_list == NULL) {
    706 		uu_list_pool_destroy(walker_pool);
    707 		walker_pool = NULL;
    708 		return (-1);
    709 	}
    710 
    711 	return (0);
    712 }
    713 
    714 
    715 static void
    716 pi_walkerlist_destroy(topo_mod_t *mod)
    717 {
    718 	void		*wvp;
    719 	pi_walkernode_t	*wp;
    720 
    721 	/* Destroy our list of items */
    722 	while ((wvp = uu_list_first(walker_list)) != NULL) {
    723 		/*
    724 		 * First, we empty the list of elements and free each one.
    725 		 * We do not free the data elements as they are libtopo nodes
    726 		 * and will be freed by libtopo
    727 		 */
    728 		wp = (pi_walkernode_t *)wvp;
    729 		uu_list_remove(walker_list, wvp);
    730 		uu_list_node_fini(wp, &(wp->walker_node), walker_pool);
    731 
    732 		topo_mod_free(mod, wvp, sizeof (pi_walkernode_t));
    733 	}
    734 	uu_list_destroy(walker_list);
    735 	uu_list_pool_destroy(walker_pool);
    736 	walker_list = NULL;
    737 	walker_pool = NULL;
    738 }
    739 
    740 
    741 static int
    742 pi_walkerlist_add(topo_mod_t *mod, tnode_t *t_parent, mde_cookie_t mde_node)
    743 {
    744 	uu_list_index_t	idx;
    745 	pi_walkernode_t	*wnp;
    746 
    747 	wnp = topo_mod_zalloc(mod, sizeof (pi_walkernode_t));
    748 	if (wnp == NULL) {
    749 		topo_mod_dprintf(mod, "failed to add node_0x%llx parent %p\n",
    750 		    (uint64_t)mde_node, t_parent);
    751 		return (-1);
    752 	}
    753 	uu_list_node_init(wnp, &(wnp->walker_node), walker_pool);
    754 
    755 	wnp->t_parent	= t_parent;
    756 	wnp->mde_node	= mde_node;
    757 
    758 	(void) uu_list_find(walker_list, wnp, NULL, &idx);
    759 	uu_list_insert(walker_list, wnp, idx);
    760 
    761 	return (0);
    762 }
    763 
    764 
    765 /*
    766  * Find the parent topo node for this machine description node.
    767  *
    768  * Nodes are removed from the list as they are found.  They are only
    769  * visited once and this eliminates the need for a separate routine
    770  * that walks the list to free elements later.
    771  */
    772 static int
    773 pi_walkerlist_find(topo_mod_t *mod, mde_cookie_t mde_node, tnode_t **tpp)
    774 {
    775 	pi_walkernode_t	*result;
    776 
    777 	uu_list_index_t	idx;
    778 	pi_walkernode_t	search_criteria;
    779 
    780 	search_criteria.mde_node = mde_node;
    781 	search_criteria.t_parent = NULL;
    782 
    783 	*tpp = NULL;
    784 	result = uu_list_find(walker_list, &search_criteria, NULL, &idx);
    785 	if (result == NULL) {
    786 		return (-1);
    787 	}
    788 	*tpp = result->t_parent;
    789 
    790 	/* Remove this element from the list */
    791 	uu_list_remove(walker_list, result);
    792 	uu_list_node_fini(result, &(result->walker_node), walker_pool);
    793 	topo_mod_free(mod, result, sizeof (pi_walkernode_t));
    794 
    795 	return (0);
    796 }
    797