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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * Walk the LDOM PRI component nodes and create appropriate topology nodes
28 */
29
30 #include <sys/types.h>
31 #include <sys/time.h>
32 #include <stddef.h>
33 #include <inttypes.h>
34 #include <strings.h>
35 #include <string.h>
36 #include <libuutil.h>
37 #include <libnvpair.h>
38 #include <sys/mdesc.h>
39 #include <fm/topo_mod.h>
40 #include <fm/topo_hc.h>
41 #include "pi_impl.h"
42
43 #define PI_STR_MIN "instance_min"
44 #define PI_STR_MAX "instance_max"
45
46 /*
47 * Allow for custom topo node creation routines based on topo-hc-name.
48 */
49 struct pi_enum_functions_s {
50 pi_enum_fn_t *func;
51 char *hc_name; /* topo-hc-name */
52 };
53 typedef struct pi_enum_functions_s pi_enum_functions_t;
54
55 struct pi_methods_s {
56 topo_method_t *meths;
57 char *hc_name;
58 };
59 typedef struct pi_methods_s pi_methods_t;
60
61 extern topo_method_t pi_cpu_methods[], pi_mem_methods[];
62
63 /*
64 * List of custom enumerators for PRI nodes that require them. The most
65 * common nodes are listed first.
66 */
67 static pi_enum_functions_t pi_enum_fns_builtin[] = {
68 {pi_enum_cpu, STRAND},
69 {pi_enum_cpu, CPU},
70 {pi_enum_mem, DIMM},
71 {pi_enum_cpu, CORE},
72 {pi_enum_cpu, CHIP},
73 {pi_enum_hostbridge, HOSTBRIDGE},
74 {pi_enum_pciexrc, PCIEX_ROOT},
75 {pi_enum_niu, NIU},
76 {pi_enum_bay, BAY},
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
pi_walker_init(topo_mod_t * mod)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
pi_walker_fini(topo_mod_t * mod)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
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)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
pi_walker_node(md_t * mdp,mde_cookie_t parent_mde_node,mde_cookie_t mde_node,void * private)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
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)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
pi_walker_node_range(topo_mod_t * mod,md_t * mdp,tnode_t * t_parent,mde_cookie_t mde_node)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 if (hc_name == NULL) {
594 topo_mod_dprintf(mod, "node_0x%llx has no "
595 "topo_hc_name.", (uint64_t)arcp[arcidx]);
596 (void) topo_mod_seterrno(mod,
597 EMOD_PARTIAL_ENUM);
598 return (MDE_WALK_ERROR);
599 }
600
601 topo_mod_dprintf(mod, "node_0x%llx type %s has no id. "
602 "Excluding from range", (uint64_t)arcp[arcidx],
603 hc_name);
604 }
605 topo_mod_strfree(mod, hc_name);
606
607 /*
608 * Associate this node with the given topo parent even if it
609 * has no instance. We do this so that later an error with
610 * the PRI node will be reported instead of an internal
611 * error about not being able to find the parent of a node
612 */
613 rc = pi_walkerlist_add(mod, t_parent, arcp[arcidx]);
614 if (rc != 0) {
615 topo_mod_dprintf(mod,
616 "could not add node_0x%llx to walker list\n",
617 (uint64_t)arcp[arcidx]);
618 }
619 }
620
621 /*
622 * We have associated all the child nodes with the given topo parent
623 * in the walker list. Now we need to create topo ranges for each
624 * set of child types under the parent.
625 */
626 nvp = nvlist_next_nvpair(typelist, NULL);
627 while (nvp != NULL) {
628 /* Get the type name and count from the list element */
629 hc_name = nvpair_name(nvp);
630 (void) nvpair_value_nvlist(nvp, &hc_range);
631 (void) nvlist_lookup_uint32(hc_range, PI_STR_MIN, &min);
632 (void) nvlist_lookup_uint32(hc_range, PI_STR_MAX, &max);
633
634 /*
635 * We have the number of children with this type.
636 * Create an appropriate range.
637 */
638 topo_mod_dprintf(mod,
639 "creating instance range %d to %d of type %s\n",
640 min, max, hc_name);
641 rc = topo_node_range_create(mod, t_parent, hc_name,
642 (topo_instance_t)min, (topo_instance_t)max);
643 if (rc != 0) {
644 topo_mod_dprintf(mod,
645 "failed to created node range %d to %d for "
646 "nodes of type %s\n", min, max, hc_name);
647 }
648
649 /* Check the next node */
650 nvp = nvlist_next_nvpair(typelist, nvp);
651 }
652 topo_mod_free(mod, arcp, arcsize);
653 nvlist_free(typelist);
654
655 return (MDE_WALK_NEXT);
656 }
657
658
659 static int
pi_walkerlist_addtype(topo_mod_t * mod,nvlist_t * typelist,char * hc_name,uint32_t min,uint32_t max)660 pi_walkerlist_addtype(topo_mod_t *mod, nvlist_t *typelist, char *hc_name,
661 uint32_t min, uint32_t max)
662 {
663 int result;
664 nvlist_t *nvl;
665
666 result = topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME);
667 if (result != 0) {
668 return (result);
669 }
670
671 /* Create min and max elements in this list */
672 if (nvlist_add_uint32(nvl, PI_STR_MIN, min) != 0 ||
673 nvlist_add_uint32(nvl, PI_STR_MAX, max) != 0 ||
674 nvlist_add_nvlist(typelist, hc_name, nvl) != 0) {
675 nvlist_free(nvl);
676 return (-1);
677 }
678 nvlist_free(nvl);
679
680 return (0);
681 }
682
683
684 /* ARGSUSED */
685 static int
pi_walkerlist_compare(const void * left,const void * right,void * private)686 pi_walkerlist_compare(const void *left, const void *right, void *private)
687 {
688 pi_walkernode_t *lp = (pi_walkernode_t *)left;
689 pi_walkernode_t *rp = (pi_walkernode_t *)right;
690
691 if (lp->mde_node > rp->mde_node) {
692 return (1);
693 }
694 if (lp->mde_node < rp->mde_node) {
695 return (-1);
696 }
697 return (0);
698 }
699
700
701 static int
pi_walkerlist_create(topo_mod_t * mod)702 pi_walkerlist_create(topo_mod_t *mod)
703 {
704 /* Initialize the uutil list structure */
705 walker_pool = uu_list_pool_create("pi_walker_pool",
706 sizeof (pi_walkernode_t), offsetof(pi_walkernode_t, walker_node),
707 pi_walkerlist_compare, NULL);
708 if (walker_pool == NULL) {
709 (void) topo_mod_seterrno(mod, EMOD_NOMEM);
710 return (-1);
711 }
712 walker_list = uu_list_create(walker_pool, NULL, 0);
713 if (walker_list == NULL) {
714 uu_list_pool_destroy(walker_pool);
715 walker_pool = NULL;
716 return (-1);
717 }
718
719 return (0);
720 }
721
722
723 static void
pi_walkerlist_destroy(topo_mod_t * mod)724 pi_walkerlist_destroy(topo_mod_t *mod)
725 {
726 void *wvp;
727 pi_walkernode_t *wp;
728
729 /* Destroy our list of items */
730 while ((wvp = uu_list_first(walker_list)) != NULL) {
731 /*
732 * First, we empty the list of elements and free each one.
733 * We do not free the data elements as they are libtopo nodes
734 * and will be freed by libtopo
735 */
736 wp = (pi_walkernode_t *)wvp;
737 uu_list_remove(walker_list, wvp);
738 uu_list_node_fini(wp, &(wp->walker_node), walker_pool);
739
740 topo_mod_free(mod, wvp, sizeof (pi_walkernode_t));
741 }
742 uu_list_destroy(walker_list);
743 uu_list_pool_destroy(walker_pool);
744 walker_list = NULL;
745 walker_pool = NULL;
746 }
747
748
749 static int
pi_walkerlist_add(topo_mod_t * mod,tnode_t * t_parent,mde_cookie_t mde_node)750 pi_walkerlist_add(topo_mod_t *mod, tnode_t *t_parent, mde_cookie_t mde_node)
751 {
752 uu_list_index_t idx;
753 pi_walkernode_t *wnp;
754
755 wnp = topo_mod_zalloc(mod, sizeof (pi_walkernode_t));
756 if (wnp == NULL) {
757 topo_mod_dprintf(mod, "failed to add node_0x%llx parent %p\n",
758 (uint64_t)mde_node, t_parent);
759 return (-1);
760 }
761 uu_list_node_init(wnp, &(wnp->walker_node), walker_pool);
762
763 wnp->t_parent = t_parent;
764 wnp->mde_node = mde_node;
765
766 (void) uu_list_find(walker_list, wnp, NULL, &idx);
767 uu_list_insert(walker_list, wnp, idx);
768
769 return (0);
770 }
771
772
773 /*
774 * Find the parent topo node for this machine description node.
775 *
776 * Nodes are removed from the list as they are found. They are only
777 * visited once and this eliminates the need for a separate routine
778 * that walks the list to free elements later.
779 */
780 static int
pi_walkerlist_find(topo_mod_t * mod,mde_cookie_t mde_node,tnode_t ** tpp)781 pi_walkerlist_find(topo_mod_t *mod, mde_cookie_t mde_node, tnode_t **tpp)
782 {
783 pi_walkernode_t *result;
784
785 uu_list_index_t idx;
786 pi_walkernode_t search_criteria;
787
788 search_criteria.mde_node = mde_node;
789 search_criteria.t_parent = NULL;
790
791 *tpp = NULL;
792 result = uu_list_find(walker_list, &search_criteria, NULL, &idx);
793 if (result == NULL) {
794 return (-1);
795 }
796 *tpp = result->t_parent;
797
798 /* Remove this element from the list */
799 uu_list_remove(walker_list, result);
800 uu_list_node_fini(result, &(result->walker_node), walker_pool);
801 topo_mod_free(mod, result, sizeof (pi_walkernode_t));
802
803 return (0);
804 }
805