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