1724365f7Ssethg /*
2724365f7Ssethg * CDDL HEADER START
3724365f7Ssethg *
4724365f7Ssethg * The contents of this file are subject to the terms of the
5724365f7Ssethg * Common Development and Distribution License (the "License").
6724365f7Ssethg * You may not use this file except in compliance with the License.
7724365f7Ssethg *
8724365f7Ssethg * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9724365f7Ssethg * or http://www.opensolaris.org/os/licensing.
10724365f7Ssethg * See the License for the specific language governing permissions
11724365f7Ssethg * and limitations under the License.
12724365f7Ssethg *
13724365f7Ssethg * When distributing Covered Code, include this CDDL HEADER in each
14724365f7Ssethg * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15724365f7Ssethg * If applicable, add the following below this CDDL HEADER, with the
16724365f7Ssethg * fields enclosed by brackets "[]" replaced with your own identifying
17724365f7Ssethg * information: Portions Copyright [yyyy] [name of copyright owner]
18724365f7Ssethg *
19724365f7Ssethg * CDDL HEADER END
20724365f7Ssethg */
21724365f7Ssethg
22724365f7Ssethg /*
239113a79cSeschrock * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24724365f7Ssethg * Use is subject to license terms.
25724365f7Ssethg */
26724365f7Ssethg
27724365f7Ssethg #include <string.h>
28724365f7Ssethg #include <inttypes.h>
29724365f7Ssethg #include <atomic.h>
30724365f7Ssethg #include <fm/fmd_api.h>
31724365f7Ssethg #include <sys/fm/protocol.h>
32724365f7Ssethg
33*184cd04cScth #include "disk_monitor.h"
34724365f7Ssethg #include "schg_mgr.h"
35724365f7Ssethg #include "hotplug_mgr.h"
36724365f7Ssethg #include "topo_gather.h"
379113a79cSeschrock #include "dm_platform.h"
38724365f7Ssethg
39724365f7Ssethg /* State-change event processing thread data */
40724365f7Ssethg static pthread_t g_schg_tid;
41724365f7Ssethg static thread_state_t g_schgt_state = TS_NOT_RUNNING;
42724365f7Ssethg static pthread_mutex_t g_schgt_state_mutex = PTHREAD_MUTEX_INITIALIZER;
43724365f7Ssethg static pthread_cond_t g_schgt_state_cvar = PTHREAD_COND_INITIALIZER;
44724365f7Ssethg static pthread_mutex_t g_schgt_add_mutex = PTHREAD_MUTEX_INITIALIZER;
45724365f7Ssethg static qu_t *g_schg_queue = NULL;
46724365f7Ssethg
47724365f7Ssethg static void dm_state_change_nolock(diskmon_t *diskp, hotplug_state_t newstate);
48724365f7Ssethg
49724365f7Ssethg /*
50724365f7Ssethg * Each disk state change is described by an instance of the following
51724365f7Ssethg * structure (which includes the disk object and the new state)
52724365f7Ssethg */
53724365f7Ssethg typedef struct disk_statechg {
54724365f7Ssethg diskmon_t *diskp;
55724365f7Ssethg hotplug_state_t newstate;
56724365f7Ssethg } disk_statechg_t;
57724365f7Ssethg
58724365f7Ssethg static disk_statechg_t *
new_statechange(diskmon_t * diskp,hotplug_state_t state)59724365f7Ssethg new_statechange(diskmon_t *diskp, hotplug_state_t state)
60724365f7Ssethg {
61724365f7Ssethg disk_statechg_t *dscp =
62724365f7Ssethg (disk_statechg_t *)dmalloc(sizeof (disk_statechg_t));
63724365f7Ssethg
64724365f7Ssethg /*
65724365f7Ssethg * The states are additive -- we don't need to preserve
66724365f7Ssethg * the current faulted state in the newstate:
67724365f7Ssethg */
68724365f7Ssethg dscp->diskp = diskp;
69724365f7Ssethg dscp->newstate = state;
70724365f7Ssethg
71724365f7Ssethg return (dscp);
72724365f7Ssethg }
73724365f7Ssethg
74724365f7Ssethg static void
free_statechange(void * dscp)75724365f7Ssethg free_statechange(void *dscp)
76724365f7Ssethg {
77724365f7Ssethg dfree(dscp, sizeof (disk_statechg_t));
78724365f7Ssethg }
79724365f7Ssethg
80724365f7Ssethg static void
add_to_statechange_queue(diskmon_t * diskp,hotplug_state_t newstate)81724365f7Ssethg add_to_statechange_queue(diskmon_t *diskp, hotplug_state_t newstate)
82724365f7Ssethg {
83724365f7Ssethg queue_add(g_schg_queue, new_statechange(diskp, newstate));
84724365f7Ssethg }
85724365f7Ssethg
86724365f7Ssethg static const char *
lookup_action_string(indicator_t * ind_listp,ind_state_t state,char * name)87724365f7Ssethg lookup_action_string(indicator_t *ind_listp, ind_state_t state, char *name)
88724365f7Ssethg {
89724365f7Ssethg const char *str = NULL;
90724365f7Ssethg
91724365f7Ssethg while (ind_listp != NULL) {
92724365f7Ssethg
93724365f7Ssethg if (state == ind_listp->ind_state &&
94724365f7Ssethg strcasecmp(ind_listp->ind_name, name) == 0) {
95724365f7Ssethg
96724365f7Ssethg str = ind_listp->ind_instr_spec;
97724365f7Ssethg break;
98724365f7Ssethg }
99724365f7Ssethg
100724365f7Ssethg ind_listp = ind_listp->next;
101724365f7Ssethg }
102724365f7Ssethg
103724365f7Ssethg return (str);
104724365f7Ssethg }
105724365f7Ssethg
106724365f7Ssethg void
dm_fault_indicator_set(diskmon_t * diskp,ind_state_t istate)107724365f7Ssethg dm_fault_indicator_set(diskmon_t *diskp, ind_state_t istate)
108724365f7Ssethg {
109724365f7Ssethg const char *astring;
110724365f7Ssethg
1117a0b67e3Ssethg dm_assert(pthread_mutex_lock(&diskp->fault_indicator_mutex) == 0);
112724365f7Ssethg
113724365f7Ssethg /*
114724365f7Ssethg * No need to execute redundant indicator actions
115724365f7Ssethg */
116724365f7Ssethg if (istate == INDICATOR_UNKNOWN ||
117724365f7Ssethg diskp->fault_indicator_state == istate) {
1187a0b67e3Ssethg dm_assert(pthread_mutex_unlock(&diskp->fault_indicator_mutex)
119724365f7Ssethg == 0);
120724365f7Ssethg return;
121724365f7Ssethg }
122724365f7Ssethg
123724365f7Ssethg astring = lookup_action_string(diskp->ind_list, istate,
124724365f7Ssethg INDICATOR_FAULT_IDENTIFIER);
125724365f7Ssethg
126724365f7Ssethg if (astring != NULL) {
127724365f7Ssethg log_msg(MM_SCHGMGR, "Executing action `%s'\n", astring);
128724365f7Ssethg
1299113a79cSeschrock if (dm_platform_indicator_execute(astring) != 0) {
130724365f7Ssethg log_warn("[Disk in %s] Action `%s' did not complete "
131724365f7Ssethg "successfully.\n",
132724365f7Ssethg diskp->location,
133724365f7Ssethg astring);
134724365f7Ssethg } else {
135724365f7Ssethg
136724365f7Ssethg diskp->fault_indicator_state = istate;
137724365f7Ssethg
138724365f7Ssethg log_msg(MM_SCHGMGR, "Action `%s' executed "
139724365f7Ssethg "successfully\n", astring);
140724365f7Ssethg }
141724365f7Ssethg }
142724365f7Ssethg
1437a0b67e3Ssethg dm_assert(pthread_mutex_unlock(&diskp->fault_indicator_mutex) == 0);
144724365f7Ssethg }
145724365f7Ssethg
146724365f7Ssethg static void
schg_execute_state_change_action(diskmon_t * diskp,hotplug_state_t oldstate,hotplug_state_t newstate)147724365f7Ssethg schg_execute_state_change_action(diskmon_t *diskp, hotplug_state_t oldstate,
148724365f7Ssethg hotplug_state_t newstate)
149724365f7Ssethg {
150724365f7Ssethg indrule_t *rulelist;
151724365f7Ssethg ind_action_t *actions;
152724365f7Ssethg const char *astring;
153724365f7Ssethg
154724365f7Ssethg log_msg(MM_SCHGMGR, "[Disk in %s] State change action: %s -> %s\n",
155724365f7Ssethg diskp->location,
156724365f7Ssethg hotplug_state_string(oldstate),
157724365f7Ssethg hotplug_state_string(newstate));
158724365f7Ssethg
159724365f7Ssethg /*
160724365f7Ssethg * Find the list of actions that correspond to this state change.
161724365f7Ssethg * If the old state is UNKNOWN, then we'll match to first action
162724365f7Ssethg * whose transition state is the new state.
163724365f7Ssethg */
164724365f7Ssethg rulelist = diskp->indrule_list;
165724365f7Ssethg
166724365f7Ssethg while (rulelist != NULL) {
167724365f7Ssethg
168724365f7Ssethg if ((oldstate == HPS_UNKNOWN ||
169724365f7Ssethg rulelist->strans.begin == oldstate) &&
170724365f7Ssethg rulelist->strans.end == newstate)
171724365f7Ssethg break;
172724365f7Ssethg
173724365f7Ssethg rulelist = rulelist->next;
174724365f7Ssethg }
175724365f7Ssethg
176724365f7Ssethg if (rulelist != NULL) {
177724365f7Ssethg /* Now we have a set of actions to perform: */
178724365f7Ssethg actions = rulelist->action_list;
179724365f7Ssethg
180724365f7Ssethg while (actions != NULL) {
181724365f7Ssethg
182724365f7Ssethg astring = lookup_action_string(diskp->ind_list,
183724365f7Ssethg actions->ind_state, actions->ind_name);
184724365f7Ssethg
1857a0b67e3Ssethg dm_assert(astring != NULL);
186724365f7Ssethg
187724365f7Ssethg log_msg(MM_SCHGMGR, "Executing action `%s'\n", astring);
188724365f7Ssethg
1899113a79cSeschrock if (dm_platform_indicator_execute(astring) != 0) {
190724365f7Ssethg log_warn("[Disk in %s][State transition from "
191724365f7Ssethg "%s to %s] Action `%s' did not complete "
192724365f7Ssethg "successfully.\n",
193724365f7Ssethg diskp->location,
194724365f7Ssethg hotplug_state_string(oldstate),
195724365f7Ssethg hotplug_state_string(newstate),
196724365f7Ssethg astring);
197724365f7Ssethg
198724365f7Ssethg } else
199724365f7Ssethg log_msg(MM_SCHGMGR,
200724365f7Ssethg "Action `%s' executed successfully\n",
201724365f7Ssethg astring);
202724365f7Ssethg
203724365f7Ssethg actions = actions->next;
204724365f7Ssethg }
205724365f7Ssethg }
206724365f7Ssethg
207724365f7Ssethg }
208724365f7Ssethg
209724365f7Ssethg static void
schg_send_fru_update(diskmon_t * diskp,dm_fru_t * frup)2109113a79cSeschrock schg_send_fru_update(diskmon_t *diskp, dm_fru_t *frup)
211724365f7Ssethg {
212724365f7Ssethg const char *action = dm_prop_lookup(diskp->props, DISK_PROP_FRUACTION);
213724365f7Ssethg
214724365f7Ssethg if (action == NULL) {
215724365f7Ssethg log_msg(MM_SCHGMGR|MM_NOTE, "No FRU update action for disk "
216724365f7Ssethg "in %s\n", diskp->location);
217724365f7Ssethg return;
218724365f7Ssethg }
219724365f7Ssethg
2209113a79cSeschrock if (dm_platform_update_fru(action, frup) != 0) {
221724365f7Ssethg log_warn("Error updating FRU information for disk in %s.\n",
222724365f7Ssethg diskp->location);
223724365f7Ssethg }
224724365f7Ssethg }
225724365f7Ssethg
226724365f7Ssethg static void
schg_update_fru_info(diskmon_t * diskp)227724365f7Ssethg schg_update_fru_info(diskmon_t *diskp)
228724365f7Ssethg {
229724365f7Ssethg if (diskp->initial_configuration ||
2300eb822a1Scindi update_configuration_from_topo(g_fm_hdl, diskp) == TOPO_SUCCESS) {
231724365f7Ssethg diskp->initial_configuration = B_FALSE;
2327a0b67e3Ssethg dm_assert(pthread_mutex_lock(&diskp->fru_mutex) == 0);
233724365f7Ssethg if (diskp->frup != NULL)
2349113a79cSeschrock schg_send_fru_update(diskp, diskp->frup);
235724365f7Ssethg else
236724365f7Ssethg log_warn("frup unexpectedly went away: not updating "
237724365f7Ssethg "FRU information for disk %s!\n", diskp->location);
2387a0b67e3Ssethg dm_assert(pthread_mutex_unlock(&diskp->fru_mutex) == 0);
239724365f7Ssethg } else {
240724365f7Ssethg log_warn_e("Error retrieving FRU information "
241724365f7Ssethg "for disk in %s", diskp->location);
242724365f7Ssethg }
243724365f7Ssethg }
244724365f7Ssethg
245724365f7Ssethg void
block_state_change_events(void)246724365f7Ssethg block_state_change_events(void)
247724365f7Ssethg {
2487a0b67e3Ssethg dm_assert(pthread_mutex_lock(&g_schgt_add_mutex) == 0);
249724365f7Ssethg }
250724365f7Ssethg
251724365f7Ssethg void
unblock_state_change_events(void)252724365f7Ssethg unblock_state_change_events(void)
253724365f7Ssethg {
2547a0b67e3Ssethg dm_assert(pthread_mutex_unlock(&g_schgt_add_mutex) == 0);
255724365f7Ssethg }
256724365f7Ssethg
257724365f7Ssethg static void
disk_state_change_first_time(diskmon_t * diskp)258724365f7Ssethg disk_state_change_first_time(diskmon_t *diskp)
259724365f7Ssethg {
260724365f7Ssethg hotplug_state_t firststate;
261724365f7Ssethg
262724365f7Ssethg /*
263724365f7Ssethg * Grab the current state of the attachment point to initialize the
264724365f7Ssethg * initial disk state. Create a disk state change with this new
265724365f7Ssethg * state so it will be processed in the loop below. If we can't get
266724365f7Ssethg * the initial state for some reason, then we'll just end up doing it
267724365f7Ssethg * later when we get a state change from the hotplug monitor or the
268724365f7Ssethg * fault monitor.
269724365f7Ssethg */
270724365f7Ssethg firststate = disk_ap_state_to_hotplug_state(diskp);
271724365f7Ssethg if (firststate != HPS_UNKNOWN)
272724365f7Ssethg dm_state_change_nolock(diskp, firststate);
273724365f7Ssethg
274724365f7Ssethg /*
275724365f7Ssethg * The fault indicators will be updated when faults are replayed
276724365f7Ssethg * based on the state of the disk as faulty in the fmd resource cache.
277724365f7Ssethg * A FAULTED state change will come from the _recv function when the
278724365f7Ssethg * fault component event is replayed.
279724365f7Ssethg */
280724365f7Ssethg }
281724365f7Ssethg
282724365f7Ssethg static void
disk_state_change_thread(void * vdisklistp)283724365f7Ssethg disk_state_change_thread(void *vdisklistp)
284724365f7Ssethg {
285724365f7Ssethg diskmon_t *disklistp = (diskmon_t *)vdisklistp;
286724365f7Ssethg diskmon_t *diskp;
287724365f7Ssethg disk_statechg_t *dscp;
288724365f7Ssethg hotplug_state_t nextstate;
289724365f7Ssethg const char *pth;
290724365f7Ssethg
291724365f7Ssethg /*
292724365f7Ssethg * Perform startup activities to initialize the state of the
293724365f7Ssethg * indicators for each disk.
294724365f7Ssethg */
295724365f7Ssethg diskp = disklistp;
296724365f7Ssethg while (diskp != NULL) {
297724365f7Ssethg disk_state_change_first_time(diskp);
298724365f7Ssethg diskp = diskp->next;
299724365f7Ssethg }
300724365f7Ssethg
301724365f7Ssethg unblock_state_change_events();
302724365f7Ssethg
3037a0b67e3Ssethg dm_assert(pthread_mutex_lock(&g_schgt_state_mutex) == 0);
304724365f7Ssethg if (g_schgt_state != TS_EXIT_REQUESTED) {
305724365f7Ssethg g_schgt_state = TS_RUNNING;
3067a0b67e3Ssethg dm_assert(pthread_cond_broadcast(&g_schgt_state_cvar) == 0);
307724365f7Ssethg }
3087a0b67e3Ssethg dm_assert(pthread_mutex_unlock(&g_schgt_state_mutex) == 0);
309724365f7Ssethg
310724365f7Ssethg while (g_schgt_state != TS_EXIT_REQUESTED) {
311724365f7Ssethg
312724365f7Ssethg if ((dscp = (disk_statechg_t *)queue_remove(g_schg_queue))
313724365f7Ssethg == NULL) {
3147a0b67e3Ssethg dm_assert(g_schgt_state == TS_EXIT_REQUESTED);
315724365f7Ssethg continue;
316724365f7Ssethg }
317724365f7Ssethg
318724365f7Ssethg diskp = dscp->diskp;
319724365f7Ssethg
320724365f7Ssethg /*
321724365f7Ssethg * If the new state is the faulted state, add that state to
322724365f7Ssethg * the disk's current state.
323724365f7Ssethg */
324724365f7Ssethg if (dscp->newstate == HPS_FAULTED) {
325724365f7Ssethg
326724365f7Ssethg /*
327724365f7Ssethg * If the disk wasn't previously in the faulted state,
328724365f7Ssethg * execute the generic fault action. Even if we're
329724365f7Ssethg * in the faulted state, accept additional faults.
330724365f7Ssethg */
331724365f7Ssethg nextstate = DISK_STATE(diskp->state) | HPS_FAULTED;
332724365f7Ssethg
333724365f7Ssethg } else if (dscp->newstate == HPS_REPAIRED) {
334724365f7Ssethg nextstate = DISK_STATE(diskp->state);
335724365f7Ssethg
336724365f7Ssethg } else if (dscp->newstate == HPS_ABSENT) {
337724365f7Ssethg /*
338724365f7Ssethg * If the new state is ABSENT, forget any faults
339724365f7Ssethg */
340724365f7Ssethg
341724365f7Ssethg nextstate = HPS_ABSENT;
342724365f7Ssethg } else
343724365f7Ssethg nextstate = dscp->newstate | DISK_FAULTED(diskp->state);
344724365f7Ssethg
345724365f7Ssethg /*
346724365f7Ssethg * When a new disk is inserted and reaches the CONFIGURED state,
347724365f7Ssethg * the following actions must be done in the following order:
348724365f7Ssethg *
349724365f7Ssethg * (1) Execute the configuration-specified action on the
350724365f7Ssethg * state change.
351724365f7Ssethg * (2) Retreive the FRU information from the disk and execute
352724365f7Ssethg * the FRU-update action specified,
353724365f7Ssethg * (3) Initialize the fault monitor state associated with
354724365f7Ssethg * the new drive.
355724365f7Ssethg *
356724365f7Ssethg * Once the disk is no longer "new" (a disk is "new" when it
357724365f7Ssethg * has not yet reached the CONFIGURED state), subsequent
358724365f7Ssethg * transitions away and back to CONFIGURED (as long as the
359724365f7Ssethg * disk is not physically removed) will result in the
360724365f7Ssethg * execution of the predefined action ONLY.
361724365f7Ssethg *
362724365f7Ssethg */
363724365f7Ssethg
36424db4641Seschrock if (dscp->newstate != HPS_FAULTED &&
365724365f7Ssethg DISK_STATE(nextstate) != HPS_UNKNOWN &&
366724365f7Ssethg dscp->newstate != HPS_REPAIRED) {
367724365f7Ssethg
368724365f7Ssethg schg_execute_state_change_action(diskp,
369724365f7Ssethg DISK_STATE(diskp->state), DISK_STATE(nextstate));
370724365f7Ssethg }
371724365f7Ssethg
372724365f7Ssethg if (!diskp->configured_yet &&
373724365f7Ssethg DISK_STATE(nextstate) == HPS_CONFIGURED) {
374724365f7Ssethg
375724365f7Ssethg schg_update_fru_info(diskp);
376724365f7Ssethg
377724365f7Ssethg /*
378724365f7Ssethg * If this state transition is lagging the true
379724365f7Ssethg * state of the system (e.g. if the true state of
380724365f7Ssethg * the disk is UNCONFIGURED, there's another
381724365f7Ssethg * state change somewhere later in the queue), then
382724365f7Ssethg * it's possible for the disk path property to not
38324db4641Seschrock * exist.
384724365f7Ssethg */
385724365f7Ssethg if (dm_prop_lookup(diskp->props,
386724365f7Ssethg DISK_PROP_DEVPATH) == NULL) {
387724365f7Ssethg
388724365f7Ssethg log_msg(MM_SCHGMGR,
389724365f7Ssethg "Processed stale state change "
390724365f7Ssethg "for disk %s\n", diskp->location);
391724365f7Ssethg
392724365f7Ssethg } else {
393724365f7Ssethg diskp->configured_yet = B_TRUE;
394724365f7Ssethg }
395724365f7Ssethg
396724365f7Ssethg }
397724365f7Ssethg
3987a0b67e3Ssethg dm_assert(pthread_mutex_lock(&diskp->manager_mutex) == 0);
399724365f7Ssethg
400724365f7Ssethg /*
401724365f7Ssethg * Make the new state visible to all observers
402724365f7Ssethg */
403724365f7Ssethg diskp->state = nextstate;
404724365f7Ssethg
405724365f7Ssethg /*
406724365f7Ssethg * Now, update the diskmon if the disk is now absent -- it's
407724365f7Ssethg * essential to do this after the state is set (above) so that
408724365f7Ssethg * state observers in other threads don't try to access the
409724365f7Ssethg * data structures that we're freeing here.
410724365f7Ssethg */
411724365f7Ssethg
412724365f7Ssethg if (diskp->configured_yet &&
413724365f7Ssethg DISK_STATE(nextstate) == HPS_ABSENT) {
414724365f7Ssethg /*
415724365f7Ssethg * When the disk is removed, the fault monitor state is
416724365f7Ssethg * useless, so discard it.
417724365f7Ssethg */
4187a0b67e3Ssethg dm_assert(DISK_STATE(nextstate) != HPS_CONFIGURED);
419724365f7Ssethg
420724365f7Ssethg diskp->configured_yet = B_FALSE;
421724365f7Ssethg
422724365f7Ssethg }
4237a0b67e3Ssethg dm_assert(pthread_mutex_unlock(&diskp->manager_mutex) == 0);
424724365f7Ssethg
425724365f7Ssethg pth = dm_prop_lookup(diskp->props, DISK_PROP_DEVPATH);
426724365f7Ssethg
427724365f7Ssethg log_msg(MM_SCHGMGR,
428724365f7Ssethg "[State change #%d][%s]: Disk path = %s\n",
429724365f7Ssethg diskp->state_change_count,
430724365f7Ssethg diskp->location, pth == NULL ? "Unknown" : pth);
431724365f7Ssethg
432724365f7Ssethg log_msg(MM_SCHGMGR,
433724365f7Ssethg "[State change #%d][%s]: New state = %s%s\n",
434724365f7Ssethg diskp->state_change_count, diskp->location,
435724365f7Ssethg hotplug_state_string(diskp->state),
436724365f7Ssethg DISK_FAULTED(diskp->state) ? "+FAULTED" : "");
437724365f7Ssethg
438724365f7Ssethg atomic_inc_uint(&diskp->state_change_count);
439724365f7Ssethg
440724365f7Ssethg /* The caller is responsible for freeing the state change: */
441724365f7Ssethg free_statechange(dscp);
442724365f7Ssethg }
4437a0b67e3Ssethg dm_assert(pthread_mutex_lock(&g_schgt_state_mutex) == 0);
444724365f7Ssethg g_schgt_state = TS_EXITED;
4457a0b67e3Ssethg dm_assert(pthread_cond_broadcast(&g_schgt_state_cvar) == 0);
4467a0b67e3Ssethg dm_assert(pthread_mutex_unlock(&g_schgt_state_mutex) == 0);
447724365f7Ssethg
448724365f7Ssethg log_msg(MM_SCHGMGR, "State change thread exiting...\n");
449724365f7Ssethg }
450724365f7Ssethg
451724365f7Ssethg static void
dm_state_change_nolock(diskmon_t * diskp,hotplug_state_t newstate)452724365f7Ssethg dm_state_change_nolock(diskmon_t *diskp, hotplug_state_t newstate)
453724365f7Ssethg {
454724365f7Ssethg /* Enqueue a new state change for the state-change thread */
455724365f7Ssethg add_to_statechange_queue(diskp, newstate);
456724365f7Ssethg }
457724365f7Ssethg
458724365f7Ssethg void
dm_state_change(diskmon_t * diskp,hotplug_state_t newstate)459724365f7Ssethg dm_state_change(diskmon_t *diskp, hotplug_state_t newstate)
460724365f7Ssethg {
4617a0b67e3Ssethg dm_assert(pthread_mutex_lock(&g_schgt_add_mutex) == 0);
462724365f7Ssethg dm_state_change_nolock(diskp, newstate);
4637a0b67e3Ssethg dm_assert(pthread_mutex_unlock(&g_schgt_add_mutex) == 0);
464724365f7Ssethg }
465724365f7Ssethg
466724365f7Ssethg int
init_state_change_manager(cfgdata_t * cfgdatap)467724365f7Ssethg init_state_change_manager(cfgdata_t *cfgdatap)
468724365f7Ssethg {
469724365f7Ssethg /* new_queue() is guaranteed to succeed */
470724365f7Ssethg g_schg_queue = new_queue(B_TRUE, dmalloc, dfree, free_statechange);
471724365f7Ssethg
4727a0b67e3Ssethg dm_assert(pthread_mutex_lock(&g_schgt_state_mutex) == 0);
473724365f7Ssethg g_schg_tid = fmd_thr_create(g_fm_hdl, disk_state_change_thread,
474724365f7Ssethg cfgdatap->disk_list);
475724365f7Ssethg
476724365f7Ssethg /*
477724365f7Ssethg * Now, wait for the thread to enter the TS_RUNNING state. This
478724365f7Ssethg * is important because we want the state-change thread to pull the
479724365f7Ssethg * initial state of the disks on startup (without the wait, we could
480724365f7Ssethg * have the hotplug event handler race and deliver a state change
481724365f7Ssethg * before the state-change thread initialized the initial disk state).
482724365f7Ssethg */
483724365f7Ssethg
484724365f7Ssethg while (g_schgt_state != TS_RUNNING) {
485724365f7Ssethg (void) pthread_cond_wait(&g_schgt_state_cvar,
486724365f7Ssethg &g_schgt_state_mutex);
487724365f7Ssethg }
488724365f7Ssethg
4897a0b67e3Ssethg dm_assert(pthread_mutex_unlock(&g_schgt_state_mutex) == 0);
490724365f7Ssethg
491724365f7Ssethg return (0);
492724365f7Ssethg }
493724365f7Ssethg
494724365f7Ssethg /*ARGSUSED*/
495724365f7Ssethg void
cleanup_state_change_manager(cfgdata_t * cfgdatap)496724365f7Ssethg cleanup_state_change_manager(cfgdata_t *cfgdatap)
497724365f7Ssethg {
498724365f7Ssethg if (g_schgt_state != TS_RUNNING)
499724365f7Ssethg return;
500724365f7Ssethg
501724365f7Ssethg g_schgt_state = TS_EXIT_REQUESTED;
502724365f7Ssethg queue_add(g_schg_queue, NULL);
5037a0b67e3Ssethg dm_assert(pthread_mutex_lock(&g_schgt_state_mutex) == 0);
504724365f7Ssethg while (g_schgt_state != TS_EXITED)
5057a0b67e3Ssethg dm_assert(pthread_cond_wait(&g_schgt_state_cvar,
506724365f7Ssethg &g_schgt_state_mutex) == 0);
5077a0b67e3Ssethg dm_assert(pthread_mutex_unlock(&g_schgt_state_mutex) == 0);
508724365f7Ssethg (void) pthread_join(g_schg_tid, NULL);
509724365f7Ssethg fmd_thr_destroy(g_fm_hdl, g_schg_tid);
510724365f7Ssethg queue_free(&g_schg_queue);
511724365f7Ssethg g_schgt_state = TS_NOT_RUNNING;
512724365f7Ssethg }
513