4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
26 * Copyright 2019 Joyent, Inc.
30 #include <sys/modctl.h>
31 #include <sys/types.h>
32 #include <netinet/in.h>
36 struct sun_sas_hba
*global_hba_head
;
38 /* free hba port info for the given hba */
40 free_hba_port(struct sun_sas_hba
*hba_ptr
)
42 struct sun_sas_port
*hba_port
= NULL
;
43 struct sun_sas_port
*last_hba_port
= NULL
;
44 struct sun_sas_port
*tgt_port
= NULL
;
45 struct sun_sas_port
*last_tgt_port
= NULL
;
46 struct ScsiEntryList
*scsi_info
= NULL
;
47 struct ScsiEntryList
*last_scsi_info
= NULL
;
48 struct phy_info
*phy_ptr
= NULL
;
49 struct phy_info
*last_phy
= NULL
;
51 /* Free the nested structures (port and attached port) */
52 hba_port
= hba_ptr
->first_port
;
53 while (hba_port
!= NULL
) {
54 /* Free discovered port structure list. */
55 tgt_port
= hba_port
->first_attached_port
;
56 while (tgt_port
!= NULL
) {
57 /* Free target mapping data list first. */
58 scsi_info
= tgt_port
->scsiInfo
;
59 while (scsi_info
!= NULL
) {
60 last_scsi_info
= scsi_info
;
61 scsi_info
= scsi_info
->next
;
64 last_tgt_port
= tgt_port
;
65 tgt_port
= tgt_port
->next
;
66 free(last_tgt_port
->port_attributes
.\
67 PortSpecificAttribute
.SASPort
);
70 hba_port
->first_attached_port
= NULL
;
72 phy_ptr
= hba_port
->first_phy
;
73 while (phy_ptr
!= NULL
) {
75 phy_ptr
= phy_ptr
->next
;
78 hba_port
->first_phy
= NULL
;
80 last_hba_port
= hba_port
;
81 hba_port
= hba_port
->next
;
82 free(last_hba_port
->port_attributes
.\
83 PortSpecificAttribute
.SASPort
);
87 hba_ptr
->first_port
= NULL
;
91 * Internal routine for adding an HBA port
94 add_hba_port_info(di_node_t portNode
, struct sun_sas_hba
*hba_ptr
, int protocol
)
96 const char ROUTINE
[] = "add_hba_port_info";
97 struct sun_sas_port
*port_ptr
;
100 char *propStringData
;
102 char *charptr
, cntlLink
[MAXPATHLEN
] = {'\0'};
104 di_node_t branchNode
;
105 uint_t state
= HBA_PORTSTATE_UNKNOWN
;
107 if (hba_ptr
== NULL
) {
108 log(LOG_DEBUG
, ROUTINE
,
109 "Sun_sas handle ptr set to NULL.");
110 return (HBA_STATUS_ERROR_ARG
);
113 if ((port_ptr
= (struct sun_sas_port
*)calloc(1,
114 sizeof (struct sun_sas_port
))) == NULL
) {
115 OUT_OF_MEMORY(ROUTINE
);
116 return (HBA_STATUS_ERROR
);
119 if ((port_ptr
->port_attributes
.PortSpecificAttribute
.SASPort
=
120 (struct SMHBA_SAS_Port
*)calloc(1, sizeof (struct SMHBA_SAS_Port
)))
122 OUT_OF_MEMORY(ROUTINE
);
123 return (HBA_STATUS_ERROR
);
126 if ((portDevpath
= di_devfs_path(portNode
)) == NULL
) {
127 log(LOG_DEBUG
, ROUTINE
,
128 "Unable to get device path from HBA Port Node.");
129 S_FREE(port_ptr
->port_attributes
.PortSpecificAttribute
.SASPort
);
131 return (HBA_STATUS_ERROR
);
135 * Let's take a branch snap shot for pulling attributes.
136 * The attribute change doesn't invalidate devinfo cache snapshot.
137 * Phy info prop and num-phys can be obsolate when the same hba
138 * connected to the same expander(SIM) thus phy numbers are increased.
139 * Also the phy number may get decreased when a connection is removed
140 * while the iport still exist through another connection.
142 branchNode
= di_init(portDevpath
, DINFOPROP
);
143 if (branchNode
== DI_NODE_NIL
) {
144 /* something is wrong here. */
146 log(LOG_DEBUG
, ROUTINE
,
147 "Unable to take devinfoi branch snapshot on HBA port \"%s\""
148 " due to %s", portDevpath
, strerror(errno
));
149 S_FREE(port_ptr
->port_attributes
.PortSpecificAttribute
.SASPort
);
151 return (HBA_STATUS_ERROR
);
154 state
= di_state(portNode
);
155 if (((state
& DI_DRIVER_DETACHED
) == DI_DRIVER_DETACHED
) ||
156 ((state
& DI_DEVICE_OFFLINE
) == DI_DEVICE_OFFLINE
)) {
157 log(LOG_DEBUG
, ROUTINE
,
158 "HBA port node %s is either OFFLINE or DETACHED",
160 port_ptr
->port_attributes
.PortState
= HBA_PORTSTATE_OFFLINE
;
162 port_ptr
->port_attributes
.PortState
= HBA_PORTSTATE_ONLINE
;
165 port_ptr
->port_attributes
.PortType
= HBA_PORTTYPE_SASDEVICE
;
167 (void) strlcpy(port_ptr
->device_path
, portDevpath
, MAXPATHLEN
+ 1);
169 if (lookupControllerLink(portDevpath
, (char *)cntlLink
) ==
171 (void) strlcpy(port_ptr
->port_attributes
.OSDeviceName
, cntlLink
,
172 sizeof (port_ptr
->port_attributes
.OSDeviceName
));
173 if ((charptr
= strrchr(cntlLink
, '/')) != NULL
) {
176 if (charptr
[0] == 'c') {
177 port_ptr
->cntlNumber
= atoi(++charptr
);
179 port_ptr
->cntlNumber
= -1;
182 (void) snprintf(port_ptr
->port_attributes
.OSDeviceName
,
183 sizeof (port_ptr
->port_attributes
.OSDeviceName
),
184 "%s%s%s", DEVICES_DIR
, portDevpath
, SCSI_SUFFIX
);
187 di_devfs_path_free(portDevpath
);
189 port_ptr
->port_attributes
.PortSpecificAttribute
.
190 SASPort
->PortProtocol
= protocol
;
192 rval
= di_prop_lookup_strings(DDI_DEV_T_ANY
, branchNode
,
193 "initiator-port", &propStringData
);
195 log(LOG_DEBUG
, ROUTINE
,
196 "Unable to get initiator-port from HBA port node %s.",
197 port_ptr
->port_attributes
.OSDeviceName
);
199 S_FREE(port_ptr
->port_attributes
.PortSpecificAttribute
.SASPort
);
201 return (HBA_STATUS_ERROR
);
203 for (charptr
= propStringData
; *charptr
!= '\0'; charptr
++) {
204 if (isxdigit(*charptr
)) {
208 if (*charptr
!= '\0') {
209 tmpAddr
= htonll(strtoll(charptr
, NULL
, 16));
210 (void) memcpy(port_ptr
->port_attributes
.
211 PortSpecificAttribute
.SASPort
->LocalSASAddress
.wwn
,
214 log(LOG_DEBUG
, ROUTINE
,
215 "No proper intiator-port prop value on HBA port %s",
216 port_ptr
->port_attributes
.OSDeviceName
);
220 rval
= di_prop_lookup_strings(DDI_DEV_T_ANY
, branchNode
,
221 "attached-port", &propStringData
);
223 log(LOG_DEBUG
, ROUTINE
,
224 "Unable to get attached-port from HBA port node %s.",
225 port_ptr
->port_attributes
.OSDeviceName
);
227 S_FREE(port_ptr
->port_attributes
.PortSpecificAttribute
.SASPort
);
229 return (HBA_STATUS_ERROR
);
231 for (charptr
= propStringData
; *charptr
!= '\0'; charptr
++) {
232 if (isxdigit(*charptr
)) {
236 if (*charptr
!= '\0') {
237 tmpAddr
= htonll(strtoll(charptr
, NULL
, 16));
238 (void) memcpy(port_ptr
->port_attributes
.
239 PortSpecificAttribute
.SASPort
->
240 AttachedSASAddress
.wwn
, &tmpAddr
, 8);
242 /* continue even if the attached port is NULL. */
243 log(LOG_DEBUG
, ROUTINE
,
244 "No proper attached-port prop value: "
245 "HBA port Local SAS Address(%016llx)",
246 wwnConversion(port_ptr
->port_attributes
.
247 PortSpecificAttribute
.
248 SASPort
->LocalSASAddress
.wwn
));
252 rval
= di_prop_lookup_ints(DDI_DEV_T_ANY
, branchNode
,
253 "num-phys", &propIntData
);
255 log(LOG_DEBUG
, ROUTINE
,
256 "Unable to get NumberofPhys from HBA port %s.",
257 port_ptr
->port_attributes
.OSDeviceName
);
259 S_FREE(port_ptr
->port_attributes
.PortSpecificAttribute
.SASPort
);
261 return (HBA_STATUS_ERROR
);
263 port_ptr
->port_attributes
.PortSpecificAttribute
.\
264 SASPort
->NumberofPhys
= *propIntData
;
267 if (port_ptr
->port_attributes
.PortSpecificAttribute
.\
268 SASPort
->NumberofPhys
> 0) {
269 if (get_phy_info(branchNode
, port_ptr
) != HBA_STATUS_OK
) {
270 log(LOG_DEBUG
, ROUTINE
,
271 "Failed to get phy info on HBA port %s.",
272 port_ptr
->port_attributes
.OSDeviceName
);
274 S_FREE(port_ptr
->port_attributes
.
275 PortSpecificAttribute
.SASPort
);
277 return (HBA_STATUS_ERROR
);
281 /* now done with prop checking. remove branchNode. */
284 /* Construct discovered target port. */
285 if (devtree_attached_devices(portNode
, port_ptr
) != HBA_STATUS_OK
) {
286 log(LOG_DEBUG
, ROUTINE
,
287 "Failed to get attached device info HBA port %s.",
288 port_ptr
->port_attributes
.OSDeviceName
);
289 S_FREE(port_ptr
->port_attributes
.PortSpecificAttribute
.SASPort
);
291 return (HBA_STATUS_ERROR
);
294 fillDomainPortWWN(port_ptr
);
296 /* add new port onto hba handle list */
297 if (hba_ptr
->first_port
== NULL
) {
299 hba_ptr
->first_port
= port_ptr
;
301 port_ptr
->index
= hba_ptr
->first_port
->index
+ 1;
302 port_ptr
->next
= hba_ptr
->first_port
;
303 hba_ptr
->first_port
= port_ptr
;
306 return (HBA_STATUS_OK
);
310 refresh_hba(di_node_t hbaNode
, struct sun_sas_hba
*hba_ptr
)
312 const char ROUTINE
[] = "refresh_hba";
318 * clean up existing hba port, discovered target, phy info.
319 * leave open handles intact.
321 free_hba_port(hba_ptr
);
323 if ((portNode
= di_child_node(hbaNode
)) == NULL
) {
324 log(LOG_DEBUG
, ROUTINE
,
325 "HBA node doesn't have iport child.");
326 return (HBA_STATUS_ERROR
);
329 if ((di_prop_lookup_ints(DDI_DEV_T_ANY
, hbaNode
,
330 "supported-protocol", &propIntData
)) == -1) {
331 log(LOG_DEBUG
, ROUTINE
,
332 "Unable to get supported-protocol from HBA node.");
334 protocol
= *propIntData
;
337 while (portNode
!= DI_NODE_NIL
) {
338 if (di_prop_lookup_ints(DDI_DEV_T_ANY
, portNode
,
339 "virtual-port", &propIntData
) >= 0) {
341 /* ignore a virtual port. */
342 portNode
= di_sibling_node(portNode
);
346 if (add_hba_port_info(portNode
, hba_ptr
, protocol
)
347 == HBA_STATUS_ERROR
) {
348 S_FREE(hba_ptr
->first_port
);
350 return (HBA_STATUS_ERROR
);
352 portNode
= di_sibling_node(portNode
);
355 return (HBA_STATUS_OK
);
359 * Discover information for one HBA in the device tree.
360 * The di_node_t argument should be a node with smhba-supported prop set
362 * Without iport support, the devinfo node will represent one port hba.
363 * This routine assumes the locks have been taken.
366 devtree_get_one_hba(di_node_t hbaNode
)
368 const char ROUTINE
[] = "devtree_get_one_hba";
369 char *propdata
= NULL
;
370 int *propIntData
= NULL
;
371 struct sun_sas_hba
*new_hba
, *hba_ptr
;
372 char *hbaDevpath
, *hba_driver
;
375 int hba_instance
= -1;
377 hba_instance
= di_instance(hbaNode
);
378 if (hba_instance
== -1) {
379 log(LOG_DEBUG
, ROUTINE
,
380 "portNode has instance of -1");
381 return (DI_WALK_CONTINUE
);
384 if ((hbaDevpath
= di_devfs_path(hbaNode
)) == NULL
) {
385 log(LOG_DEBUG
, ROUTINE
, "Unable to get "
386 "device path from hbaNode");
387 return (HBA_STATUS_ERROR
);
390 /* check to see if this is a repeat HBA */
391 if (global_hba_head
) {
392 for (hba_ptr
= global_hba_head
;
394 hba_ptr
= hba_ptr
->next
) {
395 if ((strncmp(hba_ptr
->device_path
, hbaDevpath
,
396 strlen(hbaDevpath
))) == 0) {
397 if (refresh_hba(hbaNode
, hba_ptr
) !=
399 log(LOG_DEBUG
, ROUTINE
, "Refresh failed"
400 " on hbaNode %s", hbaDevpath
);
402 di_devfs_path_free(hbaDevpath
);
403 return (HBA_STATUS_OK
);
408 /* this is a new hba */
409 if ((new_hba
= (struct sun_sas_hba
*)calloc(1,
410 sizeof (struct sun_sas_hba
))) == NULL
) {
411 OUT_OF_MEMORY(ROUTINE
);
412 di_devfs_path_free(hbaDevpath
);
413 return (HBA_STATUS_ERROR
);
416 (void) strlcpy(new_hba
->device_path
, hbaDevpath
,
417 sizeof (new_hba
->device_path
));
418 di_devfs_path_free(hbaDevpath
);
420 (void) snprintf(new_hba
->adapter_attributes
.HBASymbolicName
,
421 sizeof (new_hba
->adapter_attributes
.HBASymbolicName
),
422 "%s%s", DEVICES_DIR
, new_hba
->device_path
);
425 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, hbaNode
,
426 "Manufacturer", (char **)&propdata
)) == -1) {
427 (void) strlcpy(new_hba
->adapter_attributes
.Manufacturer
,
429 sizeof (new_hba
->adapter_attributes
.Manufacturer
));
431 (void) strlcpy(new_hba
->adapter_attributes
.Manufacturer
,
433 sizeof (new_hba
->adapter_attributes
.Manufacturer
));
437 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, hbaNode
,
438 "SerialNumber", (char **)&propdata
)) == -1) {
439 new_hba
->adapter_attributes
.SerialNumber
[0] = '\0';
441 (void) strlcpy(new_hba
->adapter_attributes
.SerialNumber
,
443 sizeof (new_hba
->adapter_attributes
.SerialNumber
));
447 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, hbaNode
,
448 "ModelName", (char **)&propdata
)) == -1) {
449 new_hba
->adapter_attributes
.Model
[0] = '\0';
451 (void) strlcpy(new_hba
->adapter_attributes
.Model
,
453 sizeof (new_hba
->adapter_attributes
.Model
));
456 /* FirmwareVersion */
457 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, hbaNode
,
458 "firmware-version", (char **)&propdata
)) == -1) {
459 log(LOG_DEBUG
, ROUTINE
,
460 "Property \"%s\" not found for device \"%s\"",
461 "firmware-version", new_hba
->device_path
);
463 (void) strlcpy(new_hba
->adapter_attributes
.FirmwareVersion
,
465 sizeof (new_hba
->adapter_attributes
.FirmwareVersion
));
468 /* HardwareVersion */
469 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, hbaNode
,
470 "hardware-version", (char **)&propdata
)) == -1) {
471 log(LOG_DEBUG
, ROUTINE
,
472 "Property \"%s\" not found for device \"%s\"",
473 "hardware-version", new_hba
->device_path
);
475 (void) strlcpy(new_hba
->adapter_attributes
.HardwareVersion
,
477 sizeof (new_hba
->adapter_attributes
.HardwareVersion
));
481 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, hbaNode
,
482 "driver-version", (char **)&propdata
)) == -1) {
483 log(LOG_DEBUG
, ROUTINE
,
484 "Property \"%s\" not found for device \"%s\"",
485 "driver-version", new_hba
->device_path
);
487 (void) strlcpy(new_hba
->adapter_attributes
.DriverVersion
,
489 sizeof (new_hba
->adapter_attributes
.DriverVersion
));
492 if ((di_prop_lookup_ints(DDI_DEV_T_ANY
, hbaNode
,
493 "supported-protocol", &propIntData
)) == -1) {
494 log(LOG_DEBUG
, ROUTINE
,
495 "Unable to get supported-protocol from HBA node.");
497 protocol
= *propIntData
;
500 /* We don't use these */
501 new_hba
->adapter_attributes
.OptionROMVersion
[0] = '\0';
502 new_hba
->adapter_attributes
.RedundantOptionROMVersion
[0] = '\0';
503 new_hba
->adapter_attributes
.RedundantFirmwareVersion
[0] = '\0';
504 new_hba
->adapter_attributes
.VendorSpecificID
= 0;
506 if ((hba_driver
= di_driver_name(hbaNode
)) != NULL
) {
507 (void) strlcpy(new_hba
->adapter_attributes
.DriverName
,
509 sizeof (new_hba
->adapter_attributes
.DriverName
));
511 log(LOG_DEBUG
, ROUTINE
,
512 "HBA driver name not found for device \"%s\"",
513 new_hba
->device_path
);
517 * Name the adapter: like SUNW-pmcs-1
518 * Using di_instance number as the suffix for the name for persistent
521 (void) snprintf(new_hba
->handle_name
, HANDLE_NAME_LENGTH
, "%s-%s-%d",
522 "SUNW", new_hba
->adapter_attributes
.DriverName
, hba_instance
);
524 if ((portNode
= di_child_node(hbaNode
)) == NULL
) {
525 log(LOG_DEBUG
, ROUTINE
,
526 "HBA driver doesn't have iport child. \"%s\"",
527 new_hba
->device_path
);
528 /* continue on with an hba without any port. */
529 new_hba
->index
= hba_count
++;
532 * add newly created handle into global_hba_head list
534 if (global_hba_head
!= NULL
) {
536 * Make sure to move the open_handles list to back to
537 * the head if it's there (for refresh scenario)
539 if (global_hba_head
->open_handles
) {
540 new_hba
->open_handles
=
541 global_hba_head
->open_handles
;
542 global_hba_head
->open_handles
= NULL
;
544 /* Now bump the new one to the head of the list */
545 new_hba
->next
= global_hba_head
;
546 global_hba_head
= new_hba
;
548 global_hba_head
= new_hba
;
550 return (HBA_STATUS_OK
);
553 while (portNode
!= DI_NODE_NIL
) {
554 if (di_prop_lookup_ints(DDI_DEV_T_ANY
, portNode
,
555 "virtual-port", &propIntData
) >= 0) {
557 /* ignore a virtual port. */
558 portNode
= di_sibling_node(portNode
);
562 if (add_hba_port_info(portNode
, new_hba
, protocol
)
563 == HBA_STATUS_ERROR
) {
564 S_FREE(new_hba
->first_port
);
566 return (HBA_STATUS_ERROR
);
568 portNode
= di_sibling_node(portNode
);
571 new_hba
->index
= hba_count
++;
574 * add newly created handle into global_hba_head list
576 if (global_hba_head
!= NULL
) {
578 * Make sure to move the open_handles list to back to the
579 * head if it's there (for refresh scenario)
581 if (global_hba_head
->open_handles
) {
582 new_hba
->open_handles
= global_hba_head
->open_handles
;
583 global_hba_head
->open_handles
= NULL
;
585 /* Now bump the new one to the head of the list */
586 new_hba
->next
= global_hba_head
;
587 global_hba_head
= new_hba
;
589 global_hba_head
= new_hba
;
592 return (HBA_STATUS_OK
);
596 * Discover information for all HBAs found on the system.
597 * The di_node_t argument should be the root of the device tree.
598 * This routine assumes the locks have been taken
601 lookup_smhba_sas_hba(di_node_t node
, void *arg
)
603 const char ROUTINE
[] = "lookup_smhba_sas_hba";
605 walkarg_t
*wa
= (walkarg_t
*)arg
;
607 /* Skip stub(instance -1) nodes */
608 if (IS_STUB_NODE(node
)) {
609 log(LOG_DEBUG
, ROUTINE
, "Walk continue");
610 return (DI_WALK_CONTINUE
);
613 rval
= di_prop_lookup_ints(DDI_DEV_T_ANY
, node
,
614 "sm-hba-supported", &propData
);
617 /* add the hba to the hba list */
618 if (devtree_get_one_hba(node
) != HBA_STATUS_OK
) {
619 *(wa
->flag
) = B_TRUE
;
621 /* Found a node. No need to walk the child. */
622 log(LOG_DEBUG
, ROUTINE
, "Walk prunechild");
623 return (DI_WALK_PRUNECHILD
);
627 return (DI_WALK_CONTINUE
);
631 * Discover information for all HBAs found on the system.
632 * The di_node_t argument should be the root of the device tree.
633 * This routine assumes the locks have been taken
636 devtree_get_all_hbas(di_node_t root
)
638 const char ROUTINE
[] = "devtree_get_all_hbas";
639 int rv
, ret
= HBA_STATUS_ERROR
;
643 if ((wa
.flag
= (boolean_t
*)calloc(1,
644 sizeof (boolean_t
))) == NULL
) {
645 OUT_OF_MEMORY(ROUTINE
);
646 return (HBA_STATUS_ERROR
);
649 rv
= di_walk_node(root
, DI_WALK_SIBFIRST
, &wa
, lookup_smhba_sas_hba
);
653 * Now determine what status code to return, taking
654 * partial failure scenarios into consideration.
656 * If we have at least one working HBA, then we return an
657 * OK status. If we have no good HBAs, but at least one
658 * failed HBA, we return an ERROR status. If we have
659 * no HBAs and no failures, we return OK.
661 if (global_hba_head
) {
663 * We've got at least one HBA and possibly some
667 } else if (*(wa
.flag
)) {
668 /* We have no HBAs but have failures */
669 ret
= HBA_STATUS_ERROR
;
671 /* We have no HBAs and no failures */
679 if (ret
== HBA_STATUS_OK
)
680 (void) registerSysevent();