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.
27 #include <sys/modctl.h>
28 #include <sys/types.h>
29 #include <netinet/in.h>
33 /* free hba port info for the given hba */
35 free_hba_port(struct sun_sas_hba
*hba_ptr
)
37 struct sun_sas_port
*hba_port
= NULL
;
38 struct sun_sas_port
*last_hba_port
= NULL
;
39 struct sun_sas_port
*tgt_port
= NULL
;
40 struct sun_sas_port
*last_tgt_port
= NULL
;
41 struct ScsiEntryList
*scsi_info
= NULL
;
42 struct ScsiEntryList
*last_scsi_info
= NULL
;
43 struct phy_info
*phy_ptr
= NULL
;
44 struct phy_info
*last_phy
= NULL
;
46 /* Free the nested structures (port and attached port) */
47 hba_port
= hba_ptr
->first_port
;
48 while (hba_port
!= NULL
) {
49 /* Free discovered port structure list. */
50 tgt_port
= hba_port
->first_attached_port
;
51 while (tgt_port
!= NULL
) {
52 /* Free target mapping data list first. */
53 scsi_info
= tgt_port
->scsiInfo
;
54 while (scsi_info
!= NULL
) {
55 last_scsi_info
= scsi_info
;
56 scsi_info
= scsi_info
->next
;
59 last_tgt_port
= tgt_port
;
60 tgt_port
= tgt_port
->next
;
61 free(last_tgt_port
->port_attributes
.\
62 PortSpecificAttribute
.SASPort
);
65 hba_port
->first_attached_port
= NULL
;
67 phy_ptr
= hba_port
->first_phy
;
68 while (phy_ptr
!= NULL
) {
70 phy_ptr
= phy_ptr
->next
;
73 hba_port
->first_phy
= NULL
;
75 last_hba_port
= hba_port
;
76 hba_port
= hba_port
->next
;
77 free(last_hba_port
->port_attributes
.\
78 PortSpecificAttribute
.SASPort
);
82 hba_ptr
->first_port
= NULL
;
86 * Internal routine for adding an HBA port
89 add_hba_port_info(di_node_t portNode
, struct sun_sas_hba
*hba_ptr
, int protocol
)
91 const char ROUTINE
[] = "add_hba_port_info";
92 struct sun_sas_port
*port_ptr
;
97 char *charptr
, cntlLink
[MAXPATHLEN
] = {'\0'};
100 uint_t state
= HBA_PORTSTATE_UNKNOWN
;
102 if (hba_ptr
== NULL
) {
103 log(LOG_DEBUG
, ROUTINE
,
104 "Sun_sas handle ptr set to NULL.");
105 return (HBA_STATUS_ERROR_ARG
);
108 if ((port_ptr
= (struct sun_sas_port
*)calloc(1,
109 sizeof (struct sun_sas_port
))) == NULL
) {
110 OUT_OF_MEMORY(ROUTINE
);
111 return (HBA_STATUS_ERROR
);
114 if ((port_ptr
->port_attributes
.PortSpecificAttribute
.SASPort
=
115 (struct SMHBA_SAS_Port
*)calloc(1, sizeof (struct SMHBA_SAS_Port
)))
117 OUT_OF_MEMORY(ROUTINE
);
118 return (HBA_STATUS_ERROR
);
121 if ((portDevpath
= di_devfs_path(portNode
)) == NULL
) {
122 log(LOG_DEBUG
, ROUTINE
,
123 "Unable to get device path from HBA Port Node.");
124 S_FREE(port_ptr
->port_attributes
.PortSpecificAttribute
.SASPort
);
126 return (HBA_STATUS_ERROR
);
130 * Let's take a branch snap shot for pulling attributes.
131 * The attribute change doesn't invalidate devinfo cache snapshot.
132 * Phy info prop and num-phys can be obsolate when the same hba
133 * connected to the same expander(SIM) thus phy numbers are increased.
134 * Also the phy number may get decreased when a connection is removed
135 * while the iport still exist through another connection.
137 branchNode
= di_init(portDevpath
, DINFOPROP
);
138 if (branchNode
== DI_NODE_NIL
) {
139 /* something is wrong here. */
141 log(LOG_DEBUG
, ROUTINE
,
142 "Unable to take devinfoi branch snapshot on HBA port \"%s\""
143 " due to %s", portDevpath
, strerror(errno
));
144 S_FREE(port_ptr
->port_attributes
.PortSpecificAttribute
.SASPort
);
146 return (HBA_STATUS_ERROR
);
149 state
= di_state(portNode
);
150 if (((state
& DI_DRIVER_DETACHED
) == DI_DRIVER_DETACHED
) ||
151 ((state
& DI_DEVICE_OFFLINE
) == DI_DEVICE_OFFLINE
)) {
152 log(LOG_DEBUG
, ROUTINE
,
153 "HBA port node %s is either OFFLINE or DETACHED",
155 port_ptr
->port_attributes
.PortState
= HBA_PORTSTATE_OFFLINE
;
157 port_ptr
->port_attributes
.PortState
= HBA_PORTSTATE_ONLINE
;
160 port_ptr
->port_attributes
.PortType
= HBA_PORTTYPE_SASDEVICE
;
162 (void) strlcpy(port_ptr
->device_path
, portDevpath
, MAXPATHLEN
+ 1);
164 if (lookupControllerLink(portDevpath
, (char *)cntlLink
) ==
166 (void) strlcpy(port_ptr
->port_attributes
.OSDeviceName
, cntlLink
,
167 sizeof (port_ptr
->port_attributes
.OSDeviceName
));
168 if ((charptr
= strrchr(cntlLink
, '/')) != NULL
) {
171 if (charptr
[0] == 'c') {
172 port_ptr
->cntlNumber
= atoi(++charptr
);
174 port_ptr
->cntlNumber
= -1;
177 (void) snprintf(port_ptr
->port_attributes
.OSDeviceName
,
178 sizeof (port_ptr
->port_attributes
.OSDeviceName
),
179 "%s%s%s", DEVICES_DIR
, portDevpath
, SCSI_SUFFIX
);
182 di_devfs_path_free(portDevpath
);
184 port_ptr
->port_attributes
.PortSpecificAttribute
.
185 SASPort
->PortProtocol
= protocol
;
187 rval
= di_prop_lookup_strings(DDI_DEV_T_ANY
, branchNode
,
188 "initiator-port", &propStringData
);
190 log(LOG_DEBUG
, ROUTINE
,
191 "Unable to get initiator-port from HBA port node %s.",
192 port_ptr
->port_attributes
.OSDeviceName
);
194 S_FREE(port_ptr
->port_attributes
.PortSpecificAttribute
.SASPort
);
196 return (HBA_STATUS_ERROR
);
198 for (charptr
= propStringData
; *charptr
!= '\0'; charptr
++) {
199 if (isxdigit(*charptr
)) {
203 if (*charptr
!= '\0') {
204 tmpAddr
= htonll(strtoll(charptr
, NULL
, 16));
205 (void) memcpy(port_ptr
->port_attributes
.
206 PortSpecificAttribute
.SASPort
->LocalSASAddress
.wwn
,
209 log(LOG_DEBUG
, ROUTINE
,
210 "No proper intiator-port prop value on HBA port %s",
211 port_ptr
->port_attributes
.OSDeviceName
);
215 rval
= di_prop_lookup_strings(DDI_DEV_T_ANY
, branchNode
,
216 "attached-port", &propStringData
);
218 log(LOG_DEBUG
, ROUTINE
,
219 "Unable to get attached-port from HBA port node %s.",
220 port_ptr
->port_attributes
.OSDeviceName
);
222 S_FREE(port_ptr
->port_attributes
.PortSpecificAttribute
.SASPort
);
224 return (HBA_STATUS_ERROR
);
226 for (charptr
= propStringData
; *charptr
!= '\0'; charptr
++) {
227 if (isxdigit(*charptr
)) {
231 if (*charptr
!= '\0') {
232 tmpAddr
= htonll(strtoll(charptr
, NULL
, 16));
233 (void) memcpy(port_ptr
->port_attributes
.
234 PortSpecificAttribute
.SASPort
->
235 AttachedSASAddress
.wwn
, &tmpAddr
, 8);
237 /* continue even if the attached port is NULL. */
238 log(LOG_DEBUG
, ROUTINE
,
239 "No proper attached-port prop value: "
240 "HBA port Local SAS Address(%016llx)",
241 wwnConversion(port_ptr
->port_attributes
.
242 PortSpecificAttribute
.
243 SASPort
->LocalSASAddress
.wwn
));
247 rval
= di_prop_lookup_ints(DDI_DEV_T_ANY
, branchNode
,
248 "num-phys", &propIntData
);
250 log(LOG_DEBUG
, ROUTINE
,
251 "Unable to get NumberofPhys from HBA port %s.",
252 port_ptr
->port_attributes
.OSDeviceName
);
254 S_FREE(port_ptr
->port_attributes
.PortSpecificAttribute
.SASPort
);
256 return (HBA_STATUS_ERROR
);
258 port_ptr
->port_attributes
.PortSpecificAttribute
.\
259 SASPort
->NumberofPhys
= *propIntData
;
262 if (port_ptr
->port_attributes
.PortSpecificAttribute
.\
263 SASPort
->NumberofPhys
> 0) {
264 if (get_phy_info(branchNode
, port_ptr
) != HBA_STATUS_OK
) {
265 log(LOG_DEBUG
, ROUTINE
,
266 "Failed to get phy info on HBA port %s.",
267 port_ptr
->port_attributes
.OSDeviceName
);
269 S_FREE(port_ptr
->port_attributes
.
270 PortSpecificAttribute
.SASPort
);
272 return (HBA_STATUS_ERROR
);
276 /* now done with prop checking. remove branchNode. */
279 /* Construct discovered target port. */
280 if (devtree_attached_devices(portNode
, port_ptr
) != HBA_STATUS_OK
) {
281 log(LOG_DEBUG
, ROUTINE
,
282 "Failed to get attached device info HBA port %s.",
283 port_ptr
->port_attributes
.OSDeviceName
);
284 S_FREE(port_ptr
->port_attributes
.PortSpecificAttribute
.SASPort
);
286 return (HBA_STATUS_ERROR
);
289 fillDomainPortWWN(port_ptr
);
291 /* add new port onto hba handle list */
292 if (hba_ptr
->first_port
== NULL
) {
294 hba_ptr
->first_port
= port_ptr
;
296 port_ptr
->index
= hba_ptr
->first_port
->index
+ 1;
297 port_ptr
->next
= hba_ptr
->first_port
;
298 hba_ptr
->first_port
= port_ptr
;
301 return (HBA_STATUS_OK
);
305 refresh_hba(di_node_t hbaNode
, struct sun_sas_hba
*hba_ptr
)
307 const char ROUTINE
[] = "refresh_hba";
313 * clean up existing hba port, discovered target, phy info.
314 * leave open handles intact.
316 free_hba_port(hba_ptr
);
318 if ((portNode
= di_child_node(hbaNode
)) == NULL
) {
319 log(LOG_DEBUG
, ROUTINE
,
320 "HBA node doesn't have iport child.");
321 return (HBA_STATUS_ERROR
);
324 if ((di_prop_lookup_ints(DDI_DEV_T_ANY
, hbaNode
,
325 "supported-protocol", &propIntData
)) == -1) {
326 log(LOG_DEBUG
, ROUTINE
,
327 "Unable to get supported-protocol from HBA node.");
329 protocol
= *propIntData
;
332 while (portNode
!= DI_NODE_NIL
) {
333 if (di_prop_lookup_ints(DDI_DEV_T_ANY
, portNode
,
334 "virtual-port", &propIntData
) >= 0) {
336 /* ignore a virtual port. */
337 portNode
= di_sibling_node(portNode
);
341 if (add_hba_port_info(portNode
, hba_ptr
, protocol
)
342 == HBA_STATUS_ERROR
) {
343 S_FREE(hba_ptr
->first_port
);
345 return (HBA_STATUS_ERROR
);
347 portNode
= di_sibling_node(portNode
);
350 return (HBA_STATUS_OK
);
354 * Discover information for one HBA in the device tree.
355 * The di_node_t argument should be a node with smhba-supported prop set
357 * Without iport support, the devinfo node will represent one port hba.
358 * This routine assumes the locks have been taken.
361 devtree_get_one_hba(di_node_t hbaNode
)
363 const char ROUTINE
[] = "devtree_get_one_hba";
364 char *propdata
= NULL
;
365 int *propIntData
= NULL
;
366 struct sun_sas_hba
*new_hba
, *hba_ptr
;
367 char *hbaDevpath
, *hba_driver
;
370 int hba_instance
= -1;
372 hba_instance
= di_instance(hbaNode
);
373 if (hba_instance
== -1) {
374 log(LOG_DEBUG
, ROUTINE
,
375 "portNode has instance of -1");
376 return (DI_WALK_CONTINUE
);
379 if ((hbaDevpath
= di_devfs_path(hbaNode
)) == NULL
) {
380 log(LOG_DEBUG
, ROUTINE
, "Unable to get "
381 "device path from hbaNode");
382 return (HBA_STATUS_ERROR
);
385 /* check to see if this is a repeat HBA */
386 if (global_hba_head
) {
387 for (hba_ptr
= global_hba_head
;
389 hba_ptr
= hba_ptr
->next
) {
390 if ((strncmp(hba_ptr
->device_path
, hbaDevpath
,
391 strlen(hbaDevpath
))) == 0) {
392 if (refresh_hba(hbaNode
, hba_ptr
) !=
394 log(LOG_DEBUG
, ROUTINE
, "Refresh failed"
395 " on hbaNode %s", hbaDevpath
);
397 di_devfs_path_free(hbaDevpath
);
398 return (HBA_STATUS_OK
);
403 /* this is a new hba */
404 if ((new_hba
= (struct sun_sas_hba
*)calloc(1,
405 sizeof (struct sun_sas_hba
))) == NULL
) {
406 OUT_OF_MEMORY(ROUTINE
);
407 di_devfs_path_free(hbaDevpath
);
408 return (HBA_STATUS_ERROR
);
411 (void) strlcpy(new_hba
->device_path
, hbaDevpath
,
412 sizeof (new_hba
->device_path
));
413 di_devfs_path_free(hbaDevpath
);
415 (void) snprintf(new_hba
->adapter_attributes
.HBASymbolicName
,
416 sizeof (new_hba
->adapter_attributes
.HBASymbolicName
),
417 "%s%s", DEVICES_DIR
, new_hba
->device_path
);
420 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, hbaNode
,
421 "Manufacturer", (char **)&propdata
)) == -1) {
422 (void) strlcpy(new_hba
->adapter_attributes
.Manufacturer
,
424 sizeof (new_hba
->adapter_attributes
.Manufacturer
));
426 (void) strlcpy(new_hba
->adapter_attributes
.Manufacturer
,
428 sizeof (new_hba
->adapter_attributes
.Manufacturer
));
432 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, hbaNode
,
433 "SerialNumber", (char **)&propdata
)) == -1) {
434 new_hba
->adapter_attributes
.SerialNumber
[0] = '\0';
436 (void) strlcpy(new_hba
->adapter_attributes
.SerialNumber
,
438 sizeof (new_hba
->adapter_attributes
.SerialNumber
));
442 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, hbaNode
,
443 "ModelName", (char **)&propdata
)) == -1) {
444 new_hba
->adapter_attributes
.Model
[0] = '\0';
446 (void) strlcpy(new_hba
->adapter_attributes
.Model
,
448 sizeof (new_hba
->adapter_attributes
.Model
));
451 /* FirmwareVersion */
452 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, hbaNode
,
453 "firmware-version", (char **)&propdata
)) == -1) {
454 log(LOG_DEBUG
, ROUTINE
,
455 "Property \"%s\" not found for device \"%s\"",
456 "firmware-version", new_hba
->device_path
);
458 (void) strlcpy(new_hba
->adapter_attributes
.FirmwareVersion
,
460 sizeof (new_hba
->adapter_attributes
.FirmwareVersion
));
463 /* HardwareVersion */
464 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, hbaNode
,
465 "hardware-version", (char **)&propdata
)) == -1) {
466 log(LOG_DEBUG
, ROUTINE
,
467 "Property \"%s\" not found for device \"%s\"",
468 "hardware-version", new_hba
->device_path
);
470 (void) strlcpy(new_hba
->adapter_attributes
.HardwareVersion
,
472 sizeof (new_hba
->adapter_attributes
.HardwareVersion
));
476 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, hbaNode
,
477 "driver-version", (char **)&propdata
)) == -1) {
478 log(LOG_DEBUG
, ROUTINE
,
479 "Property \"%s\" not found for device \"%s\"",
480 "driver-version", new_hba
->device_path
);
482 (void) strlcpy(new_hba
->adapter_attributes
.DriverVersion
,
484 sizeof (new_hba
->adapter_attributes
.DriverVersion
));
487 if ((di_prop_lookup_ints(DDI_DEV_T_ANY
, hbaNode
,
488 "supported-protocol", &propIntData
)) == -1) {
489 log(LOG_DEBUG
, ROUTINE
,
490 "Unable to get supported-protocol from HBA node.");
492 protocol
= *propIntData
;
495 /* We don't use these */
496 new_hba
->adapter_attributes
.OptionROMVersion
[0] = '\0';
497 new_hba
->adapter_attributes
.RedundantOptionROMVersion
[0] = '\0';
498 new_hba
->adapter_attributes
.RedundantFirmwareVersion
[0] = '\0';
499 new_hba
->adapter_attributes
.VendorSpecificID
= 0;
501 if ((hba_driver
= di_driver_name(hbaNode
)) != NULL
) {
502 (void) strlcpy(new_hba
->adapter_attributes
.DriverName
,
504 sizeof (new_hba
->adapter_attributes
.DriverName
));
506 log(LOG_DEBUG
, ROUTINE
,
507 "HBA driver name not found for device \"%s\"",
508 new_hba
->device_path
);
512 * Name the adapter: like SUNW-pmcs-1
513 * Using di_instance number as the suffix for the name for persistent
516 (void) snprintf(new_hba
->handle_name
, HANDLE_NAME_LENGTH
, "%s-%s-%d",
517 "SUNW", new_hba
->adapter_attributes
.DriverName
, hba_instance
);
519 if ((portNode
= di_child_node(hbaNode
)) == NULL
) {
520 log(LOG_DEBUG
, ROUTINE
,
521 "HBA driver doesn't have iport child. \"%s\"",
522 new_hba
->device_path
);
523 /* continue on with an hba without any port. */
524 new_hba
->index
= hba_count
++;
527 * add newly created handle into global_hba_head list
529 if (global_hba_head
!= NULL
) {
531 * Make sure to move the open_handles list to back to
532 * the head if it's there (for refresh scenario)
534 if (global_hba_head
->open_handles
) {
535 new_hba
->open_handles
=
536 global_hba_head
->open_handles
;
537 global_hba_head
->open_handles
= NULL
;
539 /* Now bump the new one to the head of the list */
540 new_hba
->next
= global_hba_head
;
541 global_hba_head
= new_hba
;
543 global_hba_head
= new_hba
;
545 return (HBA_STATUS_OK
);
548 while (portNode
!= DI_NODE_NIL
) {
549 if (di_prop_lookup_ints(DDI_DEV_T_ANY
, portNode
,
550 "virtual-port", &propIntData
) >= 0) {
552 /* ignore a virtual port. */
553 portNode
= di_sibling_node(portNode
);
557 if (add_hba_port_info(portNode
, new_hba
, protocol
)
558 == HBA_STATUS_ERROR
) {
559 S_FREE(new_hba
->first_port
);
561 return (HBA_STATUS_ERROR
);
563 portNode
= di_sibling_node(portNode
);
566 new_hba
->index
= hba_count
++;
569 * add newly created handle into global_hba_head list
571 if (global_hba_head
!= NULL
) {
573 * Make sure to move the open_handles list to back to the
574 * head if it's there (for refresh scenario)
576 if (global_hba_head
->open_handles
) {
577 new_hba
->open_handles
= global_hba_head
->open_handles
;
578 global_hba_head
->open_handles
= NULL
;
580 /* Now bump the new one to the head of the list */
581 new_hba
->next
= global_hba_head
;
582 global_hba_head
= new_hba
;
584 global_hba_head
= new_hba
;
587 return (HBA_STATUS_OK
);
591 * Discover information for all HBAs found on the system.
592 * The di_node_t argument should be the root of the device tree.
593 * This routine assumes the locks have been taken
596 lookup_smhba_sas_hba(di_node_t node
, void *arg
)
598 const char ROUTINE
[] = "lookup_smhba_sas_hba";
600 walkarg_t
*wa
= (walkarg_t
*)arg
;
602 /* Skip stub(instance -1) nodes */
603 if (IS_STUB_NODE(node
)) {
604 log(LOG_DEBUG
, ROUTINE
, "Walk continue");
605 return (DI_WALK_CONTINUE
);
608 rval
= di_prop_lookup_ints(DDI_DEV_T_ANY
, node
,
609 "sm-hba-supported", &propData
);
612 /* add the hba to the hba list */
613 if (devtree_get_one_hba(node
) != HBA_STATUS_OK
) {
614 *(wa
->flag
) = B_TRUE
;
616 /* Found a node. No need to walk the child. */
617 log(LOG_DEBUG
, ROUTINE
, "Walk prunechild");
618 return (DI_WALK_PRUNECHILD
);
622 return (DI_WALK_CONTINUE
);
626 * Discover information for all HBAs found on the system.
627 * The di_node_t argument should be the root of the device tree.
628 * This routine assumes the locks have been taken
631 devtree_get_all_hbas(di_node_t root
)
633 const char ROUTINE
[] = "devtree_get_all_hbas";
634 int rv
, ret
= HBA_STATUS_ERROR
;
638 if ((wa
.flag
= (boolean_t
*)calloc(1,
639 sizeof (boolean_t
))) == NULL
) {
640 OUT_OF_MEMORY(ROUTINE
);
641 return (HBA_STATUS_ERROR
);
644 rv
= di_walk_node(root
, DI_WALK_SIBFIRST
, &wa
, lookup_smhba_sas_hba
);
648 * Now determine what status code to return, taking
649 * partial failure scenarios into consideration.
651 * If we have at least one working HBA, then we return an
652 * OK status. If we have no good HBAs, but at least one
653 * failed HBA, we return an ERROR status. If we have
654 * no HBAs and no failures, we return OK.
656 if (global_hba_head
) {
658 * We've got at least one HBA and possibly some
662 } else if (*(wa
.flag
)) {
663 /* We have no HBAs but have failures */
664 ret
= HBA_STATUS_ERROR
;
666 /* We have no HBAs and no failures */
674 if (ret
== HBA_STATUS_OK
)
675 (void) registerSysevent();