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