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, Version 1.0 only 6 * (the "License"). You may not use this file except in comodliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <signal.h> 30 #include <dirent.h> 31 #include <limits.h> 32 #include <alloca.h> 33 #include <unistd.h> 34 #include <stdio.h> 35 #include <pthread.h> 36 #include <errno.h> 37 #include <strings.h> 38 #include <assert.h> 39 #include <sys/nvpair.h> 40 41 #include <topo_string.h> 42 #include <topo_alloc.h> 43 #include <topo_module.h> 44 #include <topo_error.h> 45 #include <topo_subr.h> 46 47 extern nv_alloc_ops_t topo_nv_alloc_ops; 48 49 void 50 topo_mod_release(topo_mod_t *mod, tnode_t *node) 51 { 52 topo_mod_enter(mod); 53 54 if (mod->tm_info->tmi_release != NULL) 55 mod->tm_info->tmi_release(mod, node); 56 57 topo_mod_exit(mod); 58 } 59 60 void 61 topo_mod_hold(topo_mod_t *mod) 62 { 63 (void) pthread_mutex_lock(&mod->tm_lock); 64 mod->tm_refs++; 65 assert(mod->tm_refs != 0); 66 (void) pthread_mutex_unlock(&mod->tm_lock); 67 } 68 69 void 70 topo_mod_rele(topo_mod_t *mod) 71 { 72 assert(mod->tm_refs != 0); 73 74 (void) pthread_mutex_lock(&mod->tm_lock); 75 76 /* 77 * Lazy unload module 78 */ 79 if (--mod->tm_refs == 0) 80 topo_modhash_unload(mod); 81 else 82 (void) pthread_mutex_unlock(&mod->tm_lock); 83 } 84 85 void 86 topo_mod_enter(topo_mod_t *mod) 87 { 88 (void) pthread_mutex_lock(&mod->tm_lock); 89 90 while (mod->tm_busy != 0) 91 (void) pthread_cond_wait(&mod->tm_cv, &mod->tm_lock); 92 93 ++mod->tm_busy; 94 95 (void) pthread_mutex_unlock(&mod->tm_lock); 96 } 97 98 void 99 topo_mod_exit(topo_mod_t *mod) 100 { 101 (void) pthread_mutex_lock(&mod->tm_lock); 102 --mod->tm_busy; 103 104 assert(mod->tm_busy == 0); 105 106 (void) pthread_cond_broadcast(&mod->tm_cv); 107 (void) pthread_mutex_unlock(&mod->tm_lock); 108 } 109 110 static void 111 topo_modhash_lock(topo_modhash_t *mhp) 112 { 113 (void) pthread_mutex_lock(&mhp->mh_lock); 114 } 115 116 static void 117 topo_modhash_unlock(topo_modhash_t *mhp) 118 { 119 (void) pthread_mutex_unlock(&mhp->mh_lock); 120 } 121 122 static void 123 topo_mod_stop(topo_mod_t *mod) 124 { 125 if (mod->tm_flags & TOPO_MOD_INIT) { 126 mod->tm_mops->mop_fini(mod); 127 if (mod->tm_flags & TOPO_MOD_REG) 128 topo_mod_unregister(mod); 129 } 130 131 mod->tm_flags = TOPO_MOD_FINI; 132 133 topo_dprintf(TOPO_DBG_MOD, "module %s stopped\n", mod->tm_name); 134 } 135 136 static int 137 topo_mod_start(topo_mod_t *mod) 138 { 139 topo_dprintf(TOPO_DBG_MOD, "starting module %s\n", mod->tm_name); 140 141 if (mod->tm_mops->mop_init(mod) != 0) { 142 mod->tm_errno = errno ? errno : ETOPO_MOD_INIT; 143 topo_dprintf(TOPO_DBG_ERR, 144 "module %s failed to initialize: %s\n", mod->tm_name, 145 topo_strerror(mod->tm_errno)); 146 return (-1); 147 } 148 149 mod->tm_flags |= TOPO_MOD_INIT; 150 151 if (!(mod->tm_flags & TOPO_MOD_REG)) { 152 topo_dprintf(TOPO_DBG_ERR, 153 "module %s failed to register\n", mod->tm_name); 154 mod->tm_errno = ETOPO_MOD_NOREG; 155 topo_mod_stop(mod); 156 return (-1); 157 } 158 159 topo_dprintf(TOPO_DBG_MOD, "module %s started\n", mod->tm_name); 160 161 return (0); 162 } 163 164 topo_mod_t * 165 topo_mod_lookup(topo_hdl_t *thp, const char *path) 166 { 167 char *p; 168 char name[PATH_MAX]; 169 topo_mod_t *mod; 170 topo_modhash_t *mhp = thp->th_modhash; 171 172 (void) strlcpy(name, topo_strbasename(path), sizeof (name)); 173 if ((p = strrchr(name, '.')) != NULL && strcmp(p, ".so") == 0) 174 *p = '\0'; /* strip trailing .so from any module name */ 175 176 topo_modhash_lock(mhp); 177 mod = topo_modhash_lookup(mhp, name); 178 topo_modhash_unlock(mhp); 179 180 return (mod); 181 } 182 183 static void 184 topo_mod_destroy(topo_mod_t *mod) 185 { 186 topo_hdl_t *thp = mod->tm_hdl; 187 188 if (mod == NULL) 189 return; 190 191 assert(mod->tm_refs == 0); 192 assert(!topo_mutex_held(&mod->tm_lock)); 193 194 if (mod->tm_name != NULL) 195 topo_hdl_strfree(thp, mod->tm_name); 196 if (mod->tm_path != NULL) 197 topo_hdl_strfree(thp, mod->tm_path); 198 if (mod->tm_rootdir != NULL) 199 topo_hdl_strfree(thp, mod->tm_rootdir); 200 201 topo_hdl_free(thp, mod, sizeof (topo_mod_t)); 202 } 203 204 static topo_mod_t * 205 set_create_error(topo_hdl_t *thp, topo_mod_t *mod, const char *path, int err) 206 { 207 topo_dprintf(TOPO_DBG_ERR, "unable to load module %s: %s\n", 208 path, 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_modops_t *ops) 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 (void) pthread_mutex_init(&mod->tm_lock, NULL); 231 232 mod->tm_name = topo_hdl_strdup(thp, name); 233 mod->tm_path = topo_hdl_strdup(thp, path); 234 mod->tm_rootdir = topo_hdl_strdup(thp, thp->th_rootdir); 235 if (mod->tm_name == NULL || mod->tm_path == NULL || 236 mod->tm_rootdir == NULL) 237 return (set_create_error(thp, mod, path, ETOPO_NOMEM)); 238 239 mod->tm_mops = (topo_modops_t *)ops; 240 mod->tm_hdl = thp; 241 mod->tm_alloc = thp->th_alloc; 242 mod->tm_version = TOPO_VERSION; 243 244 /* 245 * Module will be held upon a successful return from topo_mod_start() 246 */ 247 if ((topo_mod_start(mod)) < 0) 248 return (set_create_error(thp, mod, path, mod->tm_errno)); 249 250 topo_dprintf(TOPO_DBG_MOD, "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 *path, const topo_modops_t *ops) 310 { 311 char name[PATH_MAX], *p; 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 (void) strlcpy(name, topo_strbasename(path), sizeof (name)); 319 if ((p = strrchr(name, '.')) != NULL && strcmp(p, ".so") == 0) 320 *p = '\0'; /* strip trailing .so from any module name */ 321 322 if ((mod = topo_mod_create(thp, name, path, ops)) == NULL) { 323 topo_hdl_unlock(thp); 324 return (NULL); /* th_errno set */ 325 } 326 327 topo_mod_hold(mod); 328 329 h = topo_strhash(name) % mhp->mh_hashlen; 330 mod->tm_next = mhp->mh_hash[h]; 331 mhp->mh_hash[h] = mod; 332 mhp->mh_nelems++; 333 topo_modhash_unlock(mhp); 334 335 return (mod); 336 } 337 338 void 339 topo_modhash_unload(topo_mod_t *mod) 340 { 341 uint_t h; 342 topo_mod_t **pp, *mp; 343 topo_hdl_t *thp = mod->tm_hdl; 344 topo_modhash_t *mhp; 345 346 assert(topo_mutex_held(&mod->tm_lock)); 347 assert(mod->tm_busy == 0); 348 349 mhp = thp->th_modhash; 350 topo_modhash_lock(mhp); 351 352 assert(mhp != NULL); 353 354 h = topo_strhash(mod->tm_name) % mhp->mh_hashlen; 355 pp = &mhp->mh_hash[h]; 356 357 for (mp = *pp; mp != NULL; mp = mp->tm_next) { 358 if (mp == mod) 359 break; 360 else 361 pp = &mp->tm_next; 362 } 363 364 if (mp != NULL) { 365 *pp = mod->tm_next; 366 367 assert(mhp->mh_nelems != 0); 368 369 mhp->mh_nelems--; 370 371 } 372 topo_modhash_unlock(mhp); 373 374 (void) pthread_mutex_unlock(&mod->tm_lock); 375 376 topo_mod_stop(mod); 377 topo_mod_destroy(mod); 378 379 } 380 381 void 382 topo_modhash_unload_all(topo_hdl_t *thp) 383 { 384 int i; 385 topo_modhash_t *mhp = thp->th_modhash; 386 topo_mod_t *mp, **pp; 387 388 topo_modhash_lock(mhp); 389 for (i = 0; i < TOPO_HASH_BUCKETS; ++i) { 390 pp = &mhp->mh_hash[i]; 391 mp = *pp; 392 while (mp != NULL) { 393 topo_mod_stop(mp); 394 395 assert(mp->tm_refs == 1); 396 397 --mp->tm_refs; 398 *pp = mp->tm_next; 399 topo_mod_destroy(mp); 400 mp = *pp; 401 402 --mhp->mh_nelems; 403 } 404 } 405 topo_modhash_unlock(mhp); 406 } 407