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