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 2007 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 469 fmd_free(mp, sizeof (fmd_module_t)); 470 } 471 472 /* 473 * fmd_module_error() is called after the stack is unwound from a call to 474 * fmd_module_abort() to indicate that the module has failed. The mod_error 475 * field is used to hold the error code of the first fatal error to the module. 476 * An EFMD_MOD_FAIL event is then created and sent to fmd-self-diagnosis. 477 */ 478 static void 479 fmd_module_error(fmd_module_t *mp, int err) 480 { 481 fmd_event_t *e; 482 nvlist_t *nvl; 483 char *class; 484 485 ASSERT(MUTEX_HELD(&mp->mod_lock)); 486 ASSERT(err != 0); 487 488 TRACE((FMD_DBG_MOD, "module aborted: err=%d", err)); 489 490 if (mp->mod_error == 0) 491 mp->mod_error = err; 492 493 if (mp == fmd.d_self) 494 return; /* do not post event if fmd.d_self itself fails */ 495 496 /* 497 * Send an error indicating the module has now failed to fmd.d_self. 498 * Since the error causing the failure has already been logged by 499 * fmd_api_xerror(), we do not need to bother logging this event. 500 * It only exists for the purpose of notifying fmd.d_self that it can 501 * close the case associated with this module because mod_error is set. 502 */ 503 nvl = fmd_protocol_moderror(mp, EFMD_MOD_FAIL, fmd_strerror(err)); 504 (void) nvlist_lookup_string(nvl, FM_CLASS, &class); 505 e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); 506 fmd_dispq_dispatch(fmd.d_disp, e, class); 507 } 508 509 void 510 fmd_module_dispatch(fmd_module_t *mp, fmd_event_t *e) 511 { 512 const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops; 513 fmd_event_impl_t *ep = (fmd_event_impl_t *)e; 514 fmd_hdl_t *hdl = (fmd_hdl_t *)mp; 515 fmd_modtimer_t *t; 516 volatile int err; 517 518 /* 519 * Before calling the appropriate module callback, enter the module as 520 * if by fmd_module_enter() and establish mod_jmpbuf for any aborts. 521 */ 522 (void) pthread_mutex_lock(&mp->mod_lock); 523 524 ASSERT(!(mp->mod_flags & FMD_MOD_BUSY)); 525 mp->mod_flags |= FMD_MOD_BUSY; 526 527 if ((err = setjmp(mp->mod_jmpbuf)) != 0) { 528 (void) pthread_mutex_lock(&mp->mod_lock); 529 fmd_module_error(mp, err); 530 } 531 532 (void) pthread_cond_broadcast(&mp->mod_cv); 533 (void) pthread_mutex_unlock(&mp->mod_lock); 534 535 /* 536 * If it's the first time through fmd_module_dispatch(), call the 537 * appropriate module callback based on the event type. If the call 538 * triggers an fmd_module_abort(), we'll return to setjmp() above with 539 * err set to a non-zero value and then bypass this before exiting. 540 */ 541 if (err == 0) { 542 switch (ep->ev_type) { 543 case FMD_EVT_PROTOCOL: 544 ops->fmdo_recv(hdl, e, ep->ev_nvl, ep->ev_data); 545 break; 546 case FMD_EVT_TIMEOUT: 547 t = ep->ev_data; 548 ASSERT(t->mt_mod == mp); 549 ops->fmdo_timeout(hdl, t->mt_id, t->mt_arg); 550 break; 551 case FMD_EVT_CLOSE: 552 ops->fmdo_close(hdl, ep->ev_data); 553 break; 554 case FMD_EVT_STATS: 555 ops->fmdo_stats(hdl); 556 fmd_modstat_publish(mp); 557 break; 558 case FMD_EVT_GC: 559 ops->fmdo_gc(hdl); 560 break; 561 case FMD_EVT_PUBLISH: 562 fmd_case_publish(ep->ev_data, FMD_CASE_CURRENT); 563 break; 564 case FMD_EVT_TOPO: 565 fmd_topo_rele(mp->mod_topo_current); 566 mp->mod_topo_current = (fmd_topo_t *)ep->ev_data; 567 fmd_topo_addref(mp->mod_topo_current); 568 ops->fmdo_topo(hdl, mp->mod_topo_current->ft_hdl); 569 break; 570 } 571 } 572 573 fmd_module_exit(mp); 574 } 575 576 int 577 fmd_module_transport(fmd_module_t *mp, fmd_xprt_t *xp, fmd_event_t *e) 578 { 579 fmd_event_impl_t *ep = (fmd_event_impl_t *)e; 580 fmd_hdl_t *hdl = (fmd_hdl_t *)mp; 581 582 ASSERT(ep->ev_type == FMD_EVT_PROTOCOL); 583 return (mp->mod_info->fmdi_ops->fmdo_send(hdl, xp, e, ep->ev_nvl)); 584 } 585 586 void 587 fmd_module_timeout(fmd_modtimer_t *t, id_t id, hrtime_t hrt) 588 { 589 fmd_event_t *e; 590 591 t->mt_id = id; /* save id in case we need to delete from eventq */ 592 e = fmd_event_create(FMD_EVT_TIMEOUT, hrt, NULL, t); 593 fmd_eventq_insert_at_time(t->mt_mod->mod_queue, e); 594 } 595 596 /* 597 * Garbage collection is initiated by a timer callback once per day or at the 598 * request of fmadm. Purge old SERD entries and send the module a GC event. 599 */ 600 void 601 fmd_module_gc(fmd_module_t *mp) 602 { 603 fmd_hdl_info_t *info; 604 fmd_event_t *e; 605 606 if (mp->mod_error != 0) 607 return; /* do not do anything if the module has failed */ 608 609 fmd_module_lock(mp); 610 611 if ((info = mp->mod_info) != NULL) { 612 fmd_serd_hash_apply(&mp->mod_serds, 613 (fmd_serd_eng_f *)fmd_serd_eng_gc, NULL); 614 } 615 616 fmd_module_unlock(mp); 617 618 if (info != NULL) { 619 e = fmd_event_create(FMD_EVT_GC, FMD_HRT_NOW, NULL, NULL); 620 fmd_eventq_insert_at_head(mp->mod_queue, e); 621 } 622 } 623 624 void 625 fmd_module_trygc(fmd_module_t *mp) 626 { 627 if (fmd_module_trylock(mp)) { 628 fmd_serd_hash_apply(&mp->mod_serds, 629 (fmd_serd_eng_f *)fmd_serd_eng_gc, NULL); 630 fmd_module_unlock(mp); 631 } 632 } 633 634 int 635 fmd_module_contains(fmd_module_t *mp, fmd_event_t *ep) 636 { 637 fmd_case_t *cp; 638 int rv = 0; 639 640 fmd_module_lock(mp); 641 642 for (cp = fmd_list_next(&mp->mod_cases); 643 cp != NULL; cp = fmd_list_next(cp)) { 644 if ((rv = fmd_case_contains(cp, ep)) != 0) 645 break; 646 } 647 648 if (rv == 0) 649 rv = fmd_serd_hash_contains(&mp->mod_serds, ep); 650 651 fmd_module_unlock(mp); 652 return (rv); 653 } 654 655 void 656 fmd_module_setdirty(fmd_module_t *mp) 657 { 658 (void) pthread_mutex_lock(&mp->mod_lock); 659 mp->mod_flags |= FMD_MOD_MDIRTY; 660 (void) pthread_mutex_unlock(&mp->mod_lock); 661 } 662 663 void 664 fmd_module_setcdirty(fmd_module_t *mp) 665 { 666 (void) pthread_mutex_lock(&mp->mod_lock); 667 mp->mod_flags |= FMD_MOD_CDIRTY; 668 (void) pthread_mutex_unlock(&mp->mod_lock); 669 } 670 671 void 672 fmd_module_clrdirty(fmd_module_t *mp) 673 { 674 fmd_case_t *cp; 675 676 fmd_module_lock(mp); 677 678 if (mp->mod_flags & FMD_MOD_CDIRTY) { 679 for (cp = fmd_list_next(&mp->mod_cases); 680 cp != NULL; cp = fmd_list_next(cp)) 681 fmd_case_clrdirty(cp); 682 } 683 684 if (mp->mod_flags & FMD_MOD_MDIRTY) { 685 fmd_serd_hash_apply(&mp->mod_serds, 686 (fmd_serd_eng_f *)fmd_serd_eng_clrdirty, NULL); 687 fmd_buf_hash_commit(&mp->mod_bufs); 688 } 689 690 (void) pthread_mutex_lock(&mp->mod_lock); 691 mp->mod_flags &= ~(FMD_MOD_MDIRTY | FMD_MOD_CDIRTY); 692 (void) pthread_mutex_unlock(&mp->mod_lock); 693 694 fmd_module_unlock(mp); 695 } 696 697 void 698 fmd_module_commit(fmd_module_t *mp) 699 { 700 fmd_case_t *cp; 701 702 ASSERT(fmd_module_locked(mp)); 703 704 if (mp->mod_flags & FMD_MOD_CDIRTY) { 705 for (cp = fmd_list_next(&mp->mod_cases); 706 cp != NULL; cp = fmd_list_next(cp)) 707 fmd_case_commit(cp); 708 } 709 710 if (mp->mod_flags & FMD_MOD_MDIRTY) { 711 fmd_serd_hash_apply(&mp->mod_serds, 712 (fmd_serd_eng_f *)fmd_serd_eng_commit, NULL); 713 fmd_buf_hash_commit(&mp->mod_bufs); 714 } 715 716 (void) pthread_mutex_lock(&mp->mod_lock); 717 mp->mod_flags &= ~(FMD_MOD_MDIRTY | FMD_MOD_CDIRTY); 718 (void) pthread_mutex_unlock(&mp->mod_lock); 719 720 mp->mod_gen++; 721 } 722 723 void 724 fmd_module_lock(fmd_module_t *mp) 725 { 726 pthread_t self = pthread_self(); 727 728 (void) pthread_mutex_lock(&mp->mod_lock); 729 730 while (mp->mod_flags & FMD_MOD_LOCK) { 731 if (mp->mod_owner != self) 732 (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); 733 else 734 fmd_panic("recursive module lock of %p\n", (void *)mp); 735 } 736 737 mp->mod_owner = self; 738 mp->mod_flags |= FMD_MOD_LOCK; 739 740 (void) pthread_cond_broadcast(&mp->mod_cv); 741 (void) pthread_mutex_unlock(&mp->mod_lock); 742 } 743 744 void 745 fmd_module_unlock(fmd_module_t *mp) 746 { 747 (void) pthread_mutex_lock(&mp->mod_lock); 748 749 ASSERT(mp->mod_owner == pthread_self()); 750 ASSERT(mp->mod_flags & FMD_MOD_LOCK); 751 752 mp->mod_owner = 0; 753 mp->mod_flags &= ~FMD_MOD_LOCK; 754 755 (void) pthread_cond_broadcast(&mp->mod_cv); 756 (void) pthread_mutex_unlock(&mp->mod_lock); 757 } 758 759 int 760 fmd_module_trylock(fmd_module_t *mp) 761 { 762 (void) pthread_mutex_lock(&mp->mod_lock); 763 764 if (mp->mod_flags & FMD_MOD_LOCK) { 765 (void) pthread_mutex_unlock(&mp->mod_lock); 766 return (0); 767 } 768 769 mp->mod_owner = pthread_self(); 770 mp->mod_flags |= FMD_MOD_LOCK; 771 772 (void) pthread_cond_broadcast(&mp->mod_cv); 773 (void) pthread_mutex_unlock(&mp->mod_lock); 774 775 return (1); 776 } 777 778 int 779 fmd_module_locked(fmd_module_t *mp) 780 { 781 return ((mp->mod_flags & FMD_MOD_LOCK) && 782 mp->mod_owner == pthread_self()); 783 } 784 785 int 786 fmd_module_enter(fmd_module_t *mp, void (*func)(fmd_hdl_t *)) 787 { 788 volatile int err; 789 790 (void) pthread_mutex_lock(&mp->mod_lock); 791 792 ASSERT(!(mp->mod_flags & FMD_MOD_BUSY)); 793 mp->mod_flags |= FMD_MOD_BUSY; 794 795 if ((err = setjmp(mp->mod_jmpbuf)) != 0) { 796 (void) pthread_mutex_lock(&mp->mod_lock); 797 fmd_module_error(mp, err); 798 } 799 800 (void) pthread_cond_broadcast(&mp->mod_cv); 801 (void) pthread_mutex_unlock(&mp->mod_lock); 802 803 /* 804 * If it's the first time through fmd_module_enter(), call the provided 805 * function on the module. If no fmd_module_abort() results, we will 806 * fall through and return zero. Otherwise we'll longjmp with an err, 807 * return to the setjmp() above, and return the error to our caller. 808 */ 809 if (err == 0 && func != NULL) 810 (*func)((fmd_hdl_t *)mp); 811 812 return (err); 813 } 814 815 void 816 fmd_module_exit(fmd_module_t *mp) 817 { 818 (void) pthread_mutex_lock(&mp->mod_lock); 819 820 ASSERT(mp->mod_flags & FMD_MOD_BUSY); 821 mp->mod_flags &= ~FMD_MOD_BUSY; 822 823 (void) pthread_cond_broadcast(&mp->mod_cv); 824 (void) pthread_mutex_unlock(&mp->mod_lock); 825 } 826 827 /* 828 * If the client.error policy has been set by a developer, stop or dump core 829 * based on the policy; if we stop and are resumed we'll continue and execute 830 * the default behavior to discard events in fmd_module_start(). If the caller 831 * is the primary module thread, we reach this state by longjmp'ing back to 832 * fmd_module_enter(), above. If the caller is an auxiliary thread, we cancel 833 * ourself and arrange for the primary thread to call fmd_module_abort(). 834 */ 835 void 836 fmd_module_abort(fmd_module_t *mp, int err) 837 { 838 uint_t policy = FMD_CERROR_UNLOAD; 839 pthread_t tid = pthread_self(); 840 841 (void) fmd_conf_getprop(fmd.d_conf, "client.error", &policy); 842 843 if (policy == FMD_CERROR_STOP) { 844 fmd_error(err, "stopping after %s in client %s (%p)\n", 845 fmd_errclass(err), mp->mod_name, (void *)mp); 846 (void) raise(SIGSTOP); 847 } else if (policy == FMD_CERROR_ABORT) { 848 fmd_panic("aborting due to %s in client %s (%p)\n", 849 fmd_errclass(err), mp->mod_name, (void *)mp); 850 } 851 852 /* 853 * If the caller is an auxiliary thread, cancel the current thread. We 854 * prefer to cancel because it affords developers the option of using 855 * the pthread_cleanup* APIs. If cancellations have been disabled, 856 * fall through to forcing the current thread to exit. In either case 857 * we update mod_error (if zero) to enter the failed state. Once that 858 * is set, further events received by the module will be discarded. 859 * 860 * We also set the FMD_MOD_FAIL bit, indicating an unrecoverable error. 861 * When an auxiliary thread fails, the module is left in a delicate 862 * state where it is likely not able to continue execution (even to 863 * execute its _fmd_fini() routine) because our caller may hold locks 864 * that are private to the module and can no longer be released. The 865 * FMD_MOD_FAIL bit forces fmd_api_module_lock() to abort if any other 866 * module threads reach an API call, in an attempt to get them to exit. 867 */ 868 if (tid != mp->mod_thread->thr_tid) { 869 (void) pthread_mutex_lock(&mp->mod_lock); 870 871 if (mp->mod_error == 0) 872 mp->mod_error = err; 873 874 mp->mod_flags |= FMD_MOD_FAIL; 875 (void) pthread_mutex_unlock(&mp->mod_lock); 876 877 (void) pthread_cancel(tid); 878 pthread_exit(NULL); 879 } 880 881 ASSERT(mp->mod_flags & FMD_MOD_BUSY); 882 longjmp(mp->mod_jmpbuf, err); 883 } 884 885 void 886 fmd_module_hold(fmd_module_t *mp) 887 { 888 (void) pthread_mutex_lock(&mp->mod_lock); 889 890 TRACE((FMD_DBG_MOD, "hold %p (%s/%u)\n", 891 (void *)mp, mp->mod_name, mp->mod_refs)); 892 893 mp->mod_refs++; 894 ASSERT(mp->mod_refs != 0); 895 896 (void) pthread_mutex_unlock(&mp->mod_lock); 897 } 898 899 void 900 fmd_module_rele(fmd_module_t *mp) 901 { 902 (void) pthread_mutex_lock(&mp->mod_lock); 903 904 TRACE((FMD_DBG_MOD, "rele %p (%s/%u)\n", 905 (void *)mp, mp->mod_name, mp->mod_refs)); 906 907 ASSERT(mp->mod_refs != 0); 908 909 if (--mp->mod_refs == 0) 910 fmd_module_destroy(mp); 911 else 912 (void) pthread_mutex_unlock(&mp->mod_lock); 913 } 914 915 /* 916 * Wrapper around libdiagcode's fm_dc_opendict() to load module dictionaries. 917 * If the dictionary open is successful, the new dictionary is added to the 918 * mod_dictv[] array and mod_codelen is updated with the new maximum length. 919 */ 920 int 921 fmd_module_dc_opendict(fmd_module_t *mp, const char *dict) 922 { 923 struct fm_dc_handle *dcp, **dcv; 924 char *dictdir, *dictnam, *p; 925 size_t len; 926 927 ASSERT(fmd_module_locked(mp)); 928 929 dictnam = alloca(strlen(dict) + 1); 930 (void) strcpy(dictnam, fmd_strbasename(dict)); 931 932 if ((p = strrchr(dictnam, '.')) != NULL && 933 strcmp(p, ".dict") == 0) 934 *p = '\0'; /* eliminate any trailing .dict suffix */ 935 936 /* 937 * If 'dict' is an absolute path, dictdir = $rootdir/`dirname dict` 938 * If 'dict' is not an absolute path, dictdir = $dictdir/`dirname dict` 939 */ 940 if (dict[0] == '/') { 941 len = strlen(fmd.d_rootdir) + strlen(dict) + 1; 942 dictdir = alloca(len); 943 (void) snprintf(dictdir, len, "%s%s", fmd.d_rootdir, dict); 944 (void) fmd_strdirname(dictdir); 945 } else { 946 (void) fmd_conf_getprop(fmd.d_conf, "dictdir", &p); 947 len = strlen(fmd.d_rootdir) + strlen(p) + strlen(dict) + 3; 948 dictdir = alloca(len); 949 (void) snprintf(dictdir, len, 950 "%s/%s/%s", fmd.d_rootdir, p, dict); 951 (void) fmd_strdirname(dictdir); 952 } 953 954 fmd_dprintf(FMD_DBG_MOD, "module %s opening %s -> %s/%s.dict\n", 955 mp->mod_name, dict, dictdir, dictnam); 956 957 if ((dcp = fm_dc_opendict(FM_DC_VERSION, dictdir, dictnam)) == NULL) 958 return (-1); /* errno is set for us */ 959 960 dcv = fmd_alloc(sizeof (dcp) * (mp->mod_dictc + 1), FMD_SLEEP); 961 bcopy(mp->mod_dictv, dcv, sizeof (dcp) * mp->mod_dictc); 962 fmd_free(mp->mod_dictv, sizeof (dcp) * mp->mod_dictc); 963 mp->mod_dictv = dcv; 964 mp->mod_dictv[mp->mod_dictc++] = dcp; 965 966 len = fm_dc_codelen(dcp); 967 mp->mod_codelen = MAX(mp->mod_codelen, len); 968 969 return (0); 970 } 971 972 /* 973 * Wrapper around libdiagcode's fm_dc_key2code() that examines all the module's 974 * dictionaries. We adhere to the libdiagcode return values and semantics. 975 */ 976 int 977 fmd_module_dc_key2code(fmd_module_t *mp, 978 char *const keys[], char *code, size_t codelen) 979 { 980 int i, err; 981 982 for (i = 0; i < mp->mod_dictc; i++) { 983 if ((err = fm_dc_key2code(mp->mod_dictv[i], (const char **)keys, 984 code, codelen)) == 0 || errno != ENOMSG) 985 return (err); 986 } 987 988 return (fmd_set_errno(ENOMSG)); 989 } 990 991 fmd_modhash_t * 992 fmd_modhash_create(void) 993 { 994 fmd_modhash_t *mhp = fmd_alloc(sizeof (fmd_modhash_t), FMD_SLEEP); 995 996 (void) pthread_rwlock_init(&mhp->mh_lock, NULL); 997 mhp->mh_hashlen = fmd.d_str_buckets; 998 mhp->mh_hash = fmd_zalloc(sizeof (void *) * mhp->mh_hashlen, FMD_SLEEP); 999 mhp->mh_nelems = 0; 1000 1001 return (mhp); 1002 } 1003 1004 void 1005 fmd_modhash_destroy(fmd_modhash_t *mhp) 1006 { 1007 fmd_module_t *mp, *nmp; 1008 uint_t i; 1009 1010 for (i = 0; i < mhp->mh_hashlen; i++) { 1011 for (mp = mhp->mh_hash[i]; mp != NULL; mp = nmp) { 1012 nmp = mp->mod_next; 1013 mp->mod_next = NULL; 1014 fmd_module_rele(mp); 1015 } 1016 } 1017 1018 fmd_free(mhp->mh_hash, sizeof (void *) * mhp->mh_hashlen); 1019 (void) pthread_rwlock_destroy(&mhp->mh_lock); 1020 fmd_free(mhp, sizeof (fmd_modhash_t)); 1021 } 1022 1023 static void 1024 fmd_modhash_loaddir(fmd_modhash_t *mhp, const char *dir, 1025 const fmd_modops_t *ops, const char *suffix) 1026 { 1027 char path[PATH_MAX]; 1028 struct dirent *dp; 1029 const char *p; 1030 DIR *dirp; 1031 1032 if ((dirp = opendir(dir)) == NULL) 1033 return; /* failed to open directory; just skip it */ 1034 1035 while ((dp = readdir(dirp)) != NULL) { 1036 if (dp->d_name[0] == '.') 1037 continue; /* skip "." and ".." */ 1038 1039 p = strrchr(dp->d_name, '.'); 1040 1041 if (p != NULL && strcmp(p, ".conf") == 0) 1042 continue; /* skip .conf files */ 1043 1044 if (suffix != NULL && (p == NULL || strcmp(p, suffix) != 0)) 1045 continue; /* skip files with the wrong suffix */ 1046 1047 (void) snprintf(path, sizeof (path), "%s/%s", dir, dp->d_name); 1048 (void) fmd_modhash_load(mhp, path, ops); 1049 } 1050 1051 (void) closedir(dirp); 1052 } 1053 1054 void 1055 fmd_modhash_loadall(fmd_modhash_t *mhp, const fmd_conf_path_t *pap, 1056 const fmd_modops_t *ops, const char *suffix) 1057 { 1058 int i; 1059 1060 for (i = 0; i < pap->cpa_argc; i++) 1061 fmd_modhash_loaddir(mhp, pap->cpa_argv[i], ops, suffix); 1062 } 1063 1064 void 1065 fmd_modhash_apply(fmd_modhash_t *mhp, void (*func)(fmd_module_t *)) 1066 { 1067 fmd_module_t *mp, *np; 1068 uint_t i; 1069 1070 (void) pthread_rwlock_rdlock(&mhp->mh_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_tryapply(fmd_modhash_t *mhp, void (*func)(fmd_module_t *)) 1084 { 1085 fmd_module_t *mp, *np; 1086 uint_t i; 1087 1088 if (mhp == NULL || pthread_rwlock_tryrdlock(&mhp->mh_lock) != 0) 1089 return; /* not initialized or couldn't grab lock */ 1090 1091 for (i = 0; i < mhp->mh_hashlen; i++) { 1092 for (mp = mhp->mh_hash[i]; mp != NULL; mp = np) { 1093 np = mp->mod_next; 1094 func(mp); 1095 } 1096 } 1097 1098 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1099 } 1100 1101 void 1102 fmd_modhash_dispatch(fmd_modhash_t *mhp, fmd_event_t *ep) 1103 { 1104 fmd_module_t *mp; 1105 uint_t i; 1106 1107 fmd_event_hold(ep); 1108 (void) pthread_rwlock_rdlock(&mhp->mh_lock); 1109 1110 for (i = 0; i < mhp->mh_hashlen; i++) { 1111 for (mp = mhp->mh_hash[i]; mp != NULL; mp = mp->mod_next) { 1112 /* 1113 * If FMD_MOD_INIT is set but MOD_FINI, MOD_QUIT, and 1114 * mod_error are all zero, then the module is active: 1115 * enqueue the event in the corresponding event queue. 1116 */ 1117 (void) pthread_mutex_lock(&mp->mod_lock); 1118 1119 if ((mp->mod_flags & (FMD_MOD_INIT | FMD_MOD_FINI | 1120 FMD_MOD_QUIT)) == FMD_MOD_INIT && !mp->mod_error) 1121 fmd_eventq_insert_at_time(mp->mod_queue, ep); 1122 1123 (void) pthread_mutex_unlock(&mp->mod_lock); 1124 } 1125 } 1126 1127 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1128 fmd_event_rele(ep); 1129 } 1130 1131 fmd_module_t * 1132 fmd_modhash_lookup(fmd_modhash_t *mhp, const char *name) 1133 { 1134 fmd_module_t *mp; 1135 uint_t h; 1136 1137 (void) pthread_rwlock_rdlock(&mhp->mh_lock); 1138 h = fmd_strhash(name) % mhp->mh_hashlen; 1139 1140 for (mp = mhp->mh_hash[h]; mp != NULL; mp = mp->mod_next) { 1141 if (strcmp(name, mp->mod_name) == 0) 1142 break; 1143 } 1144 1145 if (mp != NULL) 1146 fmd_module_hold(mp); 1147 else 1148 (void) fmd_set_errno(EFMD_MOD_NOMOD); 1149 1150 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1151 return (mp); 1152 } 1153 1154 fmd_module_t * 1155 fmd_modhash_load(fmd_modhash_t *mhp, const char *path, const fmd_modops_t *ops) 1156 { 1157 char name[PATH_MAX], *p; 1158 fmd_module_t *mp; 1159 int tries = 0; 1160 uint_t h; 1161 1162 (void) strlcpy(name, fmd_strbasename(path), sizeof (name)); 1163 if ((p = strrchr(name, '.')) != NULL && strcmp(p, ".so") == 0) 1164 *p = '\0'; /* strip trailing .so from any module name */ 1165 1166 (void) pthread_rwlock_wrlock(&mhp->mh_lock); 1167 h = fmd_strhash(name) % mhp->mh_hashlen; 1168 1169 /* 1170 * First check to see if a module is already present in the hash table 1171 * for this name. If so, the module is already loaded: skip it. 1172 */ 1173 for (mp = mhp->mh_hash[h]; mp != NULL; mp = mp->mod_next) { 1174 if (strcmp(name, mp->mod_name) == 0) 1175 break; 1176 } 1177 1178 if (mp != NULL) { 1179 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1180 (void) fmd_set_errno(EFMD_MOD_LOADED); 1181 return (NULL); 1182 } 1183 1184 /* 1185 * fmd_module_create() will return a held (as if by fmd_module_hold()) 1186 * module. We leave this hold in place to correspond to the hash-in. 1187 */ 1188 while ((mp = fmd_module_create(path, ops)) == NULL) { 1189 if (tries++ != 0 || errno != EFMD_CKPT_INVAL) { 1190 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1191 return (NULL); /* errno is set for us */ 1192 } 1193 } 1194 1195 mp->mod_hash = mhp; 1196 mp->mod_next = mhp->mh_hash[h]; 1197 1198 mhp->mh_hash[h] = mp; 1199 mhp->mh_nelems++; 1200 1201 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1202 return (mp); 1203 } 1204 1205 int 1206 fmd_modhash_unload(fmd_modhash_t *mhp, const char *name) 1207 { 1208 fmd_module_t *mp, **pp; 1209 uint_t h; 1210 1211 (void) pthread_rwlock_wrlock(&mhp->mh_lock); 1212 h = fmd_strhash(name) % mhp->mh_hashlen; 1213 pp = &mhp->mh_hash[h]; 1214 1215 for (mp = *pp; mp != NULL; mp = mp->mod_next) { 1216 if (strcmp(name, mp->mod_name) == 0) 1217 break; 1218 else 1219 pp = &mp->mod_next; 1220 } 1221 1222 if (mp == NULL) { 1223 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1224 return (fmd_set_errno(EFMD_MOD_NOMOD)); 1225 } 1226 1227 *pp = mp->mod_next; 1228 mp->mod_next = NULL; 1229 1230 ASSERT(mhp->mh_nelems != 0); 1231 mhp->mh_nelems--; 1232 1233 (void) pthread_rwlock_unlock(&mhp->mh_lock); 1234 1235 fmd_module_unload(mp); 1236 fmd_module_rele(mp); 1237 1238 return (0); 1239 } 1240 1241 void 1242 fmd_modstat_publish(fmd_module_t *mp) 1243 { 1244 (void) pthread_mutex_lock(&mp->mod_lock); 1245 1246 ASSERT(mp->mod_flags & FMD_MOD_STSUB); 1247 mp->mod_flags |= FMD_MOD_STPUB; 1248 (void) pthread_cond_broadcast(&mp->mod_cv); 1249 1250 while (mp->mod_flags & FMD_MOD_STPUB) 1251 (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); 1252 1253 (void) pthread_mutex_unlock(&mp->mod_lock); 1254 } 1255 1256 int 1257 fmd_modstat_snapshot(fmd_module_t *mp, fmd_ustat_snap_t *uss) 1258 { 1259 fmd_event_t *e; 1260 int err; 1261 1262 /* 1263 * Grab the module lock and wait for the STSUB bit to be clear. Then 1264 * set it to indicate we are a subscriber and everyone else must wait. 1265 */ 1266 (void) pthread_mutex_lock(&mp->mod_lock); 1267 1268 while (mp->mod_error == 0 && (mp->mod_flags & FMD_MOD_STSUB)) 1269 (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); 1270 1271 if (mp->mod_error != 0) { 1272 (void) pthread_mutex_unlock(&mp->mod_lock); 1273 return (fmd_set_errno(EFMD_HDL_ABORT)); 1274 } 1275 1276 mp->mod_flags |= FMD_MOD_STSUB; 1277 (void) pthread_cond_broadcast(&mp->mod_cv); 1278 (void) pthread_mutex_unlock(&mp->mod_lock); 1279 1280 /* 1281 * Create a stats pseudo-event and dispatch it to the module, forcing 1282 * it to next execute its custom snapshot routine (or the empty one). 1283 */ 1284 e = fmd_event_create(FMD_EVT_STATS, FMD_HRT_NOW, NULL, NULL); 1285 fmd_eventq_insert_at_head(mp->mod_queue, e); 1286 1287 /* 1288 * Grab the module lock and then wait on mod_cv for STPUB to be set, 1289 * indicating the snapshot routine is completed and the module is idle. 1290 */ 1291 (void) pthread_mutex_lock(&mp->mod_lock); 1292 1293 while (mp->mod_error == 0 && !(mp->mod_flags & FMD_MOD_STPUB)) 1294 (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); 1295 1296 if (mp->mod_error != 0) { 1297 (void) pthread_mutex_unlock(&mp->mod_lock); 1298 return (fmd_set_errno(EFMD_HDL_ABORT)); 1299 } 1300 1301 (void) pthread_cond_broadcast(&mp->mod_cv); 1302 (void) pthread_mutex_unlock(&mp->mod_lock); 1303 1304 /* 1305 * Update ms_snaptime and take the actual snapshot of the various 1306 * statistics while the module is quiescent and waiting for us. 1307 */ 1308 (void) pthread_mutex_lock(&mp->mod_stats_lock); 1309 1310 if (mp->mod_stats != NULL) { 1311 mp->mod_stats->ms_snaptime.fmds_value.ui64 = gethrtime(); 1312 err = fmd_ustat_snapshot(mp->mod_ustat, uss); 1313 } else 1314 err = fmd_set_errno(EFMD_HDL_ABORT); 1315 1316 (void) pthread_mutex_unlock(&mp->mod_stats_lock); 1317 1318 /* 1319 * With the snapshot complete, grab the module lock and clear both 1320 * STSUB and STPUB, permitting everyone to wake up and continue. 1321 */ 1322 (void) pthread_mutex_lock(&mp->mod_lock); 1323 1324 ASSERT(mp->mod_flags & FMD_MOD_STSUB); 1325 ASSERT(mp->mod_flags & FMD_MOD_STPUB); 1326 mp->mod_flags &= ~(FMD_MOD_STSUB | FMD_MOD_STPUB); 1327 1328 (void) pthread_cond_broadcast(&mp->mod_cv); 1329 (void) pthread_mutex_unlock(&mp->mod_lock); 1330 1331 return (err); 1332 } 1333 1334 struct topo_hdl * 1335 fmd_module_topo_hold(fmd_module_t *mp) 1336 { 1337 fmd_modtopo_t *mtp; 1338 1339 ASSERT(fmd_module_locked(mp)); 1340 1341 mtp = fmd_zalloc(sizeof (fmd_modtopo_t), FMD_SLEEP); 1342 mtp->mt_topo = mp->mod_topo_current; 1343 fmd_topo_addref(mtp->mt_topo); 1344 fmd_list_prepend(&mp->mod_topolist, mtp); 1345 1346 return (mtp->mt_topo->ft_hdl); 1347 } 1348 1349 int 1350 fmd_module_topo_rele(fmd_module_t *mp, struct topo_hdl *hdl) 1351 { 1352 fmd_modtopo_t *mtp; 1353 1354 ASSERT(fmd_module_locked(mp)); 1355 1356 for (mtp = fmd_list_next(&mp->mod_topolist); mtp != NULL; 1357 mtp = fmd_list_next(mtp)) { 1358 if (mtp->mt_topo->ft_hdl == hdl) 1359 break; 1360 } 1361 1362 if (mtp == NULL) 1363 return (-1); 1364 1365 fmd_list_delete(&mp->mod_topolist, mtp); 1366 fmd_topo_rele(mtp->mt_topo); 1367 fmd_free(mtp, sizeof (fmd_modtopo_t)); 1368 return (0); 1369 } 1370