16664 Update AMD microcode to 20240710
[illumos-gate.git] / usr / src / lib / sun_sas / common / devtree_hba_disco.c
blob2017f97e26bbb7bd33782e4a85ccbc1134381158
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
26 * Copyright 2019 Joyent, Inc.
29 #include <sun_sas.h>
30 #include <sys/modctl.h>
31 #include <sys/types.h>
32 #include <netinet/in.h>
33 #include <inttypes.h>
34 #include <ctype.h>
36 struct sun_sas_hba *global_hba_head;
38 /* free hba port info for the given hba */
39 static void
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;
62 free(last_scsi_info);
64 last_tgt_port = tgt_port;
65 tgt_port = tgt_port->next;
66 free(last_tgt_port->port_attributes.\
67 PortSpecificAttribute.SASPort);
68 free(last_tgt_port);
70 hba_port->first_attached_port = NULL;
72 phy_ptr = hba_port->first_phy;
73 while (phy_ptr != NULL) {
74 last_phy = phy_ptr;
75 phy_ptr = phy_ptr->next;
76 free(last_phy);
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);
84 free(last_hba_port);
87 hba_ptr->first_port = NULL;
91 * Internal routine for adding an HBA port
93 static HBA_STATUS
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;
98 char *portDevpath;
99 int *propIntData;
100 char *propStringData;
101 uint64_t tmpAddr;
102 char *charptr, cntlLink[MAXPATHLEN] = {'\0'};
103 int rval;
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)))
121 == NULL) {
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);
130 S_FREE(port_ptr);
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. */
145 di_fini(branchNode);
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);
150 S_FREE(port_ptr);
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",
159 portDevpath);
160 port_ptr->port_attributes.PortState = HBA_PORTSTATE_OFFLINE;
161 } else {
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) ==
170 HBA_STATUS_OK) {
171 (void) strlcpy(port_ptr->port_attributes.OSDeviceName, cntlLink,
172 sizeof (port_ptr->port_attributes.OSDeviceName));
173 if ((charptr = strrchr(cntlLink, '/')) != NULL) {
174 charptr++;
176 if (charptr[0] == 'c') {
177 port_ptr->cntlNumber = atoi(++charptr);
178 } else {
179 port_ptr->cntlNumber = -1;
181 } else {
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);
194 if (rval < 0) {
195 log(LOG_DEBUG, ROUTINE,
196 "Unable to get initiator-port from HBA port node %s.",
197 port_ptr->port_attributes.OSDeviceName);
198 di_fini(branchNode);
199 S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
200 S_FREE(port_ptr);
201 return (HBA_STATUS_ERROR);
202 } else {
203 for (charptr = propStringData; *charptr != '\0'; charptr++) {
204 if (isxdigit(*charptr)) {
205 break;
208 if (*charptr != '\0') {
209 tmpAddr = htonll(strtoll(charptr, NULL, 16));
210 (void) memcpy(port_ptr->port_attributes.
211 PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
212 &tmpAddr, 8);
213 } else {
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);
222 if (rval < 0) {
223 log(LOG_DEBUG, ROUTINE,
224 "Unable to get attached-port from HBA port node %s.",
225 port_ptr->port_attributes.OSDeviceName);
226 di_fini(branchNode);
227 S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
228 S_FREE(port_ptr);
229 return (HBA_STATUS_ERROR);
230 } else {
231 for (charptr = propStringData; *charptr != '\0'; charptr++) {
232 if (isxdigit(*charptr)) {
233 break;
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);
241 } else {
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);
254 if (rval < 0) {
255 log(LOG_DEBUG, ROUTINE,
256 "Unable to get NumberofPhys from HBA port %s.",
257 port_ptr->port_attributes.OSDeviceName);
258 di_fini(branchNode);
259 S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
260 S_FREE(port_ptr);
261 return (HBA_STATUS_ERROR);
262 } else {
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);
273 di_fini(branchNode);
274 S_FREE(port_ptr->port_attributes.
275 PortSpecificAttribute.SASPort);
276 S_FREE(port_ptr);
277 return (HBA_STATUS_ERROR);
281 /* now done with prop checking. remove branchNode. */
282 di_fini(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);
290 S_FREE(port_ptr);
291 return (HBA_STATUS_ERROR);
294 fillDomainPortWWN(port_ptr);
296 /* add new port onto hba handle list */
297 if (hba_ptr->first_port == NULL) {
298 port_ptr->index = 0;
299 hba_ptr->first_port = port_ptr;
300 } else {
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);
309 HBA_STATUS
310 refresh_hba(di_node_t hbaNode, struct sun_sas_hba *hba_ptr)
312 const char ROUTINE[] = "refresh_hba";
313 di_node_t portNode;
314 int protocol = 0;
315 int *propIntData;
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.");
333 } else {
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) {
340 if (*propIntData) {
341 /* ignore a virtual port. */
342 portNode = di_sibling_node(portNode);
343 continue;
346 if (add_hba_port_info(portNode, hba_ptr, protocol)
347 == HBA_STATUS_ERROR) {
348 S_FREE(hba_ptr->first_port);
349 S_FREE(hba_ptr);
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
361 * to true.
362 * Without iport support, the devinfo node will represent one port hba.
363 * This routine assumes the locks have been taken.
365 HBA_STATUS
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;
373 int protocol = 0;
374 di_node_t portNode;
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;
393 hba_ptr != NULL;
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) !=
398 HBA_STATUS_OK) {
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);
424 /* Manufacturer */
425 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
426 "Manufacturer", (char **)&propdata)) == -1) {
427 (void) strlcpy(new_hba->adapter_attributes.Manufacturer,
428 SUN_MICROSYSTEMS,
429 sizeof (new_hba->adapter_attributes.Manufacturer));
430 } else {
431 (void) strlcpy(new_hba->adapter_attributes.Manufacturer,
432 propdata,
433 sizeof (new_hba->adapter_attributes.Manufacturer));
436 /* SerialNumber */
437 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
438 "SerialNumber", (char **)&propdata)) == -1) {
439 new_hba->adapter_attributes.SerialNumber[0] = '\0';
440 } else {
441 (void) strlcpy(new_hba->adapter_attributes.SerialNumber,
442 propdata,
443 sizeof (new_hba->adapter_attributes.SerialNumber));
446 /* Model */
447 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
448 "ModelName", (char **)&propdata)) == -1) {
449 new_hba->adapter_attributes.Model[0] = '\0';
450 } else {
451 (void) strlcpy(new_hba->adapter_attributes.Model,
452 propdata,
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);
462 } else {
463 (void) strlcpy(new_hba->adapter_attributes.FirmwareVersion,
464 propdata,
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);
474 } else {
475 (void) strlcpy(new_hba->adapter_attributes.HardwareVersion,
476 propdata,
477 sizeof (new_hba->adapter_attributes.HardwareVersion));
480 /* DriverVersion */
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);
486 } else {
487 (void) strlcpy(new_hba->adapter_attributes.DriverVersion,
488 propdata,
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.");
496 } else {
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,
508 hba_driver,
509 sizeof (new_hba->adapter_attributes.DriverName));
510 } else {
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
519 * among rebooting.
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;
547 } else {
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) {
556 if (*propIntData) {
557 /* ignore a virtual port. */
558 portNode = di_sibling_node(portNode);
559 continue;
562 if (add_hba_port_info(portNode, new_hba, protocol)
563 == HBA_STATUS_ERROR) {
564 S_FREE(new_hba->first_port);
565 S_FREE(new_hba);
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;
588 } else {
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
600 static int
601 lookup_smhba_sas_hba(di_node_t node, void *arg)
603 const char ROUTINE[] = "lookup_smhba_sas_hba";
604 int *propData, rval;
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);
615 if (rval >= 0) {
616 if (*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
635 HBA_STATUS
636 devtree_get_all_hbas(di_node_t root)
638 const char ROUTINE[] = "devtree_get_all_hbas";
639 int rv, ret = HBA_STATUS_ERROR;
640 walkarg_t wa;
642 wa.devpath = NULL;
643 if ((wa.flag = (boolean_t *)calloc(1,
644 sizeof (boolean_t))) == NULL) {
645 OUT_OF_MEMORY(ROUTINE);
646 return (HBA_STATUS_ERROR);
648 *wa.flag = B_FALSE;
649 rv = di_walk_node(root, DI_WALK_SIBFIRST, &wa, lookup_smhba_sas_hba);
651 if (rv == 0) {
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
664 * failures.
666 ret = HBA_STATUS_OK;
667 } else if (*(wa.flag)) {
668 /* We have no HBAs but have failures */
669 ret = HBA_STATUS_ERROR;
670 } else {
671 /* We have no HBAs and no failures */
672 ret = HBA_STATUS_OK;
677 S_FREE(wa.flag);
679 if (ret == HBA_STATUS_OK)
680 (void) registerSysevent();
682 return (ret);