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 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Some topology creation routines may need to defer completing enumeration
29  * until after the entire PRI graph has been visited.  This file includes
30  * the interfaces necessary to permit these routines to do this in a general
31  * way.
32  */
33 
34 #include <sys/types.h>
35 #include <sys/time.h>
36 #include <stddef.h>
37 #include <inttypes.h>
38 #include <strings.h>
39 #include <string.h>
40 #include <libuutil.h>
41 #include <libnvpair.h>
42 #include <sys/mdesc.h>
43 #include <fm/topo_mod.h>
44 #include <fm/topo_hc.h>
45 #include "pi_impl.h"
46 
47 static uu_list_pool_t	*defer_pool = NULL;
48 static uu_list_t	*defer_list = NULL;
49 
50 struct pi_defernode_s {
51 	uu_list_node_t	defer_node;
52 
53 	mde_cookie_t	mde_node;	/* MDE node index */
54 	tnode_t		*t_parent;	/* Parent topology node */
55 	tnode_t		*t_node;	/* Topo node associated with MDE node */
56 	void		*private;	/* Private data for defer routine */
57 
58 	pi_deferenum_fn_t *func;	/* Defered enumeration routine */
59 };
60 typedef struct pi_defernode_s pi_defernode_t;
61 
62 /* Routines to handle the list of topology parents and mde_nodes */
63 static int  pi_deferlist_create(topo_mod_t *);
64 static int  pi_deferlist_compare(const void *, const void *, void *);
65 
66 
67 /*
68  * Add a new routine to the list of deferred enumeration routines
69  */
70 int
pi_defer_add(topo_mod_t * mod,mde_cookie_t mde_node,tnode_t * t_parent,tnode_t * t_node,pi_deferenum_fn_t func,void * private)71 pi_defer_add(topo_mod_t *mod, mde_cookie_t mde_node, tnode_t *t_parent,
72     tnode_t *t_node, pi_deferenum_fn_t func, void *private)
73 {
74 	int		result;
75 	uu_list_index_t	idx;
76 	pi_defernode_t	*dnp;
77 
78 	if (defer_list == NULL) {
79 		result = pi_deferlist_create(mod);
80 		if (result != 0) {
81 			return (result);
82 		}
83 	}
84 
85 	/*
86 	 * Create a data structure to store information about the node for
87 	 * which to defer enumeration.  The defer_pool is created by the
88 	 * list creation routine, above.
89 	 */
90 	dnp = topo_mod_zalloc(mod, sizeof (pi_defernode_t));
91 	if (dnp == NULL) {
92 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
93 		return (-1);
94 	}
95 	uu_list_node_init(dnp, &(dnp->defer_node), defer_pool);
96 
97 	dnp->mde_node	= mde_node;
98 	dnp->t_parent	= t_parent;
99 	dnp->t_node	= t_node;
100 	dnp->private	= private;
101 	dnp->func	= func;
102 
103 	(void) uu_list_find(defer_list, dnp, NULL, &idx);
104 	uu_list_insert(defer_list, dnp, idx);
105 
106 	return (0);
107 }
108 
109 
110 /*
111  * Execute the list of deferred enumeration routines, destroying the list as
112  * we go.
113  */
114 int
pi_defer_exec(topo_mod_t * mod,md_t * mdp)115 pi_defer_exec(topo_mod_t *mod, md_t *mdp)
116 {
117 	int		result;
118 
119 	void		*dvp;
120 	pi_defernode_t	*dp;
121 	topo_instance_t	inst;
122 	mde_cookie_t	mde_node;
123 	tnode_t		*t_parent;
124 	tnode_t		*t_node;
125 	void		*private;
126 	char		*hc_name;
127 
128 	pi_deferenum_fn_t *func;
129 
130 	topo_mod_dprintf(mod, "beginning deferred enumerator execution\n");
131 	if (defer_list == NULL) {
132 		topo_mod_dprintf(mod, "no deferred enumerators.  done.\n");
133 		return (0);
134 	}
135 
136 	while ((dvp = uu_list_first(defer_list)) != NULL) {
137 		/* Extract the necessary information from the defernode_t */
138 		dp = (pi_defernode_t *)dvp;
139 		mde_node = dp->mde_node;
140 		t_parent = dp->t_parent;
141 		t_node   = dp->t_node;
142 		private  = dp->private;
143 		func	 = dp->func;
144 
145 		/*
146 		 * Remove the element from the list.  Once we are done calling
147 		 * the routine we do not need it any more.
148 		 */
149 		uu_list_remove(defer_list, dvp);
150 		uu_list_node_fini(dp, &(dp->defer_node), defer_pool);
151 		topo_mod_free(mod, dp, sizeof (pi_defernode_t));
152 
153 		/* Get the instance value from the mde node */
154 		if (pi_get_instance(mod, mdp, mde_node, &inst) != 0) {
155 			topo_mod_dprintf(mod, "deferred node_0x%llx invalid\n",
156 			    (uint64_t)mde_node);
157 
158 			/* Move on to the next node */
159 			(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
160 			continue;
161 		}
162 
163 		/* Get the hc name from the mde node */
164 		hc_name = pi_get_topo_hc_name(mod, mdp, mde_node);
165 		if (hc_name == NULL) {
166 			topo_mod_dprintf(mod,
167 			    "deferred node_0x%llx has invalid NULL hc_name\n",
168 			    (uint64_t)mde_node);
169 
170 			/* Move on to the next node */
171 			(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
172 			continue;
173 		}
174 		topo_mod_dprintf(mod,
175 		    "calling deferred enumerator for node_0x%llx\n",
176 		    (uint64_t)mde_node);
177 
178 		/* Call the deferred enumeration function */
179 		result = (func)(mod, mdp, mde_node, inst, t_parent, hc_name,
180 		    t_node, private);
181 		if (result != 0) {
182 			topo_mod_dprintf(mod,
183 			    "deferred enumeration for node_0x%llx failed\n",
184 			    (uint64_t)mde_node);
185 			(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
186 		}
187 
188 		/* Clean up from the deferred call */
189 		topo_mod_strfree(mod, hc_name);
190 	}
191 	topo_mod_dprintf(mod, "deferred enumeration completed.\n");
192 
193 	uu_list_destroy(defer_list);
194 	uu_list_pool_destroy(defer_pool);
195 	defer_list = NULL;
196 	defer_pool = NULL;
197 
198 	return (0);
199 }
200 
201 
202 static int
pi_deferlist_create(topo_mod_t * mod)203 pi_deferlist_create(topo_mod_t *mod)
204 {
205 	/* Initialize the uutil list structure */
206 	defer_pool = uu_list_pool_create("pi_defer_pool",
207 	    sizeof (pi_defernode_t), offsetof(pi_defernode_t, defer_node),
208 	    pi_deferlist_compare, 0);
209 	if (defer_pool == NULL) {
210 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
211 		return (-1);
212 	}
213 	defer_list = uu_list_create(defer_pool, NULL, 0);
214 	if (defer_list == NULL) {
215 		uu_list_pool_destroy(defer_pool);
216 		defer_pool = NULL;
217 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
218 		return (-1);
219 	}
220 
221 	return (0);
222 }
223 
224 
225 /* ARGSUSED */
226 static int
pi_deferlist_compare(const void * l_arg,const void * r_arg,void * private)227 pi_deferlist_compare(const void *l_arg, const void *r_arg, void *private)
228 {
229 	pi_defernode_t	*lp = (pi_defernode_t *)l_arg;
230 	pi_defernode_t	*rp = (pi_defernode_t *)r_arg;
231 
232 	if (lp->func != rp->func) {
233 		return (1);
234 	}
235 	if (lp->t_parent != rp->t_parent) {
236 		return (-1);
237 	}
238 	return (0);
239 }
240