1 /* 2 * CDDL HEADER START 3 * 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. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/sysevent/dr.h> 31 #include <sys/sysevent/eventdefs.h> 32 #include <sys/sunddi.h> /* for the EC's for DEVFS */ 33 34 #include <errno.h> 35 #include <string.h> 36 #include <strings.h> 37 #include <stdio.h> 38 #include <unistd.h> 39 #include <time.h> 40 #include <pthread.h> 41 42 #include <libsysevent.h> 43 #include <sys/sysevent_impl.h> 44 45 #include <libnvpair.h> 46 #include <config_admin.h> 47 48 #include "disk_monitor.h" 49 #include "hotplug_mgr.h" 50 #include "schg_mgr.h" 51 #include "dm_platform.h" 52 53 typedef struct sysevent_event { 54 sysevent_t *evp; 55 } sysevent_event_t; 56 57 /* Lock guarantees the ordering of the incoming sysevents */ 58 static pthread_t g_sysev_tid; 59 static pthread_mutex_t g_event_handler_lock = PTHREAD_MUTEX_INITIALIZER; 60 static pthread_cond_t g_event_handler_cond = PTHREAD_COND_INITIALIZER; 61 static qu_t *g_sysev_queue = NULL; 62 static thread_state_t g_sysev_thread_state = TS_NOT_RUNNING; 63 /* 64 * The sysevent handle is bound to the main sysevent handler 65 * (event_handler), for each of the hotplug sysevents. 66 */ 67 static sysevent_handle_t *sysevent_handle = NULL; 68 69 static void free_sysevent_event(void *p); 70 71 static int 72 nsleep(int seconds) 73 { 74 struct timespec tspec; 75 76 tspec.tv_sec = seconds; 77 tspec.tv_nsec = 0; 78 79 return (nanosleep(&tspec, NULL)); 80 } 81 82 static int 83 config_list_ext_poll(int num, char * const *path, 84 cfga_list_data_t **list_array, int *nlist, int flag) 85 { 86 boolean_t done = B_FALSE; 87 boolean_t timedout = B_FALSE; 88 boolean_t interrupted = B_FALSE; 89 int timeout = 0; 90 int e; 91 #define TIMEOUT_MAX 60 92 93 do { 94 switch ((e = config_list_ext(num, path, list_array, 95 nlist, NULL, NULL, NULL, flag))) { 96 97 case CFGA_OK: 98 99 return (CFGA_OK); 100 101 case CFGA_BUSY: 102 case CFGA_SYSTEM_BUSY: 103 104 if (timeout++ >= TIMEOUT_MAX) 105 timedout = B_TRUE; 106 else { 107 if (nsleep(1) < 0) 108 interrupted = (errno == EINTR); 109 } 110 break; 111 112 default: 113 done = B_TRUE; 114 break; 115 116 } 117 } while (!done && !timedout && !interrupted); 118 119 return (e); 120 } 121 122 /* 123 * Given a physical attachment point with a dynamic component 124 * (as in the case of SCSI APs), ensure the 'controller' 125 * portion of the dynamic component matches the physical portion. 126 * Argument 'adjusted' must point to a buffer of at least 127 * MAXPATHLEN bytes. 128 */ 129 void 130 adjust_dynamic_ap(const char *apid, char *adjusted) 131 { 132 cfga_list_data_t *list_array = NULL; 133 int nlist; 134 char *ap_path[1]; 135 char phys[MAXPATHLEN]; 136 char dev_phys[MAXPATHLEN]; 137 char *dyn; 138 int c, t, d; 139 140 dm_assert((strlen(apid) + 8 /* strlen("/devices") */) < MAXPATHLEN); 141 142 /* In the case of any error, return the unadjusted APID */ 143 (void) strcpy(adjusted, apid); 144 145 /* if AP is not dynamic or not a disk node, no need to adjust it */ 146 dyn = strstr(apid, "::"); 147 if ((dyn == NULL) || (dyn == apid) || 148 (sscanf(dyn, "::dsk/c%dt%dd%d", &c, &t, &d) != 3)) 149 return; 150 151 /* 152 * Copy the AP_ID and terminate it at the '::' that we know 153 * for a fact it contains. Pre-pend '/devices' for the sake 154 * of cfgadm_scsi, and get the cfgadm data for the controller. 155 */ 156 (void) strcpy(phys, apid); 157 *strstr(phys, "::") = '\0'; 158 (void) snprintf(dev_phys, MAXPATHLEN, "/devices%s", phys); 159 ap_path[0] = dev_phys; 160 161 if (config_list_ext_poll(1, ap_path, &list_array, &nlist, 0) 162 != CFGA_OK) 163 return; 164 165 dm_assert(nlist == 1); 166 167 if (sscanf(list_array[0].ap_log_id, "c%d", &c) == 1) 168 (void) snprintf(adjusted, MAXPATHLEN, "%s::dsk/c%dt%dd%d", 169 phys, c, t, d); 170 171 free(list_array); 172 } 173 174 static int 175 disk_ap_is_scsi(const char *ap_path) 176 { 177 return (strstr(ap_path, ":scsi:") != NULL); 178 } 179 180 /* 181 * Looks up the attachment point's state and returns it in one of 182 * the hotplug states that the state change manager understands. 183 */ 184 hotplug_state_t 185 disk_ap_state_to_hotplug_state(diskmon_t *diskp) 186 { 187 hotplug_state_t state = HPS_UNKNOWN; 188 cfga_list_data_t *list_array = NULL; 189 int rv, nlist; 190 char *app = (char *)dm_prop_lookup(diskp->app_props, 191 DISK_AP_PROP_APID); 192 char adj_app[MAXPATHLEN]; 193 char *ap_path[1]; 194 char *devices_app; 195 int len; 196 boolean_t list_valid = B_FALSE; 197 198 dm_assert(app != NULL); 199 200 adjust_dynamic_ap(app, adj_app); 201 ap_path[0] = adj_app; 202 devices_app = NULL; 203 204 rv = config_list_ext_poll(1, ap_path, &list_array, &nlist, 205 CFGA_FLAG_LIST_ALL); 206 207 if (rv != CFGA_OK) { 208 /* 209 * The SATA and SCSI libcfgadm plugins add a 210 * /devices to the phys id; to use it, we must 211 * prepend this string before the call. 212 */ 213 len = 8 /* strlen("/devices") */ + strlen(adj_app) + 1; 214 devices_app = dmalloc(len); 215 (void) snprintf(devices_app, len, "/devices%s", 216 adj_app); 217 ap_path[0] = devices_app; 218 219 rv = config_list_ext_poll(1, ap_path, &list_array, &nlist, 220 CFGA_FLAG_LIST_ALL); 221 } 222 223 /* 224 * cfgadm_scsi will return an error for an absent target, 225 * so treat an error as "absent"; otherwise, make sure 226 * cfgadm_xxx has returned a list of 1 item 227 */ 228 if (rv == CFGA_OK) { 229 dm_assert(nlist == 1); 230 list_valid = B_TRUE; 231 } else if (disk_ap_is_scsi(ap_path[0])) 232 state = HPS_ABSENT; 233 234 if (devices_app != NULL) 235 dfree(devices_app, len); 236 237 if (list_valid) { 238 /* 239 * The following truth table defines how each state is 240 * computed: 241 * 242 * +----------------------------------------------+ 243 * | | o_state | r_state | condition | 244 * | +---------+---------+-----------| 245 * | Absent |Don'tCare|Disc/Empt| Don'tCare | 246 * | Present |Unconfgrd|Connected| unknown | 247 * | Configured |Configred|Connected| Don'tCare | 248 * | Unconfigured |Unconfgrd|Connected| OK | 249 * +--------------+---------+---------+-----------+ 250 */ 251 252 if (list_array[0].ap_r_state == CFGA_STAT_EMPTY || 253 list_array[0].ap_r_state == CFGA_STAT_DISCONNECTED) 254 state = HPS_ABSENT; 255 else if (list_array[0].ap_r_state == CFGA_STAT_CONNECTED && 256 list_array[0].ap_o_state == CFGA_STAT_UNCONFIGURED && 257 list_array[0].ap_cond == CFGA_COND_UNKNOWN) 258 state = HPS_PRESENT; 259 else if (list_array[0].ap_r_state == CFGA_STAT_CONNECTED && 260 list_array[0].ap_o_state == CFGA_STAT_UNCONFIGURED && 261 list_array[0].ap_cond != CFGA_COND_UNKNOWN) 262 state = HPS_UNCONFIGURED; 263 else if (list_array[0].ap_r_state == CFGA_STAT_CONNECTED && 264 list_array[0].ap_o_state == CFGA_STAT_CONFIGURED) 265 state = HPS_CONFIGURED; 266 267 free(list_array); 268 } 269 270 return (state); 271 } 272 273 /* 274 * Examine the sysevent passed in and returns the hotplug state that 275 * the sysevent states (or implies, in the case of attachment point 276 * events). 277 */ 278 static hotplug_state_t 279 disk_sysev_to_state(diskmon_t *diskp, sysevent_t *evp) 280 { 281 const char *class_name, *subclass; 282 hotplug_state_t state = HPS_UNKNOWN; 283 sysevent_value_t se_val; 284 285 /* 286 * The state mapping is as follows: 287 * 288 * Sysevent State 289 * -------------------------------------------------------- 290 * EC_DEVFS/ESC_DEVFS_DEVI_ADD Configured 291 * EC_DEVFS/ESC_DEVFS_DEVI_REMOVE Unconfigured 292 * EC_DR/ESC_DR_AP_STATE_CHANGE *[Absent/Present] 293 * 294 * (The EC_DR event requires a probe of the attachment point 295 * to determine the AP's state if there is no usable HINT) 296 * 297 */ 298 299 class_name = sysevent_get_class_name(evp); 300 subclass = sysevent_get_subclass_name(evp); 301 302 if (strcmp(class_name, EC_DEVFS) == 0) { 303 if (strcmp(subclass, ESC_DEVFS_DEVI_ADD) == 0) { 304 305 state = HPS_CONFIGURED; 306 307 } else if (strcmp(subclass, ESC_DEVFS_DEVI_REMOVE) == 0) { 308 309 state = HPS_UNCONFIGURED; 310 311 } 312 313 } else if (strcmp(class_name, EC_DR) == 0 && 314 ((strcmp(subclass, ESC_DR_AP_STATE_CHANGE) == 0) || 315 (strcmp(subclass, ESC_DR_TARGET_STATE_CHANGE) == 0))) { 316 317 if (sysevent_lookup_attr(evp, DR_HINT, SE_DATA_TYPE_STRING, 318 &se_val) == 0 && se_val.value.sv_string != NULL) { 319 320 if (strcmp(se_val.value.sv_string, DR_HINT_INSERT) 321 == 0) { 322 323 state = HPS_PRESENT; 324 325 } else if (strcmp(se_val.value.sv_string, 326 DR_HINT_REMOVE) == 0) { 327 328 state = HPS_ABSENT; 329 } 330 331 } 332 333 /* 334 * If the state could not be determined by the hint 335 * (or there was no hint), ask the AP directly. 336 * SCSI HBAs may send an insertion sysevent 337 * *after* configuring the target node, so double- 338 * check HPS_PRESENT 339 */ 340 if ((state == HPS_UNKNOWN) || (state = HPS_PRESENT)) 341 state = disk_ap_state_to_hotplug_state(diskp); 342 } 343 344 return (state); 345 } 346 347 static void 348 disk_split_ap_path_sata(const char *ap_path, char *device, int *target) 349 { 350 char *p; 351 int n; 352 353 /* 354 * /devices/rootnode/.../device:target 355 */ 356 (void) strncpy(device, ap_path, MAXPATHLEN); 357 p = strrchr(device, ':'); 358 dm_assert(p != NULL); 359 n = sscanf(p, ":%d", target); 360 dm_assert(n == 1); 361 *p = '\0'; 362 } 363 364 static void 365 disk_split_ap_path_scsi(const char *ap_path, char *device, int *target) 366 { 367 char *p; 368 int n; 369 370 /* 371 * /devices/rootnode/.../device:scsi::dsk/cXtXdX 372 */ 373 374 (void) strncpy(device, ap_path, MAXPATHLEN); 375 p = strrchr(device, ':'); 376 dm_assert(p != NULL); 377 378 n = sscanf(p, ":dsk/c%*dt%dd%*d", target); 379 dm_assert(n == 1); 380 381 *strchr(device, ':') = '\0'; 382 } 383 384 static void 385 disk_split_ap_path(const char *ap_path, char *device, int *target) 386 { 387 /* 388 * The AP path comes in two forms; for SATA devices, 389 * is is of the form: 390 * /devices/rootnode/.../device:portnum 391 * and for SCSI devices, it is of the form: 392 * /devices/rootnode/.../device:scsi::dsk/cXtXdX 393 */ 394 395 if (disk_ap_is_scsi(ap_path)) 396 disk_split_ap_path_scsi(ap_path, device, target); 397 else 398 disk_split_ap_path_sata(ap_path, device, target); 399 } 400 401 static void 402 disk_split_device_path(const char *dev_path, char *device, int *target) 403 { 404 char *t, *p, *e; 405 406 /* 407 * The disk device path is of the form: 408 * /rootnode/.../device/target@tgtid,tgtlun 409 */ 410 411 (void) strncpy(device, dev_path, MAXPATHLEN); 412 e = t = strrchr(device, '/'); 413 dm_assert(t != NULL); 414 415 t = strchr(t, '@'); 416 dm_assert(t != NULL); 417 t += 1; 418 419 if ((p = strchr(t, ',')) != NULL) 420 *p = '\0'; 421 422 *target = strtol(t, 0, 16); 423 *e = '\0'; 424 } 425 426 /* 427 * Returns the diskmon that corresponds to the physical disk path 428 * passed in. 429 */ 430 static diskmon_t * 431 disk_match_by_device_path(diskmon_t *disklistp, const char *dev_path) 432 { 433 char dev_device[MAXPATHLEN]; 434 int dev_target; 435 char ap_device[MAXPATHLEN]; 436 int ap_target; 437 438 dm_assert(disklistp != NULL); 439 dm_assert(dev_path != NULL); 440 441 if (strncmp(dev_path, DEVICES_PREFIX, 8) == 0) 442 dev_path += 8; 443 444 /* pare dev_path into device and target components */ 445 disk_split_device_path(dev_path, (char *)&dev_device, &dev_target); 446 447 /* 448 * The AP path specified in the configuration properties is 449 * the path to an attachment point minor node whose port number is 450 * equal to the target number on the disk "major" node sent by the 451 * sysevent. To match them, we need to extract the target id and 452 * construct an AP string to compare to the AP path in the diskmon. 453 */ 454 while (disklistp != NULL) { 455 char *app = (char *)dm_prop_lookup(disklistp->app_props, 456 DISK_AP_PROP_APID); 457 dm_assert(app != NULL); 458 459 /* Not necessary to adjust the APID here */ 460 if (strncmp(app, DEVICES_PREFIX, 8) == 0) 461 app += 8; 462 463 disk_split_ap_path(app, (char *)&ap_device, &ap_target); 464 465 if ((strcmp(dev_device, ap_device) == 0) && 466 (dev_target == ap_target)) 467 return (disklistp); 468 469 disklistp = disklistp->next; 470 } 471 return (NULL); 472 } 473 474 static diskmon_t * 475 disk_match_by_ap_id(diskmon_t *disklistp, const char *ap_id) 476 { 477 const char *disk_ap_id; 478 dm_assert(disklistp != NULL); 479 dm_assert(ap_id != NULL); 480 481 /* Match only the device-tree portion of the name */ 482 if (strncmp(ap_id, DEVICES_PREFIX, 8 /* strlen("/devices") */) == 0) 483 ap_id += 8; 484 485 while (disklistp != NULL) { 486 disk_ap_id = dm_prop_lookup(disklistp->app_props, 487 DISK_AP_PROP_APID); 488 489 dm_assert(disk_ap_id != NULL); 490 491 if (strcmp(disk_ap_id, ap_id) == 0) 492 return (disklistp); 493 494 disklistp = disklistp->next; 495 } 496 return (NULL); 497 } 498 499 static diskmon_t * 500 disk_match_by_target_id(diskmon_t *disklistp, const char *target_path) 501 { 502 const char *disk_ap_id; 503 504 char match_device[MAXPATHLEN]; 505 int match_target; 506 507 char ap_device[MAXPATHLEN]; 508 int ap_target; 509 510 511 /* Match only the device-tree portion of the name */ 512 if (strncmp(target_path, DEVICES_PREFIX, 8) == 0) 513 target_path += 8; 514 disk_split_ap_path(target_path, (char *)&match_device, &match_target); 515 516 while (disklistp != NULL) { 517 518 disk_ap_id = dm_prop_lookup(disklistp->app_props, 519 DISK_AP_PROP_APID); 520 dm_assert(disk_ap_id != NULL); 521 522 disk_split_ap_path(disk_ap_id, (char *)&ap_device, &ap_target); 523 if ((match_target == ap_target) && 524 (strcmp(match_device, ap_device) == 0)) 525 return (disklistp); 526 527 disklistp = disklistp->next; 528 } 529 return (NULL); 530 } 531 532 static diskmon_t * 533 match_sysevent_to_disk(diskmon_t *disklistp, sysevent_t *evp) 534 { 535 diskmon_t *dmp = NULL; 536 sysevent_value_t se_val; 537 char *class_name = sysevent_get_class_name(evp); 538 char *subclass = sysevent_get_subclass_name(evp); 539 540 se_val.value.sv_string = NULL; 541 542 if (strcmp(class_name, EC_DEVFS) == 0) { 543 /* EC_DEVFS-class events have a `DEVFS_PATHNAME' property */ 544 if (sysevent_lookup_attr(evp, DEVFS_PATHNAME, 545 SE_DATA_TYPE_STRING, &se_val) == 0 && 546 se_val.value.sv_string != NULL) { 547 548 dmp = disk_match_by_device_path(disklistp, 549 se_val.value.sv_string); 550 551 } 552 553 } else if (strcmp(class_name, EC_DR) == 0 && 554 strcmp(subclass, ESC_DR_AP_STATE_CHANGE) == 0) { 555 556 /* EC_DR-class events have a `DR_AP_ID' property */ 557 if (sysevent_lookup_attr(evp, DR_AP_ID, SE_DATA_TYPE_STRING, 558 &se_val) == 0 && se_val.value.sv_string != NULL) { 559 560 dmp = disk_match_by_ap_id(disklistp, 561 se_val.value.sv_string); 562 } 563 } else if (strcmp(class_name, EC_DR) == 0 && 564 strcmp(subclass, ESC_DR_TARGET_STATE_CHANGE) == 0) { 565 /* get DR_TARGET_ID */ 566 if (sysevent_lookup_attr(evp, DR_TARGET_ID, 567 SE_DATA_TYPE_STRING, &se_val) == 0 && 568 se_val.value.sv_string != NULL) { 569 dmp = disk_match_by_target_id(disklistp, 570 se_val.value.sv_string); 571 } 572 } 573 574 if (se_val.value.sv_string) 575 log_msg(MM_HPMGR, "match_sysevent_to_disk: device/ap: %s\n", 576 se_val.value.sv_string); 577 578 return (dmp); 579 } 580 581 582 /* 583 * The disk hotplug monitor (DHPM) listens for disk hotplug events and calls the 584 * state-change functionality when a disk's state changes. The DHPM listens for 585 * hotplug events via sysevent subscriptions to the following sysevent 586 * classes/subclasses: { EC_DEVFS/ESC_DEVFS_BRANCH_ADD, 587 * EC_DEVFS/ESC_DEVFS_BRANCH_REMOVE, EC_DEVFS/ESC_DEVFS_DEVI_ADD, 588 * EC_DEVFS/ESC_DEVFS_DEVI_REMOVE, EC_DR/ESC_DR_AP_STATE_CHANGE }. Once the 589 * event is received, the device path sent as part of the event is matched 590 * to one of the disks described by the configuration data structures. 591 */ 592 static void 593 dm_process_sysevent(sysevent_t *dupev) 594 { 595 char *class_name; 596 char *pub; 597 char *subclass = sysevent_get_subclass_name(dupev); 598 diskmon_t *diskp; 599 600 class_name = sysevent_get_class_name(dupev); 601 log_msg(MM_HPMGR, "****EVENT: %s %s (by %s)\n", class_name, 602 subclass, 603 ((pub = sysevent_get_pub_name(dupev)) != NULL) ? pub : "UNKNOWN"); 604 605 if (pub) 606 free(pub); 607 608 if (strcmp(class_name, EC_PLATFORM) == 0 && 609 strcmp(subclass, ESC_PLATFORM_SP_RESET) == 0) { 610 if (dm_platform_resync() != 0) 611 log_warn("failed to resync SP platform\n"); 612 return; 613 } 614 615 /* 616 * We will handle this event if the event's target matches one of the 617 * disks we're monitoring 618 */ 619 if ((diskp = match_sysevent_to_disk(config_data->disk_list, dupev)) 620 != NULL) { 621 622 dm_state_change(diskp, disk_sysev_to_state(diskp, dupev)); 623 } 624 625 sysevent_free(dupev); 626 } 627 628 static void 629 dm_fmd_sysevent_thread(void *queuep) 630 { 631 qu_t *qp = (qu_t *)queuep; 632 sysevent_event_t *sevevp; 633 634 /* Signal the thread spawner that we're running */ 635 dm_assert(pthread_mutex_lock(&g_event_handler_lock) == 0); 636 if (g_sysev_thread_state != TS_EXIT_REQUESTED) 637 g_sysev_thread_state = TS_RUNNING; 638 (void) pthread_cond_broadcast(&g_event_handler_cond); 639 dm_assert(pthread_mutex_unlock(&g_event_handler_lock) == 0); 640 641 while (g_sysev_thread_state != TS_EXIT_REQUESTED) { 642 if ((sevevp = (sysevent_event_t *)queue_remove(qp)) == NULL) 643 continue; 644 645 dm_process_sysevent(sevevp->evp); 646 647 free_sysevent_event(sevevp); 648 } 649 650 /* Signal the thread spawner that we've exited */ 651 dm_assert(pthread_mutex_lock(&g_event_handler_lock) == 0); 652 g_sysev_thread_state = TS_EXITED; 653 (void) pthread_cond_broadcast(&g_event_handler_cond); 654 dm_assert(pthread_mutex_unlock(&g_event_handler_lock) == 0); 655 656 log_msg(MM_HPMGR, "FMD sysevent handler thread exiting..."); 657 } 658 659 static sysevent_event_t * 660 new_sysevent_event(sysevent_t *ev) 661 { 662 /* 663 * Cannot use dmalloc for this because the thread isn't a FMD-created 664 * thread! 665 */ 666 sysevent_event_t *sevevp = malloc(sizeof (sysevent_event_t)); 667 sevevp->evp = ev; 668 return (sevevp); 669 } 670 671 static void 672 free_sysevent_event(void *p) 673 { 674 /* the sysevent_event was allocated with malloc(): */ 675 free(p); 676 } 677 678 static void 679 event_handler(sysevent_t *ev) 680 { 681 /* The duplicated sysevent will be freed in the child thread */ 682 sysevent_t *dupev = sysevent_dup(ev); 683 684 /* 685 * Add this sysevent to the work queue of our FMA thread so we can 686 * handle the sysevent and use the FMA API (e.g. for memory 687 * allocation, etc.) in the sysevent handler. 688 */ 689 queue_add(g_sysev_queue, new_sysevent_event(dupev)); 690 } 691 692 static void 693 fini_sysevents(void) 694 { 695 sysevent_unsubscribe_event(sysevent_handle, EC_ALL); 696 } 697 698 static int 699 init_sysevents(void) 700 { 701 int rv = 0; 702 const char *devfs_subclasses[] = { 703 ESC_DEVFS_DEVI_ADD, 704 ESC_DEVFS_DEVI_REMOVE 705 }; 706 const char *dr_subclasses[] = { 707 ESC_DR_AP_STATE_CHANGE, 708 ESC_DR_TARGET_STATE_CHANGE 709 }; 710 const char *platform_subclasses[] = { 711 ESC_PLATFORM_SP_RESET 712 }; 713 714 if ((sysevent_handle = sysevent_bind_handle(event_handler)) == NULL) { 715 rv = errno; 716 log_err("Could not initialize the hotplug manager (" 717 "sysevent_bind_handle failure"); 718 } 719 720 if (sysevent_subscribe_event(sysevent_handle, EC_DEVFS, 721 devfs_subclasses, 722 sizeof (devfs_subclasses)/sizeof (devfs_subclasses[0])) != 0) { 723 724 log_err("Could not initialize the hotplug manager " 725 "sysevent_subscribe_event(event class = EC_DEVFS) " 726 "failure"); 727 728 rv = -1; 729 730 } else if (sysevent_subscribe_event(sysevent_handle, EC_DR, 731 dr_subclasses, 732 sizeof (dr_subclasses)/sizeof (dr_subclasses[0])) != 0) { 733 734 log_err("Could not initialize the hotplug manager " 735 "sysevent_subscribe_event(event class = EC_DR) " 736 "failure"); 737 738 /* Unsubscribe from all sysevents in the event of a failure */ 739 fini_sysevents(); 740 741 rv = -1; 742 } else if (sysevent_subscribe_event(sysevent_handle, EC_PLATFORM, 743 platform_subclasses, 744 sizeof (platform_subclasses)/sizeof (platform_subclasses[0])) 745 != 0) { 746 747 log_err("Could not initialize the hotplug manager " 748 "sysevent_subscribe_event(event class = EC_PLATFORM) " 749 "failure"); 750 751 /* Unsubscribe from all sysevents in the event of a failure */ 752 fini_sysevents(); 753 754 rv = -1; 755 } 756 757 758 return (rv); 759 } 760 761 /*ARGSUSED*/ 762 static void 763 stdfree(void *p, size_t sz) 764 { 765 free(p); 766 } 767 768 /* 769 * Assumptions: Each disk's current state was determined and stored in 770 * its diskmon_t. 771 */ 772 hotplug_mgr_init_err_t 773 init_hotplug_manager() 774 { 775 /* Create the queue to which we'll add sysevents */ 776 g_sysev_queue = new_queue(B_TRUE, malloc, stdfree, free_sysevent_event); 777 778 /* 779 * Grab the event handler lock before spawning the thread so we can 780 * wait for the thread to transition to the running state. 781 */ 782 dm_assert(pthread_mutex_lock(&g_event_handler_lock) == 0); 783 784 /* Create the sysevent handling thread */ 785 g_sysev_tid = fmd_thr_create(g_fm_hdl, dm_fmd_sysevent_thread, 786 g_sysev_queue); 787 788 /* Wait for the thread's acknowledgement */ 789 while (g_sysev_thread_state != TS_RUNNING) 790 (void) pthread_cond_wait(&g_event_handler_cond, 791 &g_event_handler_lock); 792 dm_assert(pthread_mutex_unlock(&g_event_handler_lock) == 0); 793 794 if (init_sysevents() != 0) { 795 log_warn_e("Error initializing sysevents"); 796 return (HPM_ERR_SYSEVENT_INIT); 797 } 798 799 return (0); 800 } 801 802 void 803 cleanup_hotplug_manager() 804 { 805 /* Unsubscribe from the sysevents */ 806 fini_sysevents(); 807 808 /* 809 * Wait for the thread to exit before we can destroy 810 * the event queue. 811 */ 812 dm_assert(pthread_mutex_lock(&g_event_handler_lock) == 0); 813 g_sysev_thread_state = TS_EXIT_REQUESTED; 814 queue_add(g_sysev_queue, NULL); 815 while (g_sysev_thread_state != TS_EXITED) 816 (void) pthread_cond_wait(&g_event_handler_cond, 817 &g_event_handler_lock); 818 dm_assert(pthread_mutex_unlock(&g_event_handler_lock) == 0); 819 (void) pthread_join(g_sysev_tid, NULL); 820 fmd_thr_destroy(g_fm_hdl, g_sysev_tid); 821 822 /* Finally, destroy the event queue and reset the thread state */ 823 queue_free(&g_sysev_queue); 824 g_sysev_thread_state = TS_NOT_RUNNING; 825 } 826