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