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