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