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 #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 static topo_imethod_t *
46 topo_method_lookup(tnode_t *node, const char *name)
47 {
48 	topo_imethod_t *mp;
49 
50 	topo_node_lock(node);
51 	for (mp = topo_list_next(&node->tn_methods); mp != NULL;
52 	    mp = topo_list_next(mp)) {
53 		if (strcmp(name, mp->tim_name) == 0) {
54 			topo_node_unlock(node);
55 			return (mp);
56 		}
57 	}
58 	topo_node_unlock(node);
59 
60 	return (NULL);
61 }
62 
63 static void
64 topo_method_enter(topo_imethod_t *mp)
65 {
66 	(void) pthread_mutex_lock(&mp->tim_lock);
67 
68 	while (mp->tim_busy != 0)
69 		(void) pthread_cond_wait(&mp->tim_cv, &mp->tim_lock);
70 
71 	++mp->tim_busy;
72 
73 	(void) pthread_mutex_unlock(&mp->tim_lock);
74 }
75 
76 static void
77 topo_method_exit(topo_imethod_t *mp)
78 {
79 	(void) pthread_mutex_lock(&mp->tim_lock);
80 	--mp->tim_busy;
81 
82 	assert(mp->tim_busy == 0);
83 
84 	(void) pthread_cond_broadcast(&mp->tim_cv);
85 	(void) pthread_mutex_unlock(&mp->tim_lock);
86 }
87 
88 static int
89 set_methregister_error(topo_mod_t *mod, tnode_t *node, topo_imethod_t *mp,
90     int err)
91 {
92 	if (mp != NULL) {
93 		topo_list_delete(&node->tn_methods, mp);
94 		if (mp->tim_name != NULL)
95 			topo_mod_strfree(mod, mp->tim_name);
96 		if (mp->tim_desc != NULL)
97 			topo_mod_strfree(mod, mp->tim_desc);
98 
99 		topo_mod_free(mod, mp, sizeof (topo_imethod_t));
100 	}
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 		if (topo_method_lookup(node, meth->tm_name) != NULL)
121 			continue;
122 
123 		if (meth->tm_stability < TOPO_STABILITY_INTERNAL ||
124 		    meth->tm_stability > TOPO_STABILITY_MAX ||
125 		    meth->tm_func == NULL)
126 			return (set_methregister_error(mod, node, NULL,
127 			    ETOPO_METHOD_INVAL));
128 
129 		imp = topo_mod_zalloc(mod, sizeof (topo_imethod_t));
130 		if (imp == NULL)
131 			return (set_methregister_error(mod, node, imp,
132 			    ETOPO_NOMEM));
133 
134 		if ((imp->tim_name = topo_mod_strdup(mod, meth->tm_name))
135 		    == NULL)
136 			return (set_methregister_error(mod, node, imp,
137 			    ETOPO_NOMEM));
138 
139 		if ((imp->tim_desc = topo_mod_strdup(mod, meth->tm_desc))
140 		    == NULL)
141 			return (set_methregister_error(mod, node, imp,
142 			    ETOPO_NOMEM));
143 
144 
145 		imp->tim_stability = meth->tm_stability;
146 		imp->tim_version = meth->tm_version;
147 		imp->tim_func = meth->tm_func;
148 		imp->tim_mod = mod;
149 
150 		topo_node_lock(node);
151 		topo_list_append(&node->tn_methods, imp);
152 		topo_node_unlock(node);
153 
154 		topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
155 		    "registered module %s method "
156 		    "%s for %s=%d\n", mod->tm_name, imp->tim_name,
157 		    topo_node_name(node), topo_node_instance(node));
158 
159 	}
160 
161 	return (0);
162 }
163 
164 void
165 topo_method_unregister(topo_mod_t *mod, tnode_t *node, const char *name)
166 {
167 	topo_imethod_t *mp;
168 
169 	topo_node_lock(node);
170 	for (mp = topo_list_next(&node->tn_methods); mp != NULL;
171 	    mp = topo_list_next(mp)) {
172 		if (strcmp(name, mp->tim_name) == 0)
173 			break;
174 	}
175 
176 	if (mp == NULL) {
177 		topo_node_unlock(node);
178 		return;
179 	}
180 
181 	topo_list_delete(&node->tn_methods, mp);
182 	topo_node_unlock(node);
183 
184 	if (mp->tim_name != NULL)
185 		topo_mod_strfree(mod, mp->tim_name);
186 	if (mp->tim_desc != NULL)
187 		topo_mod_strfree(mod, mp->tim_desc);
188 
189 	topo_mod_free(mod, mp, sizeof (topo_imethod_t));
190 }
191 
192 void
193 topo_method_unregister_all(topo_mod_t *mod, tnode_t *node)
194 {
195 	topo_imethod_t *mp;
196 
197 	topo_node_lock(node);
198 	while ((mp = topo_list_next(&node->tn_methods)) != NULL) {
199 		topo_list_delete(&node->tn_methods, mp);
200 		if (mp->tim_name != NULL)
201 			topo_mod_strfree(mod, mp->tim_name);
202 		if (mp->tim_desc != NULL)
203 			topo_mod_strfree(mod, mp->tim_desc);
204 		topo_mod_free(mod, mp, sizeof (topo_imethod_t));
205 	}
206 	topo_node_unlock(node);
207 }
208 
209 
210 int
211 topo_method_invoke(tnode_t *node, const char *method,
212     topo_version_t version, nvlist_t *in, nvlist_t **out, int *err)
213 {
214 	int rc;
215 	topo_imethod_t *mp;
216 
217 	topo_node_hold(node);
218 	for (mp = topo_list_next(&node->tn_methods); mp != NULL;
219 	    mp = topo_list_next(mp)) {
220 		if (strcmp(method, mp->tim_name) != 0)
221 			continue;
222 
223 		if (version < mp->tim_version) {
224 			*err = ETOPO_VER_NEW;
225 			topo_node_rele(node);
226 			return (-1);
227 		} else if (version > mp->tim_version) {
228 			*err = ETOPO_VER_OLD;
229 			topo_node_rele(node);
230 			return (-1);
231 		}
232 
233 		topo_method_enter(mp);
234 		if ((rc = mp->tim_func(mp->tim_mod, node, version, in, out))
235 		    < 0) {
236 			if (mp->tim_mod->tm_errno == 0)
237 				*err = ETOPO_METHOD_FAIL;
238 			else
239 				*err = mp->tim_mod->tm_errno;
240 		}
241 		topo_method_exit(mp);
242 
243 		topo_node_rele(node);
244 
245 		return (rc);
246 
247 	}
248 	topo_node_rele(node);
249 
250 	*err = ETOPO_METHOD_NOTSUP;
251 
252 	return (-1);
253 }
254