xref: /onnv-gate/usr/src/cmd/fm/modules/common/disk-monitor/schg_mgr.c (revision 4582:da3c7347dfa5)
1*4582Scth /*
2*4582Scth  * CDDL HEADER START
3*4582Scth  *
4*4582Scth  * The contents of this file are subject to the terms of the
5*4582Scth  * Common Development and Distribution License (the "License").
6*4582Scth  * You may not use this file except in compliance with the License.
7*4582Scth  *
8*4582Scth  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*4582Scth  * or http://www.opensolaris.org/os/licensing.
10*4582Scth  * See the License for the specific language governing permissions
11*4582Scth  * and limitations under the License.
12*4582Scth  *
13*4582Scth  * When distributing Covered Code, include this CDDL HEADER in each
14*4582Scth  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*4582Scth  * If applicable, add the following below this CDDL HEADER, with the
16*4582Scth  * fields enclosed by brackets "[]" replaced with your own identifying
17*4582Scth  * information: Portions Copyright [yyyy] [name of copyright owner]
18*4582Scth  *
19*4582Scth  * CDDL HEADER END
20*4582Scth  */
21*4582Scth 
22*4582Scth /*
23*4582Scth  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24*4582Scth  * Use is subject to license terms.
25*4582Scth  */
26*4582Scth 
27*4582Scth #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*4582Scth 
29*4582Scth #include <string.h>
30*4582Scth #include <inttypes.h>
31*4582Scth #include <atomic.h>
32*4582Scth #include <fm/fmd_api.h>
33*4582Scth #include <sys/fm/protocol.h>
34*4582Scth 
35*4582Scth #include "disk_monitor.h"
36*4582Scth #include "schg_mgr.h"
37*4582Scth #include "hotplug_mgr.h"
38*4582Scth #include "topo_gather.h"
39*4582Scth #include "dm_platform.h"
40*4582Scth 
41*4582Scth /* State-change event processing thread data */
42*4582Scth static pthread_t	g_schg_tid;
43*4582Scth static thread_state_t	g_schgt_state = TS_NOT_RUNNING;
44*4582Scth static pthread_mutex_t	g_schgt_state_mutex = PTHREAD_MUTEX_INITIALIZER;
45*4582Scth static pthread_cond_t	g_schgt_state_cvar = PTHREAD_COND_INITIALIZER;
46*4582Scth static pthread_mutex_t	g_schgt_add_mutex = PTHREAD_MUTEX_INITIALIZER;
47*4582Scth static qu_t		*g_schg_queue = NULL;
48*4582Scth 
49*4582Scth static void dm_state_change_nolock(diskmon_t *diskp, hotplug_state_t newstate);
50*4582Scth 
51*4582Scth /*
52*4582Scth  * Each disk state change is described by an instance of the following
53*4582Scth  * structure (which includes the disk object and the new state)
54*4582Scth  */
55*4582Scth typedef struct disk_statechg {
56*4582Scth 	diskmon_t	*diskp;
57*4582Scth 	hotplug_state_t	newstate;
58*4582Scth } disk_statechg_t;
59*4582Scth 
60*4582Scth static disk_statechg_t *
new_statechange(diskmon_t * diskp,hotplug_state_t state)61*4582Scth new_statechange(diskmon_t *diskp, hotplug_state_t state)
62*4582Scth {
63*4582Scth 	disk_statechg_t *dscp =
64*4582Scth 	    (disk_statechg_t *)dmalloc(sizeof (disk_statechg_t));
65*4582Scth 
66*4582Scth 	/*
67*4582Scth 	 * The states are additive -- we don't need to preserve
68*4582Scth 	 * the current faulted state in the newstate:
69*4582Scth 	 */
70*4582Scth 	dscp->diskp = diskp;
71*4582Scth 	dscp->newstate = state;
72*4582Scth 
73*4582Scth 	return (dscp);
74*4582Scth }
75*4582Scth 
76*4582Scth static void
free_statechange(void * dscp)77*4582Scth free_statechange(void *dscp)
78*4582Scth {
79*4582Scth 	dfree(dscp, sizeof (disk_statechg_t));
80*4582Scth }
81*4582Scth 
82*4582Scth static void
add_to_statechange_queue(diskmon_t * diskp,hotplug_state_t newstate)83*4582Scth add_to_statechange_queue(diskmon_t *diskp, hotplug_state_t newstate)
84*4582Scth {
85*4582Scth 	queue_add(g_schg_queue, new_statechange(diskp, newstate));
86*4582Scth }
87*4582Scth 
88*4582Scth static const char *
lookup_action_string(indicator_t * ind_listp,ind_state_t state,char * name)89*4582Scth lookup_action_string(indicator_t *ind_listp, ind_state_t state, char *name)
90*4582Scth {
91*4582Scth 	const char *str = NULL;
92*4582Scth 
93*4582Scth 	while (ind_listp != NULL) {
94*4582Scth 
95*4582Scth 		if (state == ind_listp->ind_state &&
96*4582Scth 		    strcasecmp(ind_listp->ind_name, name) == 0) {
97*4582Scth 
98*4582Scth 			str = ind_listp->ind_instr_spec;
99*4582Scth 			break;
100*4582Scth 		}
101*4582Scth 
102*4582Scth 		ind_listp = ind_listp->next;
103*4582Scth 	}
104*4582Scth 
105*4582Scth 	return (str);
106*4582Scth }
107*4582Scth 
108*4582Scth void
dm_fault_indicator_set(diskmon_t * diskp,ind_state_t istate)109*4582Scth dm_fault_indicator_set(diskmon_t *diskp, ind_state_t istate)
110*4582Scth {
111*4582Scth 	const char *astring;
112*4582Scth 
113*4582Scth 	dm_assert(pthread_mutex_lock(&diskp->fault_indicator_mutex) == 0);
114*4582Scth 
115*4582Scth 	/*
116*4582Scth 	 * No need to execute redundant indicator actions
117*4582Scth 	 */
118*4582Scth 	if (istate == INDICATOR_UNKNOWN ||
119*4582Scth 	    diskp->fault_indicator_state == istate) {
120*4582Scth 		dm_assert(pthread_mutex_unlock(&diskp->fault_indicator_mutex)
121*4582Scth 		    == 0);
122*4582Scth 		return;
123*4582Scth 	}
124*4582Scth 
125*4582Scth 	astring = lookup_action_string(diskp->ind_list, istate,
126*4582Scth 	    INDICATOR_FAULT_IDENTIFIER);
127*4582Scth 
128*4582Scth 	if (astring != NULL) {
129*4582Scth 		log_msg(MM_SCHGMGR, "Executing action `%s'\n", astring);
130*4582Scth 
131*4582Scth 		if (dm_platform_indicator_execute(astring) != 0) {
132*4582Scth 			log_warn("[Disk in %s] Action `%s' did not complete "
133*4582Scth 			    "successfully.\n",
134*4582Scth 			    diskp->location,
135*4582Scth 			    astring);
136*4582Scth 		} else  {
137*4582Scth 
138*4582Scth 			diskp->fault_indicator_state = istate;
139*4582Scth 
140*4582Scth 			log_msg(MM_SCHGMGR, "Action `%s' executed "
141*4582Scth 			    "successfully\n", astring);
142*4582Scth 		}
143*4582Scth 	}
144*4582Scth 
145*4582Scth 	dm_assert(pthread_mutex_unlock(&diskp->fault_indicator_mutex) == 0);
146*4582Scth }
147*4582Scth 
148*4582Scth static void
schg_execute_state_change_action(diskmon_t * diskp,hotplug_state_t oldstate,hotplug_state_t newstate)149*4582Scth schg_execute_state_change_action(diskmon_t *diskp, hotplug_state_t oldstate,
150*4582Scth     hotplug_state_t newstate)
151*4582Scth {
152*4582Scth 	indrule_t *rulelist;
153*4582Scth 	ind_action_t *actions;
154*4582Scth 	const char *astring;
155*4582Scth 
156*4582Scth 	log_msg(MM_SCHGMGR, "[Disk in %s] State change action: %s -> %s\n",
157*4582Scth 	    diskp->location,
158*4582Scth 	    hotplug_state_string(oldstate),
159*4582Scth 	    hotplug_state_string(newstate));
160*4582Scth 
161*4582Scth 	/*
162*4582Scth 	 * Find the list of actions that correspond to this state change.
163*4582Scth 	 * If the old state is UNKNOWN, then we'll match to first action
164*4582Scth 	 * whose transition state is the new state.
165*4582Scth 	 */
166*4582Scth 	rulelist = diskp->indrule_list;
167*4582Scth 
168*4582Scth 	while (rulelist != NULL) {
169*4582Scth 
170*4582Scth 		if ((oldstate == HPS_UNKNOWN ||
171*4582Scth 		    rulelist->strans.begin == oldstate) &&
172*4582Scth 		    rulelist->strans.end == newstate)
173*4582Scth 			break;
174*4582Scth 
175*4582Scth 		rulelist = rulelist->next;
176*4582Scth 	}
177*4582Scth 
178*4582Scth 	if (rulelist != NULL) {
179*4582Scth 		/* Now we have a set of actions to perform: */
180*4582Scth 		actions = rulelist->action_list;
181*4582Scth 
182*4582Scth 		while (actions != NULL) {
183*4582Scth 
184*4582Scth 			astring = lookup_action_string(diskp->ind_list,
185*4582Scth 			    actions->ind_state, actions->ind_name);
186*4582Scth 
187*4582Scth 			dm_assert(astring != NULL);
188*4582Scth 
189*4582Scth 			log_msg(MM_SCHGMGR, "Executing action `%s'\n", astring);
190*4582Scth 
191*4582Scth 			if (dm_platform_indicator_execute(astring) != 0) {
192*4582Scth 				log_warn("[Disk in %s][State transition from "
193*4582Scth 				    "%s to %s] Action `%s' did not complete "
194*4582Scth 				    "successfully.\n",
195*4582Scth 				    diskp->location,
196*4582Scth 				    hotplug_state_string(oldstate),
197*4582Scth 				    hotplug_state_string(newstate),
198*4582Scth 				    astring);
199*4582Scth 
200*4582Scth 			} else
201*4582Scth 				log_msg(MM_SCHGMGR,
202*4582Scth 				    "Action `%s' executed successfully\n",
203*4582Scth 				    astring);
204*4582Scth 
205*4582Scth 			actions = actions->next;
206*4582Scth 		}
207*4582Scth 	}
208*4582Scth 
209*4582Scth }
210*4582Scth 
211*4582Scth static void
schg_send_fru_update(diskmon_t * diskp,dm_fru_t * frup)212*4582Scth schg_send_fru_update(diskmon_t *diskp, dm_fru_t *frup)
213*4582Scth {
214*4582Scth 	const char *action = dm_prop_lookup(diskp->props, DISK_PROP_FRUACTION);
215*4582Scth 
216*4582Scth 	if (action == NULL) {
217*4582Scth 		log_msg(MM_SCHGMGR|MM_NOTE, "No FRU update action for disk "
218*4582Scth 		    "in %s\n", diskp->location);
219*4582Scth 		return;
220*4582Scth 	}
221*4582Scth 
222*4582Scth 	if (dm_platform_update_fru(action, frup) != 0) {
223*4582Scth 		log_warn("Error updating FRU information for disk in %s.\n",
224*4582Scth 		    diskp->location);
225*4582Scth 	}
226*4582Scth }
227*4582Scth 
228*4582Scth static void
schg_update_fru_info(diskmon_t * diskp)229*4582Scth schg_update_fru_info(diskmon_t *diskp)
230*4582Scth {
231*4582Scth 	if (diskp->initial_configuration ||
232*4582Scth 	    update_configuration_from_topo(g_fm_hdl, diskp) == TOPO_SUCCESS) {
233*4582Scth 		diskp->initial_configuration = B_FALSE;
234*4582Scth 		dm_assert(pthread_mutex_lock(&diskp->fru_mutex) == 0);
235*4582Scth 		if (diskp->frup != NULL)
236*4582Scth 			schg_send_fru_update(diskp, diskp->frup);
237*4582Scth 		else
238*4582Scth 			log_warn("frup unexpectedly went away: not updating "
239*4582Scth 			    "FRU information for disk %s!\n", diskp->location);
240*4582Scth 		dm_assert(pthread_mutex_unlock(&diskp->fru_mutex) == 0);
241*4582Scth 	} else {
242*4582Scth 		log_warn_e("Error retrieving FRU information "
243*4582Scth 		    "for disk in %s", diskp->location);
244*4582Scth 	}
245*4582Scth }
246*4582Scth 
247*4582Scth void
block_state_change_events(void)248*4582Scth block_state_change_events(void)
249*4582Scth {
250*4582Scth 	dm_assert(pthread_mutex_lock(&g_schgt_add_mutex) == 0);
251*4582Scth }
252*4582Scth 
253*4582Scth void
unblock_state_change_events(void)254*4582Scth unblock_state_change_events(void)
255*4582Scth {
256*4582Scth 	dm_assert(pthread_mutex_unlock(&g_schgt_add_mutex) == 0);
257*4582Scth }
258*4582Scth 
259*4582Scth static void
disk_state_change_first_time(diskmon_t * diskp)260*4582Scth disk_state_change_first_time(diskmon_t *diskp)
261*4582Scth {
262*4582Scth 	hotplug_state_t firststate;
263*4582Scth 
264*4582Scth 	/*
265*4582Scth 	 * Grab the current state of the attachment point to initialize the
266*4582Scth 	 * initial disk state.  Create a disk state change with this new
267*4582Scth 	 * state so it will be processed in the loop below.  If we can't get
268*4582Scth 	 * the initial state for some reason, then we'll just end up doing it
269*4582Scth 	 * later when we get a state change from the hotplug monitor or the
270*4582Scth 	 * fault monitor.
271*4582Scth 	 */
272*4582Scth 	firststate = disk_ap_state_to_hotplug_state(diskp);
273*4582Scth 	if (firststate != HPS_UNKNOWN)
274*4582Scth 		dm_state_change_nolock(diskp, firststate);
275*4582Scth 
276*4582Scth 	/*
277*4582Scth 	 * The fault indicators will be updated when faults are replayed
278*4582Scth 	 * based on the state of the disk as faulty in the fmd resource cache.
279*4582Scth 	 * A FAULTED state change will come from the _recv function when the
280*4582Scth 	 * fault component event is replayed.
281*4582Scth 	 */
282*4582Scth }
283*4582Scth 
284*4582Scth static void
disk_state_change_thread(void * vdisklistp)285*4582Scth disk_state_change_thread(void *vdisklistp)
286*4582Scth {
287*4582Scth 	diskmon_t	*disklistp = (diskmon_t *)vdisklistp;
288*4582Scth 	diskmon_t	*diskp;
289*4582Scth 	disk_statechg_t	*dscp;
290*4582Scth 	hotplug_state_t	nextstate;
291*4582Scth 	const char	*pth;
292*4582Scth 
293*4582Scth 	/*
294*4582Scth 	 * Perform startup activities to initialize the state of the
295*4582Scth 	 * indicators for each disk.
296*4582Scth 	 */
297*4582Scth 	diskp = disklistp;
298*4582Scth 	while (diskp != NULL) {
299*4582Scth 		disk_state_change_first_time(diskp);
300*4582Scth 		diskp = diskp->next;
301*4582Scth 	}
302*4582Scth 
303*4582Scth 	unblock_state_change_events();
304*4582Scth 
305*4582Scth 	dm_assert(pthread_mutex_lock(&g_schgt_state_mutex) == 0);
306*4582Scth 	if (g_schgt_state != TS_EXIT_REQUESTED) {
307*4582Scth 		g_schgt_state = TS_RUNNING;
308*4582Scth 		dm_assert(pthread_cond_broadcast(&g_schgt_state_cvar) == 0);
309*4582Scth 	}
310*4582Scth 	dm_assert(pthread_mutex_unlock(&g_schgt_state_mutex) == 0);
311*4582Scth 
312*4582Scth 	while (g_schgt_state != TS_EXIT_REQUESTED) {
313*4582Scth 
314*4582Scth 		if ((dscp = (disk_statechg_t *)queue_remove(g_schg_queue))
315*4582Scth 		    == NULL) {
316*4582Scth 			dm_assert(g_schgt_state == TS_EXIT_REQUESTED);
317*4582Scth 			continue;
318*4582Scth 		}
319*4582Scth 
320*4582Scth 		diskp = dscp->diskp;
321*4582Scth 
322*4582Scth 		/*
323*4582Scth 		 * If the new state is the faulted state, add that state to
324*4582Scth 		 * the disk's current state.
325*4582Scth 		 */
326*4582Scth 		if (dscp->newstate == HPS_FAULTED) {
327*4582Scth 
328*4582Scth 			/*
329*4582Scth 			 * If the disk wasn't previously in the faulted state,
330*4582Scth 			 * execute the generic fault action.  Even if we're
331*4582Scth 			 * in the faulted state, accept additional faults.
332*4582Scth 			 */
333*4582Scth 			nextstate = DISK_STATE(diskp->state) | HPS_FAULTED;
334*4582Scth 
335*4582Scth 		} else if (dscp->newstate == HPS_REPAIRED) {
336*4582Scth 			nextstate = DISK_STATE(diskp->state);
337*4582Scth 
338*4582Scth 		} else if (dscp->newstate == HPS_ABSENT) {
339*4582Scth 			/*
340*4582Scth 			 * If the new state is ABSENT, forget any faults
341*4582Scth 			 */
342*4582Scth 
343*4582Scth 			nextstate = HPS_ABSENT;
344*4582Scth 		} else
345*4582Scth 			nextstate = dscp->newstate | DISK_FAULTED(diskp->state);
346*4582Scth 
347*4582Scth 		/*
348*4582Scth 		 * When a new disk is inserted and reaches the CONFIGURED state,
349*4582Scth 		 * the following actions must be done in the following order:
350*4582Scth 		 *
351*4582Scth 		 * (1) Execute the configuration-specified action on the
352*4582Scth 		 * state change.
353*4582Scth 		 * (2) Retreive the FRU information from the disk and execute
354*4582Scth 		 * the FRU-update action specified,
355*4582Scth 		 * (3) Initialize the fault monitor state associated with
356*4582Scth 		 * the new drive.
357*4582Scth 		 *
358*4582Scth 		 * Once the disk is no longer "new" (a disk is "new" when it
359*4582Scth 		 * has not yet reached the CONFIGURED state), subsequent
360*4582Scth 		 * transitions away and back to CONFIGURED (as long as the
361*4582Scth 		 * disk is not physically removed) will result in the
362*4582Scth 		 * execution of the predefined action ONLY.
363*4582Scth 		 *
364*4582Scth 		 */
365*4582Scth 
366*4582Scth 		if (dscp->newstate != HPS_FAULTED &&
367*4582Scth 		    DISK_STATE(nextstate) != HPS_UNKNOWN &&
368*4582Scth 		    dscp->newstate != HPS_REPAIRED) {
369*4582Scth 
370*4582Scth 			schg_execute_state_change_action(diskp,
371*4582Scth 			    DISK_STATE(diskp->state), DISK_STATE(nextstate));
372*4582Scth 		}
373*4582Scth 
374*4582Scth 		if (!diskp->configured_yet &&
375*4582Scth 		    DISK_STATE(nextstate) == HPS_CONFIGURED) {
376*4582Scth 
377*4582Scth 			schg_update_fru_info(diskp);
378*4582Scth 
379*4582Scth 			/*
380*4582Scth 			 * If this state transition is lagging the true
381*4582Scth 			 * state of the system (e.g. if the true state of
382*4582Scth 			 * the disk is UNCONFIGURED, there's another
383*4582Scth 			 * state change somewhere later in the queue), then
384*4582Scth 			 * it's possible for the disk path property to not
385*4582Scth 			 * exist.
386*4582Scth 			 */
387*4582Scth 			if (dm_prop_lookup(diskp->props,
388*4582Scth 			    DISK_PROP_DEVPATH) == NULL) {
389*4582Scth 
390*4582Scth 				log_msg(MM_SCHGMGR,
391*4582Scth 				    "Processed stale state change "
392*4582Scth 				    "for disk %s\n", diskp->location);
393*4582Scth 
394*4582Scth 			} else {
395*4582Scth 				diskp->configured_yet = B_TRUE;
396*4582Scth 			}
397*4582Scth 
398*4582Scth 		}
399*4582Scth 
400*4582Scth 		dm_assert(pthread_mutex_lock(&diskp->manager_mutex) == 0);
401*4582Scth 
402*4582Scth 		/*
403*4582Scth 		 * Make the new state visible to all observers
404*4582Scth 		 */
405*4582Scth 		diskp->state = nextstate;
406*4582Scth 
407*4582Scth 		/*
408*4582Scth 		 * Now, update the diskmon if the disk is now absent -- it's
409*4582Scth 		 * essential to do this after the state is set (above) so that
410*4582Scth 		 * state observers in other threads don't try to access the
411*4582Scth 		 * data structures that we're freeing here.
412*4582Scth 		 */
413*4582Scth 
414*4582Scth 		if (diskp->configured_yet &&
415*4582Scth 		    DISK_STATE(nextstate) == HPS_ABSENT) {
416*4582Scth 			/*
417*4582Scth 			 * When the disk is removed, the fault monitor state is
418*4582Scth 			 * useless, so discard it.
419*4582Scth 			 */
420*4582Scth 			dm_assert(DISK_STATE(nextstate) != HPS_CONFIGURED);
421*4582Scth 
422*4582Scth 			diskp->configured_yet = B_FALSE;
423*4582Scth 
424*4582Scth 		}
425*4582Scth 		dm_assert(pthread_mutex_unlock(&diskp->manager_mutex) == 0);
426*4582Scth 
427*4582Scth 		pth = dm_prop_lookup(diskp->props, DISK_PROP_DEVPATH);
428*4582Scth 
429*4582Scth 		log_msg(MM_SCHGMGR,
430*4582Scth 		    "[State change #%d][%s]: Disk path = %s\n",
431*4582Scth 		    diskp->state_change_count,
432*4582Scth 		    diskp->location, pth == NULL ? "Unknown" : pth);
433*4582Scth 
434*4582Scth 		log_msg(MM_SCHGMGR,
435*4582Scth 		    "[State change #%d][%s]: New state = %s%s\n",
436*4582Scth 		    diskp->state_change_count, diskp->location,
437*4582Scth 		    hotplug_state_string(diskp->state),
438*4582Scth 		    DISK_FAULTED(diskp->state) ? "+FAULTED" : "");
439*4582Scth 
440*4582Scth 		atomic_inc_uint(&diskp->state_change_count);
441*4582Scth 
442*4582Scth 		/* The caller is responsible for freeing the state change: */
443*4582Scth 		free_statechange(dscp);
444*4582Scth 	}
445*4582Scth 	dm_assert(pthread_mutex_lock(&g_schgt_state_mutex) == 0);
446*4582Scth 	g_schgt_state = TS_EXITED;
447*4582Scth 	dm_assert(pthread_cond_broadcast(&g_schgt_state_cvar) == 0);
448*4582Scth 	dm_assert(pthread_mutex_unlock(&g_schgt_state_mutex) == 0);
449*4582Scth 
450*4582Scth 	log_msg(MM_SCHGMGR, "State change thread exiting...\n");
451*4582Scth }
452*4582Scth 
453*4582Scth static void
dm_state_change_nolock(diskmon_t * diskp,hotplug_state_t newstate)454*4582Scth dm_state_change_nolock(diskmon_t *diskp, hotplug_state_t newstate)
455*4582Scth {
456*4582Scth 	/* Enqueue a new state change for the state-change thread */
457*4582Scth 	add_to_statechange_queue(diskp, newstate);
458*4582Scth }
459*4582Scth 
460*4582Scth void
dm_state_change(diskmon_t * diskp,hotplug_state_t newstate)461*4582Scth dm_state_change(diskmon_t *diskp, hotplug_state_t newstate)
462*4582Scth {
463*4582Scth 	dm_assert(pthread_mutex_lock(&g_schgt_add_mutex) == 0);
464*4582Scth 	dm_state_change_nolock(diskp, newstate);
465*4582Scth 	dm_assert(pthread_mutex_unlock(&g_schgt_add_mutex) == 0);
466*4582Scth }
467*4582Scth 
468*4582Scth int
init_state_change_manager(cfgdata_t * cfgdatap)469*4582Scth init_state_change_manager(cfgdata_t *cfgdatap)
470*4582Scth {
471*4582Scth 	/* new_queue() is guaranteed to succeed */
472*4582Scth 	g_schg_queue = new_queue(B_TRUE, dmalloc, dfree, free_statechange);
473*4582Scth 
474*4582Scth 	dm_assert(pthread_mutex_lock(&g_schgt_state_mutex) == 0);
475*4582Scth 	g_schg_tid = fmd_thr_create(g_fm_hdl, disk_state_change_thread,
476*4582Scth 	    cfgdatap->disk_list);
477*4582Scth 
478*4582Scth 	/*
479*4582Scth 	 * Now, wait for the thread to enter the TS_RUNNING state.  This
480*4582Scth 	 * is important because we want the state-change thread to pull the
481*4582Scth 	 * initial state of the disks on startup (without the wait, we could
482*4582Scth 	 * have the hotplug event handler race and deliver a state change
483*4582Scth 	 * before the state-change thread initialized the initial disk state).
484*4582Scth 	 */
485*4582Scth 
486*4582Scth 	while (g_schgt_state != TS_RUNNING) {
487*4582Scth 		(void) pthread_cond_wait(&g_schgt_state_cvar,
488*4582Scth 		    &g_schgt_state_mutex);
489*4582Scth 	}
490*4582Scth 
491*4582Scth 	dm_assert(pthread_mutex_unlock(&g_schgt_state_mutex) == 0);
492*4582Scth 
493*4582Scth 	return (0);
494*4582Scth }
495*4582Scth 
496*4582Scth /*ARGSUSED*/
497*4582Scth void
cleanup_state_change_manager(cfgdata_t * cfgdatap)498*4582Scth cleanup_state_change_manager(cfgdata_t *cfgdatap)
499*4582Scth {
500*4582Scth 	if (g_schgt_state != TS_RUNNING)
501*4582Scth 		return;
502*4582Scth 
503*4582Scth 	g_schgt_state = TS_EXIT_REQUESTED;
504*4582Scth 	queue_add(g_schg_queue, NULL);
505*4582Scth 	dm_assert(pthread_mutex_lock(&g_schgt_state_mutex) == 0);
506*4582Scth 	while (g_schgt_state != TS_EXITED)
507*4582Scth 		dm_assert(pthread_cond_wait(&g_schgt_state_cvar,
508*4582Scth 		    &g_schgt_state_mutex) == 0);
509*4582Scth 	dm_assert(pthread_mutex_unlock(&g_schgt_state_mutex) == 0);
510*4582Scth 	(void) pthread_join(g_schg_tid, NULL);
511*4582Scth 	fmd_thr_destroy(g_fm_hdl, g_schg_tid);
512*4582Scth 	queue_free(&g_schg_queue);
513*4582Scth 	g_schgt_state = TS_NOT_RUNNING;
514*4582Scth }
515