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