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