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 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /*
26 * Snapshot Library Interfaces
27 *
28 * Consumers of topology data may use the interfaces in this file to open,
29 * snapshot and close a topology exported by FMRI scheme (hc, mem and cpu)
30 * builtin plugins and their helper modules. A topology handle is obtained
31 * by calling topo_open(). Upon a successful return, the caller may use this
32 * handle to open a new snapshot. Each snapshot is assigned a Universally
33 * Unique Identifier that in a future enchancement to the libtopo API will be
34 * used as the file locator in /var/fm/topo to persist new snapshots or lookup
35 * a previously captured snapshot. topo_snap_hold() will capture the current
36 * system topology. All consumers of the topo_hdl_t argument will be
37 * blocked from accessing the topology trees until the snapshot completes.
38 *
39 * A snapshot may be cleared by calling topo_snap_rele(). As with
40 * topo_snap_hold(), all topology accesses are blocked until the topology
41 * trees have been released and deallocated.
42 *
43 * Walker Library Interfaces
44 *
45 * Once a snapshot has been taken with topo_snap_hold(), topo_hdl_t holders
46 * may initiate topology tree walks on a scheme-tree basis. topo_walk_init()
47 * will initiate the data structures required to walk any one one of the
48 * FMRI scheme trees. The walker data structure, topo_walk_t, is an opaque
49 * handle passed to topo_walk_step to begin the walk. At each node in the
50 * topology tree, a callback function is called with access to the node at
51 * which our current walk falls. The callback function is passed in during
52 * calls to topo_walk_init() and used throughout the walk_step of the
53 * scheme tree. At any time, the callback may terminate the walk by returning
54 * TOPO_WALK_TERMINATE or TOPO_WALK_ERR. TOPO_WALK_NEXT will continue the walk.
55 *
56 * The type of walk through the tree may be sibling first or child first by
57 * respectively passing in TOPO_WALK_SIBLING or TOPO_WALK_CHILD to
58 * the topo_walk_step() function. Topology nodes
59 * associated with an outstanding walk are held in place and will not be
60 * deallocated until the walk through that node completes.
61 *
62 * Once the walk has terminated, the walking process should call
63 * topo_walk_fini() to clean-up resources created in topo_walk_init()
64 * and release nodes that may be still held.
65 */
66
67 #include <alloca.h>
68 #include <ctype.h>
69 #include <pthread.h>
70 #include <limits.h>
71 #include <assert.h>
72 #include <fcntl.h>
73 #include <smbios.h>
74 #include <sys/param.h>
75 #include <sys/types.h>
76 #include <sys/stat.h>
77 #include <sys/systeminfo.h>
78 #include <sys/utsname.h>
79 #include <uuid/uuid.h>
80 #include <zone.h>
81
82 #include <fm/libtopo.h>
83 #include <sys/fm/protocol.h>
84
85 #include <topo_alloc.h>
86 #include <topo_builtin.h>
87 #include <topo_string.h>
88 #include <topo_error.h>
89 #include <topo_subr.h>
90
91 static void topo_snap_destroy(topo_hdl_t *);
92
93 static topo_hdl_t *
set_open_errno(topo_hdl_t * thp,int * errp,int err)94 set_open_errno(topo_hdl_t *thp, int *errp, int err)
95 {
96 if (thp != NULL) {
97 topo_close(thp);
98 }
99 if (errp != NULL)
100 *errp = err;
101 return (NULL);
102 }
103
104 topo_hdl_t *
topo_open(int version,const char * rootdir,int * errp)105 topo_open(int version, const char *rootdir, int *errp)
106 {
107 topo_hdl_t *thp = NULL;
108 topo_alloc_t *tap;
109
110 char platform[MAXNAMELEN];
111 char isa[MAXNAMELEN];
112 struct utsname uts;
113 struct stat st;
114
115 smbios_hdl_t *shp;
116 smbios_system_t s1;
117 smbios_info_t s2;
118 id_t id;
119
120 char *dbflags, *dbout;
121
122 if (version != TOPO_VERSION)
123 return (set_open_errno(thp, errp, ETOPO_HDL_ABIVER));
124
125 if (rootdir != NULL && stat(rootdir, &st) < 0)
126 return (set_open_errno(thp, errp, ETOPO_HDL_INVAL));
127
128 if ((thp = topo_zalloc(sizeof (topo_hdl_t), 0)) == NULL)
129 return (set_open_errno(thp, errp, ETOPO_NOMEM));
130
131 (void) pthread_mutex_init(&thp->th_lock, NULL);
132
133 if ((tap = topo_zalloc(sizeof (topo_alloc_t), 0)) == NULL)
134 return (set_open_errno(thp, errp, ETOPO_NOMEM));
135
136 /*
137 * Install default allocators
138 */
139 tap->ta_flags = 0;
140 tap->ta_alloc = topo_alloc;
141 tap->ta_zalloc = topo_zalloc;
142 tap->ta_free = topo_free;
143 tap->ta_nvops.nv_ao_alloc = topo_nv_alloc;
144 tap->ta_nvops.nv_ao_free = topo_nv_free;
145 (void) nv_alloc_init(&tap->ta_nva, &tap->ta_nvops);
146 thp->th_alloc = tap;
147
148 if ((thp->th_modhash = topo_modhash_create(thp)) == NULL)
149 return (set_open_errno(thp, errp, ETOPO_NOMEM));
150
151 /*
152 * Set-up system information and search paths for modules
153 * and topology map files
154 */
155 if (rootdir == NULL) {
156 rootdir = topo_hdl_strdup(thp, "/");
157 thp->th_rootdir = (char *)rootdir;
158 } else {
159 int len;
160 char *rpath;
161
162 len = strlen(rootdir);
163 if (len >= PATH_MAX)
164 return (set_open_errno(thp, errp, EINVAL));
165
166 if (rootdir[len - 1] != '/') {
167 rpath = alloca(len + 2);
168 (void) snprintf(rpath, len + 2, "%s/", rootdir);
169 } else {
170 rpath = (char *)rootdir;
171 }
172 thp->th_rootdir = topo_hdl_strdup(thp, rpath);
173 }
174
175 platform[0] = '\0';
176 isa[0] = '\0';
177 (void) sysinfo(SI_PLATFORM, platform, sizeof (platform));
178 (void) sysinfo(SI_ARCHITECTURE, isa, sizeof (isa));
179 (void) uname(&uts);
180 thp->th_platform = topo_hdl_strdup(thp, platform);
181 thp->th_isa = topo_hdl_strdup(thp, isa);
182 thp->th_machine = topo_hdl_strdup(thp, uts.machine);
183 if ((shp = smbios_open(NULL, SMB_VERSION, 0, NULL)) != NULL) {
184 if ((id = smbios_info_system(shp, &s1)) != SMB_ERR &&
185 smbios_info_common(shp, id, &s2) != SMB_ERR) {
186
187 if (strcmp(s2.smbi_product, SMB_DEFAULT1) != 0 &&
188 strcmp(s2.smbi_product, SMB_DEFAULT2) != 0) {
189 thp->th_product = topo_cleanup_auth_str(thp,
190 (char *)s2.smbi_product);
191 }
192 }
193 smbios_close(shp);
194 } else {
195 thp->th_product = topo_hdl_strdup(thp, thp->th_platform);
196 }
197
198 if (thp->th_rootdir == NULL || thp->th_platform == NULL ||
199 thp->th_machine == NULL)
200 return (set_open_errno(thp, errp, ETOPO_NOMEM));
201
202 dbflags = getenv("TOPO_DEBUG");
203 dbout = getenv("TOPO_DEBUG_OUT");
204 if (dbflags != NULL)
205 topo_debug_set(thp, dbflags, dbout);
206
207 if (topo_builtin_create(thp, thp->th_rootdir) != 0) {
208 topo_dprintf(thp, TOPO_DBG_ERR,
209 "failed to load builtin modules: %s\n",
210 topo_hdl_errmsg(thp));
211 return (set_open_errno(thp, errp, topo_hdl_errno(thp)));
212 }
213
214 return (thp);
215 }
216
217 void
topo_close(topo_hdl_t * thp)218 topo_close(topo_hdl_t *thp)
219 {
220 ttree_t *tp;
221
222 topo_hdl_lock(thp);
223 if (thp->th_platform != NULL)
224 topo_hdl_strfree(thp, thp->th_platform);
225 if (thp->th_isa != NULL)
226 topo_hdl_strfree(thp, thp->th_isa);
227 if (thp->th_machine != NULL)
228 topo_hdl_strfree(thp, thp->th_machine);
229 if (thp->th_product != NULL)
230 topo_hdl_strfree(thp, thp->th_product);
231 if (thp->th_rootdir != NULL)
232 topo_hdl_strfree(thp, thp->th_rootdir);
233 if (thp->th_ipmi != NULL)
234 ipmi_close(thp->th_ipmi);
235 if (thp->th_smbios != NULL)
236 smbios_close(thp->th_smbios);
237
238 /*
239 * Clean-up snapshot
240 */
241 topo_snap_destroy(thp);
242
243 /*
244 * Clean-up trees
245 */
246 while ((tp = topo_list_next(&thp->th_trees)) != NULL) {
247 topo_list_delete(&thp->th_trees, tp);
248 topo_tree_destroy(tp);
249 }
250
251 /*
252 * Unload all plugins
253 */
254 topo_modhash_unload_all(thp);
255
256 if (thp->th_modhash != NULL)
257 topo_modhash_destroy(thp);
258 if (thp->th_alloc != NULL)
259 topo_free(thp->th_alloc, sizeof (topo_alloc_t));
260
261 topo_hdl_unlock(thp);
262
263 topo_free(thp, sizeof (topo_hdl_t));
264 }
265
266 static char *
topo_snap_create(topo_hdl_t * thp,int * errp,boolean_t need_force)267 topo_snap_create(topo_hdl_t *thp, int *errp, boolean_t need_force)
268 {
269 uuid_t uuid;
270 char *ustr = NULL;
271
272 topo_hdl_lock(thp);
273 if (thp->th_uuid != NULL) {
274 *errp = ETOPO_HDL_UUID;
275 topo_hdl_unlock(thp);
276 return (NULL);
277 }
278
279 if ((thp->th_uuid = topo_hdl_zalloc(thp, TOPO_UUID_SIZE)) == NULL) {
280 *errp = ETOPO_NOMEM;
281 topo_dprintf(thp, TOPO_DBG_ERR, "unable to allocate uuid: %s\n",
282 topo_strerror(*errp));
283 topo_hdl_unlock(thp);
284 return (NULL);
285 }
286
287 uuid_generate(uuid);
288 uuid_unparse(uuid, thp->th_uuid);
289 if ((ustr = topo_hdl_strdup(thp, thp->th_uuid)) == NULL) {
290 *errp = ETOPO_NOMEM;
291 topo_hdl_unlock(thp);
292 return (NULL);
293 }
294
295 if (need_force) {
296 topo_dprintf(thp, TOPO_DBG_FORCE,
297 "taking a DINFOFORCE snapshot\n");
298 thp->th_di = di_init("/", DINFOFORCE |
299 DINFOSUBTREE | DINFOMINOR | DINFOPROP | DINFOPATH);
300 } else {
301 thp->th_di = di_init("/", DINFOCACHE);
302 }
303 thp->th_pi = di_prom_init();
304
305 if (topo_tree_enum_all(thp) < 0) {
306 topo_dprintf(thp, TOPO_DBG_ERR, "enumeration failure: %s\n",
307 topo_hdl_errmsg(thp));
308 if (topo_hdl_errno(thp) == ETOPO_ENUM_FATAL) {
309 *errp = thp->th_errno;
310
311 if (thp->th_di != DI_NODE_NIL) {
312 di_fini(thp->th_di);
313 thp->th_di = DI_NODE_NIL;
314 }
315 if (thp->th_pi != DI_PROM_HANDLE_NIL) {
316 di_prom_fini(thp->th_pi);
317 thp->th_pi = DI_PROM_HANDLE_NIL;
318 }
319
320 topo_hdl_strfree(thp, ustr);
321 topo_hdl_unlock(thp);
322 return (NULL);
323 }
324 }
325
326 if (thp->th_ipmi != NULL &&
327 ipmi_sdr_changed(thp->th_ipmi) &&
328 ipmi_sdr_refresh(thp->th_ipmi) != 0) {
329 topo_dprintf(thp, TOPO_DBG_ERR,
330 "failed to refresh IPMI sdr repository: %s\n",
331 ipmi_errmsg(thp->th_ipmi));
332 }
333
334 topo_hdl_unlock(thp);
335
336 return (ustr);
337 }
338
339 /*ARGSUSED*/
340 static char *
topo_snap_log_create(topo_hdl_t * thp,const char * uuid,int * errp)341 topo_snap_log_create(topo_hdl_t *thp, const char *uuid, int *errp)
342 {
343 return ((char *)uuid);
344 }
345
346 /*ARGSUSED*/
347 static int
fac_walker(topo_hdl_t * thp,tnode_t * node,void * arg)348 fac_walker(topo_hdl_t *thp, tnode_t *node, void *arg)
349 {
350 int err;
351 nvlist_t *out;
352
353 if (topo_method_supported(node, TOPO_METH_FAC_ENUM, 0)) {
354 /*
355 * If the facility enumeration method fails, note the failure,
356 * but continue on with the walk.
357 */
358 if (topo_method_invoke(node, TOPO_METH_FAC_ENUM, 0, NULL, &out,
359 &err) != 0) {
360 topo_dprintf(thp, TOPO_DBG_ERR,
361 "facility enumeration method failed on node %s=%d "
362 "(%s)\n", topo_node_name(node),
363 topo_node_instance(node), topo_strerror(err));
364 }
365 }
366 return (TOPO_WALK_NEXT);
367 }
368
369 /*
370 * Return snapshot id
371 */
372 char *
topo_snap_hold(topo_hdl_t * thp,const char * uuid,int * errp)373 topo_snap_hold(topo_hdl_t *thp, const char *uuid, int *errp)
374 {
375 topo_walk_t *twp;
376
377 if (thp == NULL)
378 return (NULL);
379
380 if (uuid == NULL) {
381 char *ret;
382
383 if (thp->th_debug & TOPO_DBG_FORCE) {
384 ret = topo_snap_create(thp, errp, B_TRUE);
385 } else {
386 ret = topo_snap_create(thp, errp, B_FALSE);
387 }
388
389 /*
390 * Now walk the tree and invoke any facility enumeration methods
391 */
392 if (ret != NULL && getzoneid() == 0) {
393 if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC,
394 fac_walker, (void *)0, errp)) == NULL) {
395 return (ret);
396 }
397 (void) topo_walk_step(twp, TOPO_WALK_CHILD);
398 topo_walk_fini(twp);
399 }
400 return (ret);
401 }
402 return (topo_snap_log_create(thp, uuid, errp));
403 }
404
405 /*ARGSUSED*/
406 static int
topo_walk_destroy(topo_hdl_t * thp,tnode_t * node,void * notused)407 topo_walk_destroy(topo_hdl_t *thp, tnode_t *node, void *notused)
408 {
409 tnode_t *cnode;
410
411 cnode = topo_child_first(node);
412
413 if (cnode != NULL)
414 return (TOPO_WALK_NEXT);
415
416 topo_node_unbind(node);
417
418 return (TOPO_WALK_NEXT);
419 }
420
421 static void
topo_snap_destroy(topo_hdl_t * thp)422 topo_snap_destroy(topo_hdl_t *thp)
423 {
424 int i;
425 ttree_t *tp;
426 topo_walk_t *twp;
427 tnode_t *root;
428 topo_nodehash_t *nhp;
429 topo_mod_t *mod;
430
431 for (tp = topo_list_next(&thp->th_trees); tp != NULL;
432 tp = topo_list_next(tp)) {
433
434 root = tp->tt_root;
435 twp = tp->tt_walk;
436 /*
437 * Clean-up tree nodes from the bottom-up
438 */
439 if ((twp->tw_node = topo_child_first(root)) != NULL) {
440 twp->tw_cb = topo_walk_destroy;
441 topo_node_hold(root);
442 topo_node_hold(twp->tw_node); /* released at walk end */
443 (void) topo_walk_bottomup(twp, TOPO_WALK_CHILD);
444 topo_node_rele(root);
445 }
446
447 /*
448 * Tidy-up the root node
449 */
450 while ((nhp = topo_list_next(&root->tn_children)) != NULL) {
451 for (i = 0; i < nhp->th_arrlen; i++) {
452 assert(nhp->th_nodearr[i] == NULL);
453 }
454 mod = nhp->th_enum;
455 topo_mod_strfree(mod, nhp->th_name);
456 topo_mod_free(mod, nhp->th_nodearr,
457 nhp->th_arrlen * sizeof (tnode_t *));
458 topo_list_delete(&root->tn_children, nhp);
459 topo_mod_free(mod, nhp, sizeof (topo_nodehash_t));
460 topo_mod_rele(mod);
461 }
462
463 }
464
465 /*
466 * Clean-up our cached devinfo and prom tree handles.
467 */
468 if (thp->th_di != DI_NODE_NIL) {
469 di_fini(thp->th_di);
470 thp->th_di = DI_NODE_NIL;
471 }
472 if (thp->th_pi != DI_PROM_HANDLE_NIL) {
473 di_prom_fini(thp->th_pi);
474 thp->th_pi = DI_PROM_HANDLE_NIL;
475 }
476
477
478 if (thp->th_uuid != NULL) {
479 topo_hdl_free(thp, thp->th_uuid, TOPO_UUID_SIZE);
480 thp->th_uuid = NULL;
481 }
482 }
483
484 void
topo_snap_release(topo_hdl_t * thp)485 topo_snap_release(topo_hdl_t *thp)
486 {
487 if (thp == NULL)
488 return;
489
490 topo_hdl_lock(thp);
491 topo_snap_destroy(thp);
492 topo_hdl_unlock(thp);
493 }
494
495 topo_walk_t *
topo_walk_init(topo_hdl_t * thp,const char * scheme,topo_walk_cb_t cb_f,void * pdata,int * errp)496 topo_walk_init(topo_hdl_t *thp, const char *scheme, topo_walk_cb_t cb_f,
497 void *pdata, int *errp)
498 {
499 ttree_t *tp;
500 topo_walk_t *wp;
501
502 for (tp = topo_list_next(&thp->th_trees); tp != NULL;
503 tp = topo_list_next(tp)) {
504 if (strcmp(scheme, tp->tt_scheme) == 0) {
505
506 /*
507 * Hold the root node and start walk at the first
508 * child node
509 */
510 assert(tp->tt_root != NULL);
511
512 if ((wp = topo_node_walk_init(thp, NULL, tp->tt_root,
513 cb_f, pdata, errp)) == NULL) /* errp set */
514 return (NULL);
515
516 return (wp);
517 }
518 }
519
520 *errp = ETOPO_WALK_NOTFOUND;
521 return (NULL);
522 }
523
524 static int
step_child(tnode_t * cnp,topo_walk_t * wp,int flag,int bottomup)525 step_child(tnode_t *cnp, topo_walk_t *wp, int flag, int bottomup)
526 {
527 int status;
528 tnode_t *nnp;
529
530 nnp = topo_child_first(cnp);
531
532 if (nnp == NULL) {
533 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
534 "step_child: TOPO_WALK_TERMINATE for %s=%d\n",
535 cnp->tn_name, cnp->tn_instance);
536 return (TOPO_WALK_TERMINATE);
537 }
538
539 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
540 "step_child: walk through node %s=%d to %s=%d\n",
541 cnp->tn_name, cnp->tn_instance, nnp->tn_name, nnp->tn_instance);
542
543 topo_node_hold(nnp); /* released on return from walk_step */
544 wp->tw_node = nnp;
545 if (bottomup == 1)
546 status = topo_walk_bottomup(wp, flag);
547 else
548 status = topo_walk_step(wp, flag);
549
550 return (status);
551 }
552
553 static int
step_sibling(tnode_t * cnp,topo_walk_t * wp,int flag,int bottomup)554 step_sibling(tnode_t *cnp, topo_walk_t *wp, int flag, int bottomup)
555 {
556 int status;
557 tnode_t *nnp;
558
559 nnp = topo_child_next(cnp->tn_parent, cnp);
560
561 if (nnp == NULL) {
562 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
563 "step_sibling: TOPO_WALK_TERMINATE for %s=%d\n",
564 cnp->tn_name, cnp->tn_instance);
565 return (TOPO_WALK_TERMINATE);
566 }
567
568 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
569 "step_sibling: through sibling node %s=%d to %s=%d\n",
570 cnp->tn_name, cnp->tn_instance, nnp->tn_name, nnp->tn_instance);
571
572 topo_node_hold(nnp); /* released on return from walk_step */
573 wp->tw_node = nnp;
574 if (bottomup == 1)
575 status = topo_walk_bottomup(wp, flag);
576 else
577 status = topo_walk_step(wp, flag);
578
579 return (status);
580 }
581
582 int
topo_walk_byid(topo_walk_t * wp,const char * name,topo_instance_t inst)583 topo_walk_byid(topo_walk_t *wp, const char *name, topo_instance_t inst)
584 {
585 int status;
586 tnode_t *nnp, *cnp;
587
588 cnp = wp->tw_node;
589 nnp = topo_node_lookup(cnp, name, inst);
590 if (nnp == NULL)
591 return (TOPO_WALK_TERMINATE);
592
593 topo_node_hold(nnp);
594 wp->tw_node = nnp;
595 if (wp->tw_mod != NULL)
596 status = wp->tw_cb(wp->tw_mod, nnp, wp->tw_pdata);
597 else
598 status = wp->tw_cb(wp->tw_thp, nnp, wp->tw_pdata);
599 topo_node_rele(nnp);
600 wp->tw_node = cnp;
601
602 return (status);
603 }
604
605 int
topo_walk_bysibling(topo_walk_t * wp,const char * name,topo_instance_t inst)606 topo_walk_bysibling(topo_walk_t *wp, const char *name, topo_instance_t inst)
607 {
608 int status;
609 tnode_t *cnp, *pnp;
610
611 cnp = wp->tw_node;
612 pnp = topo_node_parent(cnp);
613 assert(pnp != NULL);
614
615 topo_node_hold(pnp);
616 wp->tw_node = pnp;
617 status = topo_walk_byid(wp, name, inst);
618 topo_node_rele(pnp);
619 wp->tw_node = cnp;
620
621 return (status);
622 }
623
624 int
topo_walk_step(topo_walk_t * wp,int flag)625 topo_walk_step(topo_walk_t *wp, int flag)
626 {
627 int status;
628 tnode_t *cnp = wp->tw_node;
629
630 if (flag != TOPO_WALK_CHILD && flag != TOPO_WALK_SIBLING) {
631 topo_node_rele(cnp);
632 return (TOPO_WALK_ERR);
633 }
634
635 /*
636 * No more nodes to walk
637 */
638 if (cnp == NULL) {
639 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
640 "walk_step terminated\n");
641 topo_node_rele(cnp);
642 return (TOPO_WALK_TERMINATE);
643 }
644
645
646 if (wp->tw_mod != NULL)
647 status = wp->tw_cb(wp->tw_mod, cnp, wp->tw_pdata);
648 else
649 status = wp->tw_cb(wp->tw_thp, cnp, wp->tw_pdata);
650
651 /*
652 * Walker callback says we're done
653 */
654 if (status != TOPO_WALK_NEXT) {
655 topo_node_rele(cnp);
656 return (status);
657 }
658
659 if (flag == TOPO_WALK_CHILD)
660 status = step_child(cnp, wp, flag, 0);
661 else
662 status = step_sibling(cnp, wp, flag, 0);
663
664 /*
665 * No more nodes in this hash, skip to next node hash by stepping
666 * to next sibling (child-first walk) or next child (sibling-first
667 * walk).
668 */
669 if (status == TOPO_WALK_TERMINATE) {
670 if (flag == TOPO_WALK_CHILD)
671 status = step_sibling(cnp, wp, flag, 0);
672 else
673 status = step_child(cnp, wp, flag, 0);
674 }
675
676 topo_node_rele(cnp); /* done with current node */
677
678 return (status);
679 }
680
681 void
topo_walk_fini(topo_walk_t * wp)682 topo_walk_fini(topo_walk_t *wp)
683 {
684 if (wp == NULL)
685 return;
686
687 topo_node_rele(wp->tw_root);
688
689 topo_hdl_free(wp->tw_thp, wp, sizeof (topo_walk_t));
690 }
691
692 int
topo_walk_bottomup(topo_walk_t * wp,int flag)693 topo_walk_bottomup(topo_walk_t *wp, int flag)
694 {
695 int status;
696 tnode_t *cnp;
697
698 if (wp == NULL)
699 return (TOPO_WALK_ERR);
700
701 cnp = wp->tw_node;
702 if (flag != TOPO_WALK_CHILD && flag != TOPO_WALK_SIBLING) {
703 topo_node_rele(cnp);
704 return (TOPO_WALK_ERR);
705 }
706
707 /*
708 * End of the line
709 */
710 if (cnp == NULL) {
711 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
712 "walk_bottomup terminated\n");
713 topo_node_rele(cnp);
714 return (TOPO_WALK_TERMINATE);
715 }
716
717 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK,
718 "%s walk_bottomup through node %s=%d\n",
719 (flag == TOPO_WALK_CHILD ? "TOPO_WALK_CHILD" : "TOPO_WALK_SIBLING"),
720 cnp->tn_name, cnp->tn_instance);
721
722 if (flag == TOPO_WALK_CHILD)
723 status = step_child(cnp, wp, flag, 1);
724 else
725 status = step_sibling(cnp, wp, flag, 1);
726
727 /*
728 * At a leaf, run the callback
729 */
730 if (status == TOPO_WALK_TERMINATE) {
731 if ((status = wp->tw_cb(wp->tw_thp, cnp, wp->tw_pdata))
732 != TOPO_WALK_NEXT) {
733 topo_node_rele(cnp);
734 return (status);
735 }
736 }
737
738 /*
739 * Try next child or sibling
740 */
741 if (status == TOPO_WALK_NEXT) {
742 if (flag == TOPO_WALK_CHILD)
743 status = step_sibling(cnp, wp, flag, 1);
744 else
745 status = step_child(cnp, wp, flag, 1);
746 }
747
748 topo_node_rele(cnp); /* done with current node */
749
750 return (status);
751 }
752
753 di_node_t
topo_hdl_devinfo(topo_hdl_t * thp)754 topo_hdl_devinfo(topo_hdl_t *thp)
755 {
756 return (thp == NULL ? DI_NODE_NIL : thp->th_di);
757 }
758
759 di_prom_handle_t
topo_hdl_prominfo(topo_hdl_t * thp)760 topo_hdl_prominfo(topo_hdl_t *thp)
761 {
762 return (thp == NULL ? DI_PROM_HANDLE_NIL : thp->th_pi);
763 }
764