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 2007 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 #include <pthread.h>
29 #include <assert.h>
30 #include <errno.h>
31 #include <dirent.h>
32 #include <limits.h>
33 #include <alloca.h>
34 #include <unistd.h>
35 #include <stdio.h>
36 #include <strings.h>
37 
38 #include <topo_mod.h>
39 
40 #include <topo_error.h>
41 #include <topo_module.h>
42 #include <topo_subr.h>
43 #include <topo_tree.h>
44 
45 topo_imethod_t *
46 topo_method_lookup(tnode_t *node, const char *name)
47 {
48 	topo_imethod_t *mp;
49 
50 	for (mp = topo_list_next(&node->tn_methods); mp != NULL;
51 	    mp = topo_list_next(mp)) {
52 		if (strcmp(name, mp->tim_name) == 0) {
53 			topo_node_unlock(node);
54 			return (mp);
55 		}
56 	}
57 
58 	return (NULL);
59 }
60 
61 static void
62 topo_method_enter(topo_imethod_t *mp)
63 {
64 	(void) pthread_mutex_lock(&mp->tim_lock);
65 
66 	while (mp->tim_busy != 0)
67 		(void) pthread_cond_wait(&mp->tim_cv, &mp->tim_lock);
68 
69 	++mp->tim_busy;
70 
71 	(void) pthread_mutex_unlock(&mp->tim_lock);
72 }
73 
74 static void
75 topo_method_exit(topo_imethod_t *mp)
76 {
77 	(void) pthread_mutex_lock(&mp->tim_lock);
78 	--mp->tim_busy;
79 
80 	assert(mp->tim_busy == 0);
81 
82 	(void) pthread_cond_broadcast(&mp->tim_cv);
83 	(void) pthread_mutex_unlock(&mp->tim_lock);
84 }
85 
86 static int
87 set_methregister_error(topo_mod_t *mod, tnode_t *node, topo_imethod_t *mp,
88     int err)
89 {
90 	if (mp != NULL) {
91 		topo_list_delete(&node->tn_methods, mp);
92 		if (mp->tim_name != NULL)
93 			topo_mod_strfree(mod, mp->tim_name);
94 		if (mp->tim_desc != NULL)
95 			topo_mod_strfree(mod, mp->tim_desc);
96 
97 		topo_mod_free(mod, mp, sizeof (topo_imethod_t));
98 	}
99 
100 	topo_node_unlock(node);
101 
102 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR,
103 	    "method registration failed for %s: %s\n",
104 	    mod->tm_name, topo_strerror(err));
105 
106 	return (topo_mod_seterrno(mod, err));
107 }
108 
109 int
110 topo_method_register(topo_mod_t *mod, tnode_t *node, const topo_method_t *mp)
111 {
112 	topo_imethod_t *imp;
113 	const topo_method_t *meth;
114 
115 	/*
116 	 * Initialize module methods
117 	 */
118 	for (meth = &mp[0]; meth->tm_name != NULL; meth++) {
119 
120 		topo_node_lock(node);
121 		if (topo_method_lookup(node, meth->tm_name) != NULL) {
122 			topo_node_unlock(node);
123 			continue;
124 		}
125 
126 		if (meth->tm_stability < TOPO_STABILITY_INTERNAL ||
127 		    meth->tm_stability > TOPO_STABILITY_MAX ||
128 		    meth->tm_func == NULL)
129 			return (set_methregister_error(mod, node, NULL,
130 			    ETOPO_METHOD_INVAL));
131 
132 		imp = topo_mod_zalloc(mod, sizeof (topo_imethod_t));
133 		if (imp == NULL)
134 			return (set_methregister_error(mod, node, imp,
135 			    ETOPO_METHOD_NOMEM));
136 
137 		if ((imp->tim_name = topo_mod_strdup(mod, meth->tm_name))
138 		    == NULL)
139 			return (set_methregister_error(mod, node, imp,
140 			    ETOPO_METHOD_NOMEM));
141 
142 		if ((imp->tim_desc = topo_mod_strdup(mod, meth->tm_desc))
143 		    == NULL)
144 			return (set_methregister_error(mod, node, imp,
145 			    ETOPO_METHOD_NOMEM));
146 
147 
148 		imp->tim_stability = meth->tm_stability;
149 		imp->tim_version = meth->tm_version;
150 		imp->tim_func = meth->tm_func;
151 		imp->tim_mod = mod;
152 
153 		topo_list_append(&node->tn_methods, imp);
154 		topo_node_unlock(node);
155 
156 		topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
157 		    "registered module %s method "
158 		    "%s for %s=%d\n", mod->tm_name, imp->tim_name,
159 		    topo_node_name(node), topo_node_instance(node));
160 
161 	}
162 
163 	return (0);
164 }
165 
166 void
167 topo_method_unregister(topo_mod_t *mod, tnode_t *node, const char *name)
168 {
169 	topo_imethod_t *mp;
170 
171 	topo_node_lock(node);
172 	for (mp = topo_list_next(&node->tn_methods); mp != NULL;
173 	    mp = topo_list_next(mp)) {
174 		if (strcmp(name, mp->tim_name) == 0)
175 			break;
176 	}
177 
178 	if (mp == NULL) {
179 		topo_node_unlock(node);
180 		return;
181 	}
182 
183 	topo_list_delete(&node->tn_methods, mp);
184 	topo_node_unlock(node);
185 
186 	if (mp->tim_name != NULL)
187 		topo_mod_strfree(mod, mp->tim_name);
188 	if (mp->tim_desc != NULL)
189 		topo_mod_strfree(mod, mp->tim_desc);
190 
191 	topo_mod_free(mod, mp, sizeof (topo_imethod_t));
192 }
193 
194 void
195 topo_method_unregister_all(topo_mod_t *mod, tnode_t *node)
196 {
197 	topo_imethod_t *mp;
198 
199 	topo_node_lock(node);
200 	while ((mp = topo_list_next(&node->tn_methods)) != NULL) {
201 		topo_list_delete(&node->tn_methods, mp);
202 		if (mp->tim_name != NULL)
203 			topo_mod_strfree(mod, mp->tim_name);
204 		if (mp->tim_desc != NULL)
205 			topo_mod_strfree(mod, mp->tim_desc);
206 		topo_mod_free(mod, mp, sizeof (topo_imethod_t));
207 	}
208 	topo_node_unlock(node);
209 }
210 
211 
212 int
213 topo_method_call(tnode_t *node, const char *method,
214     topo_version_t version, nvlist_t *in, nvlist_t **out, int *err)
215 {
216 	int rc;
217 	topo_imethod_t *mp;
218 
219 	for (mp = topo_list_next(&node->tn_methods); mp != NULL;
220 	    mp = topo_list_next(mp)) {
221 		if (strcmp(method, mp->tim_name) != 0)
222 			continue;
223 
224 		if (version < mp->tim_version) {
225 			*err = ETOPO_METHOD_VEROLD;
226 			return (-1);
227 		} else if (version > mp->tim_version) {
228 			*err = ETOPO_METHOD_VERNEW;
229 			return (-1);
230 		}
231 
232 		topo_method_enter(mp);
233 		if ((rc = mp->tim_func(mp->tim_mod, node, version, in, out))
234 		    < 0) {
235 			if (mp->tim_mod->tm_errno == 0)
236 				*err = ETOPO_METHOD_FAIL;
237 			else
238 				*err = mp->tim_mod->tm_errno;
239 		}
240 		topo_method_exit(mp);
241 
242 		return (rc);
243 
244 	}
245 
246 	*err = ETOPO_METHOD_NOTSUP;
247 	return (-1);
248 }
249 
250 int
251 topo_method_invoke(tnode_t *node, const char *method,
252     topo_version_t version, nvlist_t *in, nvlist_t **out, int *err)
253 {
254 	int rc;
255 
256 	topo_node_hold(node);
257 	rc = topo_method_call(node, method, version, in, out, err);
258 	topo_node_rele(node);
259 
260 	return (rc);
261 }
262