dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / fm / modules / common / disk-monitor / hotplug_mgr.c
blob9741011bc6bf119d05b79689704a87154adb9bea
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/types.h>
28 #include <sys/sysevent/dr.h>
29 #include <sys/sysevent/eventdefs.h>
30 #include <sys/sunddi.h> /* for the EC's for DEVFS */
32 #include <errno.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <time.h>
38 #include <pthread.h>
40 #include <libsysevent.h>
41 #include <sys/sysevent_impl.h>
43 #include <libnvpair.h>
44 #include <config_admin.h>
46 #include "disk_monitor.h"
47 #include "hotplug_mgr.h"
48 #include "schg_mgr.h"
49 #include "dm_platform.h"
51 typedef struct sysevent_event {
52 sysevent_t *evp;
53 } sysevent_event_t;
55 /* Lock guarantees the ordering of the incoming sysevents */
56 static pthread_t g_sysev_tid;
57 static pthread_mutex_t g_event_handler_lock = PTHREAD_MUTEX_INITIALIZER;
58 static pthread_cond_t g_event_handler_cond = PTHREAD_COND_INITIALIZER;
59 static qu_t *g_sysev_queue = NULL;
60 static thread_state_t g_sysev_thread_state = TS_NOT_RUNNING;
62 * The sysevent handle is bound to the main sysevent handler
63 * (event_handler), for each of the hotplug sysevents.
65 static sysevent_handle_t *sysevent_handle = NULL;
67 static void free_sysevent_event(void *p);
69 static int
70 nsleep(int seconds)
72 struct timespec tspec;
74 tspec.tv_sec = seconds;
75 tspec.tv_nsec = 0;
77 return (nanosleep(&tspec, NULL));
80 static int
81 config_list_ext_poll(int num, char * const *path,
82 cfga_list_data_t **list_array, int *nlist, int flag)
84 boolean_t done = B_FALSE;
85 boolean_t timedout = B_FALSE;
86 boolean_t interrupted = B_FALSE;
87 int timeout = 0;
88 int e;
89 #define TIMEOUT_MAX 60
91 do {
92 switch ((e = config_list_ext(num, path, list_array,
93 nlist, NULL, NULL, NULL, flag))) {
95 case CFGA_OK:
97 return (CFGA_OK);
99 case CFGA_BUSY:
100 case CFGA_SYSTEM_BUSY:
102 if (timeout++ >= TIMEOUT_MAX)
103 timedout = B_TRUE;
104 else {
105 if (nsleep(1) < 0)
106 interrupted = (errno == EINTR);
108 break;
110 default:
111 done = B_TRUE;
112 break;
115 } while (!done && !timedout && !interrupted);
117 return (e);
121 * Given a physical attachment point with a dynamic component
122 * (as in the case of SCSI APs), ensure the 'controller'
123 * portion of the dynamic component matches the physical portion.
124 * Argument 'adjusted' must point to a buffer of at least
125 * MAXPATHLEN bytes.
127 void
128 adjust_dynamic_ap(const char *apid, char *adjusted)
130 cfga_list_data_t *list_array = NULL;
131 int nlist;
132 char *ap_path[1];
133 char phys[MAXPATHLEN];
134 char dev_phys[MAXPATHLEN];
135 char *dyn;
136 int c, t, d;
138 dm_assert((strlen(apid) + 8 /* strlen("/devices") */) < MAXPATHLEN);
140 /* In the case of any error, return the unadjusted APID */
141 (void) strcpy(adjusted, apid);
143 /* if AP is not dynamic or not a disk node, no need to adjust it */
144 dyn = strstr(apid, "::");
145 if ((dyn == NULL) || (dyn == apid) ||
146 (sscanf(dyn, "::dsk/c%dt%dd%d", &c, &t, &d) != 3))
147 return;
150 * Copy the AP_ID and terminate it at the '::' that we know
151 * for a fact it contains. Pre-pend '/devices' for the sake
152 * of cfgadm_scsi, and get the cfgadm data for the controller.
154 (void) strcpy(phys, apid);
155 *strstr(phys, "::") = '\0';
156 (void) snprintf(dev_phys, MAXPATHLEN, "/devices%s", phys);
157 ap_path[0] = dev_phys;
159 if (config_list_ext_poll(1, ap_path, &list_array, &nlist, 0)
160 != CFGA_OK)
161 return;
163 dm_assert(nlist == 1);
165 if (sscanf(list_array[0].ap_log_id, "c%d", &c) == 1)
166 (void) snprintf(adjusted, MAXPATHLEN, "%s::dsk/c%dt%dd%d",
167 phys, c, t, d);
169 free(list_array);
172 static int
173 disk_ap_is_scsi(const char *ap_path)
175 return (strstr(ap_path, ":scsi:") != NULL);
179 * Looks up the attachment point's state and returns it in one of
180 * the hotplug states that the state change manager understands.
182 hotplug_state_t
183 disk_ap_state_to_hotplug_state(diskmon_t *diskp)
185 hotplug_state_t state = HPS_UNKNOWN;
186 cfga_list_data_t *list_array = NULL;
187 int rv, nlist;
188 char *app = (char *)dm_prop_lookup(diskp->app_props,
189 DISK_AP_PROP_APID);
190 char adj_app[MAXPATHLEN];
191 char *ap_path[1];
192 char *devices_app;
193 int len;
194 boolean_t list_valid = B_FALSE;
196 dm_assert(app != NULL);
198 adjust_dynamic_ap(app, adj_app);
199 ap_path[0] = adj_app;
200 devices_app = NULL;
202 rv = config_list_ext_poll(1, ap_path, &list_array, &nlist,
203 CFGA_FLAG_LIST_ALL);
205 if (rv != CFGA_OK) {
207 * The SATA and SCSI libcfgadm plugins add a
208 * /devices to the phys id; to use it, we must
209 * prepend this string before the call.
211 len = 8 /* strlen("/devices") */ + strlen(adj_app) + 1;
212 devices_app = dmalloc(len);
213 (void) snprintf(devices_app, len, "/devices%s",
214 adj_app);
215 ap_path[0] = devices_app;
217 rv = config_list_ext_poll(1, ap_path, &list_array, &nlist,
218 CFGA_FLAG_LIST_ALL);
222 * cfgadm_scsi will return an error for an absent target,
223 * so treat an error as "absent"; otherwise, make sure
224 * cfgadm_xxx has returned a list of 1 item
226 if (rv == CFGA_OK) {
227 dm_assert(nlist == 1);
228 list_valid = B_TRUE;
229 } else if (disk_ap_is_scsi(ap_path[0]))
230 state = HPS_ABSENT;
232 if (devices_app != NULL)
233 dfree(devices_app, len);
235 if (list_valid) {
237 * The following truth table defines how each state is
238 * computed:
240 * +----------------------------------------------+
241 * | | o_state | r_state | condition |
242 * | +---------+---------+-----------|
243 * | Absent |Don'tCare|Disc/Empt| Don'tCare |
244 * | Present |Unconfgrd|Connected| unknown |
245 * | Configured |Configred|Connected| Don'tCare |
246 * | Unconfigured |Unconfgrd|Connected| OK |
247 * +--------------+---------+---------+-----------+
250 if (list_array[0].ap_r_state == CFGA_STAT_EMPTY ||
251 list_array[0].ap_r_state == CFGA_STAT_DISCONNECTED)
252 state = HPS_ABSENT;
253 else if (list_array[0].ap_r_state == CFGA_STAT_CONNECTED &&
254 list_array[0].ap_o_state == CFGA_STAT_UNCONFIGURED &&
255 list_array[0].ap_cond == CFGA_COND_UNKNOWN)
256 state = HPS_PRESENT;
257 else if (list_array[0].ap_r_state == CFGA_STAT_CONNECTED &&
258 list_array[0].ap_o_state == CFGA_STAT_UNCONFIGURED &&
259 list_array[0].ap_cond != CFGA_COND_UNKNOWN)
260 state = HPS_UNCONFIGURED;
261 else if (list_array[0].ap_r_state == CFGA_STAT_CONNECTED &&
262 list_array[0].ap_o_state == CFGA_STAT_CONFIGURED)
263 state = HPS_CONFIGURED;
265 free(list_array);
268 return (state);
272 * Examine the sysevent passed in and returns the hotplug state that
273 * the sysevent states (or implies, in the case of attachment point
274 * events).
276 static hotplug_state_t
277 disk_sysev_to_state(diskmon_t *diskp, sysevent_t *evp)
279 const char *class_name, *subclass;
280 hotplug_state_t state = HPS_UNKNOWN;
281 sysevent_value_t se_val;
284 * The state mapping is as follows:
286 * Sysevent State
287 * --------------------------------------------------------
288 * EC_DEVFS/ESC_DEVFS_DEVI_ADD Configured
289 * EC_DEVFS/ESC_DEVFS_DEVI_REMOVE Unconfigured
290 * EC_DR/ESC_DR_AP_STATE_CHANGE *[Absent/Present]
292 * (The EC_DR event requires a probe of the attachment point
293 * to determine the AP's state if there is no usable HINT)
297 class_name = sysevent_get_class_name(evp);
298 subclass = sysevent_get_subclass_name(evp);
300 if (strcmp(class_name, EC_DEVFS) == 0) {
301 if (strcmp(subclass, ESC_DEVFS_DEVI_ADD) == 0) {
303 state = HPS_CONFIGURED;
305 } else if (strcmp(subclass, ESC_DEVFS_DEVI_REMOVE) == 0) {
307 state = HPS_UNCONFIGURED;
311 } else if (strcmp(class_name, EC_DR) == 0 &&
312 ((strcmp(subclass, ESC_DR_AP_STATE_CHANGE) == 0) ||
313 (strcmp(subclass, ESC_DR_TARGET_STATE_CHANGE) == 0))) {
315 if (sysevent_lookup_attr(evp, DR_HINT, SE_DATA_TYPE_STRING,
316 &se_val) == 0 && se_val.value.sv_string != NULL) {
318 if (strcmp(se_val.value.sv_string, DR_HINT_INSERT)
319 == 0) {
321 state = HPS_PRESENT;
323 } else if (strcmp(se_val.value.sv_string,
324 DR_HINT_REMOVE) == 0) {
326 state = HPS_ABSENT;
332 * If the state could not be determined by the hint
333 * (or there was no hint), ask the AP directly.
334 * SCSI HBAs may send an insertion sysevent
335 * *after* configuring the target node, so double-
336 * check HPS_PRESENT
338 if ((state == HPS_UNKNOWN) || (state = HPS_PRESENT))
339 state = disk_ap_state_to_hotplug_state(diskp);
342 return (state);
345 static void
346 disk_split_ap_path_sata(const char *ap_path, char *device, int *target)
348 char *p;
349 int n;
352 * /devices/rootnode/.../device:target
354 (void) strncpy(device, ap_path, MAXPATHLEN);
355 p = strrchr(device, ':');
356 dm_assert(p != NULL);
357 n = sscanf(p, ":%d", target);
358 dm_assert(n == 1);
359 *p = '\0';
362 static void
363 disk_split_ap_path_scsi(const char *ap_path, char *device, int *target)
365 char *p;
366 int n;
369 * /devices/rootnode/.../device:scsi::dsk/cXtXdX
372 (void) strncpy(device, ap_path, MAXPATHLEN);
373 p = strrchr(device, ':');
374 dm_assert(p != NULL);
376 n = sscanf(p, ":dsk/c%*dt%dd%*d", target);
377 dm_assert(n == 1);
379 *strchr(device, ':') = '\0';
382 static void
383 disk_split_ap_path(const char *ap_path, char *device, int *target)
386 * The AP path comes in two forms; for SATA devices,
387 * is is of the form:
388 * /devices/rootnode/.../device:portnum
389 * and for SCSI devices, it is of the form:
390 * /devices/rootnode/.../device:scsi::dsk/cXtXdX
393 if (disk_ap_is_scsi(ap_path))
394 disk_split_ap_path_scsi(ap_path, device, target);
395 else
396 disk_split_ap_path_sata(ap_path, device, target);
399 static void
400 disk_split_device_path(const char *dev_path, char *device, int *target)
402 char *t, *p, *e;
405 * The disk device path is of the form:
406 * /rootnode/.../device/target@tgtid,tgtlun
409 (void) strncpy(device, dev_path, MAXPATHLEN);
410 e = t = strrchr(device, '/');
411 dm_assert(t != NULL);
413 t = strchr(t, '@');
414 dm_assert(t != NULL);
415 t += 1;
417 if ((p = strchr(t, ',')) != NULL)
418 *p = '\0';
420 *target = strtol(t, 0, 16);
421 *e = '\0';
425 * Returns the diskmon that corresponds to the physical disk path
426 * passed in.
428 static diskmon_t *
429 disk_match_by_device_path(diskmon_t *disklistp, const char *dev_path)
431 char dev_device[MAXPATHLEN];
432 int dev_target;
433 char ap_device[MAXPATHLEN];
434 int ap_target;
436 dm_assert(disklistp != NULL);
437 dm_assert(dev_path != NULL);
439 if (strncmp(dev_path, DEVICES_PREFIX, 8) == 0)
440 dev_path += 8;
442 /* pare dev_path into device and target components */
443 disk_split_device_path(dev_path, (char *)&dev_device, &dev_target);
446 * The AP path specified in the configuration properties is
447 * the path to an attachment point minor node whose port number is
448 * equal to the target number on the disk "major" node sent by the
449 * sysevent. To match them, we need to extract the target id and
450 * construct an AP string to compare to the AP path in the diskmon.
452 while (disklistp != NULL) {
453 char *app = (char *)dm_prop_lookup(disklistp->app_props,
454 DISK_AP_PROP_APID);
455 dm_assert(app != NULL);
457 /* Not necessary to adjust the APID here */
458 if (strncmp(app, DEVICES_PREFIX, 8) == 0)
459 app += 8;
461 disk_split_ap_path(app, (char *)&ap_device, &ap_target);
463 if ((strcmp(dev_device, ap_device) == 0) &&
464 (dev_target == ap_target))
465 return (disklistp);
467 disklistp = disklistp->next;
469 return (NULL);
472 static diskmon_t *
473 disk_match_by_ap_id(diskmon_t *disklistp, const char *ap_id)
475 const char *disk_ap_id;
476 dm_assert(disklistp != NULL);
477 dm_assert(ap_id != NULL);
479 /* Match only the device-tree portion of the name */
480 if (strncmp(ap_id, DEVICES_PREFIX, 8 /* strlen("/devices") */) == 0)
481 ap_id += 8;
483 while (disklistp != NULL) {
484 disk_ap_id = dm_prop_lookup(disklistp->app_props,
485 DISK_AP_PROP_APID);
487 dm_assert(disk_ap_id != NULL);
489 if (strcmp(disk_ap_id, ap_id) == 0)
490 return (disklistp);
492 disklistp = disklistp->next;
494 return (NULL);
497 static diskmon_t *
498 disk_match_by_target_id(diskmon_t *disklistp, const char *target_path)
500 const char *disk_ap_id;
502 char match_device[MAXPATHLEN];
503 int match_target;
505 char ap_device[MAXPATHLEN];
506 int ap_target;
509 /* Match only the device-tree portion of the name */
510 if (strncmp(target_path, DEVICES_PREFIX, 8) == 0)
511 target_path += 8;
512 disk_split_ap_path(target_path, (char *)&match_device, &match_target);
514 while (disklistp != NULL) {
516 disk_ap_id = dm_prop_lookup(disklistp->app_props,
517 DISK_AP_PROP_APID);
518 dm_assert(disk_ap_id != NULL);
520 disk_split_ap_path(disk_ap_id, (char *)&ap_device, &ap_target);
521 if ((match_target == ap_target) &&
522 (strcmp(match_device, ap_device) == 0))
523 return (disklistp);
525 disklistp = disklistp->next;
527 return (NULL);
530 static diskmon_t *
531 match_sysevent_to_disk(diskmon_t *disklistp, sysevent_t *evp)
533 diskmon_t *dmp = NULL;
534 sysevent_value_t se_val;
535 char *class_name = sysevent_get_class_name(evp);
536 char *subclass = sysevent_get_subclass_name(evp);
538 se_val.value.sv_string = NULL;
540 if (strcmp(class_name, EC_DEVFS) == 0) {
541 /* EC_DEVFS-class events have a `DEVFS_PATHNAME' property */
542 if (sysevent_lookup_attr(evp, DEVFS_PATHNAME,
543 SE_DATA_TYPE_STRING, &se_val) == 0 &&
544 se_val.value.sv_string != NULL) {
546 dmp = disk_match_by_device_path(disklistp,
547 se_val.value.sv_string);
551 } else if (strcmp(class_name, EC_DR) == 0 &&
552 strcmp(subclass, ESC_DR_AP_STATE_CHANGE) == 0) {
554 /* EC_DR-class events have a `DR_AP_ID' property */
555 if (sysevent_lookup_attr(evp, DR_AP_ID, SE_DATA_TYPE_STRING,
556 &se_val) == 0 && se_val.value.sv_string != NULL) {
558 dmp = disk_match_by_ap_id(disklistp,
559 se_val.value.sv_string);
561 } else if (strcmp(class_name, EC_DR) == 0 &&
562 strcmp(subclass, ESC_DR_TARGET_STATE_CHANGE) == 0) {
563 /* get DR_TARGET_ID */
564 if (sysevent_lookup_attr(evp, DR_TARGET_ID,
565 SE_DATA_TYPE_STRING, &se_val) == 0 &&
566 se_val.value.sv_string != NULL) {
567 dmp = disk_match_by_target_id(disklistp,
568 se_val.value.sv_string);
572 if (se_val.value.sv_string)
573 log_msg(MM_HPMGR, "match_sysevent_to_disk: device/ap: %s\n",
574 se_val.value.sv_string);
576 return (dmp);
581 * The disk hotplug monitor (DHPM) listens for disk hotplug events and calls the
582 * state-change functionality when a disk's state changes. The DHPM listens for
583 * hotplug events via sysevent subscriptions to the following sysevent
584 * classes/subclasses: { EC_DEVFS/ESC_DEVFS_BRANCH_ADD,
585 * EC_DEVFS/ESC_DEVFS_BRANCH_REMOVE, EC_DEVFS/ESC_DEVFS_DEVI_ADD,
586 * EC_DEVFS/ESC_DEVFS_DEVI_REMOVE, EC_DR/ESC_DR_AP_STATE_CHANGE }. Once the
587 * event is received, the device path sent as part of the event is matched
588 * to one of the disks described by the configuration data structures.
590 static void
591 dm_process_sysevent(sysevent_t *dupev)
593 char *class_name;
594 char *pub;
595 char *subclass = sysevent_get_subclass_name(dupev);
596 diskmon_t *diskp;
598 class_name = sysevent_get_class_name(dupev);
599 log_msg(MM_HPMGR, "****EVENT: %s %s (by %s)\n", class_name,
600 subclass,
601 ((pub = sysevent_get_pub_name(dupev)) != NULL) ? pub : "UNKNOWN");
603 free(pub);
605 if (strcmp(class_name, EC_PLATFORM) == 0 &&
606 strcmp(subclass, ESC_PLATFORM_SP_RESET) == 0) {
607 if (dm_platform_resync() != 0)
608 log_warn("failed to resync SP platform\n");
609 sysevent_free(dupev);
610 return;
614 * We will handle this event if the event's target matches one of the
615 * disks we're monitoring
617 if ((diskp = match_sysevent_to_disk(config_data->disk_list, dupev))
618 != NULL) {
620 dm_state_change(diskp, disk_sysev_to_state(diskp, dupev));
623 sysevent_free(dupev);
626 static void
627 dm_fmd_sysevent_thread(void *queuep)
629 qu_t *qp = (qu_t *)queuep;
630 sysevent_event_t *sevevp;
632 /* Signal the thread spawner that we're running */
633 dm_assert(pthread_mutex_lock(&g_event_handler_lock) == 0);
634 if (g_sysev_thread_state != TS_EXIT_REQUESTED)
635 g_sysev_thread_state = TS_RUNNING;
636 (void) pthread_cond_broadcast(&g_event_handler_cond);
637 dm_assert(pthread_mutex_unlock(&g_event_handler_lock) == 0);
639 while (g_sysev_thread_state != TS_EXIT_REQUESTED) {
640 if ((sevevp = (sysevent_event_t *)queue_remove(qp)) == NULL)
641 continue;
643 dm_process_sysevent(sevevp->evp);
645 free_sysevent_event(sevevp);
648 /* Signal the thread spawner that we've exited */
649 dm_assert(pthread_mutex_lock(&g_event_handler_lock) == 0);
650 g_sysev_thread_state = TS_EXITED;
651 (void) pthread_cond_broadcast(&g_event_handler_cond);
652 dm_assert(pthread_mutex_unlock(&g_event_handler_lock) == 0);
654 log_msg(MM_HPMGR, "FMD sysevent handler thread exiting...");
657 static sysevent_event_t *
658 new_sysevent_event(sysevent_t *ev)
661 * Cannot use dmalloc for this because the thread isn't a FMD-created
662 * thread!
664 sysevent_event_t *sevevp = malloc(sizeof (sysevent_event_t));
665 sevevp->evp = ev;
666 return (sevevp);
669 static void
670 free_sysevent_event(void *p)
672 /* the sysevent_event was allocated with malloc(): */
673 free(p);
676 static void
677 event_handler(sysevent_t *ev)
679 /* The duplicated sysevent will be freed in the child thread */
680 sysevent_t *dupev = sysevent_dup(ev);
683 * Add this sysevent to the work queue of our FMA thread so we can
684 * handle the sysevent and use the FMA API (e.g. for memory
685 * allocation, etc.) in the sysevent handler.
687 queue_add(g_sysev_queue, new_sysevent_event(dupev));
690 static void
691 fini_sysevents(void)
693 sysevent_unsubscribe_event(sysevent_handle, EC_ALL);
696 static int
697 init_sysevents(void)
699 int rv = 0;
700 const char *devfs_subclasses[] = {
701 ESC_DEVFS_DEVI_ADD,
702 ESC_DEVFS_DEVI_REMOVE
704 const char *dr_subclasses[] = {
705 ESC_DR_AP_STATE_CHANGE,
706 ESC_DR_TARGET_STATE_CHANGE
708 const char *platform_subclasses[] = {
709 ESC_PLATFORM_SP_RESET
712 if ((sysevent_handle = sysevent_bind_handle(event_handler)) == NULL) {
713 rv = errno;
714 log_err("Could not initialize the hotplug manager ("
715 "sysevent_bind_handle failure");
718 if (sysevent_subscribe_event(sysevent_handle, EC_DEVFS,
719 devfs_subclasses,
720 sizeof (devfs_subclasses)/sizeof (devfs_subclasses[0])) != 0) {
722 log_err("Could not initialize the hotplug manager "
723 "sysevent_subscribe_event(event class = EC_DEVFS) "
724 "failure");
726 rv = -1;
728 } else if (sysevent_subscribe_event(sysevent_handle, EC_DR,
729 dr_subclasses,
730 sizeof (dr_subclasses)/sizeof (dr_subclasses[0])) != 0) {
732 log_err("Could not initialize the hotplug manager "
733 "sysevent_subscribe_event(event class = EC_DR) "
734 "failure");
736 /* Unsubscribe from all sysevents in the event of a failure */
737 fini_sysevents();
739 rv = -1;
740 } else if (sysevent_subscribe_event(sysevent_handle, EC_PLATFORM,
741 platform_subclasses,
742 sizeof (platform_subclasses)/sizeof (platform_subclasses[0]))
743 != 0) {
745 log_err("Could not initialize the hotplug manager "
746 "sysevent_subscribe_event(event class = EC_PLATFORM) "
747 "failure");
749 /* Unsubscribe from all sysevents in the event of a failure */
750 fini_sysevents();
752 rv = -1;
756 return (rv);
759 /*ARGSUSED*/
760 static void
761 stdfree(void *p, size_t sz)
763 free(p);
767 * Assumptions: Each disk's current state was determined and stored in
768 * its diskmon_t.
770 hotplug_mgr_init_err_t
771 init_hotplug_manager()
773 /* Create the queue to which we'll add sysevents */
774 g_sysev_queue = new_queue(B_TRUE, malloc, stdfree, free_sysevent_event);
777 * Grab the event handler lock before spawning the thread so we can
778 * wait for the thread to transition to the running state.
780 dm_assert(pthread_mutex_lock(&g_event_handler_lock) == 0);
782 /* Create the sysevent handling thread */
783 g_sysev_tid = fmd_thr_create(g_fm_hdl, dm_fmd_sysevent_thread,
784 g_sysev_queue);
786 /* Wait for the thread's acknowledgement */
787 while (g_sysev_thread_state != TS_RUNNING)
788 (void) pthread_cond_wait(&g_event_handler_cond,
789 &g_event_handler_lock);
790 dm_assert(pthread_mutex_unlock(&g_event_handler_lock) == 0);
792 if (init_sysevents() != 0) {
793 log_warn_e("Error initializing sysevents");
794 return (HPM_ERR_SYSEVENT_INIT);
797 return (0);
800 void
801 cleanup_hotplug_manager()
803 /* Unsubscribe from the sysevents */
804 fini_sysevents();
807 * Wait for the thread to exit before we can destroy
808 * the event queue.
810 dm_assert(pthread_mutex_lock(&g_event_handler_lock) == 0);
811 g_sysev_thread_state = TS_EXIT_REQUESTED;
812 queue_add(g_sysev_queue, NULL);
813 while (g_sysev_thread_state != TS_EXITED)
814 (void) pthread_cond_wait(&g_event_handler_cond,
815 &g_event_handler_lock);
816 dm_assert(pthread_mutex_unlock(&g_event_handler_lock) == 0);
817 (void) pthread_join(g_sysev_tid, NULL);
818 fmd_thr_destroy(g_fm_hdl, g_sysev_tid);
820 /* Finally, destroy the event queue and reset the thread state */
821 queue_free(&g_sysev_queue);
822 g_sysev_thread_state = TS_NOT_RUNNING;