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 /* 23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <signal.h> 27 #include <dirent.h> 28 #include <limits.h> 29 #include <alloca.h> 30 #include <unistd.h> 31 #include <stdio.h> 32 33 #include <fmd_string.h> 34 #include <fmd_alloc.h> 35 #include <fmd_module.h> 36 #include <fmd_error.h> 37 #include <fmd_conf.h> 38 #include <fmd_dispq.h> 39 #include <fmd_eventq.h> 40 #include <fmd_timerq.h> 41 #include <fmd_subr.h> 42 #include <fmd_thread.h> 43 #include <fmd_ustat.h> 44 #include <fmd_case.h> 45 #include <fmd_protocol.h> 46 #include <fmd_buf.h> 47 #include <fmd_ckpt.h> 48 #include <fmd_xprt.h> 49 #include <fmd_topo.h> 50 51 #include <fmd.h> 52 53 /* 54 * Template for per-module statistics installed by fmd on behalf of each active 55 * module. These are used to initialize the per-module mp->mod_stats below. 56 * NOTE: FMD_TYPE_STRING statistics should not be used here. If they are 57 * required in the future, the FMD_ADM_MODDSTAT service routine must change. 58 */ 59 static const fmd_modstat_t _fmd_modstat_tmpl = { 60 { 61 { "fmd.dispatched", FMD_TYPE_UINT64, "total events dispatched to module" }, 62 { "fmd.dequeued", FMD_TYPE_UINT64, "total events dequeued by module" }, 63 { "fmd.prdequeued", FMD_TYPE_UINT64, "protocol events dequeued by module" }, 64 { "fmd.dropped", FMD_TYPE_UINT64, "total events dropped on queue overflow" }, 65 { "fmd.wcnt", FMD_TYPE_UINT32, "count of events waiting on queue" }, 66 { "fmd.wtime", FMD_TYPE_TIME, "total wait time on queue" }, 67 { "fmd.wlentime", FMD_TYPE_TIME, "total wait length * time product" }, 68 { "fmd.wlastupdate", FMD_TYPE_TIME, "hrtime of last wait queue update" }, 69 { "fmd.dtime", FMD_TYPE_TIME, "total processing time after dequeue" }, 70 { "fmd.dlastupdate", FMD_TYPE_TIME, "hrtime of last event dequeue completion" }, 71 }, 72 { "fmd.loadtime", FMD_TYPE_TIME, "hrtime at which module was loaded" }, 73 { "fmd.snaptime", FMD_TYPE_TIME, "hrtime of last statistics snapshot" }, 74 { "fmd.accepted", FMD_TYPE_UINT64, "total events accepted by module" }, 75 { "fmd.debugdrop", FMD_TYPE_UINT64, "dropped debug messages" }, 76 { "fmd.memtotal", FMD_TYPE_SIZE, "total memory allocated by module" }, 77 { "fmd.memlimit", FMD_TYPE_SIZE, "limit on total memory allocated" }, 78 { "fmd.buftotal", FMD_TYPE_SIZE, "total buffer space used by module" }, 79 { "fmd.buflimit", FMD_TYPE_SIZE, "limit on total buffer space" }, 80 { "fmd.thrtotal", FMD_TYPE_UINT32, "total number of auxiliary threads" }, 81 { "fmd.thrlimit", FMD_TYPE_UINT32, "limit on number of auxiliary threads" }, 82 { "fmd.caseopen", FMD_TYPE_UINT64, "cases currently open by module" }, 83 { "fmd.casesolved", FMD_TYPE_UINT64, "total cases solved by module" }, 84 { "fmd.caseclosed", FMD_TYPE_UINT64, "total cases closed by module" }, 85 { "fmd.ckptsave", FMD_TYPE_BOOL, "save checkpoints for module" }, 86 { "fmd.ckptrestore", FMD_TYPE_BOOL, "restore checkpoints for module" }, 87 { "fmd.ckptzero", FMD_TYPE_BOOL, "zeroed checkpoint at startup" }, 88 { "fmd.ckptcnt", FMD_TYPE_UINT64, "number of checkpoints taken" }, 89 { "fmd.ckpttime", FMD_TYPE_TIME, "total checkpoint time" }, 90 { "fmd.xprtopen", FMD_TYPE_UINT32, "total number of open transports" }, 91 { "fmd.xprtlimit", FMD_TYPE_UINT32, "limit on number of open transports" }, 92 { "fmd.xprtqlimit", FMD_TYPE_UINT32, "limit on transport event queue length" }, 93 }; 94 95 static void 96 fmd_module_start(void *arg) 97 { 98 fmd_module_t *mp = arg; 99 fmd_event_t *ep; 100 fmd_xprt_t *xp; 101 102 (void) pthread_mutex_lock(&mp->mod_lock); 103 104 if (mp->mod_ops->mop_init(mp) != 0 || mp->mod_error != 0) { 105 if (mp->mod_error == 0) 106 mp->mod_error = errno ? errno : EFMD_MOD_INIT; 107 goto out; 108 } 109 110 if (fmd.d_mod_event != NULL) 111 fmd_eventq_insert_at_head(mp->mod_queue, fmd.d_mod_event); 112 113 ASSERT(MUTEX_HELD(&mp->mod_lock)); 114 mp->mod_flags |= FMD_MOD_INIT; 115 116 (void) pthread_cond_broadcast(&mp->mod_cv); 117 (void) pthread_mutex_unlock(&mp->mod_lock); 118 119 /* 120 * If the module opened any transports while executing _fmd_init(), 121 * they are suspended. Now that _fmd_init() is done, wake them up. 122 */ 123 for (xp = fmd_list_next(&mp->mod_transports); 124 xp != NULL; xp = fmd_list_next(xp)) 125 fmd_xprt_xresume(xp, FMD_XPRT_ISUSPENDED); 126 127 /* 128 * Wait for events to arrive by checking mod_error and then sleeping in 129 * fmd_eventq_delete(). If a NULL event is returned, the eventq has 130 * been aborted and we continue on to call fini and exit the thread. 131 */ 132 while ((ep = fmd_eventq_delete(mp->mod_queue)) != NULL) { 133 /* 134 * If the module has failed, discard the event without ever 135 * passing it to the module and go back to sleep. 136 */ 137 if (mp->mod_error != 0) { 138 fmd_eventq_done(mp->mod_queue); 139 fmd_event_rele(ep); 140 continue; 141 } 142 143 mp->mod_ops->mop_dispatch(mp, ep); 144 fmd_eventq_done(mp->mod_queue); 145 146 /* 147 * Once mop_dispatch() is complete, grab the lock and perform 148 * any event-specific post-processing. Finally, if necessary, 149 * checkpoint the state of the module after this event. 150 */ 151 fmd_module_lock(mp); 152 153 if (FMD_EVENT_TYPE(ep) == FMD_EVT_CLOSE) 154 fmd_case_delete(FMD_EVENT_DATA(ep)); 155 156 fmd_ckpt_save(mp); 157 fmd_module_unlock(mp); 158 fmd_event_rele(ep); 159 } 160 161 if (mp->mod_ops->mop_fini(mp) != 0 && mp->mod_error == 0) 162 mp->mod_error = errno ? errno : EFMD_MOD_FINI; 163 164 (void) pthread_mutex_lock(&mp->mod_lock); 165 mp->mod_flags |= FMD_MOD_FINI; 166 167 out: 168 (void) pthread_cond_broadcast(&mp->mod_cv); 169 (void) pthread_mutex_unlock(&mp->mod_lock); 170 } 171 172 fmd_module_t * 173 fmd_module_create(const char *path, const fmd_modops_t *ops) 174 { 175 fmd_module_t *mp = fmd_zalloc(sizeof (fmd_module_t), FMD_SLEEP); 176 177 char buf[PATH_MAX], *p; 178 const char *dir; 179 uint32_t limit; 180 int err; 181 182 (void) strlcpy(buf, fmd_strbasename(path), sizeof (buf)); 183 if ((p = strrchr(buf, '.')) != NULL && strcmp(p, ".so") == 0) 184 *p = '\0'; /* strip trailing .so from any module name */ 185 186 (void) pthread_mutex_init(&mp->mod_lock, NULL); 187 (void) pthread_cond_init(&mp->mod_cv, NULL); 188 (void) pthread_mutex_init(&mp->mod_stats_lock, NULL); 189 190 mp->mod_name = fmd_strdup(buf, FMD_SLEEP); 191 mp->mod_path = fmd_strdup(path, FMD_SLEEP); 192 mp->mod_ops = ops; 193 mp->mod_ustat = fmd_ustat_create(); 194 195 (void) fmd_conf_getprop(fmd.d_conf, "ckpt.dir", &dir); 196 (void) snprintf(buf, sizeof (buf), 197 "%s/%s/%s", fmd.d_rootdir, dir, mp->mod_name); 198 199 mp->mod_ckpt = fmd_strdup(buf, FMD_SLEEP); 200 201 (void) fmd_conf_getprop(fmd.d_conf, "client.tmrlim", &limit); 202 mp->mod_timerids = fmd_idspace_create(mp->mod_name, 1, limit + 1); 203 mp->mod_threads = fmd_idspace_create(mp->mod_name, 0, INT_MAX); 204 205 fmd_buf_hash_create(&mp->mod_bufs); 206 fmd_serd_hash_create(&mp->mod_serds); 207 208 mp->mod_topo_current = fmd_topo_hold(); 209 210 (void) pthread_mutex_lock(&fmd.d_mod_lock); 211 fmd_list_append(&fmd.d_mod_list, mp); 212 (void) pthread_mutex_unlock(&fmd.d_mod_lock); 213 214 /* 215 * Initialize the module statistics that are kept on its behalf by fmd. 216 * These are set up using a template defined at the top of this file. 217 */ 218 if ((mp->mod_stats = (fmd_modstat_t *)fmd_ustat_insert(mp->mod_ustat, 219 FMD_USTAT_ALLOC, sizeof (_fmd_modstat_tmpl) / sizeof (fmd_stat_t), 220 (fmd_stat_t *)&_fmd_modstat_tmpl, NULL)) == NULL) { 221 fmd_error(EFMD_MOD_INIT, "failed to initialize per-mod stats"); 222 fmd_module_destroy(mp); 223 return (NULL); 224 } 225 226 if (nv_alloc_init(&mp->mod_nva_sleep, 227 &fmd_module_nva_ops_sleep, mp) != 0 || 228 nv_alloc_init(&mp->mod_nva_nosleep, 229 &fmd_module_nva_ops_nosleep, mp) != 0) { 230 fmd_error(EFMD_MOD_INIT, "failed to initialize nvlist " 231 "allocation routines"); 232 fmd_module_destroy(mp); 233 return (NULL); 234 } 235 236 (void) fmd_conf_getprop(fmd.d_conf, "client.evqlim", &limit); 237 238 mp->mod_queue = fmd_eventq_create(mp, 239 &mp->mod_stats->ms_evqstat, &mp->mod_stats_lock, limit); 240 241 (void) fmd_conf_getprop(fmd.d_conf, "client.memlim", 242 &mp->mod_stats->ms_memlimit.fmds_value.ui64); 243 244 (void) fmd_conf_getprop(fmd.d_conf, "client.buflim", 245 &mp->mod_stats->ms_buflimit.fmds_value.ui64); 246 247 (void) fmd_conf_getprop(fmd.d_conf, "client.thrlim", 248 &mp->mod_stats->ms_thrlimit.fmds_value.ui32); 249 250 (void) fmd_conf_getprop(fmd.d_conf, "client.xprtlim", 251 &mp->mod_stats->ms_xprtlimit.fmds_value.ui32); 252 253 (void) fmd_conf_getprop(fmd.d_conf, "client.xprtqlim", 254 &mp->mod_stats->ms_xprtqlimit.fmds_value.ui32); 255 256 (void) fmd_conf_getprop(fmd.d_conf, "ckpt.save", 257 &mp->mod_stats->ms_ckpt_save.fmds_value.bool); 258 259 (void) fmd_conf_getprop(fmd.d_conf, "ckpt.restore", 260 &mp->mod_stats->ms_ckpt_restore.fmds_value.bool); 261 262 (void) fmd_conf_getprop(fmd.d_conf, "ckpt.zero", 263 &mp->mod_stats->ms_ckpt_zeroed.fmds_value.bool); 264 265 if (mp->mod_stats->ms_ckpt_zeroed.fmds_value.bool) 266 fmd_ckpt_delete(mp); /* blow away any pre-existing checkpoint */ 267 268 /* 269 * Place a hold on the module and grab the module lock before creating 270 * the module's thread to ensure that it cannot destroy the module and 271 * that it cannot call ops->mop_init() before we're done setting up. 272 * NOTE: from now on, we must use fmd_module_rele() for error paths. 273 */ 274 fmd_module_hold(mp); 275 (void) pthread_mutex_lock(&mp->mod_lock); 276 mp->mod_stats->ms_loadtime.fmds_value.ui64 = gethrtime(); 277 mp->mod_thread = fmd_thread_create(mp, fmd_module_start, mp); 278 279 if (mp->mod_thread == NULL) { 280 fmd_error(EFMD_MOD_THR, "failed to create thread for %s", path); 281 (void) pthread_mutex_unlock(&mp->mod_lock); 282 fmd_module_rele(mp); 283 return (NULL); 284 } 285 286 /* 287 * At this point our module structure is nearly finished and its thread 288 * is starting execution in fmd_module_start() above, which will begin 289 * by blocking for mod_lock. We now drop mod_lock and wait for either 290 * FMD_MOD_INIT or mod_error to be set before proceeding. 291 */ 292 while (!(mp->mod_flags & FMD_MOD_INIT) && mp->mod_error == 0) 293 (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); 294 295 /* 296 * If the module has failed to initialize, copy its errno to the errno 297 * of the caller, wait for it to unload, and then destroy it. 298 */ 299 if (!(mp->mod_flags & FMD_MOD_INIT)) { 300 err = mp->mod_error; 301 (void) pthread_mutex_unlock(&mp->mod_lock); 302 303 if (err == EFMD_CKPT_INVAL) 304 fmd_ckpt_rename(mp); /* move aside bad checkpoint */ 305 306 /* 307 * If we're in the background, keep quiet about failure to 308 * load because a handle wasn't registered: this is a module's 309 * way of telling us it didn't want to be loaded for some 310 * reason related to system configuration. If we're in the 311 * foreground we log this too in order to inform developers. 312 */ 313 if (fmd.d_fg || err != EFMD_HDL_INIT) { 314 fmd_error(EFMD_MOD_INIT, "failed to load %s: %s\n", 315 path, fmd_strerror(err)); 316 } 317 318 fmd_module_unload(mp); 319 fmd_module_rele(mp); 320 321 (void) fmd_set_errno(err); 322 return (NULL); 323 } 324 325 (void) pthread_cond_broadcast(&mp->mod_cv); 326 (void) pthread_mutex_unlock(&mp->mod_lock); 327 328 fmd_dprintf(FMD_DBG_MOD, "loaded module %s\n", mp->mod_name); 329 return (mp); 330 } 331 332 static void 333 fmd_module_untimeout(fmd_idspace_t *ids, id_t id, fmd_module_t *mp) 334 { 335 void *arg = fmd_timerq_remove(fmd.d_timers, ids, id); 336 337 /* 338 * The root module calls fmd_timerq_install() directly and must take 339 * responsibility for any cleanup of timer arguments that is required. 340 * All other modules use fmd_modtimer_t's as the arg data; free them. 341 */ 342 if (arg != NULL && mp != fmd.d_rmod) 343 fmd_free(arg, sizeof (fmd_modtimer_t)); 344 } 345 346 void 347 fmd_module_unload(fmd_module_t *mp) 348 { 349 fmd_modtopo_t *mtp; 350 351 (void) pthread_mutex_lock(&mp->mod_lock); 352 353 if (mp->mod_flags & FMD_MOD_QUIT) { 354 (void) pthread_mutex_unlock(&mp->mod_lock); 355 return; /* module is already unloading */ 356 } 357 358 ASSERT(mp->mod_thread != NULL); 359 mp->mod_flags |= FMD_MOD_QUIT; 360 361 if (mp->mod_queue != NULL) 362 fmd_eventq_abort(mp->mod_queue); 363 364 /* 365 * Wait for the module's thread to stop processing events and call 366 * _fmd_fini() and exit. We do this by waiting for FMD_MOD_FINI to be 367 * set if INIT was set, and then attempting to join with the thread. 368 */ 369 while ((mp->mod_flags & (FMD_MOD_INIT | FMD_MOD_FINI)) == FMD_MOD_INIT) 370 (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); 371 372 (void) pthread_cond_broadcast(&mp->mod_cv); 373 (void) pthread_mutex_unlock(&mp->mod_lock); 374 375 fmd_thread_destroy(mp->mod_thread, FMD_THREAD_JOIN); 376 mp->mod_thread = NULL; 377 378 /* 379 * Once the module is no longer active, clean up any data structures 380 * that are only required when the module is loaded. 381 */ 382 fmd_module_lock(mp); 383 384 if (mp->mod_timerids != NULL) { 385 fmd_idspace_apply(mp->mod_timerids, 386 (void (*)())fmd_module_untimeout, mp); 387 388 fmd_idspace_destroy(mp->mod_timerids); 389 mp->mod_timerids = NULL; 390 } 391 392 if (mp->mod_threads != NULL) { 393 fmd_idspace_destroy(mp->mod_threads); 394 mp->mod_threads = NULL; 395 } 396 397 (void) fmd_buf_hash_destroy(&mp->mod_bufs); 398 fmd_serd_hash_destroy(&mp->mod_serds); 399 400 while ((mtp = fmd_list_next(&mp->mod_topolist)) != NULL) { 401 fmd_list_delete(&mp->mod_topolist, mtp); 402 fmd_topo_rele(mtp->mt_topo); 403 fmd_free(mtp, sizeof (fmd_modtopo_t)); 404 } 405 406 fmd_module_unlock(mp); 407 fmd_dprintf(FMD_DBG_MOD, "unloaded module %s\n", mp->mod_name); 408 } 409 410 void 411 fmd_module_destroy(fmd_module_t *mp) 412 { 413 fmd_conf_formal_t *cfp = mp->mod_argv; 414 int i; 415 416 ASSERT(MUTEX_HELD(&mp->mod_lock)); 417 418 if (mp->mod_thread != NULL) { 419 (void) pthread_mutex_unlock(&mp->mod_lock); 420 fmd_module_unload(mp); 421 (void) pthread_mutex_lock(&mp->mod_lock); 422 } 423 424 ASSERT(mp->mod_thread == NULL); 425 ASSERT(mp->mod_refs == 0); 426 427 /* 428 * Once the module's thread is dead, we can safely remove the module 429 * from global visibility and by removing it from d_mod_list. Any 430 * modhash pointers are already gone by virtue of mod_refs being zero. 431 */ 432 (void) pthread_mutex_lock(&fmd.d_mod_lock); 433 fmd_list_delete(&fmd.d_mod_list, mp); 434 (void) pthread_mutex_unlock(&fmd.d_mod_lock); 435 436 if (mp->mod_topo_current != NULL) 437 fmd_topo_rele(mp->mod_topo_current); 438 439 if (mp->mod_nva_sleep.nva_ops != NULL) 440 nv_alloc_fini(&mp->mod_nva_sleep); 441 if (mp->mod_nva_nosleep.nva_ops != NULL) 442 nv_alloc_fini(&mp->mod_nva_nosleep); 443 444 /* 445 * Once the module is no longer processing events and no longer visible 446 * through any program data structures, we can free all of its content. 447 */ 448 if (mp->mod_queue != NULL) { 449 fmd_eventq_destroy(mp->mod_queue); 450 mp->mod_queue = NULL; 451 } 452 453 if (mp->mod_ustat != NULL) { 454 (void) pthread_mutex_lock(&mp->mod_stats_lock); 455 fmd_ustat_destroy(mp->mod_ustat); 456 mp->mod_ustat = NULL; 457 mp->mod_stats = NULL; 458 (void) pthread_mutex_unlock(&mp->mod_stats_lock); 459 } 460 461 for (i = 0; i < mp->mod_dictc; i++) 462 fm_dc_closedict(mp->mod_dictv[i]); 463 464 fmd_free(mp->mod_dictv, sizeof (struct fm_dc_handle *) * mp->mod_dictc); 465 466 if (mp->mod_conf != NULL) 467 fmd_conf_close(mp->mod_conf); 468 469 for (i = 0; i < mp->mod_argc; i++, cfp++) { 470 fmd_strfree((char *)cfp->cf_name); 471 fmd_strfree((char *)cfp->cf_default); 472 } 473 474 fmd_free(mp->mod_argv, sizeof (fmd_conf_formal_t) * mp->mod_argc); 475 476 fmd_strfree(mp->mod_name); 477 fmd_strfree(mp->mod_path); 478 fmd_strfree(mp->mod_ckpt); 479 nvlist_free(mp->mod_fmri); 480 fmd_strfree(mp->mod_vers); 481 482 fmd_free(mp, sizeof (fmd_module_t)); 483 } 484 485 /* 486 * fmd_module_error() is called after the stack is unwound from a call to 487 * fmd_module_abort() to indicate that the module has failed. The mod_error 488 * field is used to hold the error code of the first fatal error to the module. 489 * An EFMD_MOD_FAIL event is then created and sent to fmd-self-diagnosis. 490 */ 491 static void 492 fmd_module_error(fmd_module_t *mp, int err) 493 { 494 fmd_event_t *e; 495 nvlist_t *nvl; 496 char *class; 497 498 ASSERT(MUTEX_HELD(&mp->mod_lock)); 499 ASSERT(err != 0); 500 501 TRACE((FMD_DBG_MOD, "module aborted: err=%d", err)); 502 503 if (mp->mod_error == 0) 504 mp->mod_error = err; 505 506 if (mp == fmd.d_self) 507 return; /* do not post event if fmd.d_self itself fails */ 508 509 /* 510 * Send an error indicating the module has now failed to fmd.d_self. 511 * Since the error causing the failure has already been logged by 512 * fmd_api_xerror(), we do not need to bother logging this event. 513 * It only exists for the purpose of notifying fmd.d_self that it can 514 * close the case associated with this module because mod_error is set. 515 */ 516 nvl = fmd_protocol_moderror(mp, EFMD_MOD_FAIL, fmd_strerror(err)); 517 (void) nvlist_lookup_string(nvl, FM_CLASS, &class); 518 e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); 519 fmd_dispq_dispatch(fmd.d_disp, e, class); 520 } 521 522 void 523 fmd_module_dispatch(fmd_module_t *mp, fmd_event_t *e) 524 { 525 const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops; 526 fmd_event_impl_t *ep = (fmd_event_impl_t *)e; 527 fmd_hdl_t *hdl = (fmd_hdl_t *)mp; 528 fmd_modtimer_t *t; 529 volatile int err; 530 531 /* 532 * Before calling the appropriate module callback, enter the module as 533 * if by fmd_module_enter() and establish mod_jmpbuf for any aborts. 534 */ 535 (void) pthread_mutex_lock(&mp->mod_lock); 536 537 ASSERT(!(mp->mod_flags & FMD_MOD_BUSY)); 538 mp->mod_flags |= FMD_MOD_BUSY; 539 540 if ((err = setjmp(mp->mod_jmpbuf)) != 0) { 541 (void) pthread_mutex_lock(&mp->mod_lock); 542 fmd_module_error(mp, err); 543 } 544 545 (void) pthread_cond_broadcast(&mp->mod_cv); 546 (void) pthread_mutex_unlock(&mp->mod_lock); 547 548 /* 549 * If it's the first time through fmd_module_dispatch(), call the 550 * appropriate module callback based on the event type. If the call 551 * triggers an fmd_module_abort(), we'll return to setjmp() above with 552 * err set to a non-zero value and then bypass this before exiting. 553 */ 554 if (err == 0) { 555 switch (ep->ev_type) { 556 case FMD_EVT_PROTOCOL: 557 ops->fmdo_recv(hdl, e, ep->ev_nvl, ep->ev_data); 558 break; 559 case FMD_EVT_TIMEOUT: 560 t = ep->ev_data; 561 ASSERT(t->mt_mod == mp); 562 ops->fmdo_timeout(hdl, t->mt_id, t->mt_arg); 563 break; 564 case FMD_EVT_CLOSE: 565 ops->fmdo_close(hdl, ep->ev_data); 566 break; 567 case FMD_EVT_STATS: 568 ops->fmdo_stats(hdl); 569 fmd_modstat_publish(mp); 570 break; 571 case FMD_EVT_GC: 572 ops->fmdo_gc(hdl); 573 break; 574 case FMD_EVT_PUBLISH: 575 fmd_case_publish(ep->ev_data, FMD_CASE_CURRENT); 576 break; 577 case FMD_EVT_TOPO: 578 fmd_topo_rele(mp->mod_topo_current); 579 mp->mod_topo_current = (fmd_topo_t *)ep->ev_data; 580 fmd_topo_addref(mp->mod_topo_current); 581 ops->fmdo_topo(hdl, mp->mod_topo_current->ft_hdl); 582 break; 583 } 584 } 585 586 fmd_module_exit(mp); 587 } 588 589 int 590 fmd_module_transport(fmd_module_t *mp, fmd_xprt_t *xp, fmd_event_t *e) 591 { 592 fmd_event_impl_t *ep = (fmd_event_impl_t *)e; 593 fmd_hdl_t *hdl = (fmd_hdl_t *)mp; 594 595 ASSERT(ep->ev_type == FMD_EVT_PROTOCOL); 596 return (mp->mod_info->fmdi_ops->fmdo_send(hdl, xp, e, ep->ev_nvl)); 597 } 598 599 void 600 fmd_module_timeout(fmd_modtimer_t *t, id_t id, hrtime_t hrt) 601 { 602 fmd_event_t *e; 603 604 t->mt_id = id; /* save id in case we need to delete from eventq */ 605 e = fmd_event_create(FMD_EVT_TIMEOUT, hrt, NULL, t); 606 fmd_eventq_insert_at_time(t->mt_mod->mod_queue, e); 607 } 608 609 /* 610 * Garbage collection is initiated by a timer callback once per day or at the 611 * request of fmadm. Purge old SERD entries and send the module a GC event. 612 */ 613 void 614 fmd_module_gc(fmd_module_t *mp) 615 { 616 fmd_hdl_info_t *info; 617 fmd_event_t *e; 618 619 if (mp->mod_error != 0) 620 return; /* do not do anything if the module has failed */ 621 622 fmd_module_lock(mp); 623 624 if ((info = mp->mod_info) != NULL) { 625 fmd_serd_hash_apply(&mp->mod_serds, 626 (fmd_serd_eng_f *)fmd_serd_eng_gc, NULL); 627 } 628 629 fmd_module_unlock(mp); 630 631 if (info != NULL) { 632 e = fmd_event_create(FMD_EVT_GC, FMD_HRT_NOW, NULL, NULL); 633 fmd_eventq_insert_at_head(mp->mod_queue, e); 634 } 635 } 636 637 void 638 fmd_module_trygc(fmd_module_t *mp) 639 { 640 if (fmd_module_trylock(mp)) { 641 fmd_serd_hash_apply(&mp->mod_serds, 642 (fmd_serd_eng_f *)fmd_serd_eng_gc, NULL); 643 fmd_module_unlock(mp); 644 } 645 } 646 647 int 648 fmd_module_contains(fmd_module_t *mp, fmd_event_t *ep) 649 { 650 fmd_case_t *cp; 651 int rv = 0; 652 653 fmd_module_lock(mp); 654 655 for (cp = fmd_list_next(&mp->mod_cases); 656 cp != NULL; cp = fmd_list_next(cp)) { 657 if ((rv = fmd_case_contains(cp, ep)) != 0) 658 break; 659 } 660 661 if (rv == 0) 662 rv = fmd_serd_hash_contains(&mp->mod_serds, ep); 663 664 fmd_module_unlock(mp); 665 return (rv); 666 } 667 668 void 669 fmd_module_setdirty(fmd_module_t *mp) 670 { 671 (void) pthread_mutex_lock(&mp->mod_lock); 672 mp->mod_flags |= FMD_MOD_MDIRTY; 673 (void) pthread_mutex_unlock(&mp->mod_lock); 674 } 675 676 void 677 fmd_module_setcdirty(fmd_module_t *mp) 678 { 679 (void) pthread_mutex_lock(&mp->mod_lock); 680 mp->mod_flags |= FMD_MOD_CDIRTY; 681 (void) pthread_mutex_unlock(&mp->mod_lock); 682 } 683 684 void 685 fmd_module_clrdirty(fmd_module_t *mp) 686 { 687 fmd_case_t *cp; 688 689 fmd_module_lock(mp); 690 691 if (mp->mod_flags & FMD_MOD_CDIRTY) { 692 for (cp = fmd_list_next(&mp->mod_cases); 693 cp != NULL; cp = fmd_list_next(cp)) 694 fmd_case_clrdirty(cp); 695 } 696 697 if (mp->mod_flags & FMD_MOD_MDIRTY) { 698 fmd_serd_hash_apply(&mp->mod_serds, 699 (fmd_serd_eng_f *)fmd_serd_eng_clrdirty, NULL); 700 fmd_buf_hash_commit(&mp->mod_bufs); 701 } 702 703 (void) pthread_mutex_lock(&mp->mod_lock); 704 mp->mod_flags &= ~(FMD_MOD_MDIRTY | FMD_MOD_CDIRTY); 705 (void) pthread_mutex_unlock(&mp->mod_lock); 706 707 fmd_module_unlock(mp); 708 } 709 710 void 711 fmd_module_commit(fmd_module_t *mp) 712 { 713 fmd_case_t *cp; 714 715 ASSERT(fmd_module_locked(mp)); 716 717 if (mp->mod_flags & FMD_MOD_CDIRTY) { 718 for (cp = fmd_list_next(&mp->mod_cases); 719 cp != NULL; cp = fmd_list_next(cp)) 720 fmd_case_commit(cp); 721 } 722 723 if (mp->mod_flags & FMD_MOD_MDIRTY) { 724 fmd_serd_hash_apply(&mp->mod_serds, 725 (fmd_serd_eng_f *)fmd_serd_eng_commit, NULL); 726 fmd_buf_hash_commit(&mp->mod_bufs); 727 } 728 729 (void) pthread_mutex_lock(&mp->mod_lock); 730 mp->mod_flags &= ~(FMD_MOD_MDIRTY | FMD_MOD_CDIRTY); 731 (void) pthread_mutex_unlock(&mp->mod_lock); 732 733 mp->mod_gen++; 734 } 735 736 void 737 fmd_module_lock(fmd_module_t *mp) 738 { 739 pthread_t self = pthread_self(); 740 741 (void) pthread_mutex_lock(&mp->mod_lock); 742 743 while (mp->mod_flags & FMD_MOD_LOCK) { 744 if (mp->mod_owner != self) 745 (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); 746 else 747 fmd_panic("recursive module lock of %p\n", (void *)mp); 748 } 749 750 mp->mod_owner = self; 751 mp->mod_flags |= FMD_MOD_LOCK; 752 753 (void) pthread_cond_broadcast(&mp->mod_cv); 754 (void) pthread_mutex_unlock(&mp->mod_lock); 755 } 756 757 void 758 fmd_module_unlock(fmd_module_t *mp) 759 { 760 (void) pthread_mutex_lock(&mp->mod_lock); 761 762 ASSERT(mp->mod_owner == pthread_self()); 763 ASSERT(mp->mod_flags & FMD_MOD_LOCK); 764 765 mp->mod_owner = 0; 766 mp->mod_flags &= ~FMD_MOD_LOCK; 767 768 (void) pthread_cond_broadcast(&mp->mod_cv); 769 (void) pthread_mutex_unlock(&mp->mod_lock); 770 } 771 772 int 773 fmd_module_trylock(fmd_module_t *mp) 774 { 775 (void) pthread_mutex_lock(&mp->mod_lock); 776 777 if (mp->mod_flags & FMD_MOD_LOCK) { 778 (void) pthread_mutex_unlock(&mp->mod_lock); 779 return (0); 780 } 781 782 mp->mod_owner = pthread_self(); 783 mp->mod_flags |= FMD_MOD_LOCK; 784 785 (void) pthread_cond_broadcast(&mp->mod_cv); 786 (void) pthread_mutex_unlock(&mp->mod_lock); 787 788 return (1); 789 } 790 791 int 792 fmd_module_locked(fmd_module_t *mp) 793 { 794 return ((mp->mod_flags & FMD_MOD_LOCK) && 795 mp->mod_owner == pthread_self()); 796 } 797 798 int 799 fmd_module_enter(fmd_module_t *mp, void (*func)(fmd_hdl_t *)) 800 { 801 volatile int err; 802 803 (void) pthread_mutex_lock(&mp->mod_lock); 804 805 ASSERT(!(mp->mod_flags & FMD_MOD_BUSY)); 806 mp->mod_flags |= FMD_MOD_BUSY; 807 808 if ((err = setjmp(mp->mod_jmpbuf)) != 0) { 809 (void) pthread_mutex_lock(&mp->mod_lock); 810 fmd_module_error(mp, err); 811 } 812 813 (void) pthread_cond_broadcast(&mp->mod_cv); 814 (void) pthread_mutex_unlock(&mp->mod_lock); 815 816 /* 817 * If it's the first time through fmd_module_enter(), call the provided 818 * function on the module. If no fmd_module_abort() results, we will 819 * fall through and return zero. Otherwise we'll longjmp with an err, 820 * return to the setjmp() above, and return the error to our caller. 821 */ 822 if (err == 0 && func != NULL) 823 (*func)((fmd_hdl_t *)mp); 824 825 return (err); 826 } 827 828 void 829 fmd_module_exit(fmd_module_t *mp) 830 { 831 (void) pthread_mutex_lock(&mp->mod_lock); 832 833 ASSERT(mp->mod_flags & FMD_MOD_BUSY); 834 mp->mod_flags &= ~FMD_MOD_BUSY; 835 836 (void) pthread_cond_broadcast(&mp->mod_cv); 837 (void) pthread_mutex_unlock(&mp->mod_lock); 838 } 839 840 /* 841 * If the client.error policy has been set by a developer, stop or dump core 842 * based on the policy; if we stop and are resumed we'll continue and execute 843 * the default behavior to discard events in fmd_module_start(). If the caller 844 * is the primary module thread, we reach this state by longjmp'ing back to 845 * fmd_module_enter(), above. If the caller is an auxiliary thread, we cancel 846 * ourself and arrange for the primary thread to call fmd_module_abort(). 847 */ 848 void 849 fmd_module_abort(fmd_module_t *mp, int err) 850 { 851 uint_t policy = FMD_CERROR_UNLOAD; 852 pthread_t tid = pthread_self(); 853 854 (void) fmd_conf_getprop(fmd.d_conf, "client.error", &policy); 855 856 if (policy == FMD_CERROR_STOP) { 857 fmd_error(err, "stopping after %s in client %s (%p)\n", 858 fmd_errclass(err), mp->mod_name, (void *)mp); 859 (void) raise(SIGSTOP); 860 } else if (policy == FMD_CERROR_ABORT) { 861 fmd_panic("aborting due to %s in client %s (%p)\n", 862 fmd_errclass(err), mp->mod_name, (void *)mp); 863 } 864 865 /* 866 * If the caller is an auxiliary thread, cancel the current thread. We 867 * prefer to cancel because it affords developers the option of using 868 * the pthread_cleanup* APIs. If cancellations have been disabled, 869 * fall through to forcing the current thread to exit. In either case 870 * we update mod_error (if zero) to enter the failed state. Once that 871 * is set, further events received by the module will be discarded. 872 * 873 * We also set the FMD_MOD_FAIL bit, indicating an unrecoverable error. 874 * When an auxiliary thread fails, the module is left in a delicate 875 * state where it is likely not able to continue execution (even to 876 * execute its _fmd_fini() routine) because our caller may hold locks 877 * that are private to the module and can no longer be released. The 878 * FMD_MOD_FAIL bit forces fmd_api_module_lock() to abort if any other 879 * module threads reach an API call, in an attempt to get them to exit. 880 */ 881 if (tid != mp->mod_thread->thr_tid) { 882 (void) pthread_mutex_lock(&mp->mod_lock); 883 884 if (mp->mod_error == 0) 885 mp->mod_error = err; 886 887 mp->mod_flags |= FMD_MOD_FAIL; 888 (void) pthread_mutex_unlock(&mp->mod_lock); 889 890 (void) pthread_cancel(tid); 891 pthread_exit(NULL); 892 } 893 894 ASSERT(mp->mod_flags & FMD_MOD_BUSY); 895 longjmp(mp->mod_jmpbuf, err); 896 } 897 898 void 899 fmd_module_hold(fmd_module_t *mp) 900 { 901 (void) pthread_mutex_lock(&mp->mod_lock); 902 903 TRACE((FMD_DBG_MOD, "hold %p (%s/%u)\n", 904 (void *)mp, mp->mod_name, mp->mod_refs)); 905 906 mp->mod_refs++; 907 ASSERT(mp->mod_refs != 0); 908 909 (void) pthread_mutex_unlock(&mp->mod_lock); 910 } 911 912 void 913 fmd_module_rele(fmd_module_t *mp) 914 { 915 (void) pthread_mutex_lock(&mp->mod_lock); 916 917 TRACE((FMD_DBG_MOD, "rele %p (%s/%u)\n", 918 (void *)mp, mp->mod_name, mp->mod_refs)); 919 920 ASSERT(mp->mod_refs != 0); 921 922 if (--mp->mod_refs == 0) 923 fmd_module_destroy(mp); 924 else 925 (void) pthread_mutex_unlock(&mp->mod_lock); 926 } 927 928 /* 929 * Wrapper around libdiagcode's fm_dc_opendict() to load module dictionaries. 930 * If the dictionary open is successful, the new dictionary is added to the 931 * mod_dictv[] array and mod_codelen is updated with the new maximum length. 932 */ 933 int 934 fmd_module_dc_opendict(fmd_module_t *mp, const char *dict) 935 { 936 struct fm_dc_handle *dcp, **dcv; 937 char *dictdir, *dictnam, *p; 938 size_t len; 939 940 ASSERT(fmd_module_locked(mp)); 941 942 dictnam = alloca(strlen(dict) + 1); 943 (void) strcpy(dictnam, fmd_strbasename(dict)); 944 945 if ((p = strrchr(dictnam, '.')) != NULL && 946 strcmp(p, ".dict") == 0) 947 *p = '\0'; /* eliminate any trailing .dict suffix */ 948 949 /* 950 * If 'dict' is an absolute path, dictdir = $rootdir/`dirname dict` 951 * If 'dict' is not an absolute path, dictdir = $dictdir/`dirname dict` 952 */ 953 if (dict[0] == '/') { 954 len = strlen(fmd.d_rootdir) + strlen(dict) + 1; 955 dictdir = alloca(len); 956 (void) snprintf(dictdir, len, "%s%s", fmd.d_rootdir, dict); 957 (void) fmd_strdirname(dictdir); 958 } else { 959 (void) fmd_conf_getprop(fmd.d_conf, "dictdir", &p); 960 len = strlen(fmd.d_rootdir) + strlen(p) + strlen(dict) + 3; 961 dictdir = alloca(len); 962 (void) snprintf(dictdir, len, 963 "%s/%s/%s", fmd.d_rootdir, p, dict); 964 (void) fmd_strdirname(dictdir); 965 } 966 967 fmd_dprintf(FMD_DBG_MOD, "module %s opening %s -> %s/%s.dict\n", 968 mp->mod_name, dict, dictdir, dictnam); 969 970 if ((dcp = fm_dc_opendict(FM_DC_VERSION, dictdir, dictnam)) == NULL) 971 return (-1); /* errno is set for us */ 972 973 dcv = fmd_alloc(sizeof (dcp) * (mp->mod_dictc + 1), FMD_SLEEP); 974 bcopy(mp->mod_dictv, dcv, sizeof (dcp) * mp->mod_dictc); 975 fmd_free(mp->mod_dictv, sizeof (dcp) * mp->mod_dictc); 976 mp->mod_dictv = dcv; 977 mp->mod_dictv[mp->mod_dictc++] = dcp; 978 979 len = fm_dc_codelen(dcp); 980 mp->mod_codelen = MAX(mp->mod_codelen, len); 981 982 return (0); 983 } 984 985 /* 986 * Wrapper around libdiagcode's fm_dc_key2code() that examines all the module's 987 * dictionaries. We adhere to the libdiagcode return values and semantics. 988 */ 989 int 990 fmd_module_dc_key2code(fmd_module_t *mp, 991 char *const keys[], char *code, size_t codelen) 992 { 993 int i, err; 994 995 for (i = 0; i < mp->mod_dictc; i++) { 996 if ((err = fm_dc_key2code(mp->mod_dictv[i], (const char **)keys, 997 code, codelen)) == 0 || errno != ENOMSG) 998 return (err); 999 } 1000 1001 return (fmd_set_errno(ENOMSG)); 1002 } 1003 1004 fmd_modhash_t * 1005 fmd_modhash_create(void) 1006 { 1007 fmd_modhash_t *mhp = fmd_alloc(sizeof (fmd_modhash_t), FMD_SLEEP); 1008 1009 (void) pthread_rwlock_init(&mhp->mh_lock, NULL); 1010 mhp->mh_hashlen = fmd.d_str_buckets; 1011 mhp->mh_hash = fmd_zalloc(sizeof (void *) * mhp->mh_hashlen, FMD_SLEEP); 1012 mhp->mh_nelems = 0; 1013 1014 return (mhp); 1015 } 1016 1017 void 1018 fmd_modhash_destroy(fmd_modhash_t *mhp) 1019 { 1020 fmd_module_t *mp, *nmp; 1021 uint_t i; 1022 1023 for (i = 0; i < mhp->mh_hashlen; i++) { 1024 for (mp = mhp->mh_hash[i]; mp != NULL; mp = nmp) { 1025 nmp = mp->mod_next; 1026 mp->mod_next = NULL; 1027 fmd_module_rele(mp); 1028 } 1029 } 1030 1031 fmd_free(mhp->mh_hash, sizeof (void *) * mhp->mh_hashlen); 1032 (void) pthread_rwlock_destroy(&mhp->mh_lock); 1033 fmd_free(mhp, sizeof (fmd_modhash_t)); 1034 } 1035 1036 static void 1037 fmd_modhash_loaddir(fmd_modhash_t *mhp, const char *dir, 1038 const fmd_modops_t *ops, const char *suffix) 1039 { 1040 char path[PATH_MAX]; 1041 struct dirent *dp; 1042 const char *p; 1043 DIR *dirp; 1044 1045 if ((dirp = opendir(dir)) == NULL) 1046 return; /* failed to open directory; just skip it */ 1047 1048 while ((dp = readdir(dirp)) != NULL) { 1049 if (dp->d_name[0] == '.') 1050 continue; /* skip "." and ".." */ 1051 1052 p = strrchr(dp->d_name, '.'); 1053 1054 if (p != NULL && strcmp(p, ".conf") == 0) 1055 continue; /* skip .conf files */ 1056 1057 if (suffix != NULL && (p == NULL || strcmp(p, suffix) != 0)) 1058 continue; /* skip files with the wrong suffix */ 1059 1060 (void) snprintf(path, sizeof (path), "%s/%s", dir, dp->d_name); 1061 (void) fmd_modhash_load(mhp, path, ops); 1062 } 1063 1064 (void) closedir(dirp); 1065 } 1066 1067 void 1068 fmd_modhash_loadall(fmd_modhash_t *mhp, const fmd_conf_path_t *pap, 1069 const fmd_modops_t *ops, const char *suffix) 1070 { 1071 int i; 1072 1073 for (i = 0; i < pap->cpa_argc; i++) 1074 fmd_modhash_loaddir(mhp, pap->cpa_argv[i], ops, suffix); 1075 } 1076 1077 void 1078 fmd_modhash_apply(fmd_modhash_t *mhp, void (*func)(fmd_module_t *)) 1079 { 1080 fmd_module_t *mp, *np; 1081 uint_t i; 1082 1083 (void) pthread_rwlock_rdlock(&mhp->mh_lock); 1084 1085 for (i = 0; i < mhp->mh_hashlen; i++) { 1086 for (mp = mhp->mh_hash[i]; mp != NULL; mp = np) { 1087 np = mp->mod_next; 1088 func(mp); 1089 } 1090 } 1091 1092 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1093 } 1094 1095 void 1096 fmd_modhash_tryapply(fmd_modhash_t *mhp, void (*func)(fmd_module_t *)) 1097 { 1098 fmd_module_t *mp, *np; 1099 uint_t i; 1100 1101 if (mhp == NULL || pthread_rwlock_tryrdlock(&mhp->mh_lock) != 0) 1102 return; /* not initialized or couldn't grab lock */ 1103 1104 for (i = 0; i < mhp->mh_hashlen; i++) { 1105 for (mp = mhp->mh_hash[i]; mp != NULL; mp = np) { 1106 np = mp->mod_next; 1107 func(mp); 1108 } 1109 } 1110 1111 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1112 } 1113 1114 void 1115 fmd_modhash_dispatch(fmd_modhash_t *mhp, fmd_event_t *ep) 1116 { 1117 fmd_module_t *mp; 1118 uint_t i; 1119 1120 fmd_event_hold(ep); 1121 (void) pthread_rwlock_rdlock(&mhp->mh_lock); 1122 1123 for (i = 0; i < mhp->mh_hashlen; i++) { 1124 for (mp = mhp->mh_hash[i]; mp != NULL; mp = mp->mod_next) { 1125 /* 1126 * If FMD_MOD_INIT is set but MOD_FINI, MOD_QUIT, and 1127 * mod_error are all zero, then the module is active: 1128 * enqueue the event in the corresponding event queue. 1129 */ 1130 (void) pthread_mutex_lock(&mp->mod_lock); 1131 1132 if ((mp->mod_flags & (FMD_MOD_INIT | FMD_MOD_FINI | 1133 FMD_MOD_QUIT)) == FMD_MOD_INIT && !mp->mod_error) { 1134 1135 /* 1136 * If the event we're dispatching is of type 1137 * FMD_EVT_TOPO and there are already redundant 1138 * FMD_EVT_TOPO events in this module's queue, 1139 * then drop those before adding the new one. 1140 */ 1141 if (FMD_EVENT_TYPE(ep) == FMD_EVT_TOPO) 1142 fmd_eventq_drop_topo(mp->mod_queue); 1143 1144 fmd_eventq_insert_at_time(mp->mod_queue, ep); 1145 1146 } 1147 (void) pthread_mutex_unlock(&mp->mod_lock); 1148 } 1149 } 1150 1151 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1152 fmd_event_rele(ep); 1153 } 1154 1155 fmd_module_t * 1156 fmd_modhash_lookup(fmd_modhash_t *mhp, const char *name) 1157 { 1158 fmd_module_t *mp; 1159 uint_t h; 1160 1161 (void) pthread_rwlock_rdlock(&mhp->mh_lock); 1162 h = fmd_strhash(name) % mhp->mh_hashlen; 1163 1164 for (mp = mhp->mh_hash[h]; mp != NULL; mp = mp->mod_next) { 1165 if (strcmp(name, mp->mod_name) == 0) 1166 break; 1167 } 1168 1169 if (mp != NULL) 1170 fmd_module_hold(mp); 1171 else 1172 (void) fmd_set_errno(EFMD_MOD_NOMOD); 1173 1174 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1175 return (mp); 1176 } 1177 1178 fmd_module_t * 1179 fmd_modhash_load(fmd_modhash_t *mhp, const char *path, const fmd_modops_t *ops) 1180 { 1181 char name[PATH_MAX], *p; 1182 fmd_module_t *mp; 1183 int tries = 0; 1184 uint_t h; 1185 1186 (void) strlcpy(name, fmd_strbasename(path), sizeof (name)); 1187 if ((p = strrchr(name, '.')) != NULL && strcmp(p, ".so") == 0) 1188 *p = '\0'; /* strip trailing .so from any module name */ 1189 1190 (void) pthread_rwlock_wrlock(&mhp->mh_lock); 1191 h = fmd_strhash(name) % mhp->mh_hashlen; 1192 1193 /* 1194 * First check to see if a module is already present in the hash table 1195 * for this name. If so, the module is already loaded: skip it. 1196 */ 1197 for (mp = mhp->mh_hash[h]; mp != NULL; mp = mp->mod_next) { 1198 if (strcmp(name, mp->mod_name) == 0) 1199 break; 1200 } 1201 1202 if (mp != NULL) { 1203 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1204 (void) fmd_set_errno(EFMD_MOD_LOADED); 1205 return (NULL); 1206 } 1207 1208 /* 1209 * fmd_module_create() will return a held (as if by fmd_module_hold()) 1210 * module. We leave this hold in place to correspond to the hash-in. 1211 */ 1212 while ((mp = fmd_module_create(path, ops)) == NULL) { 1213 if (tries++ != 0 || errno != EFMD_CKPT_INVAL) { 1214 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1215 return (NULL); /* errno is set for us */ 1216 } 1217 } 1218 1219 mp->mod_hash = mhp; 1220 mp->mod_next = mhp->mh_hash[h]; 1221 1222 mhp->mh_hash[h] = mp; 1223 mhp->mh_nelems++; 1224 1225 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1226 return (mp); 1227 } 1228 1229 int 1230 fmd_modhash_unload(fmd_modhash_t *mhp, const char *name) 1231 { 1232 fmd_module_t *mp, **pp; 1233 uint_t h; 1234 1235 (void) pthread_rwlock_wrlock(&mhp->mh_lock); 1236 h = fmd_strhash(name) % mhp->mh_hashlen; 1237 pp = &mhp->mh_hash[h]; 1238 1239 for (mp = *pp; mp != NULL; mp = mp->mod_next) { 1240 if (strcmp(name, mp->mod_name) == 0) 1241 break; 1242 else 1243 pp = &mp->mod_next; 1244 } 1245 1246 if (mp == NULL) { 1247 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1248 return (fmd_set_errno(EFMD_MOD_NOMOD)); 1249 } 1250 1251 *pp = mp->mod_next; 1252 mp->mod_next = NULL; 1253 1254 ASSERT(mhp->mh_nelems != 0); 1255 mhp->mh_nelems--; 1256 1257 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1258 1259 fmd_module_unload(mp); 1260 fmd_module_rele(mp); 1261 1262 return (0); 1263 } 1264 1265 void 1266 fmd_modstat_publish(fmd_module_t *mp) 1267 { 1268 (void) pthread_mutex_lock(&mp->mod_lock); 1269 1270 ASSERT(mp->mod_flags & FMD_MOD_STSUB); 1271 mp->mod_flags |= FMD_MOD_STPUB; 1272 (void) pthread_cond_broadcast(&mp->mod_cv); 1273 1274 while (mp->mod_flags & FMD_MOD_STPUB) 1275 (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); 1276 1277 (void) pthread_mutex_unlock(&mp->mod_lock); 1278 } 1279 1280 int 1281 fmd_modstat_snapshot(fmd_module_t *mp, fmd_ustat_snap_t *uss) 1282 { 1283 fmd_event_t *e; 1284 int err; 1285 1286 /* 1287 * Grab the module lock and wait for the STSUB bit to be clear. Then 1288 * set it to indicate we are a subscriber and everyone else must wait. 1289 */ 1290 (void) pthread_mutex_lock(&mp->mod_lock); 1291 1292 while (mp->mod_error == 0 && (mp->mod_flags & FMD_MOD_STSUB)) 1293 (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); 1294 1295 if (mp->mod_error != 0) { 1296 (void) pthread_mutex_unlock(&mp->mod_lock); 1297 return (fmd_set_errno(EFMD_HDL_ABORT)); 1298 } 1299 1300 mp->mod_flags |= FMD_MOD_STSUB; 1301 (void) pthread_cond_broadcast(&mp->mod_cv); 1302 (void) pthread_mutex_unlock(&mp->mod_lock); 1303 1304 /* 1305 * Create a stats pseudo-event and dispatch it to the module, forcing 1306 * it to next execute its custom snapshot routine (or the empty one). 1307 */ 1308 e = fmd_event_create(FMD_EVT_STATS, FMD_HRT_NOW, NULL, NULL); 1309 fmd_eventq_insert_at_head(mp->mod_queue, e); 1310 1311 /* 1312 * Grab the module lock and then wait on mod_cv for STPUB to be set, 1313 * indicating the snapshot routine is completed and the module is idle. 1314 */ 1315 (void) pthread_mutex_lock(&mp->mod_lock); 1316 1317 while (mp->mod_error == 0 && !(mp->mod_flags & FMD_MOD_STPUB)) 1318 (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); 1319 1320 if (mp->mod_error != 0) { 1321 (void) pthread_mutex_unlock(&mp->mod_lock); 1322 return (fmd_set_errno(EFMD_HDL_ABORT)); 1323 } 1324 1325 (void) pthread_cond_broadcast(&mp->mod_cv); 1326 (void) pthread_mutex_unlock(&mp->mod_lock); 1327 1328 /* 1329 * Update ms_snaptime and take the actual snapshot of the various 1330 * statistics while the module is quiescent and waiting for us. 1331 */ 1332 (void) pthread_mutex_lock(&mp->mod_stats_lock); 1333 1334 if (mp->mod_stats != NULL) { 1335 mp->mod_stats->ms_snaptime.fmds_value.ui64 = gethrtime(); 1336 err = fmd_ustat_snapshot(mp->mod_ustat, uss); 1337 } else 1338 err = fmd_set_errno(EFMD_HDL_ABORT); 1339 1340 (void) pthread_mutex_unlock(&mp->mod_stats_lock); 1341 1342 /* 1343 * With the snapshot complete, grab the module lock and clear both 1344 * STSUB and STPUB, permitting everyone to wake up and continue. 1345 */ 1346 (void) pthread_mutex_lock(&mp->mod_lock); 1347 1348 ASSERT(mp->mod_flags & FMD_MOD_STSUB); 1349 ASSERT(mp->mod_flags & FMD_MOD_STPUB); 1350 mp->mod_flags &= ~(FMD_MOD_STSUB | FMD_MOD_STPUB); 1351 1352 (void) pthread_cond_broadcast(&mp->mod_cv); 1353 (void) pthread_mutex_unlock(&mp->mod_lock); 1354 1355 return (err); 1356 } 1357 1358 struct topo_hdl * 1359 fmd_module_topo_hold(fmd_module_t *mp) 1360 { 1361 fmd_modtopo_t *mtp; 1362 1363 ASSERT(fmd_module_locked(mp)); 1364 1365 mtp = fmd_zalloc(sizeof (fmd_modtopo_t), FMD_SLEEP); 1366 mtp->mt_topo = mp->mod_topo_current; 1367 fmd_topo_addref(mtp->mt_topo); 1368 fmd_list_prepend(&mp->mod_topolist, mtp); 1369 1370 return (mtp->mt_topo->ft_hdl); 1371 } 1372 1373 int 1374 fmd_module_topo_rele(fmd_module_t *mp, struct topo_hdl *hdl) 1375 { 1376 fmd_modtopo_t *mtp; 1377 1378 ASSERT(fmd_module_locked(mp)); 1379 1380 for (mtp = fmd_list_next(&mp->mod_topolist); mtp != NULL; 1381 mtp = fmd_list_next(mtp)) { 1382 if (mtp->mt_topo->ft_hdl == hdl) 1383 break; 1384 } 1385 1386 if (mtp == NULL) 1387 return (-1); 1388 1389 fmd_list_delete(&mp->mod_topolist, mtp); 1390 fmd_topo_rele(mtp->mt_topo); 1391 fmd_free(mtp, sizeof (fmd_modtopo_t)); 1392 return (0); 1393 } 1394