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 <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 topo_dprintf(thp, TOPO_DBG_ERR, "unable to load module %s: %s\n", 204 path, topo_strerror(err)); 205 206 if (mod != NULL) 207 topo_mod_destroy(mod); 208 209 (void) topo_hdl_seterrno(thp, err); 210 211 return (NULL); 212 } 213 214 static topo_mod_t * 215 topo_mod_create(topo_hdl_t *thp, const char *name, const char *path, 216 const topo_imodops_t *ops, topo_version_t version) 217 { 218 topo_mod_t *mod; 219 220 if (topo_modhash_lookup(thp->th_modhash, name) != NULL) 221 return (set_create_error(thp, NULL, path, ETOPO_MOD_LOADED)); 222 223 if ((mod = topo_hdl_zalloc(thp, sizeof (topo_mod_t))) == NULL) 224 return (set_create_error(thp, mod, path, ETOPO_NOMEM)); 225 226 (void) pthread_mutex_init(&mod->tm_lock, NULL); 227 228 mod->tm_name = topo_hdl_strdup(thp, name); 229 if (path != NULL) 230 mod->tm_path = topo_hdl_strdup(thp, path); 231 mod->tm_rootdir = topo_hdl_strdup(thp, thp->th_rootdir); 232 if (mod->tm_name == NULL || mod->tm_rootdir == NULL) 233 return (set_create_error(thp, mod, path, ETOPO_NOMEM)); 234 235 mod->tm_mops = (topo_imodops_t *)ops; 236 mod->tm_hdl = thp; 237 mod->tm_alloc = thp->th_alloc; 238 239 /* 240 * Module will be held upon a successful return from topo_mod_start() 241 */ 242 if ((topo_mod_start(mod, version)) < 0) 243 return (set_create_error(thp, mod, path, mod->tm_errno)); 244 245 topo_dprintf(thp, TOPO_DBG_MODSVC, "loaded module %s\n", mod->tm_name); 246 247 return (mod); 248 } 249 250 topo_modhash_t * 251 topo_modhash_create(topo_hdl_t *thp) 252 { 253 topo_modhash_t *mhp; 254 255 if ((mhp = topo_hdl_zalloc(thp, sizeof (topo_modhash_t))) == NULL) 256 return (NULL); 257 258 mhp->mh_hashlen = TOPO_HASH_BUCKETS; 259 if ((mhp->mh_hash = topo_hdl_zalloc(thp, 260 sizeof (void *) * mhp->mh_hashlen)) == NULL) { 261 topo_hdl_free(thp, mhp, sizeof (topo_modhash_t)); 262 return (NULL); 263 } 264 mhp->mh_nelems = 0; 265 (void) pthread_mutex_init(&mhp->mh_lock, NULL); 266 267 thp->th_modhash = mhp; 268 269 return (mhp); 270 } 271 272 void 273 topo_modhash_destroy(topo_hdl_t *thp) 274 { 275 topo_modhash_t *mhp = thp->th_modhash; 276 277 if (mhp == NULL) 278 return; 279 280 assert(mhp->mh_nelems == 0); 281 282 topo_hdl_free(thp, mhp->mh_hash, sizeof (void *) * mhp->mh_hashlen); 283 topo_hdl_free(thp, mhp, sizeof (topo_modhash_t)); 284 thp->th_modhash = NULL; 285 } 286 287 topo_mod_t * 288 topo_modhash_lookup(topo_modhash_t *mhp, const char *name) 289 { 290 topo_mod_t *mod = NULL; 291 uint_t h; 292 293 h = topo_strhash(name) % mhp->mh_hashlen; 294 295 for (mod = mhp->mh_hash[h]; mod != NULL; mod = mod->tm_next) { 296 if (strcmp(name, mod->tm_name) == 0) 297 break; 298 } 299 300 return (mod); 301 } 302 303 topo_mod_t * 304 topo_modhash_load(topo_hdl_t *thp, const char *name, const char *path, 305 const topo_imodops_t *ops, topo_version_t version) 306 { 307 topo_modhash_t *mhp = thp->th_modhash; 308 topo_mod_t *mod; 309 uint_t h; 310 311 topo_modhash_lock(mhp); 312 313 if ((mod = topo_mod_create(thp, name, path, ops, version)) == NULL) { 314 topo_modhash_unlock(mhp); 315 return (NULL); /* th_errno set */ 316 } 317 318 topo_mod_hold(mod); 319 320 h = topo_strhash(name) % mhp->mh_hashlen; 321 mod->tm_next = mhp->mh_hash[h]; 322 mhp->mh_hash[h] = mod; 323 mhp->mh_nelems++; 324 topo_modhash_unlock(mhp); 325 326 return (mod); 327 } 328 329 void 330 topo_modhash_unload(topo_mod_t *mod) 331 { 332 uint_t h; 333 topo_mod_t **pp, *mp; 334 topo_hdl_t *thp = mod->tm_hdl; 335 topo_modhash_t *mhp; 336 337 assert(topo_mutex_held(&mod->tm_lock)); 338 assert(mod->tm_busy == 0); 339 340 mhp = thp->th_modhash; 341 topo_modhash_lock(mhp); 342 343 assert(mhp != NULL); 344 345 h = topo_strhash(mod->tm_name) % mhp->mh_hashlen; 346 pp = &mhp->mh_hash[h]; 347 348 for (mp = *pp; mp != NULL; mp = mp->tm_next) { 349 if (mp == mod) 350 break; 351 else 352 pp = &mp->tm_next; 353 } 354 355 if (mp != NULL) { 356 *pp = mod->tm_next; 357 358 assert(mhp->mh_nelems != 0); 359 360 mhp->mh_nelems--; 361 362 } 363 topo_modhash_unlock(mhp); 364 365 (void) pthread_mutex_unlock(&mod->tm_lock); 366 367 topo_mod_stop(mod); 368 topo_mod_destroy(mod); 369 370 } 371 372 void 373 topo_modhash_unload_all(topo_hdl_t *thp) 374 { 375 int i; 376 topo_modhash_t *mhp = thp->th_modhash; 377 topo_mod_t *mp, **pp; 378 379 topo_modhash_lock(mhp); 380 for (i = 0; i < TOPO_HASH_BUCKETS; ++i) { 381 pp = &mhp->mh_hash[i]; 382 mp = *pp; 383 while (mp != NULL) { 384 topo_mod_stop(mp); 385 386 /* 387 * At this point we are forcing all modules to 388 * stop, ignore any remaining module reference counts. 389 */ 390 mp->tm_refs = 0; 391 392 *pp = mp->tm_next; 393 topo_mod_destroy(mp); 394 mp = *pp; 395 396 --mhp->mh_nelems; 397 } 398 } 399 topo_modhash_unlock(mhp); 400 } 401