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