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.
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.
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]
22 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
26 * Snapshot Library Interfaces
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.
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.
43 * Walker Library Interfaces
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.
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.
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.
74 #include <sys/param.h>
75 #include <sys/types.h>
77 #include <sys/systeminfo.h>
78 #include <sys/utsname.h>
79 #include <uuid/uuid.h>
82 #include <fm/libtopo.h>
83 #include <sys/fm/protocol.h>
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>
91 static void topo_snap_destroy(topo_hdl_t
*);
94 set_open_errno(topo_hdl_t
*thp
, int *errp
, int err
)
105 topo_open(int version
, const char *rootdir
, int *errp
)
107 topo_hdl_t
*thp
= NULL
;
110 char platform
[MAXNAMELEN
];
111 char isa
[MAXNAMELEN
];
120 char *dbflags
, *dbout
;
122 if (version
!= TOPO_VERSION
)
123 return (set_open_errno(thp
, errp
, ETOPO_HDL_ABIVER
));
125 if (rootdir
!= NULL
&& stat(rootdir
, &st
) < 0)
126 return (set_open_errno(thp
, errp
, ETOPO_HDL_INVAL
));
128 if ((thp
= topo_zalloc(sizeof (topo_hdl_t
), 0)) == NULL
)
129 return (set_open_errno(thp
, errp
, ETOPO_NOMEM
));
131 (void) pthread_mutex_init(&thp
->th_lock
, NULL
);
133 if ((tap
= topo_zalloc(sizeof (topo_alloc_t
), 0)) == NULL
)
134 return (set_open_errno(thp
, errp
, ETOPO_NOMEM
));
137 * Install default allocators
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
);
148 if ((thp
->th_modhash
= topo_modhash_create(thp
)) == NULL
)
149 return (set_open_errno(thp
, errp
, ETOPO_NOMEM
));
152 * Set-up system information and search paths for modules
153 * and topology map files
155 if (rootdir
== NULL
) {
156 rootdir
= topo_hdl_strdup(thp
, "/");
157 thp
->th_rootdir
= (char *)rootdir
;
162 len
= strlen(rootdir
);
164 return (set_open_errno(thp
, errp
, EINVAL
));
166 if (rootdir
[len
- 1] != '/') {
167 rpath
= alloca(len
+ 2);
168 (void) snprintf(rpath
, len
+ 2, "%s/", rootdir
);
170 rpath
= (char *)rootdir
;
172 thp
->th_rootdir
= topo_hdl_strdup(thp
, rpath
);
177 (void) sysinfo(SI_PLATFORM
, platform
, sizeof (platform
));
178 (void) sysinfo(SI_ARCHITECTURE
, isa
, sizeof (isa
));
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
) {
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
);
195 thp
->th_product
= topo_hdl_strdup(thp
, thp
->th_platform
);
198 if (thp
->th_rootdir
== NULL
|| thp
->th_platform
== NULL
||
199 thp
->th_machine
== NULL
)
200 return (set_open_errno(thp
, errp
, ETOPO_NOMEM
));
202 dbflags
= getenv("TOPO_DEBUG");
203 dbout
= getenv("TOPO_DEBUG_OUT");
205 topo_debug_set(thp
, dbflags
, dbout
);
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
)));
218 topo_close(topo_hdl_t
*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
);
241 topo_snap_destroy(thp
);
246 while ((tp
= topo_list_next(&thp
->th_trees
)) != NULL
) {
247 topo_list_delete(&thp
->th_trees
, tp
);
248 topo_tree_destroy(tp
);
254 topo_modhash_unload_all(thp
);
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
));
261 topo_hdl_unlock(thp
);
263 topo_free(thp
, sizeof (topo_hdl_t
));
267 topo_snap_create(topo_hdl_t
*thp
, int *errp
, boolean_t need_force
)
273 if (thp
->th_uuid
!= NULL
) {
274 *errp
= ETOPO_HDL_UUID
;
275 topo_hdl_unlock(thp
);
279 if ((thp
->th_uuid
= topo_hdl_zalloc(thp
, TOPO_UUID_SIZE
)) == NULL
) {
281 topo_dprintf(thp
, TOPO_DBG_ERR
, "unable to allocate uuid: %s\n",
282 topo_strerror(*errp
));
283 topo_hdl_unlock(thp
);
288 uuid_unparse(uuid
, thp
->th_uuid
);
289 if ((ustr
= topo_hdl_strdup(thp
, thp
->th_uuid
)) == NULL
) {
291 topo_hdl_unlock(thp
);
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
);
301 thp
->th_di
= di_init("/", DINFOCACHE
);
303 thp
->th_pi
= di_prom_init();
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
;
311 if (thp
->th_di
!= DI_NODE_NIL
) {
313 thp
->th_di
= DI_NODE_NIL
;
315 if (thp
->th_pi
!= DI_PROM_HANDLE_NIL
) {
316 di_prom_fini(thp
->th_pi
);
317 thp
->th_pi
= DI_PROM_HANDLE_NIL
;
320 topo_hdl_strfree(thp
, ustr
);
321 topo_hdl_unlock(thp
);
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
));
334 topo_hdl_unlock(thp
);
341 topo_snap_log_create(topo_hdl_t
*thp
, const char *uuid
, int *errp
)
343 return ((char *)uuid
);
348 fac_walker(topo_hdl_t
*thp
, tnode_t
*node
, void *arg
)
353 if (topo_method_supported(node
, TOPO_METH_FAC_ENUM
, 0)) {
355 * If the facility enumeration method fails, note the failure,
356 * but continue on with the walk.
358 if (topo_method_invoke(node
, TOPO_METH_FAC_ENUM
, 0, NULL
, &out
,
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
));
366 return (TOPO_WALK_NEXT
);
373 topo_snap_hold(topo_hdl_t
*thp
, const char *uuid
, int *errp
)
383 if (thp
->th_debug
& TOPO_DBG_FORCE
) {
384 ret
= topo_snap_create(thp
, errp
, B_TRUE
);
386 ret
= topo_snap_create(thp
, errp
, B_FALSE
);
390 * Now walk the tree and invoke any facility enumeration methods
392 if (ret
!= NULL
&& getzoneid() == 0) {
393 if ((twp
= topo_walk_init(thp
, FM_FMRI_SCHEME_HC
,
394 fac_walker
, (void *)0, errp
)) == NULL
) {
397 (void) topo_walk_step(twp
, TOPO_WALK_CHILD
);
402 return (topo_snap_log_create(thp
, uuid
, errp
));
407 topo_walk_destroy(topo_hdl_t
*thp
, tnode_t
*node
, void *notused
)
411 cnode
= topo_child_first(node
);
414 return (TOPO_WALK_NEXT
);
416 topo_node_unbind(node
);
418 return (TOPO_WALK_NEXT
);
422 topo_snap_destroy(topo_hdl_t
*thp
)
428 topo_nodehash_t
*nhp
;
431 for (tp
= topo_list_next(&thp
->th_trees
); tp
!= NULL
;
432 tp
= topo_list_next(tp
)) {
437 * Clean-up tree nodes from the bottom-up
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
);
448 * Tidy-up the root node
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
);
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
));
466 * Clean-up our cached devinfo and prom tree handles.
468 if (thp
->th_di
!= DI_NODE_NIL
) {
470 thp
->th_di
= DI_NODE_NIL
;
472 if (thp
->th_pi
!= DI_PROM_HANDLE_NIL
) {
473 di_prom_fini(thp
->th_pi
);
474 thp
->th_pi
= DI_PROM_HANDLE_NIL
;
478 if (thp
->th_uuid
!= NULL
) {
479 topo_hdl_free(thp
, thp
->th_uuid
, TOPO_UUID_SIZE
);
485 topo_snap_release(topo_hdl_t
*thp
)
491 topo_snap_destroy(thp
);
492 topo_hdl_unlock(thp
);
496 topo_walk_init(topo_hdl_t
*thp
, const char *scheme
, topo_walk_cb_t cb_f
,
497 void *pdata
, int *errp
)
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) {
507 * Hold the root node and start walk at the first
510 assert(tp
->tt_root
!= NULL
);
512 if ((wp
= topo_node_walk_init(thp
, NULL
, tp
->tt_root
,
513 cb_f
, pdata
, errp
)) == NULL
) /* errp set */
520 *errp
= ETOPO_WALK_NOTFOUND
;
525 step_child(tnode_t
*cnp
, topo_walk_t
*wp
, int flag
, int bottomup
)
530 nnp
= topo_child_first(cnp
);
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
);
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
);
543 topo_node_hold(nnp
); /* released on return from walk_step */
546 status
= topo_walk_bottomup(wp
, flag
);
548 status
= topo_walk_step(wp
, flag
);
554 step_sibling(tnode_t
*cnp
, topo_walk_t
*wp
, int flag
, int bottomup
)
559 nnp
= topo_child_next(cnp
->tn_parent
, cnp
);
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
);
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
);
572 topo_node_hold(nnp
); /* released on return from walk_step */
575 status
= topo_walk_bottomup(wp
, flag
);
577 status
= topo_walk_step(wp
, flag
);
583 topo_walk_byid(topo_walk_t
*wp
, const char *name
, topo_instance_t inst
)
589 nnp
= topo_node_lookup(cnp
, name
, inst
);
591 return (TOPO_WALK_TERMINATE
);
595 if (wp
->tw_mod
!= NULL
)
596 status
= wp
->tw_cb(wp
->tw_mod
, nnp
, wp
->tw_pdata
);
598 status
= wp
->tw_cb(wp
->tw_thp
, nnp
, wp
->tw_pdata
);
606 topo_walk_bysibling(topo_walk_t
*wp
, const char *name
, topo_instance_t inst
)
612 pnp
= topo_node_parent(cnp
);
617 status
= topo_walk_byid(wp
, name
, inst
);
625 topo_walk_step(topo_walk_t
*wp
, int flag
)
628 tnode_t
*cnp
= wp
->tw_node
;
630 if (flag
!= TOPO_WALK_CHILD
&& flag
!= TOPO_WALK_SIBLING
) {
632 return (TOPO_WALK_ERR
);
636 * No more nodes to walk
639 topo_dprintf(wp
->tw_thp
, TOPO_DBG_WALK
,
640 "walk_step terminated\n");
642 return (TOPO_WALK_TERMINATE
);
646 if (wp
->tw_mod
!= NULL
)
647 status
= wp
->tw_cb(wp
->tw_mod
, cnp
, wp
->tw_pdata
);
649 status
= wp
->tw_cb(wp
->tw_thp
, cnp
, wp
->tw_pdata
);
652 * Walker callback says we're done
654 if (status
!= TOPO_WALK_NEXT
) {
659 if (flag
== TOPO_WALK_CHILD
)
660 status
= step_child(cnp
, wp
, flag
, 0);
662 status
= step_sibling(cnp
, wp
, flag
, 0);
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
669 if (status
== TOPO_WALK_TERMINATE
) {
670 if (flag
== TOPO_WALK_CHILD
)
671 status
= step_sibling(cnp
, wp
, flag
, 0);
673 status
= step_child(cnp
, wp
, flag
, 0);
676 topo_node_rele(cnp
); /* done with current node */
682 topo_walk_fini(topo_walk_t
*wp
)
687 topo_node_rele(wp
->tw_root
);
689 topo_hdl_free(wp
->tw_thp
, wp
, sizeof (topo_walk_t
));
693 topo_walk_bottomup(topo_walk_t
*wp
, int flag
)
699 return (TOPO_WALK_ERR
);
702 if (flag
!= TOPO_WALK_CHILD
&& flag
!= TOPO_WALK_SIBLING
) {
704 return (TOPO_WALK_ERR
);
711 topo_dprintf(wp
->tw_thp
, TOPO_DBG_WALK
,
712 "walk_bottomup terminated\n");
714 return (TOPO_WALK_TERMINATE
);
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
);
722 if (flag
== TOPO_WALK_CHILD
)
723 status
= step_child(cnp
, wp
, flag
, 1);
725 status
= step_sibling(cnp
, wp
, flag
, 1);
728 * At a leaf, run the callback
730 if (status
== TOPO_WALK_TERMINATE
) {
731 if ((status
= wp
->tw_cb(wp
->tw_thp
, cnp
, wp
->tw_pdata
))
739 * Try next child or sibling
741 if (status
== TOPO_WALK_NEXT
) {
742 if (flag
== TOPO_WALK_CHILD
)
743 status
= step_sibling(cnp
, wp
, flag
, 1);
745 status
= step_child(cnp
, wp
, flag
, 1);
748 topo_node_rele(cnp
); /* done with current node */
754 topo_hdl_devinfo(topo_hdl_t
*thp
)
756 return (thp
== NULL
? DI_NODE_NIL
: thp
->th_di
);
760 topo_hdl_prominfo(topo_hdl_t
*thp
)
762 return (thp
== NULL
? DI_PROM_HANDLE_NIL
: thp
->th_pi
);