17205Ssd77468 /*
27205Ssd77468 * CDDL HEADER START
37205Ssd77468 *
47205Ssd77468 * The contents of this file are subject to the terms of the
57205Ssd77468 * Common Development and Distribution License (the "License").
67205Ssd77468 * You may not use this file except in compliance with the License.
77205Ssd77468 *
87205Ssd77468 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97205Ssd77468 * or http://www.opensolaris.org/os/licensing.
107205Ssd77468 * See the License for the specific language governing permissions
117205Ssd77468 * and limitations under the License.
127205Ssd77468 *
137205Ssd77468 * When distributing Covered Code, include this CDDL HEADER in each
147205Ssd77468 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157205Ssd77468 * If applicable, add the following below this CDDL HEADER, with the
167205Ssd77468 * fields enclosed by brackets "[]" replaced with your own identifying
177205Ssd77468 * information: Portions Copyright [yyyy] [name of copyright owner]
187205Ssd77468 *
197205Ssd77468 * CDDL HEADER END
207205Ssd77468 */
217205Ssd77468
227205Ssd77468 /*
23*12956STom.Pothier@Sun.COM * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
247205Ssd77468 */
257205Ssd77468
267205Ssd77468 /*
277205Ssd77468 * Walk the LDOM PRI component nodes and create appropriate topology nodes
287205Ssd77468 */
297205Ssd77468
307205Ssd77468 #include <sys/types.h>
317205Ssd77468 #include <sys/time.h>
327205Ssd77468 #include <stddef.h>
337205Ssd77468 #include <inttypes.h>
347205Ssd77468 #include <strings.h>
357205Ssd77468 #include <string.h>
367205Ssd77468 #include <libuutil.h>
377205Ssd77468 #include <libnvpair.h>
387205Ssd77468 #include <sys/mdesc.h>
397205Ssd77468 #include <fm/topo_mod.h>
407205Ssd77468 #include <fm/topo_hc.h>
417205Ssd77468 #include "pi_impl.h"
427205Ssd77468
437205Ssd77468 #define PI_STR_MIN "instance_min"
447205Ssd77468 #define PI_STR_MAX "instance_max"
457205Ssd77468
467205Ssd77468 /*
477205Ssd77468 * Allow for custom topo node creation routines based on topo-hc-name.
487205Ssd77468 */
497205Ssd77468 struct pi_enum_functions_s {
507205Ssd77468 pi_enum_fn_t *func;
517205Ssd77468 char *hc_name; /* topo-hc-name */
527205Ssd77468 };
537205Ssd77468 typedef struct pi_enum_functions_s pi_enum_functions_t;
547205Ssd77468
558221SSean.Ye@Sun.COM struct pi_methods_s {
568221SSean.Ye@Sun.COM topo_method_t *meths;
578221SSean.Ye@Sun.COM char *hc_name;
588221SSean.Ye@Sun.COM };
598221SSean.Ye@Sun.COM typedef struct pi_methods_s pi_methods_t;
608221SSean.Ye@Sun.COM
618221SSean.Ye@Sun.COM extern topo_method_t pi_cpu_methods[], pi_mem_methods[];
628221SSean.Ye@Sun.COM
637205Ssd77468 /*
647205Ssd77468 * List of custom enumerators for PRI nodes that require them. The most
657205Ssd77468 * common nodes are listed first.
667205Ssd77468 */
677205Ssd77468 static pi_enum_functions_t pi_enum_fns_builtin[] = {
687358SScott.Davenport@Sun.COM {pi_enum_cpu, STRAND},
697205Ssd77468 {pi_enum_cpu, CPU},
708221SSean.Ye@Sun.COM {pi_enum_mem, DIMM},
718221SSean.Ye@Sun.COM {pi_enum_cpu, CORE},
728221SSean.Ye@Sun.COM {pi_enum_cpu, CHIP},
739225STom.Pothier@Sun.COM {pi_enum_hostbridge, HOSTBRIDGE},
747205Ssd77468 {pi_enum_pciexrc, PCIEX_ROOT},
757205Ssd77468 {pi_enum_niu, NIU},
76*12956STom.Pothier@Sun.COM {pi_enum_bay, BAY},
777205Ssd77468 {NULL, NULL}
787205Ssd77468 };
797205Ssd77468 static nvlist_t *pi_enum_fns;
807205Ssd77468
818221SSean.Ye@Sun.COM /* List of methods that will be registered in the nodes. */
828221SSean.Ye@Sun.COM static pi_methods_t pi_meths_builtin[] = {
838221SSean.Ye@Sun.COM {pi_cpu_methods, CHIP},
848221SSean.Ye@Sun.COM {pi_cpu_methods, CORE},
858221SSean.Ye@Sun.COM {pi_cpu_methods, STRAND},
868221SSean.Ye@Sun.COM {pi_cpu_methods, CPU},
878221SSean.Ye@Sun.COM {pi_mem_methods, DIMM},
888221SSean.Ye@Sun.COM {NULL, NULL}
898221SSean.Ye@Sun.COM };
908221SSean.Ye@Sun.COM nvlist_t *pi_meths;
917205Ssd77468
927205Ssd77468 /*
937205Ssd77468 * In order to create a topology node from a PRI MDE node we need to know the
947205Ssd77468 * topology parent node that should be used. So, after creating a topology
957205Ssd77468 * node from an MDE node, we associate children of the MDE node with the new
967205Ssd77468 * topology node. Thus, when the children are visited we can know the
977205Ssd77468 * appropriate parent topology node to use.
987205Ssd77468 *
997205Ssd77468 * We take advantage of the libtopo threading model here, which guarantees a
1007205Ssd77468 * single thread and a single invocation at a time for an enumerator. This
1017205Ssd77468 * makes using a file-global safe.
1027205Ssd77468 */
10310665STom.Pothier@Sun.COM static uu_list_pool_t *walker_pool = NULL;
10410665STom.Pothier@Sun.COM static uu_list_t *walker_list = NULL;
1057205Ssd77468
1067205Ssd77468 struct pi_walkernode_s {
1077205Ssd77468 uu_list_node_t walker_node;
1087205Ssd77468 tnode_t *t_parent; /* Parent topology node */
1097205Ssd77468 mde_cookie_t mde_node; /* Child MDE node index */
1107205Ssd77468 };
1117205Ssd77468 typedef struct pi_walkernode_s pi_walkernode_t;
1127205Ssd77468
1137205Ssd77468
1147205Ssd77468 /* The routine called for each node in the PRI while walking the graph */
1157205Ssd77468 static int pi_walker_node(md_t *, mde_cookie_t, mde_cookie_t, void *);
1167205Ssd77468
1177205Ssd77468 /*
1187205Ssd77468 * Create a sub-range for a given PRI node and associate the given topology
1197205Ssd77468 * node with the children.
1207205Ssd77468 */
1217205Ssd77468 static int pi_walker_node_range(topo_mod_t *, md_t *, tnode_t *, mde_cookie_t);
1227205Ssd77468 static int pi_walker_node_create(topo_mod_t *, md_t *, mde_cookie_t, tnode_t *,
1237205Ssd77468 topo_instance_t, tnode_t **);
1247205Ssd77468
1257205Ssd77468 /* Routines to handle the list of topology parents and mde_nodes */
1267205Ssd77468 static int pi_walkerlist_compare(const void *, const void *, void *);
1277205Ssd77468 static int pi_walkerlist_create(topo_mod_t *);
1287205Ssd77468 static void pi_walkerlist_destroy(topo_mod_t *);
1297205Ssd77468 static int pi_walkerlist_add(topo_mod_t *, tnode_t *, mde_cookie_t);
1307205Ssd77468 static int pi_walkerlist_addtype(topo_mod_t *, nvlist_t *, char *, uint32_t,
1317205Ssd77468 uint32_t);
1327205Ssd77468 static int pi_walkerlist_find(topo_mod_t *, mde_cookie_t, tnode_t **);
1337205Ssd77468
1347205Ssd77468
1357205Ssd77468 int
pi_walker_init(topo_mod_t * mod)1367205Ssd77468 pi_walker_init(topo_mod_t *mod)
1377205Ssd77468 {
1387205Ssd77468 int result;
1397205Ssd77468 pi_enum_functions_t *fp;
1408221SSean.Ye@Sun.COM pi_methods_t *mp;
1417205Ssd77468
1427205Ssd77468 result = topo_mod_nvalloc(mod, &pi_enum_fns, NV_UNIQUE_NAME);
1438221SSean.Ye@Sun.COM result |= topo_mod_nvalloc(mod, &pi_meths, NV_UNIQUE_NAME);
1447205Ssd77468 if (result != 0) {
1457205Ssd77468 topo_mod_dprintf(mod, "pi_walker_init failed\n");
1468221SSean.Ye@Sun.COM nvlist_free(pi_enum_fns);
1478221SSean.Ye@Sun.COM nvlist_free(pi_meths);
1487205Ssd77468 return (-1);
1497205Ssd77468 }
1507205Ssd77468
1517205Ssd77468 /* Add the builtin functions to the list */
1527205Ssd77468 fp = pi_enum_fns_builtin;
1537205Ssd77468 while (fp != NULL && fp->hc_name != NULL) {
1547205Ssd77468 uint64_t faddr;
1557205Ssd77468
1567205Ssd77468 faddr = (uint64_t)(uintptr_t)*(fp->func);
1578221SSean.Ye@Sun.COM result |= nvlist_add_uint64(pi_enum_fns, fp->hc_name, faddr);
1587205Ssd77468 fp++;
1597205Ssd77468 }
1607205Ssd77468
1618221SSean.Ye@Sun.COM /* Add the builtin methods to the list */
1628221SSean.Ye@Sun.COM mp = pi_meths_builtin;
1638221SSean.Ye@Sun.COM while (mp != NULL && mp->hc_name != NULL) {
1648221SSean.Ye@Sun.COM uint64_t maddr;
1658221SSean.Ye@Sun.COM
1668221SSean.Ye@Sun.COM maddr = (uint64_t)(uintptr_t)mp->meths;
1678221SSean.Ye@Sun.COM result |= nvlist_add_uint64(pi_meths, mp->hc_name, maddr);
1688221SSean.Ye@Sun.COM mp++;
1698221SSean.Ye@Sun.COM }
1708221SSean.Ye@Sun.COM
1718221SSean.Ye@Sun.COM if (result != 0) {
1728221SSean.Ye@Sun.COM topo_mod_dprintf(mod, "pi_walker_init failed\n");
1738221SSean.Ye@Sun.COM nvlist_free(pi_enum_fns);
1748221SSean.Ye@Sun.COM nvlist_free(pi_meths);
1758221SSean.Ye@Sun.COM return (-1);
1768221SSean.Ye@Sun.COM }
1778221SSean.Ye@Sun.COM
1787205Ssd77468 return (0);
1797205Ssd77468 }
1807205Ssd77468
1817205Ssd77468
1827205Ssd77468 void
pi_walker_fini(topo_mod_t * mod)1837205Ssd77468 pi_walker_fini(topo_mod_t *mod)
1847205Ssd77468 {
1857205Ssd77468 topo_mod_dprintf(mod, "pi_walker_fini: enter\n");
1867205Ssd77468 nvlist_free(pi_enum_fns);
1878221SSean.Ye@Sun.COM nvlist_free(pi_meths);
1887205Ssd77468 }
1897205Ssd77468
1907205Ssd77468
1917205Ssd77468 /*
1927205Ssd77468 * Begin to walk the machine description array starting at the given PRI node.
1937205Ssd77468 */
1947205Ssd77468 int
pi_walker(pi_enum_t * pip,tnode_t * t_parent,const char * hc_name,mde_cookie_t mde_node,mde_str_cookie_t component_cookie,mde_str_cookie_t arc_cookie)1957205Ssd77468 pi_walker(pi_enum_t *pip, tnode_t *t_parent, const char *hc_name,
1967205Ssd77468 mde_cookie_t mde_node, mde_str_cookie_t component_cookie,
1977205Ssd77468 mde_str_cookie_t arc_cookie)
1987205Ssd77468 {
1997205Ssd77468 int result;
2007205Ssd77468 hrtime_t starttime;
2017205Ssd77468 hrtime_t endtime;
2027205Ssd77468 topo_mod_t *mod;
2037205Ssd77468
2047205Ssd77468 if (pip == NULL) {
2057205Ssd77468 return (-1);
2067205Ssd77468 }
2077205Ssd77468 mod = pip->mod;
2087205Ssd77468
2097205Ssd77468 starttime = gethrtime();
2107205Ssd77468 topo_mod_dprintf(mod, "walker starting at node_0x%llx\n",
2117205Ssd77468 mde_node);
2127205Ssd77468
2137205Ssd77468 /*
2147205Ssd77468 * Create a list to store topology nodes and their associated machine
2157205Ssd77468 * description index. This allows the code to know the parent of a
2167205Ssd77468 * node when creating topology entries.
2177205Ssd77468 */
2187205Ssd77468 result = pi_walkerlist_create(mod);
2197205Ssd77468 if (result != 0) {
2207205Ssd77468 topo_mod_dprintf(mod, "walker could not create list\n");
2217205Ssd77468 return (result);
2227205Ssd77468 }
2237205Ssd77468
2247205Ssd77468 /* Create a walker node for the parent of the start node */
2257205Ssd77468 result = pi_walkerlist_add(mod, t_parent, mde_node);
2267205Ssd77468 if (result != 0) {
2277205Ssd77468 pi_walkerlist_destroy(mod);
2287205Ssd77468 topo_mod_dprintf(mod, "walker could not add to list\n");
22911583SSurya.Prakki@Sun.COM (void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
2307205Ssd77468 return (result);
2317205Ssd77468 }
2327205Ssd77468
2337205Ssd77468 /*
2347205Ssd77468 * This is a top-level node. Make sure we call the top level
2357205Ssd77468 * enumerator if there is not already a custom enumerator registered.
2367205Ssd77468 */
2377205Ssd77468 if (! nvlist_exists(pi_enum_fns, hc_name)) {
2387205Ssd77468 uint64_t faddr;
2397205Ssd77468
2407205Ssd77468 /*
2417205Ssd77468 * There is no enumerator function registered for this
2427205Ssd77468 * hc name. Automatically register the top level node
2437205Ssd77468 * enumerator function.
2447205Ssd77468 */
2457205Ssd77468 faddr = (uint64_t)(uintptr_t)pi_enum_top;
2467205Ssd77468 result = nvlist_add_uint64(pi_enum_fns, hc_name, faddr);
2477205Ssd77468 if (result != 0) {
2487205Ssd77468 pi_walkerlist_destroy(mod);
2497205Ssd77468 topo_mod_dprintf(mod,
2507205Ssd77468 "walker could not register enumerator for type "
2517205Ssd77468 "%s\n", hc_name);
25211583SSurya.Prakki@Sun.COM (void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
2537205Ssd77468 return (-1);
2547205Ssd77468 }
2557205Ssd77468 topo_mod_dprintf(mod,
2567205Ssd77468 "walker registered pi_enum_top enumerator for type %s\n",
2577205Ssd77468 hc_name);
2587205Ssd77468 }
2597205Ssd77468
2607205Ssd77468 /* Walk the machine description list starting at the given node */
2617205Ssd77468 result = md_walk_dag(pip->mdp, mde_node, component_cookie, arc_cookie,
2627205Ssd77468 pi_walker_node, (void *)pip);
2637205Ssd77468 switch (result) {
2647205Ssd77468 case 0:
2657205Ssd77468 /* Successful completion */
2667205Ssd77468 /* DO NOTHING */
2677205Ssd77468 break;
2687205Ssd77468
2697205Ssd77468 case MDE_WALK_ERROR:
2707205Ssd77468 /*
2717205Ssd77468 * Store that we have a partial enumeration and return
2727205Ssd77468 * that we have encountered an error.
2737205Ssd77468 */
27411583SSurya.Prakki@Sun.COM (void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
2757205Ssd77468 result = -1;
2767205Ssd77468 break;
2777205Ssd77468
2787205Ssd77468 default:
2797205Ssd77468 /*
2807205Ssd77468 * This should not happen. We want to always produce
2817205Ssd77468 * as complete a topology as possible, even in the face
2827205Ssd77468 * of errors, however, so set an error and continue.
2837205Ssd77468 */
2847205Ssd77468 topo_mod_dprintf(mod,
2857205Ssd77468 "walker encountered invalid result: %d. "
2867205Ssd77468 "Continuing\n", result);
28711583SSurya.Prakki@Sun.COM (void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
2887205Ssd77468 result = 0;
2897205Ssd77468 break;
2907205Ssd77468 }
2917205Ssd77468
2927205Ssd77468 /* Destroy the walker list, which is no longer necessary */
2937205Ssd77468 pi_walkerlist_destroy(mod);
2947205Ssd77468
2957205Ssd77468 topo_mod_dprintf(mod, "walker done with node_0x%llx\n", mde_node);
2967205Ssd77468
2977205Ssd77468 endtime = gethrtime();
2987205Ssd77468 topo_mod_dprintf(mod, "walker scan time %lld ms\n",
2997205Ssd77468 (endtime-starttime)/MICROSEC);
3007205Ssd77468
3017205Ssd77468 return (result);
3027205Ssd77468 }
3037205Ssd77468
3047205Ssd77468
3057205Ssd77468 /*
3067205Ssd77468 * Visited once for each node in the machine description. Creates a topo
3077205Ssd77468 * node for the machine description node and associates it with it's parent,
3087205Ssd77468 * by calling an appropriate creation routine for the node type.
3097205Ssd77468 *
3107205Ssd77468 * Output:
3117205Ssd77468 * This routine returns MDE_WALK_NEXT, MDE_WALK_DONE or MDE_WALK_ERROR
3127205Ssd77468 * only.
3137205Ssd77468 */
3147205Ssd77468 static int
pi_walker_node(md_t * mdp,mde_cookie_t parent_mde_node,mde_cookie_t mde_node,void * private)3157205Ssd77468 pi_walker_node(md_t *mdp, mde_cookie_t parent_mde_node, mde_cookie_t mde_node,
3167205Ssd77468 void *private)
3177205Ssd77468 {
3187205Ssd77468 int result;
3197205Ssd77468 pi_enum_t *pip = (pi_enum_t *)private;
3207205Ssd77468 uint64_t skip; /* flag in md to skip this node */
3217205Ssd77468 tnode_t *t_parent; /* topo parent to this md node */
3227205Ssd77468 tnode_t *t_node; /* topo parent to this md node */
3237205Ssd77468 topo_instance_t inst;
3247205Ssd77468
3257205Ssd77468 topo_mod_t *mod;
3267205Ssd77468
3277205Ssd77468 /* Make sure we have our private data */
3287205Ssd77468 if (pip == NULL) {
3297205Ssd77468 return (MDE_WALK_ERROR);
3307205Ssd77468 }
3317205Ssd77468 mod = pip->mod;
3327205Ssd77468
3337205Ssd77468 topo_mod_dprintf(pip->mod,
3347205Ssd77468 "walker processing node_0x%llx parent node 0x%llx\n",
3357205Ssd77468 (uint64_t)mde_node, (uint64_t)parent_mde_node);
3367205Ssd77468
3377205Ssd77468 /* Should we skip this node ? */
3387205Ssd77468 skip = pi_skip_node(mod, pip->mdp, mde_node);
3397205Ssd77468 if (skip) {
3407205Ssd77468 /* Skip this node and continue to the next node */
3417205Ssd77468 topo_mod_dprintf(mod, "walker skipping node_0x%llx\n",
3427205Ssd77468 (uint64_t)mde_node);
3437205Ssd77468 return (MDE_WALK_NEXT);
3447205Ssd77468 }
3457205Ssd77468
3467205Ssd77468 result = pi_get_instance(mod, mdp, mde_node, &inst);
3477205Ssd77468 if (result != 0) {
3487205Ssd77468 /*
3497205Ssd77468 * No ID available to place this mde node in the topology so
3507205Ssd77468 * we cannot create a topology node.
3517205Ssd77468 */
3527205Ssd77468 topo_mod_dprintf(mod, "walker skipping node_0x%llx: "
3537205Ssd77468 "no instance\n", (uint64_t)mde_node);
35411583SSurya.Prakki@Sun.COM (void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
3557205Ssd77468 return (MDE_WALK_NEXT);
3567205Ssd77468 }
3577205Ssd77468
3587205Ssd77468 /*
3597205Ssd77468 * Find the parent topo node for this machine description node.
3607205Ssd77468 *
3617205Ssd77468 * If found, the element will also be removed from the list and the
3627205Ssd77468 * memory used to keep track of it released. We will only visit an
3637205Ssd77468 * MDE node once and so the memory is no longer needed.
3647205Ssd77468 */
3657205Ssd77468 t_parent = NULL;
3667205Ssd77468 result = pi_walkerlist_find(mod, mde_node, &t_parent);
3677205Ssd77468 if (result != 0 || t_parent == NULL) {
3687205Ssd77468 /*
3697205Ssd77468 * No parent was found or a NULL parent encountered. We
3707205Ssd77468 * cannot create a new topology node without a parent (
3717205Ssd77468 * even for top level nodes). We associate children of
3727205Ssd77468 * this MDE node with a NULL parent to silently skip the
3737205Ssd77468 * remainder of this MDE branch.
3747205Ssd77468 */
3757205Ssd77468 topo_mod_dprintf(mod, "no topo parent found for node_0x%llx\n",
3767205Ssd77468 mde_node);
3777205Ssd77468 result = pi_walker_node_range(mod, mdp, NULL, mde_node);
37811583SSurya.Prakki@Sun.COM (void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
3797205Ssd77468
3807205Ssd77468 return (result);
3817205Ssd77468 }
3827205Ssd77468
3837205Ssd77468 /*
3847205Ssd77468 * We have the mde node instance and parent information.
3857205Ssd77468 * Attempt to create a topology node for this mde node.
3867205Ssd77468 */
3877205Ssd77468 t_node = NULL;
3887205Ssd77468 result = pi_walker_node_create(mod, mdp, mde_node, t_parent, inst,
3897205Ssd77468 &t_node);
3907205Ssd77468 if (result != MDE_WALK_NEXT || t_node == NULL) {
3917205Ssd77468 /*
3927205Ssd77468 * We have failed to create a new topology node based on
3937205Ssd77468 * the current MDE node. We set partial enumeration and
3947205Ssd77468 * return without associating the children of this MDE
3957205Ssd77468 * node with a topology parent. This will propgate the
3967205Ssd77468 * creation error down this MDE branch.
3977205Ssd77468 */
39811583SSurya.Prakki@Sun.COM (void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
3997205Ssd77468 return (result);
4007205Ssd77468 }
4017205Ssd77468
4027205Ssd77468 /*
4037205Ssd77468 * Associate the new topology node with any children of this mde node.
4047205Ssd77468 */
4057205Ssd77468 result = pi_walker_node_range(mod, mdp, t_node, mde_node);
4067205Ssd77468
4077205Ssd77468 topo_mod_dprintf(mod, "walker completed node_0x%llx result = %d\n",
4087205Ssd77468 (uint64_t)mde_node, result);
4097205Ssd77468
4107205Ssd77468 return (result);
4117205Ssd77468 }
4127205Ssd77468
4137205Ssd77468
4147205Ssd77468 static int
pi_walker_node_create(topo_mod_t * mod,md_t * mdp,mde_cookie_t mde_node,tnode_t * t_parent,topo_instance_t inst,tnode_t ** t_node)4157205Ssd77468 pi_walker_node_create(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
4167205Ssd77468 tnode_t *t_parent, topo_instance_t inst, tnode_t **t_node)
4177205Ssd77468 {
4187205Ssd77468 int result;
4197205Ssd77468 char *hc_name;
4207205Ssd77468 uint64_t faddr;
4217205Ssd77468 pi_enum_fn_t *func;
4227205Ssd77468
4237205Ssd77468 if (t_parent == NULL) {
4247205Ssd77468 /*
4257205Ssd77468 * A parent topology node is required even for top-level
4267205Ssd77468 * nodes.
4277205Ssd77468 */
4287205Ssd77468 return (MDE_WALK_NEXT);
4297205Ssd77468 }
4307205Ssd77468
4317205Ssd77468 /*
4327205Ssd77468 * Find the topo-hc-name for this node which is used to find
4337205Ssd77468 * the specific creation function
4347205Ssd77468 */
4357205Ssd77468 hc_name = pi_get_topo_hc_name(mod, mdp, mde_node);
4367205Ssd77468 if (hc_name == NULL) {
4377205Ssd77468 /* Cannot get the hc-name */
4387205Ssd77468 topo_mod_dprintf(mod,
4397205Ssd77468 "failed to find hc-name for node_0x%llx\n", mde_node);
4407205Ssd77468 return (MDE_WALK_NEXT);
4417205Ssd77468 }
4427205Ssd77468
4437205Ssd77468 /* Determine the topology node creation routine to use */
4447205Ssd77468 func = pi_enum_generic;
4457205Ssd77468 faddr = 0;
4467205Ssd77468 result = nvlist_lookup_uint64(pi_enum_fns, hc_name, &faddr);
4477205Ssd77468 if (result == 0) {
4487205Ssd77468 /*
4497205Ssd77468 * A function is registered for this node. Convert the
4507205Ssd77468 * address to a pointer to function
4517205Ssd77468 */
4527205Ssd77468 func = (pi_enum_fn_t *)(uintptr_t)faddr;
4537205Ssd77468 }
4547205Ssd77468
4557205Ssd77468 /*
4567205Ssd77468 * Create a topology node for this mde node by calling the identified
4577205Ssd77468 * enumeration function
4587205Ssd77468 */
4597205Ssd77468 *t_node = NULL;
4607205Ssd77468 result = (func)(mod, mdp, mde_node, inst, t_parent, hc_name, t_node);
4617205Ssd77468 if (result != 0) {
4627205Ssd77468 topo_mod_dprintf(mod,
4637205Ssd77468 "failed to create topo entry for node_0x%llx type %s\n",
4647205Ssd77468 (uint64_t)mde_node, hc_name);
4657205Ssd77468 }
4668221SSean.Ye@Sun.COM
4677205Ssd77468 topo_mod_strfree(mod, hc_name);
4687205Ssd77468
4697205Ssd77468 return (MDE_WALK_NEXT);
4707205Ssd77468 }
4717205Ssd77468
4727205Ssd77468
4737205Ssd77468 /*
4747205Ssd77468 * Scan the children of a given MDE node and find all the sets of topo-hc-name
4757205Ssd77468 * types and their instance ranges. From this information we create topology
4767205Ssd77468 * node ranges on the given parent so that when the children are visited and a
4777205Ssd77468 * topology node is created, the range exists and the creation will succeed.
4787205Ssd77468 */
4797205Ssd77468 static int
pi_walker_node_range(topo_mod_t * mod,md_t * mdp,tnode_t * t_parent,mde_cookie_t mde_node)4807205Ssd77468 pi_walker_node_range(topo_mod_t *mod, md_t *mdp, tnode_t *t_parent,
4817205Ssd77468 mde_cookie_t mde_node)
4827205Ssd77468 {
4837205Ssd77468 int result;
4847205Ssd77468 int rc;
4857205Ssd77468 int num_arcs;
4867205Ssd77468 nvlist_t *typelist;
4877205Ssd77468 nvpair_t *nvp;
4887205Ssd77468 mde_cookie_t *arcp;
4897205Ssd77468 size_t arcsize;
4907205Ssd77468 int arcidx;
4917205Ssd77468 char *hc_name;
4927205Ssd77468 nvlist_t *hc_range;
4937205Ssd77468 topo_instance_t inst;
4947205Ssd77468 uint32_t min;
4957205Ssd77468 uint32_t max;
4967205Ssd77468
4977205Ssd77468 if (t_parent == NULL) {
4987205Ssd77468 topo_mod_dprintf(mod,
4997205Ssd77468 "walker failed to create node range with a NULL parent\n");
5007205Ssd77468 return (MDE_WALK_NEXT);
5017205Ssd77468 }
5027205Ssd77468
5037205Ssd77468 /* Determine how many children the given node has */
5047205Ssd77468 num_arcs = md_get_prop_arcs(mdp, mde_node, MD_STR_FWD, NULL, 0);
5057205Ssd77468 if (num_arcs == 0) {
5067205Ssd77468 /* This node has no children */
5077205Ssd77468 return (MDE_WALK_NEXT);
5087205Ssd77468 }
5097205Ssd77468 topo_mod_dprintf(mod, "node_0x%llx has %d children\n",
5107205Ssd77468 (uint64_t)mde_node, num_arcs);
5117205Ssd77468
5127205Ssd77468 /* Get the indexes for all the child nodes and put them in an array */
5137205Ssd77468 arcsize = sizeof (mde_cookie_t) * num_arcs;
5147205Ssd77468 arcp = topo_mod_zalloc(mod, arcsize);
5157205Ssd77468 if (arcp == NULL) {
5167205Ssd77468 topo_mod_dprintf(mod, "out of memory\n");
51711583SSurya.Prakki@Sun.COM (void) topo_mod_seterrno(mod, EMOD_NOMEM);
5187205Ssd77468 return (MDE_WALK_ERROR);
5197205Ssd77468 }
5207205Ssd77468 num_arcs = md_get_prop_arcs(mdp, mde_node, MD_STR_FWD, arcp, arcsize);
5217205Ssd77468
5227205Ssd77468 /*
5237205Ssd77468 * The children of the given node may have multiple types.
5247205Ssd77468 * Potentially, each child may have a different type and we need to
5257205Ssd77468 * create a topo node range for each one.
5267205Ssd77468 *
5277205Ssd77468 * We loop through the children and collect the type information for
5287205Ssd77468 * each one and associate the child with the given parent topo node.
5297205Ssd77468 */
5307205Ssd77468 result = topo_mod_nvalloc(mod, &typelist, NV_UNIQUE_NAME);
5317205Ssd77468 if (result != 0) {
5327205Ssd77468 topo_mod_free(mod, arcp, arcsize);
53311583SSurya.Prakki@Sun.COM (void) topo_mod_seterrno(mod, EMOD_NOMEM);
5347205Ssd77468 return (MDE_WALK_ERROR);
5357205Ssd77468 }
5367205Ssd77468
5377205Ssd77468 arcidx = 0;
5387205Ssd77468 for (arcidx = 0; arcidx < num_arcs; arcidx++) {
5397205Ssd77468 /* Should this node be skipped? */
5407205Ssd77468 if (pi_skip_node(mod, mdp, arcp[arcidx])) {
5417205Ssd77468 /* Skip this node */
5427205Ssd77468 topo_mod_dprintf(mod, "skipping node_0x%llx\n",
5437205Ssd77468 (uint64_t)arcp[arcidx]);
5447205Ssd77468 continue;
5457205Ssd77468 }
5467205Ssd77468
5477205Ssd77468 /* Get the type of this node */
5487205Ssd77468 hc_name = pi_get_topo_hc_name(mod, mdp, arcp[arcidx]);
5497205Ssd77468 rc = pi_get_instance(mod, mdp, arcp[arcidx], &inst);
5507205Ssd77468 if (rc == 0 && hc_name != NULL) {
5517205Ssd77468 /* Increment the count of nodes with this type */
5527205Ssd77468 hc_range = NULL;
5537205Ssd77468 rc = nvlist_lookup_nvlist(typelist, hc_name, &hc_range);
5547205Ssd77468 if (rc != 0) {
5557205Ssd77468 /*
5567205Ssd77468 * We have not visited this type yet. Create
5577205Ssd77468 * a new range based on this nodes instance
5587205Ssd77468 * information.
5597205Ssd77468 */
5607205Ssd77468 result = pi_walkerlist_addtype(mod, typelist,
5617205Ssd77468 hc_name, (uint32_t)inst, (uint32_t)inst);
5627205Ssd77468 if (result != 0) {
5637205Ssd77468 /*
5647205Ssd77468 * This error can only if there was a
5657205Ssd77468 * memory failure of some kind. Stop
5667205Ssd77468 * the walk or it will just get worse.
5677205Ssd77468 */
5687205Ssd77468 nvlist_free(typelist);
5697205Ssd77468 topo_mod_strfree(mod, hc_name);
5707205Ssd77468 topo_mod_free(mod, arcp, arcsize);
57111583SSurya.Prakki@Sun.COM (void) topo_mod_seterrno(mod,
5727205Ssd77468 EMOD_PARTIAL_ENUM);
5737205Ssd77468 return (MDE_WALK_ERROR);
5747205Ssd77468 }
5757205Ssd77468
5767205Ssd77468 /*
5777205Ssd77468 * We know the list exists now or the above
5787205Ssd77468 * would have failed. Just look it up.
5797205Ssd77468 */
5807205Ssd77468 (void) nvlist_lookup_nvlist(typelist, hc_name,
5817205Ssd77468 &hc_range);
5827205Ssd77468 }
5837205Ssd77468
5847205Ssd77468 /* Re-calculate the range minimums and maximums */
5857205Ssd77468 (void) nvlist_lookup_uint32(hc_range, PI_STR_MIN, &min);
5867205Ssd77468 (void) nvlist_lookup_uint32(hc_range, PI_STR_MAX, &max);
5877205Ssd77468 min = MIN(min, (uint32_t)inst);
5887205Ssd77468 max = MAX(max, (uint32_t)inst);
5897205Ssd77468 (void) nvlist_add_uint32(hc_range, PI_STR_MIN, min);
5907205Ssd77468 (void) nvlist_add_uint32(hc_range, PI_STR_MAX, max);
5917205Ssd77468
5927205Ssd77468 } else {
593*12956STom.Pothier@Sun.COM if (hc_name == NULL) {
594*12956STom.Pothier@Sun.COM topo_mod_dprintf(mod, "node_0x%llx has no "
595*12956STom.Pothier@Sun.COM "topo_hc_name.", (uint64_t)arcp[arcidx]);
596*12956STom.Pothier@Sun.COM (void) topo_mod_seterrno(mod,
597*12956STom.Pothier@Sun.COM EMOD_PARTIAL_ENUM);
598*12956STom.Pothier@Sun.COM return (MDE_WALK_ERROR);
599*12956STom.Pothier@Sun.COM }
600*12956STom.Pothier@Sun.COM
6017205Ssd77468 topo_mod_dprintf(mod, "node_0x%llx type %s has no id. "
6027205Ssd77468 "Excluding from range", (uint64_t)arcp[arcidx],
6037205Ssd77468 hc_name);
6047205Ssd77468 }
6057205Ssd77468 topo_mod_strfree(mod, hc_name);
6067205Ssd77468
6077205Ssd77468 /*
6087205Ssd77468 * Associate this node with the given topo parent even if it
6097205Ssd77468 * has no instance. We do this so that later an error with
6107205Ssd77468 * the PRI node will be reported instead of an internal
6117205Ssd77468 * error about not being able to find the parent of a node
6127205Ssd77468 */
6137205Ssd77468 rc = pi_walkerlist_add(mod, t_parent, arcp[arcidx]);
6147205Ssd77468 if (rc != 0) {
6157205Ssd77468 topo_mod_dprintf(mod,
6167205Ssd77468 "could not add node_0x%llx to walker list\n",
6177205Ssd77468 (uint64_t)arcp[arcidx]);
6187205Ssd77468 }
6197205Ssd77468 }
6207205Ssd77468
6217205Ssd77468 /*
6227205Ssd77468 * We have associated all the child nodes with the given topo parent
6237205Ssd77468 * in the walker list. Now we need to create topo ranges for each
6247205Ssd77468 * set of child types under the parent.
6257205Ssd77468 */
6267205Ssd77468 nvp = nvlist_next_nvpair(typelist, NULL);
6277205Ssd77468 while (nvp != NULL) {
6287205Ssd77468 /* Get the type name and count from the list element */
6297205Ssd77468 hc_name = nvpair_name(nvp);
6307205Ssd77468 (void) nvpair_value_nvlist(nvp, &hc_range);
6317205Ssd77468 (void) nvlist_lookup_uint32(hc_range, PI_STR_MIN, &min);
6327205Ssd77468 (void) nvlist_lookup_uint32(hc_range, PI_STR_MAX, &max);
6337205Ssd77468
6347205Ssd77468 /*
6357205Ssd77468 * We have the number of children with this type.
6367205Ssd77468 * Create an appropriate range.
6377205Ssd77468 */
6387205Ssd77468 topo_mod_dprintf(mod,
6397205Ssd77468 "creating instance range %d to %d of type %s\n",
6407205Ssd77468 min, max, hc_name);
6417205Ssd77468 rc = topo_node_range_create(mod, t_parent, hc_name,
6427205Ssd77468 (topo_instance_t)min, (topo_instance_t)max);
6437205Ssd77468 if (rc != 0) {
6447205Ssd77468 topo_mod_dprintf(mod,
6457205Ssd77468 "failed to created node range %d to %d for "
6467205Ssd77468 "nodes of type %s\n", min, max, hc_name);
6477205Ssd77468 }
6487205Ssd77468
6497205Ssd77468 /* Check the next node */
6507205Ssd77468 nvp = nvlist_next_nvpair(typelist, nvp);
6517205Ssd77468 }
6527205Ssd77468 topo_mod_free(mod, arcp, arcsize);
6537205Ssd77468 nvlist_free(typelist);
6547205Ssd77468
6557205Ssd77468 return (MDE_WALK_NEXT);
6567205Ssd77468 }
6577205Ssd77468
6587205Ssd77468
6597205Ssd77468 static int
pi_walkerlist_addtype(topo_mod_t * mod,nvlist_t * typelist,char * hc_name,uint32_t min,uint32_t max)6607205Ssd77468 pi_walkerlist_addtype(topo_mod_t *mod, nvlist_t *typelist, char *hc_name,
6617205Ssd77468 uint32_t min, uint32_t max)
6627205Ssd77468 {
6637205Ssd77468 int result;
6647205Ssd77468 nvlist_t *nvl;
6657205Ssd77468
6667205Ssd77468 result = topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME);
6677205Ssd77468 if (result != 0) {
6687205Ssd77468 return (result);
6697205Ssd77468 }
6707205Ssd77468
6717205Ssd77468 /* Create min and max elements in this list */
6727205Ssd77468 if (nvlist_add_uint32(nvl, PI_STR_MIN, min) != 0 ||
6737205Ssd77468 nvlist_add_uint32(nvl, PI_STR_MAX, max) != 0 ||
6747205Ssd77468 nvlist_add_nvlist(typelist, hc_name, nvl) != 0) {
6757205Ssd77468 nvlist_free(nvl);
6767205Ssd77468 return (-1);
6777205Ssd77468 }
6787205Ssd77468 nvlist_free(nvl);
6797205Ssd77468
6807205Ssd77468 return (0);
6817205Ssd77468 }
6827205Ssd77468
6837205Ssd77468
6847205Ssd77468 /* ARGSUSED */
6857205Ssd77468 static int
pi_walkerlist_compare(const void * left,const void * right,void * private)6867205Ssd77468 pi_walkerlist_compare(const void *left, const void *right, void *private)
6877205Ssd77468 {
6887205Ssd77468 pi_walkernode_t *lp = (pi_walkernode_t *)left;
6897205Ssd77468 pi_walkernode_t *rp = (pi_walkernode_t *)right;
6907205Ssd77468
6917205Ssd77468 if (lp->mde_node > rp->mde_node) {
6927205Ssd77468 return (1);
6937205Ssd77468 }
6947205Ssd77468 if (lp->mde_node < rp->mde_node) {
6957205Ssd77468 return (-1);
6967205Ssd77468 }
6977205Ssd77468 return (0);
6987205Ssd77468 }
6997205Ssd77468
7007205Ssd77468
7017205Ssd77468 static int
pi_walkerlist_create(topo_mod_t * mod)7027205Ssd77468 pi_walkerlist_create(topo_mod_t *mod)
7037205Ssd77468 {
7047205Ssd77468 /* Initialize the uutil list structure */
7057205Ssd77468 walker_pool = uu_list_pool_create("pi_walker_pool",
7067205Ssd77468 sizeof (pi_walkernode_t), offsetof(pi_walkernode_t, walker_node),
7077205Ssd77468 pi_walkerlist_compare, NULL);
7087205Ssd77468 if (walker_pool == NULL) {
70911583SSurya.Prakki@Sun.COM (void) topo_mod_seterrno(mod, EMOD_NOMEM);
7107205Ssd77468 return (-1);
7117205Ssd77468 }
7127205Ssd77468 walker_list = uu_list_create(walker_pool, NULL, 0);
7137205Ssd77468 if (walker_list == NULL) {
7147205Ssd77468 uu_list_pool_destroy(walker_pool);
71510665STom.Pothier@Sun.COM walker_pool = NULL;
7167205Ssd77468 return (-1);
7177205Ssd77468 }
7187205Ssd77468
7197205Ssd77468 return (0);
7207205Ssd77468 }
7217205Ssd77468
7227205Ssd77468
7237205Ssd77468 static void
pi_walkerlist_destroy(topo_mod_t * mod)7247205Ssd77468 pi_walkerlist_destroy(topo_mod_t *mod)
7257205Ssd77468 {
7267205Ssd77468 void *wvp;
7277205Ssd77468 pi_walkernode_t *wp;
7287205Ssd77468
7297205Ssd77468 /* Destroy our list of items */
7307205Ssd77468 while ((wvp = uu_list_first(walker_list)) != NULL) {
7317205Ssd77468 /*
7327205Ssd77468 * First, we empty the list of elements and free each one.
7337205Ssd77468 * We do not free the data elements as they are libtopo nodes
7347205Ssd77468 * and will be freed by libtopo
7357205Ssd77468 */
7367205Ssd77468 wp = (pi_walkernode_t *)wvp;
7377205Ssd77468 uu_list_remove(walker_list, wvp);
7387205Ssd77468 uu_list_node_fini(wp, &(wp->walker_node), walker_pool);
7397205Ssd77468
7407205Ssd77468 topo_mod_free(mod, wvp, sizeof (pi_walkernode_t));
7417205Ssd77468 }
7427205Ssd77468 uu_list_destroy(walker_list);
7437205Ssd77468 uu_list_pool_destroy(walker_pool);
74410665STom.Pothier@Sun.COM walker_list = NULL;
74510665STom.Pothier@Sun.COM walker_pool = NULL;
7467205Ssd77468 }
7477205Ssd77468
7487205Ssd77468
7497205Ssd77468 static int
pi_walkerlist_add(topo_mod_t * mod,tnode_t * t_parent,mde_cookie_t mde_node)7507205Ssd77468 pi_walkerlist_add(topo_mod_t *mod, tnode_t *t_parent, mde_cookie_t mde_node)
7517205Ssd77468 {
7527205Ssd77468 uu_list_index_t idx;
7537205Ssd77468 pi_walkernode_t *wnp;
7547205Ssd77468
7557205Ssd77468 wnp = topo_mod_zalloc(mod, sizeof (pi_walkernode_t));
7567205Ssd77468 if (wnp == NULL) {
7577205Ssd77468 topo_mod_dprintf(mod, "failed to add node_0x%llx parent %p\n",
7587205Ssd77468 (uint64_t)mde_node, t_parent);
7597205Ssd77468 return (-1);
7607205Ssd77468 }
7617205Ssd77468 uu_list_node_init(wnp, &(wnp->walker_node), walker_pool);
7627205Ssd77468
7637205Ssd77468 wnp->t_parent = t_parent;
7647205Ssd77468 wnp->mde_node = mde_node;
7657205Ssd77468
7667205Ssd77468 (void) uu_list_find(walker_list, wnp, NULL, &idx);
7677205Ssd77468 uu_list_insert(walker_list, wnp, idx);
7687205Ssd77468
7697205Ssd77468 return (0);
7707205Ssd77468 }
7717205Ssd77468
7727205Ssd77468
7737205Ssd77468 /*
7747205Ssd77468 * Find the parent topo node for this machine description node.
7757205Ssd77468 *
7767205Ssd77468 * Nodes are removed from the list as they are found. They are only
7777205Ssd77468 * visited once and this eliminates the need for a separate routine
7787205Ssd77468 * that walks the list to free elements later.
7797205Ssd77468 */
7807205Ssd77468 static int
pi_walkerlist_find(topo_mod_t * mod,mde_cookie_t mde_node,tnode_t ** tpp)7817205Ssd77468 pi_walkerlist_find(topo_mod_t *mod, mde_cookie_t mde_node, tnode_t **tpp)
7827205Ssd77468 {
7837205Ssd77468 pi_walkernode_t *result;
7847205Ssd77468
7857205Ssd77468 uu_list_index_t idx;
7867205Ssd77468 pi_walkernode_t search_criteria;
7877205Ssd77468
7887205Ssd77468 search_criteria.mde_node = mde_node;
7897205Ssd77468 search_criteria.t_parent = NULL;
7907205Ssd77468
7917205Ssd77468 *tpp = NULL;
7927205Ssd77468 result = uu_list_find(walker_list, &search_criteria, NULL, &idx);
7937205Ssd77468 if (result == NULL) {
7947205Ssd77468 return (-1);
7957205Ssd77468 }
7967205Ssd77468 *tpp = result->t_parent;
7977205Ssd77468
7987205Ssd77468 /* Remove this element from the list */
7997205Ssd77468 uu_list_remove(walker_list, result);
8007205Ssd77468 uu_list_node_fini(result, &(result->walker_node), walker_pool);
8017205Ssd77468 topo_mod_free(mod, result, sizeof (pi_walkernode_t));
8027205Ssd77468
8037205Ssd77468 return (0);
8047205Ssd77468 }
805