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