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 /*
23*e5dcf7beSRobert Johnston  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24724365f7Ssethg  * Use is subject to license terms.
25724365f7Ssethg  */
26724365f7Ssethg 
27724365f7Ssethg #include <sys/types.h>
28724365f7Ssethg #include <sys/sysevent/dr.h>
29724365f7Ssethg #include <sys/sysevent/eventdefs.h>
30724365f7Ssethg #include <sys/sunddi.h>	/* for the EC's for DEVFS */
31724365f7Ssethg 
32724365f7Ssethg #include <errno.h>
33724365f7Ssethg #include <string.h>
34724365f7Ssethg #include <strings.h>
35724365f7Ssethg #include <stdio.h>
36724365f7Ssethg #include <unistd.h>
37724365f7Ssethg #include <time.h>
38724365f7Ssethg #include <pthread.h>
39724365f7Ssethg 
40724365f7Ssethg #include <libsysevent.h>
41724365f7Ssethg #include <sys/sysevent_impl.h>
42724365f7Ssethg 
43724365f7Ssethg #include <libnvpair.h>
44724365f7Ssethg #include <config_admin.h>
45724365f7Ssethg 
46184cd04cScth #include "disk_monitor.h"
47724365f7Ssethg #include "hotplug_mgr.h"
48724365f7Ssethg #include "schg_mgr.h"
499113a79cSeschrock #include "dm_platform.h"
50724365f7Ssethg 
51724365f7Ssethg typedef struct sysevent_event {
52724365f7Ssethg 	sysevent_t	*evp;
53724365f7Ssethg } sysevent_event_t;
54724365f7Ssethg 
55724365f7Ssethg /* Lock guarantees the ordering of the incoming sysevents */
56724365f7Ssethg static pthread_t g_sysev_tid;
57724365f7Ssethg static pthread_mutex_t g_event_handler_lock = PTHREAD_MUTEX_INITIALIZER;
58724365f7Ssethg static pthread_cond_t g_event_handler_cond = PTHREAD_COND_INITIALIZER;
59724365f7Ssethg static qu_t *g_sysev_queue = NULL;
60724365f7Ssethg static thread_state_t g_sysev_thread_state = TS_NOT_RUNNING;
61724365f7Ssethg /*
62724365f7Ssethg  * The sysevent handle is bound to the main sysevent handler
63724365f7Ssethg  * (event_handler), for each of the hotplug sysevents.
64724365f7Ssethg  */
65724365f7Ssethg static sysevent_handle_t *sysevent_handle = NULL;
66724365f7Ssethg 
67724365f7Ssethg static void free_sysevent_event(void *p);
68724365f7Ssethg 
69724365f7Ssethg static int
nsleep(int seconds)70724365f7Ssethg nsleep(int seconds)
71724365f7Ssethg {
72724365f7Ssethg 	struct timespec tspec;
73724365f7Ssethg 
74724365f7Ssethg 	tspec.tv_sec = seconds;
75724365f7Ssethg 	tspec.tv_nsec = 0;
76724365f7Ssethg 
77724365f7Ssethg 	return (nanosleep(&tspec, NULL));
78724365f7Ssethg }
79724365f7Ssethg 
80724365f7Ssethg static int
config_list_ext_poll(int num,char * const * path,cfga_list_data_t ** list_array,int * nlist,int flag)81724365f7Ssethg config_list_ext_poll(int num, char * const *path,
82d95706aeSmyers     cfga_list_data_t **list_array, int *nlist, int flag)
83724365f7Ssethg {
84724365f7Ssethg 	boolean_t done = B_FALSE;
85724365f7Ssethg 	boolean_t timedout = B_FALSE;
86724365f7Ssethg 	boolean_t interrupted = B_FALSE;
87724365f7Ssethg 	int timeout = 0;
88724365f7Ssethg 	int e;
89724365f7Ssethg #define	TIMEOUT_MAX 60
90724365f7Ssethg 
91724365f7Ssethg 	do {
92724365f7Ssethg 		switch ((e = config_list_ext(num, path, list_array,
93d95706aeSmyers 		    nlist, NULL, NULL, NULL, flag))) {
94724365f7Ssethg 
95724365f7Ssethg 		case CFGA_OK:
96724365f7Ssethg 
97724365f7Ssethg 			return (CFGA_OK);
98724365f7Ssethg 
99724365f7Ssethg 		case CFGA_BUSY:
100724365f7Ssethg 		case CFGA_SYSTEM_BUSY:
101724365f7Ssethg 
102724365f7Ssethg 			if (timeout++ >= TIMEOUT_MAX)
103724365f7Ssethg 				timedout = B_TRUE;
104724365f7Ssethg 			else {
105724365f7Ssethg 				if (nsleep(1) < 0)
106724365f7Ssethg 					interrupted = (errno == EINTR);
107724365f7Ssethg 			}
108724365f7Ssethg 			break;
109724365f7Ssethg 
110724365f7Ssethg 		default:
111724365f7Ssethg 			done = B_TRUE;
112724365f7Ssethg 			break;
113724365f7Ssethg 
114724365f7Ssethg 		}
115724365f7Ssethg 	} while (!done && !timedout && !interrupted);
116724365f7Ssethg 
117724365f7Ssethg 	return (e);
118724365f7Ssethg }
119724365f7Ssethg 
120d95706aeSmyers /*
121d95706aeSmyers  * Given a physical attachment point with a dynamic component
122d95706aeSmyers  * (as in the case of SCSI APs), ensure the 'controller'
123d95706aeSmyers  * portion of the dynamic component matches the physical portion.
124d95706aeSmyers  * Argument 'adjusted' must point to a buffer of at least
125d95706aeSmyers  * MAXPATHLEN bytes.
126d95706aeSmyers  */
127d95706aeSmyers void
adjust_dynamic_ap(const char * apid,char * adjusted)128d95706aeSmyers adjust_dynamic_ap(const char *apid, char *adjusted)
129d95706aeSmyers {
130d95706aeSmyers 	cfga_list_data_t *list_array = NULL;
131d95706aeSmyers 	int nlist;
132d95706aeSmyers 	char *ap_path[1];
133d95706aeSmyers 	char phys[MAXPATHLEN];
134d95706aeSmyers 	char dev_phys[MAXPATHLEN];
135d95706aeSmyers 	char *dyn;
136d95706aeSmyers 	int c, t, d;
137d95706aeSmyers 
138d95706aeSmyers 	dm_assert((strlen(apid) + 8 /* strlen("/devices") */) < MAXPATHLEN);
139d95706aeSmyers 
140d95706aeSmyers 	/* In the case of any error, return the unadjusted APID */
141d95706aeSmyers 	(void) strcpy(adjusted, apid);
142d95706aeSmyers 
143d95706aeSmyers 	/* if AP is not dynamic or not a disk node, no need to adjust it */
144d95706aeSmyers 	dyn = strstr(apid, "::");
145d95706aeSmyers 	if ((dyn == NULL) || (dyn == apid) ||
146d95706aeSmyers 	    (sscanf(dyn, "::dsk/c%dt%dd%d", &c, &t, &d) != 3))
147d95706aeSmyers 		return;
148d95706aeSmyers 
149d95706aeSmyers 	/*
150d95706aeSmyers 	 * Copy the AP_ID and terminate it at the '::' that we know
151d95706aeSmyers 	 * for a fact it contains.  Pre-pend '/devices' for the sake
152d95706aeSmyers 	 * of cfgadm_scsi, and get the cfgadm data for the controller.
153d95706aeSmyers 	 */
154d95706aeSmyers 	(void) strcpy(phys, apid);
155d95706aeSmyers 	*strstr(phys, "::") = '\0';
156d95706aeSmyers 	(void) snprintf(dev_phys, MAXPATHLEN, "/devices%s", phys);
157d95706aeSmyers 	ap_path[0] = dev_phys;
158d95706aeSmyers 
159d95706aeSmyers 	if (config_list_ext_poll(1, ap_path, &list_array, &nlist, 0)
160d95706aeSmyers 	    != CFGA_OK)
161d95706aeSmyers 		return;
162d95706aeSmyers 
163d95706aeSmyers 	dm_assert(nlist == 1);
164d95706aeSmyers 
165d95706aeSmyers 	if (sscanf(list_array[0].ap_log_id, "c%d", &c) == 1)
166d95706aeSmyers 		(void) snprintf(adjusted, MAXPATHLEN, "%s::dsk/c%dt%dd%d",
167d95706aeSmyers 		    phys, c, t, d);
168d95706aeSmyers 
169d95706aeSmyers 	free(list_array);
170d95706aeSmyers }
171d95706aeSmyers 
172d95706aeSmyers static int
disk_ap_is_scsi(const char * ap_path)173d95706aeSmyers disk_ap_is_scsi(const char *ap_path)
174d95706aeSmyers {
175d95706aeSmyers 	return (strstr(ap_path, ":scsi:") != NULL);
176d95706aeSmyers }
177d95706aeSmyers 
178724365f7Ssethg /*
179724365f7Ssethg  * Looks up the attachment point's state and returns it in one of
180724365f7Ssethg  * the hotplug states that the state change manager understands.
181724365f7Ssethg  */
182724365f7Ssethg hotplug_state_t
disk_ap_state_to_hotplug_state(diskmon_t * diskp)183724365f7Ssethg disk_ap_state_to_hotplug_state(diskmon_t *diskp)
184724365f7Ssethg {
185724365f7Ssethg 	hotplug_state_t state = HPS_UNKNOWN;
186724365f7Ssethg 	cfga_list_data_t *list_array = NULL;
187d95706aeSmyers 	int rv, nlist;
188724365f7Ssethg 	char *app = (char *)dm_prop_lookup(diskp->app_props,
189724365f7Ssethg 	    DISK_AP_PROP_APID);
190d95706aeSmyers 	char adj_app[MAXPATHLEN];
191724365f7Ssethg 	char *ap_path[1];
192724365f7Ssethg 	char *devices_app;
193724365f7Ssethg 	int len;
194724365f7Ssethg 	boolean_t list_valid = B_FALSE;
195724365f7Ssethg 
1967a0b67e3Ssethg 	dm_assert(app != NULL);
197724365f7Ssethg 
198d95706aeSmyers 	adjust_dynamic_ap(app, adj_app);
199d95706aeSmyers 	ap_path[0] = adj_app;
200d95706aeSmyers 	devices_app = NULL;
201724365f7Ssethg 
202d95706aeSmyers 	rv = config_list_ext_poll(1, ap_path, &list_array, &nlist,
203d95706aeSmyers 	    CFGA_FLAG_LIST_ALL);
204724365f7Ssethg 
205d95706aeSmyers 	if (rv != CFGA_OK) {
206724365f7Ssethg 		/*
207d95706aeSmyers 		 * The SATA and SCSI libcfgadm plugins add a
208724365f7Ssethg 		 * /devices to the phys id; to use it, we must
209724365f7Ssethg 		 * prepend this string before the call.
210724365f7Ssethg 		 */
211d95706aeSmyers 		len = 8 /* strlen("/devices") */ + strlen(adj_app) + 1;
212724365f7Ssethg 		devices_app = dmalloc(len);
213724365f7Ssethg 		(void) snprintf(devices_app, len, "/devices%s",
214d95706aeSmyers 		    adj_app);
215724365f7Ssethg 		ap_path[0] = devices_app;
216724365f7Ssethg 
217d95706aeSmyers 		rv = config_list_ext_poll(1, ap_path, &list_array, &nlist,
218d95706aeSmyers 		    CFGA_FLAG_LIST_ALL);
219d95706aeSmyers 	}
220724365f7Ssethg 
221d95706aeSmyers 	/*
222d95706aeSmyers 	 * cfgadm_scsi will return an error for an absent target,
223d95706aeSmyers 	 * so treat an error as "absent"; otherwise, make sure
224d95706aeSmyers 	 * cfgadm_xxx has returned a list of 1 item
225d95706aeSmyers 	 */
226d95706aeSmyers 	if (rv == CFGA_OK) {
227d95706aeSmyers 		dm_assert(nlist == 1);
228d95706aeSmyers 		list_valid = B_TRUE;
229d95706aeSmyers 	} else if (disk_ap_is_scsi(ap_path[0]))
230d95706aeSmyers 		state = HPS_ABSENT;
231724365f7Ssethg 
232d95706aeSmyers 	if (devices_app != NULL)
233724365f7Ssethg 		dfree(devices_app, len);
234724365f7Ssethg 
235724365f7Ssethg 	if (list_valid) {
236724365f7Ssethg 		/*
237724365f7Ssethg 		 * The following truth table defines how each state is
238724365f7Ssethg 		 * computed:
239724365f7Ssethg 		 *
240724365f7Ssethg 		 * +----------------------------------------------+
241724365f7Ssethg 		 * |		  | o_state | r_state | condition |
242724365f7Ssethg 		 * |		  +---------+---------+-----------|
243724365f7Ssethg 		 * | Absent	  |Don'tCare|Disc/Empt|	Don'tCare |
244724365f7Ssethg 		 * | Present	  |Unconfgrd|Connected|	 unknown  |
245724365f7Ssethg 		 * | Configured	  |Configred|Connected|	Don'tCare |
246724365f7Ssethg 		 * | Unconfigured |Unconfgrd|Connected|	   OK	  |
247724365f7Ssethg 		 * +--------------+---------+---------+-----------+
248724365f7Ssethg 		 */
249724365f7Ssethg 
250724365f7Ssethg 		if (list_array[0].ap_r_state == CFGA_STAT_EMPTY ||
251724365f7Ssethg 		    list_array[0].ap_r_state == CFGA_STAT_DISCONNECTED)
252724365f7Ssethg 			state = HPS_ABSENT;
253724365f7Ssethg 		else if (list_array[0].ap_r_state == CFGA_STAT_CONNECTED &&
254724365f7Ssethg 		    list_array[0].ap_o_state == CFGA_STAT_UNCONFIGURED &&
255724365f7Ssethg 		    list_array[0].ap_cond == CFGA_COND_UNKNOWN)
256724365f7Ssethg 			state = HPS_PRESENT;
257724365f7Ssethg 		else if (list_array[0].ap_r_state == CFGA_STAT_CONNECTED &&
258724365f7Ssethg 		    list_array[0].ap_o_state == CFGA_STAT_UNCONFIGURED &&
259724365f7Ssethg 		    list_array[0].ap_cond != CFGA_COND_UNKNOWN)
260724365f7Ssethg 			state = HPS_UNCONFIGURED;
261724365f7Ssethg 		else if (list_array[0].ap_r_state == CFGA_STAT_CONNECTED &&
262724365f7Ssethg 		    list_array[0].ap_o_state == CFGA_STAT_CONFIGURED)
263724365f7Ssethg 			state = HPS_CONFIGURED;
264724365f7Ssethg 
265724365f7Ssethg 		free(list_array);
266724365f7Ssethg 	}
267724365f7Ssethg 
268724365f7Ssethg 	return (state);
269724365f7Ssethg }
270724365f7Ssethg 
271724365f7Ssethg /*
272724365f7Ssethg  * Examine the sysevent passed in and returns the hotplug state that
273724365f7Ssethg  * the sysevent states (or implies, in the case of attachment point
274724365f7Ssethg  * events).
275724365f7Ssethg  */
276724365f7Ssethg static hotplug_state_t
disk_sysev_to_state(diskmon_t * diskp,sysevent_t * evp)277724365f7Ssethg disk_sysev_to_state(diskmon_t *diskp, sysevent_t *evp)
278724365f7Ssethg {
279724365f7Ssethg 	const char *class_name, *subclass;
280724365f7Ssethg 	hotplug_state_t state = HPS_UNKNOWN;
281724365f7Ssethg 	sysevent_value_t se_val;
282724365f7Ssethg 
283724365f7Ssethg 	/*
284724365f7Ssethg 	 * The state mapping is as follows:
285724365f7Ssethg 	 *
286724365f7Ssethg 	 * Sysevent				State
287724365f7Ssethg 	 * --------------------------------------------------------
288724365f7Ssethg 	 * EC_DEVFS/ESC_DEVFS_DEVI_ADD		Configured
289724365f7Ssethg 	 * EC_DEVFS/ESC_DEVFS_DEVI_REMOVE	Unconfigured
290724365f7Ssethg 	 * EC_DR/ESC_DR_AP_STATE_CHANGE		*[Absent/Present]
291724365f7Ssethg 	 *
292724365f7Ssethg 	 * (The EC_DR event requires a probe of the attachment point
293724365f7Ssethg 	 * to determine the AP's state if there is no usable HINT)
294724365f7Ssethg 	 *
295724365f7Ssethg 	 */
296724365f7Ssethg 
297724365f7Ssethg 	class_name = sysevent_get_class_name(evp);
298724365f7Ssethg 	subclass = sysevent_get_subclass_name(evp);
299724365f7Ssethg 
300724365f7Ssethg 	if (strcmp(class_name, EC_DEVFS) == 0) {
301724365f7Ssethg 		if (strcmp(subclass, ESC_DEVFS_DEVI_ADD) == 0) {
302724365f7Ssethg 
303724365f7Ssethg 			state = HPS_CONFIGURED;
304724365f7Ssethg 
305724365f7Ssethg 		} else if (strcmp(subclass, ESC_DEVFS_DEVI_REMOVE) == 0) {
306724365f7Ssethg 
307724365f7Ssethg 			state = HPS_UNCONFIGURED;
308724365f7Ssethg 
309724365f7Ssethg 		}
310724365f7Ssethg 
311724365f7Ssethg 	} else if (strcmp(class_name, EC_DR) == 0 &&
312d95706aeSmyers 	    ((strcmp(subclass, ESC_DR_AP_STATE_CHANGE) == 0) ||
313d95706aeSmyers 	    (strcmp(subclass, ESC_DR_TARGET_STATE_CHANGE) == 0))) {
314724365f7Ssethg 
315724365f7Ssethg 		if (sysevent_lookup_attr(evp, DR_HINT, SE_DATA_TYPE_STRING,
316724365f7Ssethg 		    &se_val) == 0 && se_val.value.sv_string != NULL) {
317724365f7Ssethg 
318724365f7Ssethg 			if (strcmp(se_val.value.sv_string, DR_HINT_INSERT)
319724365f7Ssethg 			    == 0) {
320724365f7Ssethg 
321724365f7Ssethg 				state = HPS_PRESENT;
322724365f7Ssethg 
323724365f7Ssethg 			} else if (strcmp(se_val.value.sv_string,
324724365f7Ssethg 			    DR_HINT_REMOVE) == 0) {
325724365f7Ssethg 
326724365f7Ssethg 				state = HPS_ABSENT;
327724365f7Ssethg 			}
328724365f7Ssethg 
329724365f7Ssethg 		}
330724365f7Ssethg 
331724365f7Ssethg 		/*
332724365f7Ssethg 		 * If the state could not be determined by the hint
333724365f7Ssethg 		 * (or there was no hint), ask the AP directly.
334d95706aeSmyers 		 * SCSI HBAs may send an insertion sysevent
335d95706aeSmyers 		 * *after* configuring the target node, so double-
336d95706aeSmyers 		 * check HPS_PRESENT
337724365f7Ssethg 		 */
338d95706aeSmyers 		if ((state == HPS_UNKNOWN) || (state = HPS_PRESENT))
339724365f7Ssethg 			state = disk_ap_state_to_hotplug_state(diskp);
340724365f7Ssethg 	}
341724365f7Ssethg 
342724365f7Ssethg 	return (state);
343724365f7Ssethg }
344724365f7Ssethg 
345d95706aeSmyers static void
disk_split_ap_path_sata(const char * ap_path,char * device,int * target)346d95706aeSmyers disk_split_ap_path_sata(const char *ap_path, char *device, int *target)
347d95706aeSmyers {
348d95706aeSmyers 	char *p;
349d95706aeSmyers 	int n;
350d95706aeSmyers 
351d95706aeSmyers 	/*
352d95706aeSmyers 	 *  /devices/rootnode/.../device:target
353d95706aeSmyers 	 */
354d95706aeSmyers 	(void) strncpy(device, ap_path, MAXPATHLEN);
355d95706aeSmyers 	p = strrchr(device, ':');
356d95706aeSmyers 	dm_assert(p != NULL);
357d95706aeSmyers 	n = sscanf(p, ":%d", target);
358d95706aeSmyers 	dm_assert(n == 1);
359d95706aeSmyers 	*p = '\0';
360d95706aeSmyers }
361d95706aeSmyers 
362d95706aeSmyers static void
disk_split_ap_path_scsi(const char * ap_path,char * device,int * target)363d95706aeSmyers disk_split_ap_path_scsi(const char *ap_path, char *device, int *target)
364d95706aeSmyers {
365d95706aeSmyers 	char *p;
366d95706aeSmyers 	int n;
367d95706aeSmyers 
368d95706aeSmyers 	/*
369d95706aeSmyers 	 *  /devices/rootnode/.../device:scsi::dsk/cXtXdX
370d95706aeSmyers 	 */
371d95706aeSmyers 
372d95706aeSmyers 	(void) strncpy(device, ap_path, MAXPATHLEN);
373d95706aeSmyers 	p = strrchr(device, ':');
374d95706aeSmyers 	dm_assert(p != NULL);
375d95706aeSmyers 
376d95706aeSmyers 	n = sscanf(p, ":dsk/c%*dt%dd%*d", target);
377d95706aeSmyers 	dm_assert(n == 1);
378d95706aeSmyers 
379d95706aeSmyers 	*strchr(device, ':') = '\0';
380d95706aeSmyers }
381d95706aeSmyers 
382d95706aeSmyers static void
disk_split_ap_path(const char * ap_path,char * device,int * target)383d95706aeSmyers disk_split_ap_path(const char *ap_path, char *device, int *target)
384d95706aeSmyers {
385d95706aeSmyers 	/*
386d95706aeSmyers 	 * The AP path comes in two forms; for SATA devices,
387d95706aeSmyers 	 * is is of the form:
388d95706aeSmyers 	 *   /devices/rootnode/.../device:portnum
389d95706aeSmyers 	 * and for SCSI devices, it is of the form:
390d95706aeSmyers 	 *  /devices/rootnode/.../device:scsi::dsk/cXtXdX
391d95706aeSmyers 	 */
392d95706aeSmyers 
393d95706aeSmyers 	if (disk_ap_is_scsi(ap_path))
394d95706aeSmyers 		disk_split_ap_path_scsi(ap_path, device, target);
395d95706aeSmyers 	else
396d95706aeSmyers 		disk_split_ap_path_sata(ap_path, device, target);
397d95706aeSmyers }
398d95706aeSmyers 
399d95706aeSmyers static void
disk_split_device_path(const char * dev_path,char * device,int * target)400d95706aeSmyers disk_split_device_path(const char *dev_path, char *device, int *target)
401d95706aeSmyers {
402d95706aeSmyers 	char *t, *p, *e;
403d95706aeSmyers 
404d95706aeSmyers 	/*
405d95706aeSmyers 	 * The disk device path is of the form:
406d95706aeSmyers 	 * /rootnode/.../device/target@tgtid,tgtlun
407d95706aeSmyers 	 */
408d95706aeSmyers 
409d95706aeSmyers 	(void) strncpy(device, dev_path, MAXPATHLEN);
410d95706aeSmyers 	e = t = strrchr(device, '/');
411d95706aeSmyers 	dm_assert(t != NULL);
412d95706aeSmyers 
413d95706aeSmyers 	t = strchr(t, '@');
414d95706aeSmyers 	dm_assert(t != NULL);
415d95706aeSmyers 	t += 1;
416d95706aeSmyers 
417d95706aeSmyers 	if ((p = strchr(t, ',')) != NULL)
418d95706aeSmyers 		*p = '\0';
419d95706aeSmyers 
420d95706aeSmyers 	*target = strtol(t, 0, 16);
421d95706aeSmyers 	*e = '\0';
422d95706aeSmyers }
423d95706aeSmyers 
424724365f7Ssethg /*
425724365f7Ssethg  * Returns the diskmon that corresponds to the physical disk path
426724365f7Ssethg  * passed in.
427724365f7Ssethg  */
428724365f7Ssethg static diskmon_t *
disk_match_by_device_path(diskmon_t * disklistp,const char * dev_path)429724365f7Ssethg disk_match_by_device_path(diskmon_t *disklistp, const char *dev_path)
430724365f7Ssethg {
431d95706aeSmyers 	char dev_device[MAXPATHLEN];
432d95706aeSmyers 	int dev_target;
433d95706aeSmyers 	char ap_device[MAXPATHLEN];
434d95706aeSmyers 	int ap_target;
435d95706aeSmyers 
4367a0b67e3Ssethg 	dm_assert(disklistp != NULL);
4377a0b67e3Ssethg 	dm_assert(dev_path != NULL);
438724365f7Ssethg 
439724365f7Ssethg 	if (strncmp(dev_path, DEVICES_PREFIX, 8) == 0)
440724365f7Ssethg 		dev_path += 8;
441724365f7Ssethg 
442d95706aeSmyers 	/* pare dev_path into device and target components */
443d95706aeSmyers 	disk_split_device_path(dev_path, (char *)&dev_device, &dev_target);
444d95706aeSmyers 
445724365f7Ssethg 	/*
446724365f7Ssethg 	 * The AP path specified in the configuration properties is
447724365f7Ssethg 	 * the path to an attachment point minor node whose port number is
448724365f7Ssethg 	 * equal to the target number on the disk "major" node sent by the
449724365f7Ssethg 	 * sysevent.  To match them, we need to extract the target id and
450724365f7Ssethg 	 * construct an AP string to compare to the AP path in the diskmon.
451724365f7Ssethg 	 */
452724365f7Ssethg 	while (disklistp != NULL) {
453724365f7Ssethg 		char *app = (char *)dm_prop_lookup(disklistp->app_props,
454724365f7Ssethg 		    DISK_AP_PROP_APID);
4557a0b67e3Ssethg 		dm_assert(app != NULL);
456724365f7Ssethg 
457d95706aeSmyers 		/* Not necessary to adjust the APID here */
458724365f7Ssethg 		if (strncmp(app, DEVICES_PREFIX, 8) == 0)
459724365f7Ssethg 			app += 8;
460724365f7Ssethg 
461d95706aeSmyers 		disk_split_ap_path(app, (char *)&ap_device, &ap_target);
462724365f7Ssethg 
463d95706aeSmyers 		if ((strcmp(dev_device, ap_device) == 0) &&
464d95706aeSmyers 		    (dev_target == ap_target))
465724365f7Ssethg 			return (disklistp);
466724365f7Ssethg 
467724365f7Ssethg 		disklistp = disklistp->next;
468724365f7Ssethg 	}
469724365f7Ssethg 	return (NULL);
470724365f7Ssethg }
471724365f7Ssethg 
472724365f7Ssethg static diskmon_t *
disk_match_by_ap_id(diskmon_t * disklistp,const char * ap_id)473724365f7Ssethg disk_match_by_ap_id(diskmon_t *disklistp, const char *ap_id)
474724365f7Ssethg {
475724365f7Ssethg 	const char *disk_ap_id;
4767a0b67e3Ssethg 	dm_assert(disklistp != NULL);
4777a0b67e3Ssethg 	dm_assert(ap_id != NULL);
478724365f7Ssethg 
479724365f7Ssethg 	/* Match only the device-tree portion of the name */
480724365f7Ssethg 	if (strncmp(ap_id, DEVICES_PREFIX, 8 /* strlen("/devices") */) == 0)
481724365f7Ssethg 		ap_id += 8;
482724365f7Ssethg 
483724365f7Ssethg 	while (disklistp != NULL) {
484724365f7Ssethg 		disk_ap_id = dm_prop_lookup(disklistp->app_props,
485724365f7Ssethg 		    DISK_AP_PROP_APID);
486724365f7Ssethg 
4877a0b67e3Ssethg 		dm_assert(disk_ap_id != NULL);
488724365f7Ssethg 
489724365f7Ssethg 		if (strcmp(disk_ap_id, ap_id) == 0)
490724365f7Ssethg 			return (disklistp);
491724365f7Ssethg 
492724365f7Ssethg 		disklistp = disklistp->next;
493724365f7Ssethg 	}
494724365f7Ssethg 	return (NULL);
495724365f7Ssethg }
496724365f7Ssethg 
497d95706aeSmyers static diskmon_t *
disk_match_by_target_id(diskmon_t * disklistp,const char * target_path)498d95706aeSmyers disk_match_by_target_id(diskmon_t *disklistp, const char *target_path)
499d95706aeSmyers {
500d95706aeSmyers 	const char *disk_ap_id;
501d95706aeSmyers 
502d95706aeSmyers 	char match_device[MAXPATHLEN];
503d95706aeSmyers 	int match_target;
504d95706aeSmyers 
505d95706aeSmyers 	char ap_device[MAXPATHLEN];
506d95706aeSmyers 	int ap_target;
507d95706aeSmyers 
508d95706aeSmyers 
509d95706aeSmyers 	/* Match only the device-tree portion of the name */
510d95706aeSmyers 	if (strncmp(target_path, DEVICES_PREFIX, 8) == 0)
511d95706aeSmyers 		target_path += 8;
512d95706aeSmyers 	disk_split_ap_path(target_path, (char *)&match_device, &match_target);
513d95706aeSmyers 
514d95706aeSmyers 	while (disklistp != NULL) {
515d95706aeSmyers 
516d95706aeSmyers 		disk_ap_id = dm_prop_lookup(disklistp->app_props,
517d95706aeSmyers 		    DISK_AP_PROP_APID);
518d95706aeSmyers 		dm_assert(disk_ap_id != NULL);
519d95706aeSmyers 
520d95706aeSmyers 		disk_split_ap_path(disk_ap_id, (char *)&ap_device, &ap_target);
521d95706aeSmyers 		if ((match_target == ap_target) &&
522d95706aeSmyers 		    (strcmp(match_device, ap_device) == 0))
523d95706aeSmyers 			return (disklistp);
524d95706aeSmyers 
525d95706aeSmyers 		disklistp = disklistp->next;
526d95706aeSmyers 	}
527d95706aeSmyers 	return (NULL);
528d95706aeSmyers }
529d95706aeSmyers 
530724365f7Ssethg static diskmon_t *
match_sysevent_to_disk(diskmon_t * disklistp,sysevent_t * evp)531724365f7Ssethg match_sysevent_to_disk(diskmon_t *disklistp, sysevent_t *evp)
532724365f7Ssethg {
533724365f7Ssethg 	diskmon_t *dmp = NULL;
534724365f7Ssethg 	sysevent_value_t se_val;
535724365f7Ssethg 	char *class_name = sysevent_get_class_name(evp);
536724365f7Ssethg 	char *subclass = sysevent_get_subclass_name(evp);
537724365f7Ssethg 
538724365f7Ssethg 	se_val.value.sv_string = NULL;
539724365f7Ssethg 
540724365f7Ssethg 	if (strcmp(class_name, EC_DEVFS) == 0) {
541724365f7Ssethg 		/* EC_DEVFS-class events have a `DEVFS_PATHNAME' property */
542724365f7Ssethg 		if (sysevent_lookup_attr(evp, DEVFS_PATHNAME,
543724365f7Ssethg 		    SE_DATA_TYPE_STRING, &se_val) == 0 &&
544724365f7Ssethg 		    se_val.value.sv_string != NULL) {
545724365f7Ssethg 
546724365f7Ssethg 			dmp = disk_match_by_device_path(disklistp,
547724365f7Ssethg 			    se_val.value.sv_string);
548724365f7Ssethg 
549724365f7Ssethg 		}
550724365f7Ssethg 
551724365f7Ssethg 	} else if (strcmp(class_name, EC_DR) == 0 &&
552724365f7Ssethg 	    strcmp(subclass, ESC_DR_AP_STATE_CHANGE) == 0) {
553724365f7Ssethg 
554724365f7Ssethg 		/* EC_DR-class events have a `DR_AP_ID' property */
555724365f7Ssethg 		if (sysevent_lookup_attr(evp, DR_AP_ID, SE_DATA_TYPE_STRING,
556724365f7Ssethg 		    &se_val) == 0 && se_val.value.sv_string != NULL) {
557724365f7Ssethg 
558724365f7Ssethg 			dmp = disk_match_by_ap_id(disklistp,
559724365f7Ssethg 			    se_val.value.sv_string);
560724365f7Ssethg 		}
561d95706aeSmyers 	} else if (strcmp(class_name, EC_DR) == 0 &&
562d95706aeSmyers 	    strcmp(subclass, ESC_DR_TARGET_STATE_CHANGE) == 0) {
563d95706aeSmyers 		/* get DR_TARGET_ID */
564d95706aeSmyers 		if (sysevent_lookup_attr(evp, DR_TARGET_ID,
565d95706aeSmyers 		    SE_DATA_TYPE_STRING, &se_val) == 0 &&
566d95706aeSmyers 		    se_val.value.sv_string != NULL) {
567d95706aeSmyers 			dmp = disk_match_by_target_id(disklistp,
568d95706aeSmyers 			    se_val.value.sv_string);
569d95706aeSmyers 		}
570724365f7Ssethg 	}
571724365f7Ssethg 
572724365f7Ssethg 	if (se_val.value.sv_string)
573724365f7Ssethg 		log_msg(MM_HPMGR, "match_sysevent_to_disk: device/ap: %s\n",
574724365f7Ssethg 		    se_val.value.sv_string);
575724365f7Ssethg 
576724365f7Ssethg 	return (dmp);
577724365f7Ssethg }
578724365f7Ssethg 
579724365f7Ssethg 
580724365f7Ssethg /*
581724365f7Ssethg  * The disk hotplug monitor (DHPM) listens for disk hotplug events and calls the
582724365f7Ssethg  * state-change functionality when a disk's state changes.  The DHPM listens for
583724365f7Ssethg  * hotplug events via sysevent subscriptions to the following sysevent
584724365f7Ssethg  * classes/subclasses: { EC_DEVFS/ESC_DEVFS_BRANCH_ADD,
585724365f7Ssethg  * EC_DEVFS/ESC_DEVFS_BRANCH_REMOVE, EC_DEVFS/ESC_DEVFS_DEVI_ADD,
586724365f7Ssethg  * EC_DEVFS/ESC_DEVFS_DEVI_REMOVE, EC_DR/ESC_DR_AP_STATE_CHANGE }.  Once the
587724365f7Ssethg  * event is received, the device path sent as part of the event is matched
588724365f7Ssethg  * to one of the disks described by the configuration data structures.
589724365f7Ssethg  */
590724365f7Ssethg static void
dm_process_sysevent(sysevent_t * dupev)591724365f7Ssethg dm_process_sysevent(sysevent_t *dupev)
592724365f7Ssethg {
593724365f7Ssethg 	char		*class_name;
594724365f7Ssethg 	char		*pub;
5959113a79cSeschrock 	char		*subclass = sysevent_get_subclass_name(dupev);
596724365f7Ssethg 	diskmon_t	*diskp;
597724365f7Ssethg 
598724365f7Ssethg 	class_name = sysevent_get_class_name(dupev);
599724365f7Ssethg 	log_msg(MM_HPMGR, "****EVENT: %s %s (by %s)\n", class_name,
6009113a79cSeschrock 	    subclass,
601724365f7Ssethg 	    ((pub = sysevent_get_pub_name(dupev)) != NULL) ? pub : "UNKNOWN");
602724365f7Ssethg 
603724365f7Ssethg 	if (pub)
604724365f7Ssethg 		free(pub);
605724365f7Ssethg 
6069113a79cSeschrock 	if (strcmp(class_name, EC_PLATFORM) == 0 &&
6079113a79cSeschrock 	    strcmp(subclass, ESC_PLATFORM_SP_RESET) == 0) {
6089113a79cSeschrock 		if (dm_platform_resync() != 0)
6099113a79cSeschrock 			log_warn("failed to resync SP platform\n");
610*e5dcf7beSRobert Johnston 		sysevent_free(dupev);
6119113a79cSeschrock 		return;
6129113a79cSeschrock 	}
6139113a79cSeschrock 
614724365f7Ssethg 	/*
615724365f7Ssethg 	 * We will handle this event if the event's target matches one of the
616724365f7Ssethg 	 * disks we're monitoring
617724365f7Ssethg 	 */
618724365f7Ssethg 	if ((diskp = match_sysevent_to_disk(config_data->disk_list, dupev))
619724365f7Ssethg 	    != NULL) {
620724365f7Ssethg 
621724365f7Ssethg 		dm_state_change(diskp, disk_sysev_to_state(diskp, dupev));
622724365f7Ssethg 	}
623724365f7Ssethg 
624724365f7Ssethg 	sysevent_free(dupev);
625724365f7Ssethg }
626724365f7Ssethg 
627724365f7Ssethg static void
dm_fmd_sysevent_thread(void * queuep)628724365f7Ssethg dm_fmd_sysevent_thread(void *queuep)
629724365f7Ssethg {
630724365f7Ssethg 	qu_t			*qp = (qu_t *)queuep;
631724365f7Ssethg 	sysevent_event_t	*sevevp;
632724365f7Ssethg 
633724365f7Ssethg 	/* Signal the thread spawner that we're running */
6347a0b67e3Ssethg 	dm_assert(pthread_mutex_lock(&g_event_handler_lock) == 0);
635724365f7Ssethg 	if (g_sysev_thread_state != TS_EXIT_REQUESTED)
636724365f7Ssethg 		g_sysev_thread_state = TS_RUNNING;
637724365f7Ssethg 	(void) pthread_cond_broadcast(&g_event_handler_cond);
6387a0b67e3Ssethg 	dm_assert(pthread_mutex_unlock(&g_event_handler_lock) == 0);
639724365f7Ssethg 
640724365f7Ssethg 	while (g_sysev_thread_state != TS_EXIT_REQUESTED) {
641724365f7Ssethg 		if ((sevevp = (sysevent_event_t *)queue_remove(qp)) == NULL)
642724365f7Ssethg 			continue;
643724365f7Ssethg 
644724365f7Ssethg 		dm_process_sysevent(sevevp->evp);
645724365f7Ssethg 
646724365f7Ssethg 		free_sysevent_event(sevevp);
647724365f7Ssethg 	}
648724365f7Ssethg 
649724365f7Ssethg 	/* Signal the thread spawner that we've exited */
6507a0b67e3Ssethg 	dm_assert(pthread_mutex_lock(&g_event_handler_lock) == 0);
651724365f7Ssethg 	g_sysev_thread_state = TS_EXITED;
652724365f7Ssethg 	(void) pthread_cond_broadcast(&g_event_handler_cond);
6537a0b67e3Ssethg 	dm_assert(pthread_mutex_unlock(&g_event_handler_lock) == 0);
654724365f7Ssethg 
655724365f7Ssethg 	log_msg(MM_HPMGR, "FMD sysevent handler thread exiting...");
656724365f7Ssethg }
657724365f7Ssethg 
658724365f7Ssethg static sysevent_event_t *
new_sysevent_event(sysevent_t * ev)659724365f7Ssethg new_sysevent_event(sysevent_t *ev)
660724365f7Ssethg {
661724365f7Ssethg 	/*
662724365f7Ssethg 	 * Cannot use dmalloc for this because the thread isn't a FMD-created
663724365f7Ssethg 	 * thread!
664724365f7Ssethg 	 */
665724365f7Ssethg 	sysevent_event_t *sevevp = malloc(sizeof (sysevent_event_t));
666724365f7Ssethg 	sevevp->evp = ev;
667724365f7Ssethg 	return (sevevp);
668724365f7Ssethg }
669724365f7Ssethg 
670724365f7Ssethg static void
free_sysevent_event(void * p)671724365f7Ssethg free_sysevent_event(void *p)
672724365f7Ssethg {
673724365f7Ssethg 	/* the sysevent_event was allocated with malloc(): */
674724365f7Ssethg 	free(p);
675724365f7Ssethg }
676724365f7Ssethg 
677724365f7Ssethg static void
event_handler(sysevent_t * ev)678724365f7Ssethg event_handler(sysevent_t *ev)
679724365f7Ssethg {
680724365f7Ssethg 	/* The duplicated sysevent will be freed in the child thread */
681724365f7Ssethg 	sysevent_t	*dupev = sysevent_dup(ev);
682724365f7Ssethg 
683724365f7Ssethg 	/*
684724365f7Ssethg 	 * Add this sysevent to the work queue of our FMA thread so we can
685724365f7Ssethg 	 * handle the sysevent and use the FMA API (e.g. for memory
686724365f7Ssethg 	 * allocation, etc.) in the sysevent handler.
687724365f7Ssethg 	 */
688724365f7Ssethg 	queue_add(g_sysev_queue, new_sysevent_event(dupev));
689724365f7Ssethg }
690724365f7Ssethg 
691724365f7Ssethg static void
fini_sysevents(void)692724365f7Ssethg fini_sysevents(void)
693724365f7Ssethg {
694724365f7Ssethg 	sysevent_unsubscribe_event(sysevent_handle, EC_ALL);
695724365f7Ssethg }
696724365f7Ssethg 
697724365f7Ssethg static int
init_sysevents(void)698724365f7Ssethg init_sysevents(void)
699724365f7Ssethg {
700724365f7Ssethg 	int rv = 0;
701724365f7Ssethg 	const char *devfs_subclasses[] = {
702724365f7Ssethg 		ESC_DEVFS_DEVI_ADD,
703724365f7Ssethg 		ESC_DEVFS_DEVI_REMOVE
704724365f7Ssethg 	};
705724365f7Ssethg 	const char *dr_subclasses[] = {
706d95706aeSmyers 		ESC_DR_AP_STATE_CHANGE,
707d95706aeSmyers 		ESC_DR_TARGET_STATE_CHANGE
708724365f7Ssethg 	};
7099113a79cSeschrock 	const char *platform_subclasses[] = {
7109113a79cSeschrock 		ESC_PLATFORM_SP_RESET
7119113a79cSeschrock 	};
712724365f7Ssethg 
713724365f7Ssethg 	if ((sysevent_handle = sysevent_bind_handle(event_handler)) == NULL) {
714724365f7Ssethg 		rv = errno;
715724365f7Ssethg 		log_err("Could not initialize the hotplug manager ("
716724365f7Ssethg 		    "sysevent_bind_handle failure");
717724365f7Ssethg 	}
718724365f7Ssethg 
719724365f7Ssethg 	if (sysevent_subscribe_event(sysevent_handle, EC_DEVFS,
720d95706aeSmyers 	    devfs_subclasses,
721d95706aeSmyers 	    sizeof (devfs_subclasses)/sizeof (devfs_subclasses[0])) != 0) {
722724365f7Ssethg 
723724365f7Ssethg 		log_err("Could not initialize the hotplug manager "
724724365f7Ssethg 		    "sysevent_subscribe_event(event class = EC_DEVFS) "
725724365f7Ssethg 		    "failure");
726724365f7Ssethg 
727724365f7Ssethg 		rv = -1;
728724365f7Ssethg 
729724365f7Ssethg 	} else if (sysevent_subscribe_event(sysevent_handle, EC_DR,
730d95706aeSmyers 	    dr_subclasses,
731d95706aeSmyers 	    sizeof (dr_subclasses)/sizeof (dr_subclasses[0])) != 0) {
732724365f7Ssethg 
733724365f7Ssethg 		log_err("Could not initialize the hotplug manager "
734724365f7Ssethg 		    "sysevent_subscribe_event(event class = EC_DR) "
735724365f7Ssethg 		    "failure");
736724365f7Ssethg 
737724365f7Ssethg 		/* Unsubscribe from all sysevents in the event of a failure */
738724365f7Ssethg 		fini_sysevents();
739724365f7Ssethg 
7409113a79cSeschrock 		rv = -1;
7419113a79cSeschrock 	} else if (sysevent_subscribe_event(sysevent_handle, EC_PLATFORM,
742d95706aeSmyers 	    platform_subclasses,
743d95706aeSmyers 	    sizeof (platform_subclasses)/sizeof (platform_subclasses[0]))
744d95706aeSmyers 	    != 0) {
7459113a79cSeschrock 
7469113a79cSeschrock 		log_err("Could not initialize the hotplug manager "
7479113a79cSeschrock 		    "sysevent_subscribe_event(event class = EC_PLATFORM) "
7489113a79cSeschrock 		    "failure");
7499113a79cSeschrock 
7509113a79cSeschrock 		/* Unsubscribe from all sysevents in the event of a failure */
7519113a79cSeschrock 		fini_sysevents();
7529113a79cSeschrock 
753724365f7Ssethg 		rv = -1;
754724365f7Ssethg 	}
755724365f7Ssethg 
7569113a79cSeschrock 
757724365f7Ssethg 	return (rv);
758724365f7Ssethg }
759724365f7Ssethg 
760724365f7Ssethg /*ARGSUSED*/
761724365f7Ssethg static void
stdfree(void * p,size_t sz)762724365f7Ssethg stdfree(void *p, size_t sz)
763724365f7Ssethg {
764724365f7Ssethg 	free(p);
765724365f7Ssethg }
766724365f7Ssethg 
767724365f7Ssethg /*
768724365f7Ssethg  * Assumptions: Each disk's current state was determined and stored in
769724365f7Ssethg  * its diskmon_t.
770724365f7Ssethg  */
771724365f7Ssethg hotplug_mgr_init_err_t
init_hotplug_manager()772724365f7Ssethg init_hotplug_manager()
773724365f7Ssethg {
774724365f7Ssethg 	/* Create the queue to which we'll add sysevents */
775724365f7Ssethg 	g_sysev_queue = new_queue(B_TRUE, malloc, stdfree, free_sysevent_event);
776724365f7Ssethg 
777724365f7Ssethg 	/*
778724365f7Ssethg 	 * Grab the event handler lock before spawning the thread so we can
779724365f7Ssethg 	 * wait for the thread to transition to the running state.
780724365f7Ssethg 	 */
7817a0b67e3Ssethg 	dm_assert(pthread_mutex_lock(&g_event_handler_lock) == 0);
782724365f7Ssethg 
783724365f7Ssethg 	/* Create the sysevent handling thread */
784724365f7Ssethg 	g_sysev_tid = fmd_thr_create(g_fm_hdl, dm_fmd_sysevent_thread,
785724365f7Ssethg 	    g_sysev_queue);
786724365f7Ssethg 
787724365f7Ssethg 	/* Wait for the thread's acknowledgement */
788724365f7Ssethg 	while (g_sysev_thread_state != TS_RUNNING)
789724365f7Ssethg 		(void) pthread_cond_wait(&g_event_handler_cond,
790724365f7Ssethg 		    &g_event_handler_lock);
7917a0b67e3Ssethg 	dm_assert(pthread_mutex_unlock(&g_event_handler_lock) == 0);
792724365f7Ssethg 
793724365f7Ssethg 	if (init_sysevents() != 0) {
794724365f7Ssethg 		log_warn_e("Error initializing sysevents");
795724365f7Ssethg 		return (HPM_ERR_SYSEVENT_INIT);
796724365f7Ssethg 	}
797724365f7Ssethg 
798724365f7Ssethg 	return (0);
799724365f7Ssethg }
800724365f7Ssethg 
801724365f7Ssethg void
cleanup_hotplug_manager()802724365f7Ssethg cleanup_hotplug_manager()
803724365f7Ssethg {
804724365f7Ssethg 	/* Unsubscribe from the sysevents */
805724365f7Ssethg 	fini_sysevents();
806724365f7Ssethg 
807724365f7Ssethg 	/*
808724365f7Ssethg 	 * Wait for the thread to exit before we can destroy
809724365f7Ssethg 	 * the event queue.
810724365f7Ssethg 	 */
8117a0b67e3Ssethg 	dm_assert(pthread_mutex_lock(&g_event_handler_lock) == 0);
812724365f7Ssethg 	g_sysev_thread_state = TS_EXIT_REQUESTED;
813724365f7Ssethg 	queue_add(g_sysev_queue, NULL);
814724365f7Ssethg 	while (g_sysev_thread_state != TS_EXITED)
815724365f7Ssethg 		(void) pthread_cond_wait(&g_event_handler_cond,
816724365f7Ssethg 		    &g_event_handler_lock);
8177a0b67e3Ssethg 	dm_assert(pthread_mutex_unlock(&g_event_handler_lock) == 0);
818724365f7Ssethg 	(void) pthread_join(g_sysev_tid, NULL);
819724365f7Ssethg 	fmd_thr_destroy(g_fm_hdl, g_sysev_tid);
820724365f7Ssethg 
821724365f7Ssethg 	/* Finally, destroy the event queue and reset the thread state */
822724365f7Ssethg 	queue_free(&g_sysev_queue);
823724365f7Ssethg 	g_sysev_thread_state = TS_NOT_RUNNING;
824724365f7Ssethg }
825