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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <pthread.h>
30 #include <assert.h>
31 #include <errno.h>
32 #include <dirent.h>
33 #include <limits.h>
34 #include <alloca.h>
35 #include <unistd.h>
36 #include <stdio.h>
37 #include <strings.h>
38 
39 #include <topo_mod.h>
40 
41 #include <topo_error.h>
42 #include <topo_module.h>
43 #include <topo_subr.h>
44 #include <topo_tree.h>
45 
46 static topo_imethod_t *
47 topo_method_lookup(tnode_t *node, const char *name)
48 {
49 	topo_imethod_t *mp;
50 
51 	topo_node_lock(node);
52 	for (mp = topo_list_next(&node->tn_methods); mp != NULL;
53 	    mp = topo_list_next(mp)) {
54 		if (strcmp(name, mp->tim_name) == 0) {
55 			topo_node_unlock(node);
56 			return (mp);
57 		}
58 	}
59 	topo_node_unlock(node);
60 
61 	return (NULL);
62 }
63 
64 static void
65 topo_method_enter(topo_imethod_t *mp)
66 {
67 	(void) pthread_mutex_lock(&mp->tim_lock);
68 
69 	while (mp->tim_busy != 0)
70 		(void) pthread_cond_wait(&mp->tim_cv, &mp->tim_lock);
71 
72 	++mp->tim_busy;
73 
74 	(void) pthread_mutex_unlock(&mp->tim_lock);
75 }
76 
77 static void
78 topo_method_exit(topo_imethod_t *mp)
79 {
80 	(void) pthread_mutex_lock(&mp->tim_lock);
81 	--mp->tim_busy;
82 
83 	assert(mp->tim_busy == 0);
84 
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(TOPO_DBG_ERR, "method registration failed for %s: %s\n",
103 	    mod->tm_name, topo_strerror(err));
104 
105 	return (topo_mod_seterrno(mod, err));
106 }
107 
108 int
109 topo_method_register(topo_mod_t *mod, tnode_t *node, const topo_method_t *mp)
110 {
111 	topo_imethod_t *imp;
112 	const topo_method_t *meth;
113 
114 	/*
115 	 * Initialize module methods
116 	 */
117 	for (meth = &mp[0]; meth->tm_name != NULL; meth++) {
118 
119 		if (topo_method_lookup(node, meth->tm_name) != NULL)
120 			continue;
121 
122 		if (meth->tm_stability < TOPO_STABILITY_INTERNAL ||
123 		    meth->tm_stability > TOPO_STABILITY_MAX ||
124 		    meth->tm_func == NULL)
125 			return (set_methregister_error(mod, node, NULL,
126 			    ETOPO_METHOD_INVAL));
127 
128 		imp = topo_mod_zalloc(mod, sizeof (topo_imethod_t));
129 		if (imp == NULL)
130 			return (set_methregister_error(mod, node, imp,
131 			    ETOPO_NOMEM));
132 
133 		if ((imp->tim_name = topo_mod_strdup(mod, meth->tm_name))
134 		    == NULL)
135 			return (set_methregister_error(mod, node, imp,
136 			    ETOPO_NOMEM));
137 
138 		if ((imp->tim_desc = topo_mod_strdup(mod, meth->tm_desc))
139 		    == NULL)
140 			return (set_methregister_error(mod, node, imp,
141 			    ETOPO_NOMEM));
142 
143 
144 		imp->tim_stability = meth->tm_stability;
145 		imp->tim_version = meth->tm_version;
146 		imp->tim_func = meth->tm_func;
147 		imp->tim_mod = mod;
148 
149 		topo_node_lock(node);
150 		topo_list_append(&node->tn_methods, imp);
151 		topo_node_unlock(node);
152 
153 		topo_dprintf(TOPO_DBG_MOD, "registered module %s method "
154 		    "%s for %s=%d\n", mod->tm_name, imp->tim_name,
155 		    topo_node_name(node), topo_node_instance(node));
156 
157 	}
158 
159 	return (0);
160 }
161 
162 void
163 topo_method_unregister(topo_mod_t *mod, tnode_t *node, const char *name)
164 {
165 	topo_imethod_t *mp;
166 
167 	topo_node_lock(node);
168 	for (mp = topo_list_next(&node->tn_methods); mp != NULL;
169 	    mp = topo_list_next(mp)) {
170 		if (strcmp(name, mp->tim_name) == 0)
171 			break;
172 	}
173 
174 	if (mp == NULL) {
175 		topo_node_unlock(node);
176 		return;
177 	}
178 
179 	topo_list_delete(&node->tn_methods, mp);
180 	topo_node_unlock(node);
181 
182 	if (mp->tim_name != NULL)
183 		topo_mod_strfree(mod, mp->tim_name);
184 	if (mp->tim_desc != NULL)
185 		topo_mod_strfree(mod, mp->tim_desc);
186 
187 	topo_mod_free(mod, mp, sizeof (topo_imethod_t));
188 }
189 
190 void
191 topo_method_unregister_all(topo_mod_t *mod, tnode_t *node)
192 {
193 	topo_imethod_t *mp;
194 
195 	topo_node_lock(node);
196 	while ((mp = topo_list_next(&node->tn_methods)) != NULL) {
197 		topo_list_delete(&node->tn_methods, mp);
198 		if (mp->tim_name != NULL)
199 			topo_mod_strfree(mod, mp->tim_name);
200 		if (mp->tim_desc != NULL)
201 			topo_mod_strfree(mod, mp->tim_desc);
202 		topo_mod_free(mod, mp, sizeof (topo_imethod_t));
203 	}
204 	topo_node_unlock(node);
205 }
206 
207 
208 int
209 topo_method_invoke(tnode_t *node, const char *method,
210     topo_version_t version, nvlist_t *in, nvlist_t **out, int *err)
211 {
212 	int rc;
213 	topo_imethod_t *mp;
214 
215 	topo_node_hold(node);
216 	for (mp = topo_list_next(&node->tn_methods); mp != NULL;
217 	    mp = topo_list_next(mp)) {
218 		if (strcmp(method, mp->tim_name) != 0)
219 			continue;
220 
221 		if (version < mp->tim_version) {
222 			*err = ETOPO_VER_NEW;
223 			topo_node_rele(node);
224 			return (-1);
225 		} else if (version > mp->tim_version) {
226 			*err = ETOPO_VER_OLD;
227 			topo_node_rele(node);
228 			return (-1);
229 		}
230 
231 		topo_method_enter(mp);
232 		if ((rc = mp->tim_func(mp->tim_mod, node, version, in, out))
233 		    < 0) {
234 			if (mp->tim_mod->tm_errno == 0)
235 				*err = ETOPO_METHOD_FAIL;
236 			else
237 				*err = mp->tim_mod->tm_errno;
238 		}
239 		topo_method_exit(mp);
240 
241 		topo_node_rele(node);
242 
243 		return (rc);
244 
245 	}
246 	topo_node_rele(node);
247 
248 	*err = ETOPO_METHOD_NOTSUP;
249 
250 	return (-1);
251 }
252