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
47static uu_list_pool_t	*defer_pool = NULL;
48static uu_list_t	*defer_list = NULL;
49
50struct 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};
60typedef struct pi_defernode_s pi_defernode_t;
61
62/* Routines to handle the list of topology parents and mde_nodes */
63static int  pi_deferlist_create(topo_mod_t *);
64static 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 */
70int
71pi_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 */
114int
115pi_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
202static int
203pi_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 */
226static int
227pi_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