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_release != NULL) 54 mod->tm_info->tmi_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(TOPO_DBG_MOD, "module %s stopped\n", mod->tm_name); 133 } 134 135 static int 136 topo_mod_start(topo_mod_t *mod) 137 { 138 topo_dprintf(TOPO_DBG_MOD, "starting module %s\n", mod->tm_name); 139 140 if (mod->tm_mops->mop_init(mod) != 0) { 141 mod->tm_errno = errno ? errno : ETOPO_MOD_INIT; 142 topo_dprintf(TOPO_DBG_ERR, 143 "module %s failed to initialize: %s\n", mod->tm_name, 144 topo_strerror(mod->tm_errno)); 145 return (-1); 146 } 147 148 mod->tm_flags |= TOPO_MOD_INIT; 149 150 if (!(mod->tm_flags & TOPO_MOD_REG)) { 151 topo_dprintf(TOPO_DBG_ERR, 152 "module %s failed to register\n", mod->tm_name); 153 mod->tm_errno = ETOPO_MOD_NOREG; 154 topo_mod_stop(mod); 155 return (-1); 156 } 157 158 topo_dprintf(TOPO_DBG_MOD, "module %s started\n", mod->tm_name); 159 160 return (0); 161 } 162 163 topo_mod_t * 164 topo_mod_lookup(topo_hdl_t *thp, const char *path) 165 { 166 char *p; 167 char name[PATH_MAX]; 168 topo_mod_t *mod; 169 topo_modhash_t *mhp = thp->th_modhash; 170 171 (void) strlcpy(name, topo_strbasename(path), sizeof (name)); 172 if ((p = strrchr(name, '.')) != NULL && strcmp(p, ".so") == 0) 173 *p = '\0'; /* strip trailing .so from any module name */ 174 175 topo_modhash_lock(mhp); 176 mod = topo_modhash_lookup(mhp, name); 177 topo_modhash_unlock(mhp); 178 179 return (mod); 180 } 181 182 static void 183 topo_mod_destroy(topo_mod_t *mod) 184 { 185 topo_hdl_t *thp = mod->tm_hdl; 186 187 if (mod == NULL) 188 return; 189 190 assert(mod->tm_refs == 0); 191 assert(!topo_mutex_held(&mod->tm_lock)); 192 193 if (mod->tm_name != NULL) 194 topo_hdl_strfree(thp, mod->tm_name); 195 if (mod->tm_path != NULL) 196 topo_hdl_strfree(thp, mod->tm_path); 197 if (mod->tm_rootdir != NULL) 198 topo_hdl_strfree(thp, mod->tm_rootdir); 199 200 topo_hdl_free(thp, mod, sizeof (topo_mod_t)); 201 } 202 203 static topo_mod_t * 204 set_create_error(topo_hdl_t *thp, topo_mod_t *mod, const char *path, int err) 205 { 206 topo_dprintf(TOPO_DBG_ERR, "unable to load module %s: %s\n", 207 path, topo_strerror(err)); 208 209 if (mod != NULL) 210 topo_mod_destroy(mod); 211 212 (void) topo_hdl_seterrno(thp, err); 213 214 return (NULL); 215 } 216 217 static topo_mod_t * 218 topo_mod_create(topo_hdl_t *thp, const char *name, const char *path, 219 const topo_modops_t *ops) 220 { 221 topo_mod_t *mod; 222 223 if (topo_modhash_lookup(thp->th_modhash, name) != NULL) 224 return (set_create_error(thp, NULL, path, ETOPO_MOD_LOADED)); 225 226 if ((mod = topo_hdl_zalloc(thp, sizeof (topo_mod_t))) == NULL) 227 return (set_create_error(thp, mod, path, ETOPO_NOMEM)); 228 229 (void) pthread_mutex_init(&mod->tm_lock, NULL); 230 231 mod->tm_name = topo_hdl_strdup(thp, name); 232 mod->tm_path = topo_hdl_strdup(thp, path); 233 mod->tm_rootdir = topo_hdl_strdup(thp, thp->th_rootdir); 234 if (mod->tm_name == NULL || mod->tm_path == NULL || 235 mod->tm_rootdir == NULL) 236 return (set_create_error(thp, mod, path, ETOPO_NOMEM)); 237 238 mod->tm_mops = (topo_modops_t *)ops; 239 mod->tm_hdl = thp; 240 mod->tm_alloc = thp->th_alloc; 241 mod->tm_version = TOPO_VERSION; 242 243 /* 244 * Module will be held upon a successful return from topo_mod_start() 245 */ 246 if ((topo_mod_start(mod)) < 0) 247 return (set_create_error(thp, mod, path, mod->tm_errno)); 248 249 topo_dprintf(TOPO_DBG_MOD, "loaded module %s\n", mod->tm_name); 250 251 return (mod); 252 } 253 254 topo_modhash_t * 255 topo_modhash_create(topo_hdl_t *thp) 256 { 257 topo_modhash_t *mhp; 258 259 if ((mhp = topo_hdl_zalloc(thp, sizeof (topo_modhash_t))) == NULL) 260 return (NULL); 261 262 mhp->mh_hashlen = TOPO_HASH_BUCKETS; 263 if ((mhp->mh_hash = topo_hdl_zalloc(thp, 264 sizeof (void *) * mhp->mh_hashlen)) == NULL) { 265 topo_hdl_free(thp, mhp, sizeof (topo_modhash_t)); 266 return (NULL); 267 } 268 mhp->mh_nelems = 0; 269 (void) pthread_mutex_init(&mhp->mh_lock, NULL); 270 271 thp->th_modhash = mhp; 272 273 return (mhp); 274 } 275 276 void 277 topo_modhash_destroy(topo_hdl_t *thp) 278 { 279 topo_modhash_t *mhp = thp->th_modhash; 280 281 if (mhp == NULL) 282 return; 283 284 assert(mhp->mh_nelems == 0); 285 286 topo_hdl_free(thp, mhp->mh_hash, sizeof (void *) * mhp->mh_hashlen); 287 topo_hdl_free(thp, mhp, sizeof (topo_modhash_t)); 288 thp->th_modhash = NULL; 289 } 290 291 topo_mod_t * 292 topo_modhash_lookup(topo_modhash_t *mhp, const char *name) 293 { 294 topo_mod_t *mod = NULL; 295 uint_t h; 296 297 h = topo_strhash(name) % mhp->mh_hashlen; 298 299 for (mod = mhp->mh_hash[h]; mod != NULL; mod = mod->tm_next) { 300 if (strcmp(name, mod->tm_name) == 0) 301 break; 302 } 303 304 return (mod); 305 } 306 307 topo_mod_t * 308 topo_modhash_load(topo_hdl_t *thp, const char *path, const topo_modops_t *ops) 309 { 310 char name[PATH_MAX], *p; 311 topo_modhash_t *mhp = thp->th_modhash; 312 topo_mod_t *mod; 313 uint_t h; 314 315 topo_modhash_lock(mhp); 316 317 (void) strlcpy(name, topo_strbasename(path), sizeof (name)); 318 if ((p = strrchr(name, '.')) != NULL && strcmp(p, ".so") == 0) 319 *p = '\0'; /* strip trailing .so from any module name */ 320 321 if ((mod = topo_mod_create(thp, name, path, ops)) == NULL) { 322 topo_modhash_unlock(mhp); 323 return (NULL); /* th_errno set */ 324 } 325 326 topo_mod_hold(mod); 327 328 h = topo_strhash(name) % mhp->mh_hashlen; 329 mod->tm_next = mhp->mh_hash[h]; 330 mhp->mh_hash[h] = mod; 331 mhp->mh_nelems++; 332 topo_modhash_unlock(mhp); 333 334 return (mod); 335 } 336 337 void 338 topo_modhash_unload(topo_mod_t *mod) 339 { 340 uint_t h; 341 topo_mod_t **pp, *mp; 342 topo_hdl_t *thp = mod->tm_hdl; 343 topo_modhash_t *mhp; 344 345 assert(topo_mutex_held(&mod->tm_lock)); 346 assert(mod->tm_busy == 0); 347 348 mhp = thp->th_modhash; 349 topo_modhash_lock(mhp); 350 351 assert(mhp != NULL); 352 353 h = topo_strhash(mod->tm_name) % mhp->mh_hashlen; 354 pp = &mhp->mh_hash[h]; 355 356 for (mp = *pp; mp != NULL; mp = mp->tm_next) { 357 if (mp == mod) 358 break; 359 else 360 pp = &mp->tm_next; 361 } 362 363 if (mp != NULL) { 364 *pp = mod->tm_next; 365 366 assert(mhp->mh_nelems != 0); 367 368 mhp->mh_nelems--; 369 370 } 371 topo_modhash_unlock(mhp); 372 373 (void) pthread_mutex_unlock(&mod->tm_lock); 374 375 topo_mod_stop(mod); 376 topo_mod_destroy(mod); 377 378 } 379 380 void 381 topo_modhash_unload_all(topo_hdl_t *thp) 382 { 383 int i; 384 topo_modhash_t *mhp = thp->th_modhash; 385 topo_mod_t *mp, **pp; 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 assert(mp->tm_refs == 1); 395 396 --mp->tm_refs; 397 *pp = mp->tm_next; 398 topo_mod_destroy(mp); 399 mp = *pp; 400 401 --mhp->mh_nelems; 402 } 403 } 404 topo_modhash_unlock(mhp); 405 } 406