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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 #include <sun_sas.h> 29 #include <sys/modctl.h> 30 #include <sys/types.h> 31 #include <netinet/in.h> 32 #include <inttypes.h> 33 #include <ctype.h> 34 35 /* free hba port info for the given hba */ 36 static void 37 free_hba_port(struct sun_sas_hba *hba_ptr) 38 { 39 struct sun_sas_port *hba_port = NULL; 40 struct sun_sas_port *last_hba_port = NULL; 41 struct sun_sas_port *tgt_port = NULL; 42 struct sun_sas_port *last_tgt_port = NULL; 43 struct ScsiEntryList *scsi_info = NULL; 44 struct ScsiEntryList *last_scsi_info = NULL; 45 struct phy_info *phy_ptr = NULL; 46 struct phy_info *last_phy = NULL; 47 48 /* Free the nested structures (port and attached port) */ 49 hba_port = hba_ptr->first_port; 50 while (hba_port != NULL) { 51 /* Free discovered port structure list. */ 52 tgt_port = hba_port->first_attached_port; 53 while (tgt_port != NULL) { 54 /* Free target mapping data list first. */ 55 scsi_info = tgt_port->scsiInfo; 56 while (scsi_info != NULL) { 57 last_scsi_info = scsi_info; 58 scsi_info = scsi_info->next; 59 free(last_scsi_info); 60 } 61 last_tgt_port = tgt_port; 62 tgt_port = tgt_port->next; 63 free(last_tgt_port->port_attributes.\ 64 PortSpecificAttribute.SASPort); 65 free(last_tgt_port); 66 } 67 hba_port->first_attached_port = NULL; 68 69 phy_ptr = hba_port->first_phy; 70 while (phy_ptr != NULL) { 71 last_phy = phy_ptr; 72 phy_ptr = phy_ptr->next; 73 free(last_phy); 74 } 75 hba_port->first_phy = NULL; 76 77 last_hba_port = hba_port; 78 hba_port = hba_port->next; 79 free(last_hba_port->port_attributes.\ 80 PortSpecificAttribute.SASPort); 81 free(last_hba_port); 82 } 83 84 hba_ptr->first_port = NULL; 85 } 86 87 /* 88 * Internal routine for adding an HBA port 89 */ 90 static HBA_STATUS 91 add_hba_port_info(di_node_t portNode, struct sun_sas_hba *hba_ptr, int protocol) 92 { 93 const char ROUTINE[] = "add_hba_port_info"; 94 struct sun_sas_port *port_ptr; 95 char *portDevpath; 96 int *propIntData; 97 char *propStringData; 98 uint64_t tmpAddr; 99 char *charptr, cntlLink[MAXPATHLEN] = {'\0'}; 100 int rval; 101 di_node_t branchNode; 102 uint_t state = HBA_PORTSTATE_UNKNOWN; 103 104 if (hba_ptr == NULL) { 105 log(LOG_DEBUG, ROUTINE, 106 "Sun_sas handle ptr set to NULL."); 107 return (HBA_STATUS_ERROR_ARG); 108 } 109 110 if ((port_ptr = (struct sun_sas_port *)calloc(1, 111 sizeof (struct sun_sas_port))) == NULL) { 112 OUT_OF_MEMORY(ROUTINE); 113 return (HBA_STATUS_ERROR); 114 } 115 116 if ((port_ptr->port_attributes.PortSpecificAttribute.SASPort = 117 (struct SMHBA_SAS_Port *)calloc(1, sizeof (struct SMHBA_SAS_Port))) 118 == NULL) { 119 OUT_OF_MEMORY(ROUTINE); 120 return (HBA_STATUS_ERROR); 121 } 122 123 if ((portDevpath = di_devfs_path(portNode)) == NULL) { 124 log(LOG_DEBUG, ROUTINE, 125 "Unable to get device path from HBA Port Node."); 126 S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort); 127 S_FREE(port_ptr); 128 return (HBA_STATUS_ERROR); 129 } 130 131 /* 132 * Let's take a branch snap shot for pulling attributes. 133 * The attribute change doesn't invalidate devinfo cache snapshot. 134 * Phy info prop and num-phys can be obsolate when the same hba 135 * connected to the same expander(SIM) thus phy numbers are increased. 136 * Also the phy number may get decreased when a connection is removed 137 * while the iport still exist through another connection. 138 */ 139 branchNode = di_init(portDevpath, DINFOPROP); 140 if (branchNode == DI_NODE_NIL) { 141 /* something is wrong here. */ 142 di_fini(branchNode); 143 log(LOG_DEBUG, ROUTINE, 144 "Unable to take devinfoi branch snapshot on HBA port \"%s\"" 145 " due to %s", portDevpath, strerror(errno)); 146 S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort); 147 S_FREE(port_ptr); 148 return (HBA_STATUS_ERROR); 149 } 150 151 state = di_state(portNode); 152 if (((state & DI_DRIVER_DETACHED) == DI_DRIVER_DETACHED) || 153 ((state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE)) { 154 log(LOG_DEBUG, ROUTINE, 155 "HBA port node %s is either OFFLINE or DETACHED", 156 portDevpath); 157 port_ptr->port_attributes.PortState = HBA_PORTSTATE_OFFLINE; 158 } else { 159 port_ptr->port_attributes.PortState = HBA_PORTSTATE_ONLINE; 160 } 161 162 port_ptr->port_attributes.PortType = HBA_PORTTYPE_SASDEVICE; 163 164 (void) strlcpy(port_ptr->device_path, portDevpath, MAXPATHLEN + 1); 165 166 if (lookupControllerLink(portDevpath, (char *)cntlLink) == 167 HBA_STATUS_OK) { 168 (void) strlcpy(port_ptr->port_attributes.OSDeviceName, cntlLink, 169 sizeof (port_ptr->port_attributes.OSDeviceName)); 170 if ((charptr = strrchr(cntlLink, '/')) != NULL) { 171 charptr++; 172 } 173 if (charptr[0] == 'c') { 174 port_ptr->cntlNumber = atoi(++charptr); 175 } else { 176 port_ptr->cntlNumber = -1; 177 } 178 } else { 179 (void) snprintf(port_ptr->port_attributes.OSDeviceName, 180 sizeof (port_ptr->port_attributes.OSDeviceName), 181 "%s%s%s", DEVICES_DIR, portDevpath, SCSI_SUFFIX); 182 } 183 184 di_devfs_path_free(portDevpath); 185 186 port_ptr->port_attributes.PortSpecificAttribute. 187 SASPort->PortProtocol = protocol; 188 189 rval = di_prop_lookup_strings(DDI_DEV_T_ANY, branchNode, 190 "initiator-port", &propStringData); 191 if (rval < 0) { 192 log(LOG_DEBUG, ROUTINE, 193 "Unable to get initiator-port from HBA port node %s.", 194 port_ptr->port_attributes.OSDeviceName); 195 di_fini(branchNode); 196 S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort); 197 S_FREE(port_ptr); 198 return (HBA_STATUS_ERROR); 199 } else { 200 for (charptr = propStringData; *charptr != '\0'; charptr++) { 201 if (isxdigit(*charptr)) { 202 break; 203 } 204 } 205 if (*charptr != '\0') { 206 tmpAddr = htonll(strtoll(charptr, NULL, 16)); 207 (void) memcpy(port_ptr->port_attributes. 208 PortSpecificAttribute.SASPort->LocalSASAddress.wwn, 209 &tmpAddr, 8); 210 } else { 211 log(LOG_DEBUG, ROUTINE, 212 "No proper intiator-port prop value on HBA port %s", 213 port_ptr->port_attributes.OSDeviceName); 214 } 215 } 216 217 rval = di_prop_lookup_strings(DDI_DEV_T_ANY, branchNode, 218 "attached-port", &propStringData); 219 if (rval < 0) { 220 log(LOG_DEBUG, ROUTINE, 221 "Unable to get attached-port from HBA port node %s.", 222 port_ptr->port_attributes.OSDeviceName); 223 di_fini(branchNode); 224 S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort); 225 S_FREE(port_ptr); 226 return (HBA_STATUS_ERROR); 227 } else { 228 for (charptr = propStringData; *charptr != '\0'; charptr++) { 229 if (isxdigit(*charptr)) { 230 break; 231 } 232 } 233 if (*charptr != '\0') { 234 tmpAddr = htonll(strtoll(charptr, NULL, 16)); 235 (void) memcpy(port_ptr->port_attributes. 236 PortSpecificAttribute.SASPort-> 237 AttachedSASAddress.wwn, &tmpAddr, 8); 238 } else { 239 /* continue even if the attached port is NULL. */ 240 log(LOG_DEBUG, ROUTINE, 241 "No proper attached-port prop value: " 242 "HBA port Local SAS Address(%016llx)", 243 wwnConversion(port_ptr->port_attributes. 244 PortSpecificAttribute. 245 SASPort->LocalSASAddress.wwn)); 246 } 247 } 248 249 rval = di_prop_lookup_ints(DDI_DEV_T_ANY, branchNode, 250 "num-phys", &propIntData); 251 if (rval < 0) { 252 log(LOG_DEBUG, ROUTINE, 253 "Unable to get NumberofPhys from HBA port %s.", 254 port_ptr->port_attributes.OSDeviceName); 255 di_fini(branchNode); 256 S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort); 257 S_FREE(port_ptr); 258 return (HBA_STATUS_ERROR); 259 } else { 260 port_ptr->port_attributes.PortSpecificAttribute.\ 261 SASPort->NumberofPhys = *propIntData; 262 } 263 264 if (port_ptr->port_attributes.PortSpecificAttribute.\ 265 SASPort->NumberofPhys > 0) { 266 if (get_phy_info(branchNode, port_ptr) != HBA_STATUS_OK) { 267 log(LOG_DEBUG, ROUTINE, 268 "Failed to get phy info on HBA port %s.", 269 port_ptr->port_attributes.OSDeviceName); 270 di_fini(branchNode); 271 S_FREE(port_ptr->port_attributes. 272 PortSpecificAttribute.SASPort); 273 S_FREE(port_ptr); 274 return (HBA_STATUS_ERROR); 275 } 276 } 277 278 /* now done with prop checking. remove branchNode. */ 279 di_fini(branchNode); 280 281 /* Construct discovered target port. */ 282 if (devtree_attached_devices(portNode, port_ptr) != HBA_STATUS_OK) { 283 log(LOG_DEBUG, ROUTINE, 284 "Failed to get attached device info HBA port %s.", 285 port_ptr->port_attributes.OSDeviceName); 286 S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort); 287 S_FREE(port_ptr); 288 return (HBA_STATUS_ERROR); 289 } 290 291 fillDomainPortWWN(port_ptr); 292 293 /* add new port onto hba handle list */ 294 if (hba_ptr->first_port == NULL) { 295 port_ptr->index = 0; 296 hba_ptr->first_port = port_ptr; 297 } else { 298 port_ptr->index = hba_ptr->first_port->index + 1; 299 port_ptr->next = hba_ptr->first_port; 300 hba_ptr->first_port = port_ptr; 301 } 302 303 return (HBA_STATUS_OK); 304 } 305 306 HBA_STATUS 307 refresh_hba(di_node_t hbaNode, struct sun_sas_hba *hba_ptr) 308 { 309 const char ROUTINE[] = "refresh_hba"; 310 di_node_t portNode; 311 int protocol = 0; 312 int *propIntData; 313 314 /* 315 * clean up existing hba port, discovered target, phy info. 316 * leave open handles intact. 317 */ 318 free_hba_port(hba_ptr); 319 320 if ((portNode = di_child_node(hbaNode)) == NULL) { 321 log(LOG_DEBUG, ROUTINE, 322 "HBA node doesn't have iport child."); 323 return (HBA_STATUS_ERROR); 324 } 325 326 if ((di_prop_lookup_ints(DDI_DEV_T_ANY, hbaNode, 327 "supported-protocol", &propIntData)) == -1) { 328 log(LOG_DEBUG, ROUTINE, 329 "Unable to get supported-protocol from HBA node."); 330 } else { 331 protocol = *propIntData; 332 } 333 334 while (portNode != DI_NODE_NIL) { 335 if (add_hba_port_info(portNode, hba_ptr, protocol) 336 == HBA_STATUS_ERROR) { 337 S_FREE(hba_ptr->first_port); 338 S_FREE(hba_ptr); 339 return (HBA_STATUS_ERROR); 340 } 341 portNode = di_sibling_node(portNode); 342 } 343 344 return (HBA_STATUS_OK); 345 } 346 347 /* 348 * Discover information for one HBA in the device tree. 349 * The di_node_t argument should be a node with smhba-supported prop set 350 * to true. 351 * Without iport support, the devinfo node will represent one port hba. 352 * This routine assumes the locks have been taken. 353 */ 354 HBA_STATUS 355 devtree_get_one_hba(di_node_t hbaNode) 356 { 357 const char ROUTINE[] = "devtree_get_one_hba"; 358 char *propdata = NULL; 359 int *propIntData = NULL; 360 struct sun_sas_hba *new_hba, *hba_ptr; 361 char *hbaDevpath, *hba_driver; 362 int protocol = 0; 363 di_node_t portNode; 364 int hba_instance = -1; 365 366 hba_instance = di_instance(hbaNode); 367 if (hba_instance == -1) { 368 log(LOG_DEBUG, ROUTINE, 369 "portNode has instance of -1"); 370 return (DI_WALK_CONTINUE); 371 } 372 373 if ((hbaDevpath = di_devfs_path(hbaNode)) == NULL) { 374 log(LOG_DEBUG, ROUTINE, "Unable to get " 375 "device path from hbaNode"); 376 return (HBA_STATUS_ERROR); 377 } 378 379 /* check to see if this is a repeat HBA */ 380 if (global_hba_head) { 381 for (hba_ptr = global_hba_head; 382 hba_ptr != NULL; 383 hba_ptr = hba_ptr->next) { 384 if ((strncmp(hba_ptr->device_path, hbaDevpath, 385 strlen(hbaDevpath))) == 0) { 386 if (refresh_hba(hbaNode, hba_ptr) != 387 HBA_STATUS_OK) { 388 log(LOG_DEBUG, ROUTINE, "Refresh failed" 389 " on hbaNode %s", hbaDevpath); 390 } 391 di_devfs_path_free(hbaDevpath); 392 return (HBA_STATUS_OK); 393 } 394 } 395 } 396 397 /* this is a new hba */ 398 if ((new_hba = (struct sun_sas_hba *)calloc(1, 399 sizeof (struct sun_sas_hba))) == NULL) { 400 OUT_OF_MEMORY(ROUTINE); 401 di_devfs_path_free(hbaDevpath); 402 return (HBA_STATUS_ERROR); 403 } 404 405 (void) strlcpy(new_hba->device_path, hbaDevpath, 406 sizeof (new_hba->device_path)); 407 di_devfs_path_free(hbaDevpath); 408 409 (void) snprintf(new_hba->adapter_attributes.HBASymbolicName, 410 sizeof (new_hba->adapter_attributes.HBASymbolicName), 411 "%s%s", DEVICES_DIR, new_hba->device_path); 412 413 /* Manufacturer */ 414 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode, 415 "Manufacturer", (char **)&propdata)) == -1) { 416 (void) strlcpy(new_hba->adapter_attributes.Manufacturer, 417 SUN_MICROSYSTEMS, 418 sizeof (new_hba->adapter_attributes.Manufacturer)); 419 } else { 420 (void) strlcpy(new_hba->adapter_attributes.Manufacturer, 421 propdata, 422 sizeof (new_hba->adapter_attributes.Manufacturer)); 423 } 424 425 /* SerialNumber */ 426 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode, 427 "SerialNumber", (char **)&propdata)) == -1) { 428 new_hba->adapter_attributes.SerialNumber[0] = '\0'; 429 } else { 430 (void) strlcpy(new_hba->adapter_attributes.SerialNumber, 431 propdata, 432 sizeof (new_hba->adapter_attributes.SerialNumber)); 433 } 434 435 /* Model */ 436 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode, 437 "ModelName", (char **)&propdata)) == -1) { 438 new_hba->adapter_attributes.Model[0] = '\0'; 439 } else { 440 (void) strlcpy(new_hba->adapter_attributes.Model, 441 propdata, 442 sizeof (new_hba->adapter_attributes.Model)); 443 } 444 445 /* FirmwareVersion */ 446 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode, 447 "firmware-version", (char **)&propdata)) == -1) { 448 log(LOG_DEBUG, ROUTINE, 449 "Property \"%s\" not found for device \"%s\"", 450 "firmware-version", new_hba->device_path); 451 } else { 452 (void) strlcpy(new_hba->adapter_attributes.FirmwareVersion, 453 propdata, 454 sizeof (new_hba->adapter_attributes.FirmwareVersion)); 455 } 456 457 /* HardwareVersion */ 458 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode, 459 "hardware-version", (char **)&propdata)) == -1) { 460 log(LOG_DEBUG, ROUTINE, 461 "Property \"%s\" not found for device \"%s\"", 462 "hardware-version", new_hba->device_path); 463 } else { 464 (void) strlcpy(new_hba->adapter_attributes.HardwareVersion, 465 propdata, 466 sizeof (new_hba->adapter_attributes.HardwareVersion)); 467 } 468 469 /* DriverVersion */ 470 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode, 471 "driver-version", (char **)&propdata)) == -1) { 472 log(LOG_DEBUG, ROUTINE, 473 "Property \"%s\" not found for device \"%s\"", 474 "driver-version", new_hba->device_path); 475 } else { 476 (void) strlcpy(new_hba->adapter_attributes.DriverVersion, 477 propdata, 478 sizeof (new_hba->adapter_attributes.DriverVersion)); 479 } 480 481 if ((di_prop_lookup_ints(DDI_DEV_T_ANY, hbaNode, 482 "supported-protocol", &propIntData)) == -1) { 483 log(LOG_DEBUG, ROUTINE, 484 "Unable to get supported-protocol from HBA node."); 485 } else { 486 protocol = *propIntData; 487 } 488 489 /* We don't use these */ 490 new_hba->adapter_attributes.OptionROMVersion[0] = '\0'; 491 new_hba->adapter_attributes.RedundantOptionROMVersion[0] = '\0'; 492 new_hba->adapter_attributes.RedundantFirmwareVersion[0] = '\0'; 493 new_hba->adapter_attributes.VendorSpecificID = 0; 494 495 if ((hba_driver = di_driver_name(hbaNode)) != NULL) { 496 (void) strlcpy(new_hba->adapter_attributes.DriverName, 497 hba_driver, 498 sizeof (new_hba->adapter_attributes.DriverName)); 499 } else { 500 log(LOG_DEBUG, ROUTINE, 501 "HBA driver name not found for device \"%s\"", 502 new_hba->device_path); 503 } 504 505 /* 506 * Name the adapter: like SUNW-pmcs-1 507 * Using di_instance number as the suffix for the name for persistent 508 * among rebooting. 509 */ 510 (void) snprintf(new_hba->handle_name, HANDLE_NAME_LENGTH, "%s-%s-%d", 511 "SUNW", new_hba->adapter_attributes.DriverName, hba_instance); 512 513 if ((portNode = di_child_node(hbaNode)) == NULL) { 514 log(LOG_DEBUG, ROUTINE, 515 "HBA driver doesn't have iport child. \"%s\"", 516 new_hba->device_path); 517 /* continue on with an hba without any port. */ 518 new_hba->index = hba_count++; 519 520 /* 521 * add newly created handle into global_hba_head list 522 */ 523 if (global_hba_head != NULL) { 524 /* 525 * Make sure to move the open_handles list to back to 526 * the head if it's there (for refresh scenario) 527 */ 528 if (global_hba_head->open_handles) { 529 new_hba->open_handles = 530 global_hba_head->open_handles; 531 global_hba_head->open_handles = NULL; 532 } 533 /* Now bump the new one to the head of the list */ 534 new_hba->next = global_hba_head; 535 global_hba_head = new_hba; 536 } else { 537 global_hba_head = new_hba; 538 } 539 return (HBA_STATUS_OK); 540 } 541 542 while (portNode != DI_NODE_NIL) { 543 if (add_hba_port_info(portNode, new_hba, protocol) 544 == HBA_STATUS_ERROR) { 545 S_FREE(new_hba->first_port); 546 S_FREE(new_hba); 547 return (HBA_STATUS_ERROR); 548 } 549 portNode = di_sibling_node(portNode); 550 } 551 552 new_hba->index = hba_count++; 553 554 /* 555 * add newly created handle into global_hba_head list 556 */ 557 if (global_hba_head != NULL) { 558 /* 559 * Make sure to move the open_handles list to back to the 560 * head if it's there (for refresh scenario) 561 */ 562 if (global_hba_head->open_handles) { 563 new_hba->open_handles = global_hba_head->open_handles; 564 global_hba_head->open_handles = NULL; 565 } 566 /* Now bump the new one to the head of the list */ 567 new_hba->next = global_hba_head; 568 global_hba_head = new_hba; 569 } else { 570 global_hba_head = new_hba; 571 } 572 573 return (HBA_STATUS_OK); 574 } 575 576 /* 577 * Discover information for all HBAs found on the system. 578 * The di_node_t argument should be the root of the device tree. 579 * This routine assumes the locks have been taken 580 */ 581 static int 582 lookup_smhba_sas_hba(di_node_t node, void *arg) 583 { 584 const char ROUTINE[] = "lookup_smhba_sas_hba"; 585 int *propData, rval; 586 walkarg_t *wa = (walkarg_t *)arg; 587 588 /* Skip stub(instance -1) nodes */ 589 if (IS_STUB_NODE(node)) { 590 log(LOG_DEBUG, ROUTINE, "Walk continue"); 591 return (DI_WALK_CONTINUE); 592 } 593 594 rval = di_prop_lookup_ints(DDI_DEV_T_ANY, node, 595 "sm-hba-supported", &propData); 596 if (rval >= 0) { 597 if (*propData) { 598 /* add the hba to the hba list */ 599 if (devtree_get_one_hba(node) != HBA_STATUS_OK) { 600 *(wa->flag) = B_TRUE; 601 } 602 /* Found a node. No need to walk the child. */ 603 log(LOG_DEBUG, ROUTINE, "Walk prunechild"); 604 return (DI_WALK_PRUNECHILD); 605 } 606 } 607 608 return (DI_WALK_CONTINUE); 609 } 610 611 /* 612 * Discover information for all HBAs found on the system. 613 * The di_node_t argument should be the root of the device tree. 614 * This routine assumes the locks have been taken 615 */ 616 HBA_STATUS 617 devtree_get_all_hbas(di_node_t root) 618 { 619 const char ROUTINE[] = "devtree_get_all_hbas"; 620 int rv, ret = HBA_STATUS_ERROR; 621 walkarg_t wa; 622 623 wa.devpath = NULL; 624 if ((wa.flag = (boolean_t *)calloc(1, 625 sizeof (boolean_t))) == NULL) { 626 OUT_OF_MEMORY(ROUTINE); 627 return (HBA_STATUS_ERROR); 628 } 629 *wa.flag = B_FALSE; 630 rv = di_walk_node(root, DI_WALK_SIBFIRST, &wa, lookup_smhba_sas_hba); 631 632 if (rv == 0) { 633 /* 634 * Now determine what status code to return, taking 635 * partial failure scenarios into consideration. 636 * 637 * If we have at least one working HBA, then we return an 638 * OK status. If we have no good HBAs, but at least one 639 * failed HBA, we return an ERROR status. If we have 640 * no HBAs and no failures, we return OK. 641 */ 642 if (global_hba_head) { 643 /* 644 * We've got at least one HBA and possibly some 645 * failures. 646 */ 647 ret = HBA_STATUS_OK; 648 } else if (*(wa.flag)) { 649 /* We have no HBAs but have failures */ 650 ret = HBA_STATUS_ERROR; 651 } else { 652 /* We have no HBAs and no failures */ 653 ret = HBA_STATUS_OK; 654 } 655 } 656 657 658 S_FREE(wa.flag); 659 660 if (ret == HBA_STATUS_OK) 661 (void) registerSysevent(); 662 663 return (ret); 664 } 665