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