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 2007 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 <signal.h> 29 #include <dirent.h> 30 #include <limits.h> 31 #include <alloca.h> 32 #include <unistd.h> 33 #include <stdio.h> 34 #include <pthread.h> 35 #include <errno.h> 36 #include <strings.h> 37 #include <assert.h> 38 #include <sys/nvpair.h> 39 40 #include <topo_string.h> 41 #include <topo_alloc.h> 42 #include <topo_module.h> 43 #include <topo_error.h> 44 #include <topo_subr.h> 45 46 extern nv_alloc_ops_t topo_nv_alloc_ops; 47 48 void 49 topo_mod_release(topo_mod_t *mod, tnode_t *node) 50 { 51 topo_mod_enter(mod); 52 53 if (mod->tm_info->tmi_ops->tmo_release != NULL) 54 mod->tm_info->tmi_ops->tmo_release(mod, node); 55 56 topo_mod_exit(mod); 57 } 58 59 void 60 topo_mod_hold(topo_mod_t *mod) 61 { 62 (void) pthread_mutex_lock(&mod->tm_lock); 63 mod->tm_refs++; 64 assert(mod->tm_refs != 0); 65 (void) pthread_mutex_unlock(&mod->tm_lock); 66 } 67 68 void 69 topo_mod_rele(topo_mod_t *mod) 70 { 71 assert(mod->tm_refs != 0); 72 73 (void) pthread_mutex_lock(&mod->tm_lock); 74 75 /* 76 * Lazy unload module 77 */ 78 if (--mod->tm_refs == 0) 79 topo_modhash_unload(mod); 80 else 81 (void) pthread_mutex_unlock(&mod->tm_lock); 82 } 83 84 void 85 topo_mod_enter(topo_mod_t *mod) 86 { 87 (void) pthread_mutex_lock(&mod->tm_lock); 88 89 while (mod->tm_busy != 0) 90 (void) pthread_cond_wait(&mod->tm_cv, &mod->tm_lock); 91 92 ++mod->tm_busy; 93 94 (void) pthread_mutex_unlock(&mod->tm_lock); 95 } 96 97 void 98 topo_mod_exit(topo_mod_t *mod) 99 { 100 (void) pthread_mutex_lock(&mod->tm_lock); 101 --mod->tm_busy; 102 103 assert(mod->tm_busy == 0); 104 105 (void) pthread_cond_broadcast(&mod->tm_cv); 106 (void) pthread_mutex_unlock(&mod->tm_lock); 107 } 108 109 static void 110 topo_modhash_lock(topo_modhash_t *mhp) 111 { 112 (void) pthread_mutex_lock(&mhp->mh_lock); 113 } 114 115 static void 116 topo_modhash_unlock(topo_modhash_t *mhp) 117 { 118 (void) pthread_mutex_unlock(&mhp->mh_lock); 119 } 120 121 static void 122 topo_mod_stop(topo_mod_t *mod) 123 { 124 if (mod->tm_flags & TOPO_MOD_INIT) { 125 mod->tm_mops->mop_fini(mod); 126 if (mod->tm_flags & TOPO_MOD_REG) 127 topo_mod_unregister(mod); 128 } 129 130 mod->tm_flags = TOPO_MOD_FINI; 131 132 topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC, 133 "module %s stopped\n", mod->tm_name); 134 } 135 136 static int 137 topo_mod_start(topo_mod_t *mod, topo_version_t version) 138 { 139 topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC, 140 "starting module %s\n", mod->tm_name); 141 142 if (mod->tm_mops->mop_init(mod, version) != 0) { 143 if (mod->tm_errno == 0) 144 mod->tm_errno = ETOPO_MOD_INIT; 145 topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, 146 "module %s failed to initialize: %s\n", mod->tm_name, 147 topo_strerror(mod->tm_errno)); 148 return (-1); 149 } 150 151 mod->tm_flags |= TOPO_MOD_INIT; 152 153 if (!(mod->tm_flags & TOPO_MOD_REG)) { 154 topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, 155 "module %s failed to register\n", mod->tm_name); 156 mod->tm_errno = ETOPO_MOD_NOREG; 157 topo_mod_stop(mod); 158 return (-1); 159 } 160 161 return (0); 162 } 163 164 topo_mod_t * 165 topo_mod_lookup(topo_hdl_t *thp, const char *name, int bump) 166 { 167 topo_mod_t *mod; 168 topo_modhash_t *mhp = thp->th_modhash; 169 170 topo_modhash_lock(mhp); 171 mod = topo_modhash_lookup(mhp, name); 172 if (mod != NULL && bump != 0) 173 topo_mod_hold(mod); 174 topo_modhash_unlock(mhp); 175 176 return (mod); 177 } 178 179 static void 180 topo_mod_destroy(topo_mod_t *mod) 181 { 182 topo_hdl_t *thp = mod->tm_hdl; 183 184 if (mod == NULL) 185 return; 186 187 assert(mod->tm_refs == 0); 188 assert(!topo_mutex_held(&mod->tm_lock)); 189 190 if (mod->tm_name != NULL) 191 topo_hdl_strfree(thp, mod->tm_name); 192 if (mod->tm_path != NULL) 193 topo_hdl_strfree(thp, mod->tm_path); 194 if (mod->tm_rootdir != NULL) 195 topo_hdl_strfree(thp, mod->tm_rootdir); 196 197 topo_hdl_free(thp, mod, sizeof (topo_mod_t)); 198 } 199 200 static topo_mod_t * 201 set_create_error(topo_hdl_t *thp, topo_mod_t *mod, const char *path, int err) 202 { 203 if (path != NULL) 204 topo_dprintf(thp, TOPO_DBG_ERR, "unable to load module %s: " 205 "%s\n", path, topo_strerror(err)); 206 else 207 topo_dprintf(thp, TOPO_DBG_ERR, "unable to load module: " 208 "%s\n", topo_strerror(err)); 209 210 if (mod != NULL) 211 topo_mod_destroy(mod); 212 213 (void) topo_hdl_seterrno(thp, err); 214 215 return (NULL); 216 } 217 218 static topo_mod_t * 219 topo_mod_create(topo_hdl_t *thp, const char *name, const char *path, 220 const topo_imodops_t *ops, topo_version_t version) 221 { 222 topo_mod_t *mod; 223 224 if (topo_modhash_lookup(thp->th_modhash, name) != NULL) 225 return (set_create_error(thp, NULL, path, ETOPO_MOD_LOADED)); 226 227 if ((mod = topo_hdl_zalloc(thp, sizeof (topo_mod_t))) == NULL) 228 return (set_create_error(thp, mod, path, ETOPO_NOMEM)); 229 230 mod->tm_hdl = thp; 231 232 (void) pthread_mutex_init(&mod->tm_lock, NULL); 233 234 mod->tm_name = topo_hdl_strdup(thp, name); 235 if (path != NULL) 236 mod->tm_path = topo_hdl_strdup(thp, path); 237 mod->tm_rootdir = topo_hdl_strdup(thp, thp->th_rootdir); 238 if (mod->tm_name == NULL || mod->tm_rootdir == NULL) 239 return (set_create_error(thp, mod, path, ETOPO_NOMEM)); 240 241 mod->tm_mops = (topo_imodops_t *)ops; 242 mod->tm_alloc = thp->th_alloc; 243 244 /* 245 * Module will be held upon a successful return from topo_mod_start() 246 */ 247 if ((topo_mod_start(mod, version)) < 0) 248 return (set_create_error(thp, mod, path, mod->tm_errno)); 249 250 topo_dprintf(thp, TOPO_DBG_MODSVC, "loaded module %s\n", mod->tm_name); 251 252 return (mod); 253 } 254 255 topo_modhash_t * 256 topo_modhash_create(topo_hdl_t *thp) 257 { 258 topo_modhash_t *mhp; 259 260 if ((mhp = topo_hdl_zalloc(thp, sizeof (topo_modhash_t))) == NULL) 261 return (NULL); 262 263 mhp->mh_hashlen = TOPO_HASH_BUCKETS; 264 if ((mhp->mh_hash = topo_hdl_zalloc(thp, 265 sizeof (void *) * mhp->mh_hashlen)) == NULL) { 266 topo_hdl_free(thp, mhp, sizeof (topo_modhash_t)); 267 return (NULL); 268 } 269 mhp->mh_nelems = 0; 270 (void) pthread_mutex_init(&mhp->mh_lock, NULL); 271 272 thp->th_modhash = mhp; 273 274 return (mhp); 275 } 276 277 void 278 topo_modhash_destroy(topo_hdl_t *thp) 279 { 280 topo_modhash_t *mhp = thp->th_modhash; 281 282 if (mhp == NULL) 283 return; 284 285 assert(mhp->mh_nelems == 0); 286 287 topo_hdl_free(thp, mhp->mh_hash, sizeof (void *) * mhp->mh_hashlen); 288 topo_hdl_free(thp, mhp, sizeof (topo_modhash_t)); 289 thp->th_modhash = NULL; 290 } 291 292 topo_mod_t * 293 topo_modhash_lookup(topo_modhash_t *mhp, const char *name) 294 { 295 topo_mod_t *mod = NULL; 296 uint_t h; 297 298 h = topo_strhash(name) % mhp->mh_hashlen; 299 300 for (mod = mhp->mh_hash[h]; mod != NULL; mod = mod->tm_next) { 301 if (strcmp(name, mod->tm_name) == 0) 302 break; 303 } 304 305 return (mod); 306 } 307 308 topo_mod_t * 309 topo_modhash_load(topo_hdl_t *thp, const char *name, const char *path, 310 const topo_imodops_t *ops, topo_version_t version) 311 { 312 topo_modhash_t *mhp = thp->th_modhash; 313 topo_mod_t *mod; 314 uint_t h; 315 316 topo_modhash_lock(mhp); 317 318 if ((mod = topo_mod_create(thp, name, path, ops, version)) == NULL) { 319 topo_modhash_unlock(mhp); 320 return (NULL); /* th_errno set */ 321 } 322 323 topo_mod_hold(mod); 324 325 h = topo_strhash(name) % mhp->mh_hashlen; 326 mod->tm_next = mhp->mh_hash[h]; 327 mhp->mh_hash[h] = mod; 328 mhp->mh_nelems++; 329 topo_modhash_unlock(mhp); 330 331 return (mod); 332 } 333 334 void 335 topo_modhash_unload(topo_mod_t *mod) 336 { 337 uint_t h; 338 topo_mod_t **pp, *mp; 339 topo_hdl_t *thp = mod->tm_hdl; 340 topo_modhash_t *mhp; 341 342 assert(topo_mutex_held(&mod->tm_lock)); 343 assert(mod->tm_busy == 0); 344 345 mhp = thp->th_modhash; 346 topo_modhash_lock(mhp); 347 348 assert(mhp != NULL); 349 350 h = topo_strhash(mod->tm_name) % mhp->mh_hashlen; 351 pp = &mhp->mh_hash[h]; 352 353 for (mp = *pp; mp != NULL; mp = mp->tm_next) { 354 if (mp == mod) 355 break; 356 else 357 pp = &mp->tm_next; 358 } 359 360 if (mp != NULL) { 361 *pp = mod->tm_next; 362 363 assert(mhp->mh_nelems != 0); 364 365 mhp->mh_nelems--; 366 367 } 368 topo_modhash_unlock(mhp); 369 370 (void) pthread_mutex_unlock(&mod->tm_lock); 371 372 topo_mod_stop(mod); 373 topo_mod_destroy(mod); 374 375 } 376 377 void 378 topo_modhash_unload_all(topo_hdl_t *thp) 379 { 380 int i; 381 topo_modhash_t *mhp = thp->th_modhash; 382 topo_mod_t *mp, **pp; 383 384 if (mhp == NULL) 385 return; 386 387 topo_modhash_lock(mhp); 388 for (i = 0; i < TOPO_HASH_BUCKETS; ++i) { 389 pp = &mhp->mh_hash[i]; 390 mp = *pp; 391 while (mp != NULL) { 392 topo_mod_stop(mp); 393 394 /* 395 * At this point we are forcing all modules to 396 * stop, ignore any remaining module reference counts. 397 */ 398 mp->tm_refs = 0; 399 400 *pp = mp->tm_next; 401 topo_mod_destroy(mp); 402 mp = *pp; 403 404 --mhp->mh_nelems; 405 } 406 } 407 topo_modhash_unlock(mhp); 408 } 409