14582Scth /*
24582Scth * CDDL HEADER START
34582Scth *
44582Scth * The contents of this file are subject to the terms of the
54582Scth * Common Development and Distribution License (the "License").
64582Scth * You may not use this file except in compliance with the License.
74582Scth *
84582Scth * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94582Scth * or http://www.opensolaris.org/os/licensing.
104582Scth * See the License for the specific language governing permissions
114582Scth * and limitations under the License.
124582Scth *
134582Scth * When distributing Covered Code, include this CDDL HEADER in each
144582Scth * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154582Scth * If applicable, add the following below this CDDL HEADER, with the
164582Scth * fields enclosed by brackets "[]" replaced with your own identifying
174582Scth * information: Portions Copyright [yyyy] [name of copyright owner]
184582Scth *
194582Scth * CDDL HEADER END
204582Scth */
214582Scth
224582Scth /*
23*8526SRobert.Johnston@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
244582Scth * Use is subject to license terms.
254582Scth */
264582Scth
274582Scth #include <sys/types.h>
284582Scth #include <sys/sysevent/dr.h>
294582Scth #include <sys/sysevent/eventdefs.h>
304582Scth #include <sys/sunddi.h> /* for the EC's for DEVFS */
314582Scth
324582Scth #include <errno.h>
334582Scth #include <string.h>
344582Scth #include <strings.h>
354582Scth #include <stdio.h>
364582Scth #include <unistd.h>
374582Scth #include <time.h>
384582Scth #include <pthread.h>
394582Scth
404582Scth #include <libsysevent.h>
414582Scth #include <sys/sysevent_impl.h>
424582Scth
434582Scth #include <libnvpair.h>
444582Scth #include <config_admin.h>
454582Scth
464582Scth #include "disk_monitor.h"
474582Scth #include "hotplug_mgr.h"
484582Scth #include "schg_mgr.h"
494582Scth #include "dm_platform.h"
504582Scth
514582Scth typedef struct sysevent_event {
524582Scth sysevent_t *evp;
534582Scth } sysevent_event_t;
544582Scth
554582Scth /* Lock guarantees the ordering of the incoming sysevents */
564582Scth static pthread_t g_sysev_tid;
574582Scth static pthread_mutex_t g_event_handler_lock = PTHREAD_MUTEX_INITIALIZER;
584582Scth static pthread_cond_t g_event_handler_cond = PTHREAD_COND_INITIALIZER;
594582Scth static qu_t *g_sysev_queue = NULL;
604582Scth static thread_state_t g_sysev_thread_state = TS_NOT_RUNNING;
614582Scth /*
624582Scth * The sysevent handle is bound to the main sysevent handler
634582Scth * (event_handler), for each of the hotplug sysevents.
644582Scth */
654582Scth static sysevent_handle_t *sysevent_handle = NULL;
664582Scth
674582Scth static void free_sysevent_event(void *p);
684582Scth
694582Scth static int
nsleep(int seconds)704582Scth nsleep(int seconds)
714582Scth {
724582Scth struct timespec tspec;
734582Scth
744582Scth tspec.tv_sec = seconds;
754582Scth tspec.tv_nsec = 0;
764582Scth
774582Scth return (nanosleep(&tspec, NULL));
784582Scth }
794582Scth
804582Scth static int
config_list_ext_poll(int num,char * const * path,cfga_list_data_t ** list_array,int * nlist,int flag)814582Scth config_list_ext_poll(int num, char * const *path,
825117Smyers cfga_list_data_t **list_array, int *nlist, int flag)
834582Scth {
844582Scth boolean_t done = B_FALSE;
854582Scth boolean_t timedout = B_FALSE;
864582Scth boolean_t interrupted = B_FALSE;
874582Scth int timeout = 0;
884582Scth int e;
894582Scth #define TIMEOUT_MAX 60
904582Scth
914582Scth do {
924582Scth switch ((e = config_list_ext(num, path, list_array,
935117Smyers nlist, NULL, NULL, NULL, flag))) {
944582Scth
954582Scth case CFGA_OK:
964582Scth
974582Scth return (CFGA_OK);
984582Scth
994582Scth case CFGA_BUSY:
1004582Scth case CFGA_SYSTEM_BUSY:
1014582Scth
1024582Scth if (timeout++ >= TIMEOUT_MAX)
1034582Scth timedout = B_TRUE;
1044582Scth else {
1054582Scth if (nsleep(1) < 0)
1064582Scth interrupted = (errno == EINTR);
1074582Scth }
1084582Scth break;
1094582Scth
1104582Scth default:
1114582Scth done = B_TRUE;
1124582Scth break;
1134582Scth
1144582Scth }
1154582Scth } while (!done && !timedout && !interrupted);
1164582Scth
1174582Scth return (e);
1184582Scth }
1194582Scth
1204582Scth /*
1215117Smyers * Given a physical attachment point with a dynamic component
1225117Smyers * (as in the case of SCSI APs), ensure the 'controller'
1235117Smyers * portion of the dynamic component matches the physical portion.
1245117Smyers * Argument 'adjusted' must point to a buffer of at least
1255117Smyers * MAXPATHLEN bytes.
1265117Smyers */
1275117Smyers void
adjust_dynamic_ap(const char * apid,char * adjusted)1285117Smyers adjust_dynamic_ap(const char *apid, char *adjusted)
1295117Smyers {
1305117Smyers cfga_list_data_t *list_array = NULL;
1315117Smyers int nlist;
1325117Smyers char *ap_path[1];
1335117Smyers char phys[MAXPATHLEN];
1345117Smyers char dev_phys[MAXPATHLEN];
1355117Smyers char *dyn;
1365117Smyers int c, t, d;
1375117Smyers
1385117Smyers dm_assert((strlen(apid) + 8 /* strlen("/devices") */) < MAXPATHLEN);
1395117Smyers
1405117Smyers /* In the case of any error, return the unadjusted APID */
1415117Smyers (void) strcpy(adjusted, apid);
1425117Smyers
1435117Smyers /* if AP is not dynamic or not a disk node, no need to adjust it */
1445117Smyers dyn = strstr(apid, "::");
1455117Smyers if ((dyn == NULL) || (dyn == apid) ||
1465117Smyers (sscanf(dyn, "::dsk/c%dt%dd%d", &c, &t, &d) != 3))
1475117Smyers return;
1485117Smyers
1495117Smyers /*
1505117Smyers * Copy the AP_ID and terminate it at the '::' that we know
1515117Smyers * for a fact it contains. Pre-pend '/devices' for the sake
1525117Smyers * of cfgadm_scsi, and get the cfgadm data for the controller.
1535117Smyers */
1545117Smyers (void) strcpy(phys, apid);
1555117Smyers *strstr(phys, "::") = '\0';
1565117Smyers (void) snprintf(dev_phys, MAXPATHLEN, "/devices%s", phys);
1575117Smyers ap_path[0] = dev_phys;
1585117Smyers
1595117Smyers if (config_list_ext_poll(1, ap_path, &list_array, &nlist, 0)
1605117Smyers != CFGA_OK)
1615117Smyers return;
1625117Smyers
1635117Smyers dm_assert(nlist == 1);
1645117Smyers
1655117Smyers if (sscanf(list_array[0].ap_log_id, "c%d", &c) == 1)
1665117Smyers (void) snprintf(adjusted, MAXPATHLEN, "%s::dsk/c%dt%dd%d",
1675117Smyers phys, c, t, d);
1685117Smyers
1695117Smyers free(list_array);
1705117Smyers }
1715117Smyers
1725117Smyers static int
disk_ap_is_scsi(const char * ap_path)1735117Smyers disk_ap_is_scsi(const char *ap_path)
1745117Smyers {
1755117Smyers return (strstr(ap_path, ":scsi:") != NULL);
1765117Smyers }
1775117Smyers
1785117Smyers /*
1794582Scth * Looks up the attachment point's state and returns it in one of
1804582Scth * the hotplug states that the state change manager understands.
1814582Scth */
1824582Scth hotplug_state_t
disk_ap_state_to_hotplug_state(diskmon_t * diskp)1834582Scth disk_ap_state_to_hotplug_state(diskmon_t *diskp)
1844582Scth {
1854582Scth hotplug_state_t state = HPS_UNKNOWN;
1864582Scth cfga_list_data_t *list_array = NULL;
1875117Smyers int rv, nlist;
1884582Scth char *app = (char *)dm_prop_lookup(diskp->app_props,
1894582Scth DISK_AP_PROP_APID);
1905117Smyers char adj_app[MAXPATHLEN];
1914582Scth char *ap_path[1];
1924582Scth char *devices_app;
1934582Scth int len;
1944582Scth boolean_t list_valid = B_FALSE;
1954582Scth
1964582Scth dm_assert(app != NULL);
1974582Scth
1985117Smyers adjust_dynamic_ap(app, adj_app);
1995117Smyers ap_path[0] = adj_app;
2005117Smyers devices_app = NULL;
2014582Scth
2025117Smyers rv = config_list_ext_poll(1, ap_path, &list_array, &nlist,
2035117Smyers CFGA_FLAG_LIST_ALL);
2044582Scth
2055117Smyers if (rv != CFGA_OK) {
2064582Scth /*
2075117Smyers * The SATA and SCSI libcfgadm plugins add a
2084582Scth * /devices to the phys id; to use it, we must
2094582Scth * prepend this string before the call.
2104582Scth */
2115117Smyers len = 8 /* strlen("/devices") */ + strlen(adj_app) + 1;
2124582Scth devices_app = dmalloc(len);
2134582Scth (void) snprintf(devices_app, len, "/devices%s",
2145117Smyers adj_app);
2154582Scth ap_path[0] = devices_app;
2164582Scth
2175117Smyers rv = config_list_ext_poll(1, ap_path, &list_array, &nlist,
2185117Smyers CFGA_FLAG_LIST_ALL);
2195117Smyers }
2204582Scth
2215117Smyers /*
2225117Smyers * cfgadm_scsi will return an error for an absent target,
2235117Smyers * so treat an error as "absent"; otherwise, make sure
2245117Smyers * cfgadm_xxx has returned a list of 1 item
2255117Smyers */
2265117Smyers if (rv == CFGA_OK) {
2275117Smyers dm_assert(nlist == 1);
2285117Smyers list_valid = B_TRUE;
2295117Smyers } else if (disk_ap_is_scsi(ap_path[0]))
2305117Smyers state = HPS_ABSENT;
2314582Scth
2325117Smyers if (devices_app != NULL)
2334582Scth dfree(devices_app, len);
2344582Scth
2354582Scth if (list_valid) {
2364582Scth /*
2374582Scth * The following truth table defines how each state is
2384582Scth * computed:
2394582Scth *
2404582Scth * +----------------------------------------------+
2414582Scth * | | o_state | r_state | condition |
2424582Scth * | +---------+---------+-----------|
2434582Scth * | Absent |Don'tCare|Disc/Empt| Don'tCare |
2444582Scth * | Present |Unconfgrd|Connected| unknown |
2454582Scth * | Configured |Configred|Connected| Don'tCare |
2464582Scth * | Unconfigured |Unconfgrd|Connected| OK |
2474582Scth * +--------------+---------+---------+-----------+
2484582Scth */
2494582Scth
2504582Scth if (list_array[0].ap_r_state == CFGA_STAT_EMPTY ||
2514582Scth list_array[0].ap_r_state == CFGA_STAT_DISCONNECTED)
2524582Scth state = HPS_ABSENT;
2534582Scth else if (list_array[0].ap_r_state == CFGA_STAT_CONNECTED &&
2544582Scth list_array[0].ap_o_state == CFGA_STAT_UNCONFIGURED &&
2554582Scth list_array[0].ap_cond == CFGA_COND_UNKNOWN)
2564582Scth state = HPS_PRESENT;
2574582Scth else if (list_array[0].ap_r_state == CFGA_STAT_CONNECTED &&
2584582Scth list_array[0].ap_o_state == CFGA_STAT_UNCONFIGURED &&
2594582Scth list_array[0].ap_cond != CFGA_COND_UNKNOWN)
2604582Scth state = HPS_UNCONFIGURED;
2614582Scth else if (list_array[0].ap_r_state == CFGA_STAT_CONNECTED &&
2624582Scth list_array[0].ap_o_state == CFGA_STAT_CONFIGURED)
2634582Scth state = HPS_CONFIGURED;
2644582Scth
2654582Scth free(list_array);
2664582Scth }
2674582Scth
2684582Scth return (state);
2694582Scth }
2704582Scth
2714582Scth /*
2724582Scth * Examine the sysevent passed in and returns the hotplug state that
2734582Scth * the sysevent states (or implies, in the case of attachment point
2744582Scth * events).
2754582Scth */
2764582Scth static hotplug_state_t
disk_sysev_to_state(diskmon_t * diskp,sysevent_t * evp)2774582Scth disk_sysev_to_state(diskmon_t *diskp, sysevent_t *evp)
2784582Scth {
2794582Scth const char *class_name, *subclass;
2804582Scth hotplug_state_t state = HPS_UNKNOWN;
2814582Scth sysevent_value_t se_val;
2824582Scth
2834582Scth /*
2844582Scth * The state mapping is as follows:
2854582Scth *
2864582Scth * Sysevent State
2874582Scth * --------------------------------------------------------
2884582Scth * EC_DEVFS/ESC_DEVFS_DEVI_ADD Configured
2894582Scth * EC_DEVFS/ESC_DEVFS_DEVI_REMOVE Unconfigured
2904582Scth * EC_DR/ESC_DR_AP_STATE_CHANGE *[Absent/Present]
2914582Scth *
2924582Scth * (The EC_DR event requires a probe of the attachment point
2934582Scth * to determine the AP's state if there is no usable HINT)
2944582Scth *
2954582Scth */
2964582Scth
2974582Scth class_name = sysevent_get_class_name(evp);
2984582Scth subclass = sysevent_get_subclass_name(evp);
2994582Scth
3004582Scth if (strcmp(class_name, EC_DEVFS) == 0) {
3014582Scth if (strcmp(subclass, ESC_DEVFS_DEVI_ADD) == 0) {
3024582Scth
3034582Scth state = HPS_CONFIGURED;
3044582Scth
3054582Scth } else if (strcmp(subclass, ESC_DEVFS_DEVI_REMOVE) == 0) {
3064582Scth
3074582Scth state = HPS_UNCONFIGURED;
3084582Scth
3094582Scth }
3104582Scth
3114582Scth } else if (strcmp(class_name, EC_DR) == 0 &&
3125117Smyers ((strcmp(subclass, ESC_DR_AP_STATE_CHANGE) == 0) ||
3135117Smyers (strcmp(subclass, ESC_DR_TARGET_STATE_CHANGE) == 0))) {
3144582Scth
3154582Scth if (sysevent_lookup_attr(evp, DR_HINT, SE_DATA_TYPE_STRING,
3164582Scth &se_val) == 0 && se_val.value.sv_string != NULL) {
3174582Scth
3184582Scth if (strcmp(se_val.value.sv_string, DR_HINT_INSERT)
3194582Scth == 0) {
3204582Scth
3214582Scth state = HPS_PRESENT;
3224582Scth
3234582Scth } else if (strcmp(se_val.value.sv_string,
3244582Scth DR_HINT_REMOVE) == 0) {
3254582Scth
3264582Scth state = HPS_ABSENT;
3274582Scth }
3284582Scth
3294582Scth }
3304582Scth
3314582Scth /*
3324582Scth * If the state could not be determined by the hint
3334582Scth * (or there was no hint), ask the AP directly.
3345117Smyers * SCSI HBAs may send an insertion sysevent
3355117Smyers * *after* configuring the target node, so double-
3365117Smyers * check HPS_PRESENT
3374582Scth */
3385117Smyers if ((state == HPS_UNKNOWN) || (state = HPS_PRESENT))
3394582Scth state = disk_ap_state_to_hotplug_state(diskp);
3404582Scth }
3414582Scth
3424582Scth return (state);
3434582Scth }
3444582Scth
3455117Smyers static void
disk_split_ap_path_sata(const char * ap_path,char * device,int * target)3465117Smyers disk_split_ap_path_sata(const char *ap_path, char *device, int *target)
3475117Smyers {
3485117Smyers char *p;
3495117Smyers int n;
3505117Smyers
3515117Smyers /*
3525117Smyers * /devices/rootnode/.../device:target
3535117Smyers */
3545117Smyers (void) strncpy(device, ap_path, MAXPATHLEN);
3555117Smyers p = strrchr(device, ':');
3565117Smyers dm_assert(p != NULL);
3575117Smyers n = sscanf(p, ":%d", target);
3585117Smyers dm_assert(n == 1);
3595117Smyers *p = '\0';
3605117Smyers }
3615117Smyers
3625117Smyers static void
disk_split_ap_path_scsi(const char * ap_path,char * device,int * target)3635117Smyers disk_split_ap_path_scsi(const char *ap_path, char *device, int *target)
3645117Smyers {
3655117Smyers char *p;
3665117Smyers int n;
3675117Smyers
3685117Smyers /*
3695117Smyers * /devices/rootnode/.../device:scsi::dsk/cXtXdX
3705117Smyers */
3715117Smyers
3725117Smyers (void) strncpy(device, ap_path, MAXPATHLEN);
3735117Smyers p = strrchr(device, ':');
3745117Smyers dm_assert(p != NULL);
3755117Smyers
3765117Smyers n = sscanf(p, ":dsk/c%*dt%dd%*d", target);
3775117Smyers dm_assert(n == 1);
3785117Smyers
3795117Smyers *strchr(device, ':') = '\0';
3805117Smyers }
3815117Smyers
3825117Smyers static void
disk_split_ap_path(const char * ap_path,char * device,int * target)3835117Smyers disk_split_ap_path(const char *ap_path, char *device, int *target)
3845117Smyers {
3855117Smyers /*
3865117Smyers * The AP path comes in two forms; for SATA devices,
3875117Smyers * is is of the form:
3885117Smyers * /devices/rootnode/.../device:portnum
3895117Smyers * and for SCSI devices, it is of the form:
3905117Smyers * /devices/rootnode/.../device:scsi::dsk/cXtXdX
3915117Smyers */
3925117Smyers
3935117Smyers if (disk_ap_is_scsi(ap_path))
3945117Smyers disk_split_ap_path_scsi(ap_path, device, target);
3955117Smyers else
3965117Smyers disk_split_ap_path_sata(ap_path, device, target);
3975117Smyers }
3985117Smyers
3995117Smyers static void
disk_split_device_path(const char * dev_path,char * device,int * target)4005117Smyers disk_split_device_path(const char *dev_path, char *device, int *target)
4015117Smyers {
4025117Smyers char *t, *p, *e;
4035117Smyers
4045117Smyers /*
4055117Smyers * The disk device path is of the form:
4065117Smyers * /rootnode/.../device/target@tgtid,tgtlun
4075117Smyers */
4085117Smyers
4095117Smyers (void) strncpy(device, dev_path, MAXPATHLEN);
4105117Smyers e = t = strrchr(device, '/');
4115117Smyers dm_assert(t != NULL);
4125117Smyers
4135117Smyers t = strchr(t, '@');
4145117Smyers dm_assert(t != NULL);
4155117Smyers t += 1;
4165117Smyers
4175117Smyers if ((p = strchr(t, ',')) != NULL)
4185117Smyers *p = '\0';
4195117Smyers
4205117Smyers *target = strtol(t, 0, 16);
4215117Smyers *e = '\0';
4225117Smyers }
4235117Smyers
4244582Scth /*
4254582Scth * Returns the diskmon that corresponds to the physical disk path
4264582Scth * passed in.
4274582Scth */
4284582Scth static diskmon_t *
disk_match_by_device_path(diskmon_t * disklistp,const char * dev_path)4294582Scth disk_match_by_device_path(diskmon_t *disklistp, const char *dev_path)
4304582Scth {
4315117Smyers char dev_device[MAXPATHLEN];
4325117Smyers int dev_target;
4335117Smyers char ap_device[MAXPATHLEN];
4345117Smyers int ap_target;
4355117Smyers
4364582Scth dm_assert(disklistp != NULL);
4374582Scth dm_assert(dev_path != NULL);
4384582Scth
4394582Scth if (strncmp(dev_path, DEVICES_PREFIX, 8) == 0)
4404582Scth dev_path += 8;
4414582Scth
4425117Smyers /* pare dev_path into device and target components */
4435117Smyers disk_split_device_path(dev_path, (char *)&dev_device, &dev_target);
4445117Smyers
4454582Scth /*
4464582Scth * The AP path specified in the configuration properties is
4474582Scth * the path to an attachment point minor node whose port number is
4484582Scth * equal to the target number on the disk "major" node sent by the
4494582Scth * sysevent. To match them, we need to extract the target id and
4504582Scth * construct an AP string to compare to the AP path in the diskmon.
4514582Scth */
4524582Scth while (disklistp != NULL) {
4534582Scth char *app = (char *)dm_prop_lookup(disklistp->app_props,
4544582Scth DISK_AP_PROP_APID);
4554582Scth dm_assert(app != NULL);
4564582Scth
4575117Smyers /* Not necessary to adjust the APID here */
4584582Scth if (strncmp(app, DEVICES_PREFIX, 8) == 0)
4594582Scth app += 8;
4604582Scth
4615117Smyers disk_split_ap_path(app, (char *)&ap_device, &ap_target);
4624582Scth
4635117Smyers if ((strcmp(dev_device, ap_device) == 0) &&
4645117Smyers (dev_target == ap_target))
4654582Scth return (disklistp);
4664582Scth
4674582Scth disklistp = disklistp->next;
4684582Scth }
4694582Scth return (NULL);
4704582Scth }
4714582Scth
4724582Scth static diskmon_t *
disk_match_by_ap_id(diskmon_t * disklistp,const char * ap_id)4734582Scth disk_match_by_ap_id(diskmon_t *disklistp, const char *ap_id)
4744582Scth {
4754582Scth const char *disk_ap_id;
4764582Scth dm_assert(disklistp != NULL);
4774582Scth dm_assert(ap_id != NULL);
4784582Scth
4794582Scth /* Match only the device-tree portion of the name */
4804582Scth if (strncmp(ap_id, DEVICES_PREFIX, 8 /* strlen("/devices") */) == 0)
4814582Scth ap_id += 8;
4824582Scth
4834582Scth while (disklistp != NULL) {
4844582Scth disk_ap_id = dm_prop_lookup(disklistp->app_props,
4854582Scth DISK_AP_PROP_APID);
4864582Scth
4874582Scth dm_assert(disk_ap_id != NULL);
4884582Scth
4894582Scth if (strcmp(disk_ap_id, ap_id) == 0)
4904582Scth return (disklistp);
4914582Scth
4924582Scth disklistp = disklistp->next;
4934582Scth }
4944582Scth return (NULL);
4954582Scth }
4964582Scth
4974582Scth static diskmon_t *
disk_match_by_target_id(diskmon_t * disklistp,const char * target_path)4985117Smyers disk_match_by_target_id(diskmon_t *disklistp, const char *target_path)
4995117Smyers {
5005117Smyers const char *disk_ap_id;
5015117Smyers
5025117Smyers char match_device[MAXPATHLEN];
5035117Smyers int match_target;
5045117Smyers
5055117Smyers char ap_device[MAXPATHLEN];
5065117Smyers int ap_target;
5075117Smyers
5085117Smyers
5095117Smyers /* Match only the device-tree portion of the name */
5105117Smyers if (strncmp(target_path, DEVICES_PREFIX, 8) == 0)
5115117Smyers target_path += 8;
5125117Smyers disk_split_ap_path(target_path, (char *)&match_device, &match_target);
5135117Smyers
5145117Smyers while (disklistp != NULL) {
5155117Smyers
5165117Smyers disk_ap_id = dm_prop_lookup(disklistp->app_props,
5175117Smyers DISK_AP_PROP_APID);
5185117Smyers dm_assert(disk_ap_id != NULL);
5195117Smyers
5205117Smyers disk_split_ap_path(disk_ap_id, (char *)&ap_device, &ap_target);
5215117Smyers if ((match_target == ap_target) &&
5225117Smyers (strcmp(match_device, ap_device) == 0))
5235117Smyers return (disklistp);
5245117Smyers
5255117Smyers disklistp = disklistp->next;
5265117Smyers }
5275117Smyers return (NULL);
5285117Smyers }
5295117Smyers
5305117Smyers static diskmon_t *
match_sysevent_to_disk(diskmon_t * disklistp,sysevent_t * evp)5314582Scth match_sysevent_to_disk(diskmon_t *disklistp, sysevent_t *evp)
5324582Scth {
5334582Scth diskmon_t *dmp = NULL;
5344582Scth sysevent_value_t se_val;
5354582Scth char *class_name = sysevent_get_class_name(evp);
5364582Scth char *subclass = sysevent_get_subclass_name(evp);
5374582Scth
5384582Scth se_val.value.sv_string = NULL;
5394582Scth
5404582Scth if (strcmp(class_name, EC_DEVFS) == 0) {
5414582Scth /* EC_DEVFS-class events have a `DEVFS_PATHNAME' property */
5424582Scth if (sysevent_lookup_attr(evp, DEVFS_PATHNAME,
5434582Scth SE_DATA_TYPE_STRING, &se_val) == 0 &&
5444582Scth se_val.value.sv_string != NULL) {
5454582Scth
5464582Scth dmp = disk_match_by_device_path(disklistp,
5474582Scth se_val.value.sv_string);
5484582Scth
5494582Scth }
5504582Scth
5514582Scth } else if (strcmp(class_name, EC_DR) == 0 &&
5524582Scth strcmp(subclass, ESC_DR_AP_STATE_CHANGE) == 0) {
5534582Scth
5544582Scth /* EC_DR-class events have a `DR_AP_ID' property */
5554582Scth if (sysevent_lookup_attr(evp, DR_AP_ID, SE_DATA_TYPE_STRING,
5564582Scth &se_val) == 0 && se_val.value.sv_string != NULL) {
5574582Scth
5584582Scth dmp = disk_match_by_ap_id(disklistp,
5594582Scth se_val.value.sv_string);
5604582Scth }
5615117Smyers } else if (strcmp(class_name, EC_DR) == 0 &&
5625117Smyers strcmp(subclass, ESC_DR_TARGET_STATE_CHANGE) == 0) {
5635117Smyers /* get DR_TARGET_ID */
5645117Smyers if (sysevent_lookup_attr(evp, DR_TARGET_ID,
5655117Smyers SE_DATA_TYPE_STRING, &se_val) == 0 &&
5665117Smyers se_val.value.sv_string != NULL) {
5675117Smyers dmp = disk_match_by_target_id(disklistp,
5685117Smyers se_val.value.sv_string);
5695117Smyers }
5704582Scth }
5714582Scth
5724582Scth if (se_val.value.sv_string)
5734582Scth log_msg(MM_HPMGR, "match_sysevent_to_disk: device/ap: %s\n",
5744582Scth se_val.value.sv_string);
5754582Scth
5764582Scth return (dmp);
5774582Scth }
5784582Scth
5794582Scth
5804582Scth /*
5814582Scth * The disk hotplug monitor (DHPM) listens for disk hotplug events and calls the
5824582Scth * state-change functionality when a disk's state changes. The DHPM listens for
5834582Scth * hotplug events via sysevent subscriptions to the following sysevent
5844582Scth * classes/subclasses: { EC_DEVFS/ESC_DEVFS_BRANCH_ADD,
5854582Scth * EC_DEVFS/ESC_DEVFS_BRANCH_REMOVE, EC_DEVFS/ESC_DEVFS_DEVI_ADD,
5864582Scth * EC_DEVFS/ESC_DEVFS_DEVI_REMOVE, EC_DR/ESC_DR_AP_STATE_CHANGE }. Once the
5874582Scth * event is received, the device path sent as part of the event is matched
5884582Scth * to one of the disks described by the configuration data structures.
5894582Scth */
5904582Scth static void
dm_process_sysevent(sysevent_t * dupev)5914582Scth dm_process_sysevent(sysevent_t *dupev)
5924582Scth {
5934582Scth char *class_name;
5944582Scth char *pub;
5954582Scth char *subclass = sysevent_get_subclass_name(dupev);
5964582Scth diskmon_t *diskp;
5974582Scth
5984582Scth class_name = sysevent_get_class_name(dupev);
5994582Scth log_msg(MM_HPMGR, "****EVENT: %s %s (by %s)\n", class_name,
6004582Scth subclass,
6014582Scth ((pub = sysevent_get_pub_name(dupev)) != NULL) ? pub : "UNKNOWN");
6024582Scth
6034582Scth if (pub)
6044582Scth free(pub);
6054582Scth
6064582Scth if (strcmp(class_name, EC_PLATFORM) == 0 &&
6074582Scth strcmp(subclass, ESC_PLATFORM_SP_RESET) == 0) {
6084582Scth if (dm_platform_resync() != 0)
6094582Scth log_warn("failed to resync SP platform\n");
610*8526SRobert.Johnston@Sun.COM sysevent_free(dupev);
6114582Scth return;
6124582Scth }
6134582Scth
6144582Scth /*
6154582Scth * We will handle this event if the event's target matches one of the
6164582Scth * disks we're monitoring
6174582Scth */
6184582Scth if ((diskp = match_sysevent_to_disk(config_data->disk_list, dupev))
6194582Scth != NULL) {
6204582Scth
6214582Scth dm_state_change(diskp, disk_sysev_to_state(diskp, dupev));
6224582Scth }
6234582Scth
6244582Scth sysevent_free(dupev);
6254582Scth }
6264582Scth
6274582Scth static void
dm_fmd_sysevent_thread(void * queuep)6284582Scth dm_fmd_sysevent_thread(void *queuep)
6294582Scth {
6304582Scth qu_t *qp = (qu_t *)queuep;
6314582Scth sysevent_event_t *sevevp;
6324582Scth
6334582Scth /* Signal the thread spawner that we're running */
6344582Scth dm_assert(pthread_mutex_lock(&g_event_handler_lock) == 0);
6354582Scth if (g_sysev_thread_state != TS_EXIT_REQUESTED)
6364582Scth g_sysev_thread_state = TS_RUNNING;
6374582Scth (void) pthread_cond_broadcast(&g_event_handler_cond);
6384582Scth dm_assert(pthread_mutex_unlock(&g_event_handler_lock) == 0);
6394582Scth
6404582Scth while (g_sysev_thread_state != TS_EXIT_REQUESTED) {
6414582Scth if ((sevevp = (sysevent_event_t *)queue_remove(qp)) == NULL)
6424582Scth continue;
6434582Scth
6444582Scth dm_process_sysevent(sevevp->evp);
6454582Scth
6464582Scth free_sysevent_event(sevevp);
6474582Scth }
6484582Scth
6494582Scth /* Signal the thread spawner that we've exited */
6504582Scth dm_assert(pthread_mutex_lock(&g_event_handler_lock) == 0);
6514582Scth g_sysev_thread_state = TS_EXITED;
6524582Scth (void) pthread_cond_broadcast(&g_event_handler_cond);
6534582Scth dm_assert(pthread_mutex_unlock(&g_event_handler_lock) == 0);
6544582Scth
6554582Scth log_msg(MM_HPMGR, "FMD sysevent handler thread exiting...");
6564582Scth }
6574582Scth
6584582Scth static sysevent_event_t *
new_sysevent_event(sysevent_t * ev)6594582Scth new_sysevent_event(sysevent_t *ev)
6604582Scth {
6614582Scth /*
6624582Scth * Cannot use dmalloc for this because the thread isn't a FMD-created
6634582Scth * thread!
6644582Scth */
6654582Scth sysevent_event_t *sevevp = malloc(sizeof (sysevent_event_t));
6664582Scth sevevp->evp = ev;
6674582Scth return (sevevp);
6684582Scth }
6694582Scth
6704582Scth static void
free_sysevent_event(void * p)6714582Scth free_sysevent_event(void *p)
6724582Scth {
6734582Scth /* the sysevent_event was allocated with malloc(): */
6744582Scth free(p);
6754582Scth }
6764582Scth
6774582Scth static void
event_handler(sysevent_t * ev)6784582Scth event_handler(sysevent_t *ev)
6794582Scth {
6804582Scth /* The duplicated sysevent will be freed in the child thread */
6814582Scth sysevent_t *dupev = sysevent_dup(ev);
6824582Scth
6834582Scth /*
6844582Scth * Add this sysevent to the work queue of our FMA thread so we can
6854582Scth * handle the sysevent and use the FMA API (e.g. for memory
6864582Scth * allocation, etc.) in the sysevent handler.
6874582Scth */
6884582Scth queue_add(g_sysev_queue, new_sysevent_event(dupev));
6894582Scth }
6904582Scth
6914582Scth static void
fini_sysevents(void)6924582Scth fini_sysevents(void)
6934582Scth {
6944582Scth sysevent_unsubscribe_event(sysevent_handle, EC_ALL);
6954582Scth }
6964582Scth
6974582Scth static int
init_sysevents(void)6984582Scth init_sysevents(void)
6994582Scth {
7004582Scth int rv = 0;
7014582Scth const char *devfs_subclasses[] = {
7024582Scth ESC_DEVFS_DEVI_ADD,
7034582Scth ESC_DEVFS_DEVI_REMOVE
7044582Scth };
7054582Scth const char *dr_subclasses[] = {
7065117Smyers ESC_DR_AP_STATE_CHANGE,
7075117Smyers ESC_DR_TARGET_STATE_CHANGE
7084582Scth };
7094582Scth const char *platform_subclasses[] = {
7104582Scth ESC_PLATFORM_SP_RESET
7114582Scth };
7124582Scth
7134582Scth if ((sysevent_handle = sysevent_bind_handle(event_handler)) == NULL) {
7144582Scth rv = errno;
7154582Scth log_err("Could not initialize the hotplug manager ("
7164582Scth "sysevent_bind_handle failure");
7174582Scth }
7184582Scth
7194582Scth if (sysevent_subscribe_event(sysevent_handle, EC_DEVFS,
7205117Smyers devfs_subclasses,
7215117Smyers sizeof (devfs_subclasses)/sizeof (devfs_subclasses[0])) != 0) {
7224582Scth
7234582Scth log_err("Could not initialize the hotplug manager "
7244582Scth "sysevent_subscribe_event(event class = EC_DEVFS) "
7254582Scth "failure");
7264582Scth
7274582Scth rv = -1;
7284582Scth
7294582Scth } else if (sysevent_subscribe_event(sysevent_handle, EC_DR,
7305117Smyers dr_subclasses,
7315117Smyers sizeof (dr_subclasses)/sizeof (dr_subclasses[0])) != 0) {
7324582Scth
7334582Scth log_err("Could not initialize the hotplug manager "
7344582Scth "sysevent_subscribe_event(event class = EC_DR) "
7354582Scth "failure");
7364582Scth
7374582Scth /* Unsubscribe from all sysevents in the event of a failure */
7384582Scth fini_sysevents();
7394582Scth
7404582Scth rv = -1;
7414582Scth } else if (sysevent_subscribe_event(sysevent_handle, EC_PLATFORM,
7425117Smyers platform_subclasses,
7435117Smyers sizeof (platform_subclasses)/sizeof (platform_subclasses[0]))
7445117Smyers != 0) {
7454582Scth
7464582Scth log_err("Could not initialize the hotplug manager "
7474582Scth "sysevent_subscribe_event(event class = EC_PLATFORM) "
7484582Scth "failure");
7494582Scth
7504582Scth /* Unsubscribe from all sysevents in the event of a failure */
7514582Scth fini_sysevents();
7524582Scth
7534582Scth rv = -1;
7544582Scth }
7554582Scth
7564582Scth
7574582Scth return (rv);
7584582Scth }
7594582Scth
7604582Scth /*ARGSUSED*/
7614582Scth static void
stdfree(void * p,size_t sz)7624582Scth stdfree(void *p, size_t sz)
7634582Scth {
7644582Scth free(p);
7654582Scth }
7664582Scth
7674582Scth /*
7684582Scth * Assumptions: Each disk's current state was determined and stored in
7694582Scth * its diskmon_t.
7704582Scth */
7714582Scth hotplug_mgr_init_err_t
init_hotplug_manager()7724582Scth init_hotplug_manager()
7734582Scth {
7744582Scth /* Create the queue to which we'll add sysevents */
7754582Scth g_sysev_queue = new_queue(B_TRUE, malloc, stdfree, free_sysevent_event);
7764582Scth
7774582Scth /*
7784582Scth * Grab the event handler lock before spawning the thread so we can
7794582Scth * wait for the thread to transition to the running state.
7804582Scth */
7814582Scth dm_assert(pthread_mutex_lock(&g_event_handler_lock) == 0);
7824582Scth
7834582Scth /* Create the sysevent handling thread */
7844582Scth g_sysev_tid = fmd_thr_create(g_fm_hdl, dm_fmd_sysevent_thread,
7854582Scth g_sysev_queue);
7864582Scth
7874582Scth /* Wait for the thread's acknowledgement */
7884582Scth while (g_sysev_thread_state != TS_RUNNING)
7894582Scth (void) pthread_cond_wait(&g_event_handler_cond,
7904582Scth &g_event_handler_lock);
7914582Scth dm_assert(pthread_mutex_unlock(&g_event_handler_lock) == 0);
7924582Scth
7934582Scth if (init_sysevents() != 0) {
7944582Scth log_warn_e("Error initializing sysevents");
7954582Scth return (HPM_ERR_SYSEVENT_INIT);
7964582Scth }
7974582Scth
7984582Scth return (0);
7994582Scth }
8004582Scth
8014582Scth void
cleanup_hotplug_manager()8024582Scth cleanup_hotplug_manager()
8034582Scth {
8044582Scth /* Unsubscribe from the sysevents */
8054582Scth fini_sysevents();
8064582Scth
8074582Scth /*
8084582Scth * Wait for the thread to exit before we can destroy
8094582Scth * the event queue.
8104582Scth */
8114582Scth dm_assert(pthread_mutex_lock(&g_event_handler_lock) == 0);
8124582Scth g_sysev_thread_state = TS_EXIT_REQUESTED;
8134582Scth queue_add(g_sysev_queue, NULL);
8144582Scth while (g_sysev_thread_state != TS_EXITED)
8154582Scth (void) pthread_cond_wait(&g_event_handler_cond,
8164582Scth &g_event_handler_lock);
8174582Scth dm_assert(pthread_mutex_unlock(&g_event_handler_lock) == 0);
8184582Scth (void) pthread_join(g_sysev_tid, NULL);
8194582Scth fmd_thr_destroy(g_fm_hdl, g_sysev_tid);
8204582Scth
8214582Scth /* Finally, destroy the event queue and reset the thread state */
8224582Scth queue_free(&g_sysev_queue);
8234582Scth g_sysev_thread_state = TS_NOT_RUNNING;
8244582Scth }
825