xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_method.c (revision 80ab886d233f514d54c2a6bdeb9fdfd951bd6881)
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(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