xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_method.c (revision 2c20fda2feee4850b7ec4297e4120bc72c2bac09)
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 #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 /*
62  * Simple API to determine if the specified node supports a given topo method
63  * (specified by the method name and version).  Returns true if supported, false
64  * otherwise.
65  */
66 boolean_t
67 topo_method_supported(tnode_t *node, const char *name, topo_version_t vers)
68 {
69 	topo_imethod_t *mp;
70 
71 	topo_node_lock(node);
72 	for (mp = topo_list_next(&node->tn_methods); mp != NULL;
73 	    mp = topo_list_next(mp)) {
74 		if ((strcmp(name, mp->tim_name) == 0) &&
75 		    (vers == mp->tim_version)) {
76 			topo_node_unlock(node);
77 			return (B_TRUE);
78 		}
79 	}
80 	topo_node_unlock(node);
81 	return (B_FALSE);
82 }
83 
84 static void
85 topo_method_enter(topo_imethod_t *mp)
86 {
87 	(void) pthread_mutex_lock(&mp->tim_lock);
88 
89 	while (mp->tim_busy != 0)
90 		(void) pthread_cond_wait(&mp->tim_cv, &mp->tim_lock);
91 
92 	++mp->tim_busy;
93 
94 	(void) pthread_mutex_unlock(&mp->tim_lock);
95 }
96 
97 static void
98 topo_method_exit(topo_imethod_t *mp)
99 {
100 	(void) pthread_mutex_lock(&mp->tim_lock);
101 	--mp->tim_busy;
102 
103 	assert(mp->tim_busy == 0);
104 
105 	(void) pthread_cond_broadcast(&mp->tim_cv);
106 	(void) pthread_mutex_unlock(&mp->tim_lock);
107 }
108 
109 static int
110 set_methregister_error(topo_mod_t *mod, tnode_t *node, topo_imethod_t *mp,
111     int err)
112 {
113 	if (mp != NULL) {
114 		topo_list_delete(&node->tn_methods, mp);
115 		if (mp->tim_name != NULL)
116 			topo_mod_strfree(mod, mp->tim_name);
117 		if (mp->tim_desc != NULL)
118 			topo_mod_strfree(mod, mp->tim_desc);
119 
120 		topo_mod_free(mod, mp, sizeof (topo_imethod_t));
121 	}
122 
123 	topo_node_unlock(node);
124 
125 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR,
126 	    "method registration failed for %s: %s\n",
127 	    mod->tm_name, topo_strerror(err));
128 
129 	return (topo_mod_seterrno(mod, err));
130 }
131 
132 int
133 topo_method_register(topo_mod_t *mod, tnode_t *node, const topo_method_t *mp)
134 {
135 	topo_imethod_t *imp;
136 	const topo_method_t *meth;
137 
138 	/*
139 	 * Initialize module methods
140 	 */
141 	for (meth = &mp[0]; meth->tm_name != NULL; meth++) {
142 
143 		topo_node_lock(node);
144 		if (topo_method_lookup(node, meth->tm_name) != NULL) {
145 			topo_node_unlock(node);
146 			continue;
147 		}
148 
149 		if (meth->tm_stability < TOPO_STABILITY_INTERNAL ||
150 		    meth->tm_stability > TOPO_STABILITY_MAX ||
151 		    meth->tm_func == NULL)
152 			return (set_methregister_error(mod, node, NULL,
153 			    ETOPO_METHOD_INVAL));
154 
155 		imp = topo_mod_zalloc(mod, sizeof (topo_imethod_t));
156 		if (imp == NULL)
157 			return (set_methregister_error(mod, node, imp,
158 			    ETOPO_METHOD_NOMEM));
159 
160 		if ((imp->tim_name = topo_mod_strdup(mod, meth->tm_name))
161 		    == NULL)
162 			return (set_methregister_error(mod, node, imp,
163 			    ETOPO_METHOD_NOMEM));
164 
165 		if ((imp->tim_desc = topo_mod_strdup(mod, meth->tm_desc))
166 		    == NULL)
167 			return (set_methregister_error(mod, node, imp,
168 			    ETOPO_METHOD_NOMEM));
169 
170 
171 		imp->tim_stability = meth->tm_stability;
172 		imp->tim_version = meth->tm_version;
173 		imp->tim_func = meth->tm_func;
174 		imp->tim_mod = mod;
175 
176 		topo_list_append(&node->tn_methods, imp);
177 		topo_node_unlock(node);
178 
179 		topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
180 		    "registered module %s method "
181 		    "%s for %s=%d\n", mod->tm_name, imp->tim_name,
182 		    topo_node_name(node), topo_node_instance(node));
183 
184 	}
185 
186 	return (0);
187 }
188 
189 void
190 topo_method_unregister(topo_mod_t *mod, tnode_t *node, const char *name)
191 {
192 	topo_imethod_t *mp;
193 
194 	topo_node_lock(node);
195 	for (mp = topo_list_next(&node->tn_methods); mp != NULL;
196 	    mp = topo_list_next(mp)) {
197 		if (strcmp(name, mp->tim_name) == 0)
198 			break;
199 	}
200 
201 	if (mp == NULL) {
202 		topo_node_unlock(node);
203 		return;
204 	}
205 
206 	topo_list_delete(&node->tn_methods, mp);
207 	topo_node_unlock(node);
208 
209 	if (mp->tim_name != NULL)
210 		topo_mod_strfree(mod, mp->tim_name);
211 	if (mp->tim_desc != NULL)
212 		topo_mod_strfree(mod, mp->tim_desc);
213 
214 	topo_mod_free(mod, mp, sizeof (topo_imethod_t));
215 }
216 
217 void
218 topo_method_unregister_all(topo_mod_t *mod, tnode_t *node)
219 {
220 	topo_imethod_t *mp;
221 
222 	topo_node_lock(node);
223 	while ((mp = topo_list_next(&node->tn_methods)) != NULL) {
224 		topo_list_delete(&node->tn_methods, mp);
225 		if (mp->tim_name != NULL)
226 			topo_mod_strfree(mod, mp->tim_name);
227 		if (mp->tim_desc != NULL)
228 			topo_mod_strfree(mod, mp->tim_desc);
229 		topo_mod_free(mod, mp, sizeof (topo_imethod_t));
230 	}
231 	topo_node_unlock(node);
232 }
233 
234 
235 int
236 topo_method_call(tnode_t *node, const char *method,
237     topo_version_t version, nvlist_t *in, nvlist_t **out, int *err)
238 {
239 	int rc;
240 	topo_imethod_t *mp;
241 
242 	for (mp = topo_list_next(&node->tn_methods); mp != NULL;
243 	    mp = topo_list_next(mp)) {
244 		if (strcmp(method, mp->tim_name) != 0)
245 			continue;
246 
247 		if (version < mp->tim_version) {
248 			*err = ETOPO_METHOD_VEROLD;
249 			return (-1);
250 		} else if (version > mp->tim_version) {
251 			*err = ETOPO_METHOD_VERNEW;
252 			return (-1);
253 		}
254 
255 		topo_method_enter(mp);
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 		topo_method_exit(mp);
264 
265 		return (rc);
266 
267 	}
268 
269 	*err = ETOPO_METHOD_NOTSUP;
270 	return (-1);
271 }
272 
273 int
274 topo_method_invoke(tnode_t *node, const char *method,
275     topo_version_t version, nvlist_t *in, nvlist_t **out, int *err)
276 {
277 	int rc;
278 
279 	topo_node_hold(node);
280 	rc = topo_method_call(node, method, version, in, out, err);
281 	topo_node_rele(node);
282 
283 	return (rc);
284 }
285