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