0
|
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, Version 1.0 only
|
|
6 * (the "License"). You may not use this file except in compliance
|
|
7 * with the License.
|
|
8 *
|
|
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
|
10 * or http://www.opensolaris.org/os/licensing.
|
|
11 * See the License for the specific language governing permissions
|
|
12 * and limitations under the License.
|
|
13 *
|
|
14 * When distributing Covered Code, include this CDDL HEADER in each
|
|
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
|
16 * If applicable, add the following below this CDDL HEADER, with the
|
|
17 * fields enclosed by brackets "[]" replaced with your own identifying
|
|
18 * information: Portions Copyright [yyyy] [name of copyright owner]
|
|
19 *
|
|
20 * CDDL HEADER END
|
|
21 */
|
|
22
|
|
23 /*
|
|
24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
|
|
25 * Use is subject to license terms.
|
|
26 */
|
|
27
|
|
28 #pragma ident "@(#)fmd_ctl.c 1.3 05/12/27 SMI"
|
|
29
|
|
30 /*
|
|
31 * FMD Control Event Subsystem
|
|
32 *
|
|
33 * This file provides a simple and extensible subsystem for the processing of
|
|
34 * synchronous control events that can be received from the event transport
|
|
35 * and used to control the behavior of the fault manager itself. At present
|
|
36 * this feature is used for the implementation of simulation controls such as
|
|
37 * advancing the simulated clock using events sent by the fminject utility.
|
|
38 * Control events are assigned a class of the form "resource.fm.fmd.*" and
|
|
39 * are assigned a callback function defined in the _fmd_ctls[] table below.
|
|
40 * As control events are received by the event transport, they are assigned a
|
|
41 * special event type (ev_type = FMD_EVT_CTL) and the ev_data member is used
|
|
42 * to refer to a fmd_ctl_t data structure, managed by the functions below.
|
|
43 *
|
|
44 * Control events are implemented so that they are synchronous with respect to
|
|
45 * the rest of the fault manager event stream, which is usually asynchronous
|
|
46 * (that is, the transport dispatch thread and the module receive threads all
|
|
47 * execute in parallel). Synchronous processing is required for control events
|
|
48 * so that they can affect global state (e.g. the simulated clock) and ensure
|
|
49 * that the results of any state changes are seen by *all* subsequent events.
|
|
50 *
|
|
51 * To achieve synchronization, the event itself implements a thread barrier:
|
|
52 * the fmd_ctl_t maintains a reference count that mirrors the fmd_event_t
|
|
53 * reference count (which for ctls counts the number of modules the event
|
|
54 * was dispatched to). As each module receive thread dequeues the event, it
|
|
55 * calls fmd_event_rele() to discard the event, which calls fmd_ctl_rele().
|
|
56 * fmd_ctl_rele() decrements the ctl's reference count but blocks there waiting
|
|
57 * for *all* other references to be released. When all threads have reached
|
|
58 * the barrier, the final caller of fmd_ctl_rele() executes the control event
|
|
59 * callback function and then wakes everyone else up. The transport dispatch
|
|
60 * thread, blocked in fmd_modhash_dispatch(), is typically this final caller.
|
|
61 */
|
|
62
|
|
63 #include <strings.h>
|
|
64 #include <limits.h>
|
|
65 #include <signal.h>
|
|
66
|
|
67 #include <fmd_protocol.h>
|
|
68 #include <fmd_alloc.h>
|
|
69 #include <fmd_error.h>
|
|
70 #include <fmd_subr.h>
|
|
71 #include <fmd_time.h>
|
|
72 #include <fmd_module.h>
|
|
73 #include <fmd_thread.h>
|
|
74 #include <fmd_ctl.h>
|
|
75
|
|
76 #include <fmd.h>
|
|
77
|
|
78 static void
|
|
79 fmd_ctl_addhrt(nvlist_t *nvl)
|
|
80 {
|
|
81 int64_t delta = 0;
|
|
82
|
|
83 (void) nvlist_lookup_int64(nvl, FMD_CTL_ADDHRT_DELTA, &delta);
|
|
84 fmd_time_addhrtime(delta);
|
|
85
|
|
86 /*
|
|
87 * If the non-adjustable clock has reached the apocalypse, fmd(1M)
|
|
88 * should exit gracefully: queue a SIGTERM for the main thread.
|
|
89 */
|
|
90 if (fmd_time_gethrtime() == INT64_MAX)
|
|
91 (void) pthread_kill(fmd.d_rmod->mod_thread->thr_tid, SIGTERM);
|
|
92 }
|
|
93
|
|
94 static void
|
|
95 fmd_ctl_inval(nvlist_t *nvl)
|
|
96 {
|
|
97 char *class = "<unknown>";
|
|
98
|
|
99 (void) nvlist_lookup_string(nvl, FM_CLASS, &class);
|
|
100 fmd_error(EFMD_CTL_INVAL, "ignoring invalid control event %s\n", class);
|
|
101 }
|
|
102
|
|
103 /*ARGSUSED*/
|
|
104 static void
|
|
105 fmd_ctl_pause(nvlist_t *nvl)
|
|
106 {
|
|
107 fmd_dprintf(FMD_DBG_DISP, "unpausing modules from ctl barrier\n");
|
|
108 }
|
|
109
|
|
110 static const fmd_ctl_desc_t _fmd_ctls[] = {
|
|
111 { FMD_CTL_ADDHRT, FMD_CTL_ADDHRT_VERS1, fmd_ctl_addhrt },
|
|
112 { NULL, UINT_MAX, fmd_ctl_inval }
|
|
113 };
|
|
114
|
|
115 fmd_ctl_t *
|
|
116 fmd_ctl_init(nvlist_t *nvl)
|
|
117 {
|
|
118 fmd_ctl_t *cp = fmd_alloc(sizeof (fmd_ctl_t), FMD_SLEEP);
|
|
119
|
|
120 const fmd_ctl_desc_t *dp;
|
|
121 uint8_t vers;
|
|
122 char *class;
|
|
123
|
|
124 (void) pthread_mutex_init(&cp->ctl_lock, NULL);
|
|
125 (void) pthread_cond_init(&cp->ctl_cv, NULL);
|
|
126
|
|
127 cp->ctl_nvl = nvl;
|
|
128 cp->ctl_refs = 0;
|
|
129
|
|
130 if (nvl == NULL) {
|
|
131 cp->ctl_func = fmd_ctl_pause;
|
|
132 return (cp);
|
|
133 }
|
|
134
|
|
135 if (nvlist_lookup_string(nvl, FM_CLASS, &class) != 0 ||
|
|
136 nvlist_lookup_uint8(nvl, FM_VERSION, &vers) != 0)
|
|
137 fmd_panic("ctl_init called with bad nvlist %p", (void *)nvl);
|
|
138
|
|
139 for (dp = _fmd_ctls; dp->cde_class != NULL; dp++) {
|
|
140 if (strcmp(class, dp->cde_class) == 0)
|
|
141 break;
|
|
142 }
|
|
143
|
|
144 cp->ctl_func = vers > dp->cde_vers ? &fmd_ctl_inval : dp->cde_func;
|
|
145 return (cp);
|
|
146 }
|
|
147
|
|
148 void
|
|
149 fmd_ctl_fini(fmd_ctl_t *cp)
|
|
150 {
|
|
151 fmd_free(cp, sizeof (fmd_ctl_t));
|
|
152 }
|
|
153
|
|
154 /*
|
|
155 * Increment the ref count on the fmd_ctl_t to correspond to a reference to the
|
|
156 * fmd_event_t. This count is used to implement a barrier in fmd_ctl_rele().
|
|
157 */
|
|
158 void
|
|
159 fmd_ctl_hold(fmd_ctl_t *cp)
|
|
160 {
|
|
161 (void) pthread_mutex_lock(&cp->ctl_lock);
|
|
162
|
|
163 cp->ctl_refs++;
|
|
164 ASSERT(cp->ctl_refs != 0);
|
|
165
|
|
166 (void) pthread_mutex_unlock(&cp->ctl_lock);
|
|
167 }
|
|
168
|
|
169 /*
|
|
170 * Decrement the reference count on the fmd_ctl_t. If this rele() is the last
|
|
171 * one, then execute the callback function and release all the other callers.
|
|
172 * Otherwise enter a loop waiting on ctl_cv for other threads to call rele().
|
|
173 */
|
|
174 void
|
|
175 fmd_ctl_rele(fmd_ctl_t *cp)
|
|
176 {
|
|
177 (void) pthread_mutex_lock(&cp->ctl_lock);
|
|
178
|
|
179 ASSERT(cp->ctl_refs != 0);
|
|
180 cp->ctl_refs--;
|
|
181
|
|
182 if (cp->ctl_refs == 0) {
|
|
183 cp->ctl_func(cp->ctl_nvl);
|
|
184 (void) pthread_cond_broadcast(&cp->ctl_cv);
|
|
185 } else {
|
|
186 while (cp->ctl_refs != 0)
|
|
187 (void) pthread_cond_wait(&cp->ctl_cv, &cp->ctl_lock);
|
|
188 }
|
|
189
|
|
190 (void) pthread_mutex_unlock(&cp->ctl_lock);
|
|
191 }
|