libuutil: move under bmake
[unleashed.git] / usr / src / lib / sun_sas / common / devtree_hba_disco.c
blob2ba9ef6e5740dcda000919ff9ad3f43fe2ccee3e
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 #include <sun_sas.h>
27 #include <sys/modctl.h>
28 #include <sys/types.h>
29 #include <netinet/in.h>
30 #include <inttypes.h>
31 #include <ctype.h>
33 /* free hba port info for the given hba */
34 static void
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;
57 free(last_scsi_info);
59 last_tgt_port = tgt_port;
60 tgt_port = tgt_port->next;
61 free(last_tgt_port->port_attributes.\
62 PortSpecificAttribute.SASPort);
63 free(last_tgt_port);
65 hba_port->first_attached_port = NULL;
67 phy_ptr = hba_port->first_phy;
68 while (phy_ptr != NULL) {
69 last_phy = phy_ptr;
70 phy_ptr = phy_ptr->next;
71 free(last_phy);
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);
79 free(last_hba_port);
82 hba_ptr->first_port = NULL;
86 * Internal routine for adding an HBA port
88 static HBA_STATUS
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;
93 char *portDevpath;
94 int *propIntData;
95 char *propStringData;
96 uint64_t tmpAddr;
97 char *charptr, cntlLink[MAXPATHLEN] = {'\0'};
98 int rval;
99 di_node_t branchNode;
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)))
116 == NULL) {
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);
125 S_FREE(port_ptr);
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. */
140 di_fini(branchNode);
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);
145 S_FREE(port_ptr);
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",
154 portDevpath);
155 port_ptr->port_attributes.PortState = HBA_PORTSTATE_OFFLINE;
156 } else {
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) ==
165 HBA_STATUS_OK) {
166 (void) strlcpy(port_ptr->port_attributes.OSDeviceName, cntlLink,
167 sizeof (port_ptr->port_attributes.OSDeviceName));
168 if ((charptr = strrchr(cntlLink, '/')) != NULL) {
169 charptr++;
171 if (charptr[0] == 'c') {
172 port_ptr->cntlNumber = atoi(++charptr);
173 } else {
174 port_ptr->cntlNumber = -1;
176 } else {
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);
189 if (rval < 0) {
190 log(LOG_DEBUG, ROUTINE,
191 "Unable to get initiator-port from HBA port node %s.",
192 port_ptr->port_attributes.OSDeviceName);
193 di_fini(branchNode);
194 S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
195 S_FREE(port_ptr);
196 return (HBA_STATUS_ERROR);
197 } else {
198 for (charptr = propStringData; *charptr != '\0'; charptr++) {
199 if (isxdigit(*charptr)) {
200 break;
203 if (*charptr != '\0') {
204 tmpAddr = htonll(strtoll(charptr, NULL, 16));
205 (void) memcpy(port_ptr->port_attributes.
206 PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
207 &tmpAddr, 8);
208 } else {
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);
217 if (rval < 0) {
218 log(LOG_DEBUG, ROUTINE,
219 "Unable to get attached-port from HBA port node %s.",
220 port_ptr->port_attributes.OSDeviceName);
221 di_fini(branchNode);
222 S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
223 S_FREE(port_ptr);
224 return (HBA_STATUS_ERROR);
225 } else {
226 for (charptr = propStringData; *charptr != '\0'; charptr++) {
227 if (isxdigit(*charptr)) {
228 break;
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);
236 } else {
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);
249 if (rval < 0) {
250 log(LOG_DEBUG, ROUTINE,
251 "Unable to get NumberofPhys from HBA port %s.",
252 port_ptr->port_attributes.OSDeviceName);
253 di_fini(branchNode);
254 S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
255 S_FREE(port_ptr);
256 return (HBA_STATUS_ERROR);
257 } else {
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);
268 di_fini(branchNode);
269 S_FREE(port_ptr->port_attributes.
270 PortSpecificAttribute.SASPort);
271 S_FREE(port_ptr);
272 return (HBA_STATUS_ERROR);
276 /* now done with prop checking. remove branchNode. */
277 di_fini(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);
285 S_FREE(port_ptr);
286 return (HBA_STATUS_ERROR);
289 fillDomainPortWWN(port_ptr);
291 /* add new port onto hba handle list */
292 if (hba_ptr->first_port == NULL) {
293 port_ptr->index = 0;
294 hba_ptr->first_port = port_ptr;
295 } else {
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);
304 HBA_STATUS
305 refresh_hba(di_node_t hbaNode, struct sun_sas_hba *hba_ptr)
307 const char ROUTINE[] = "refresh_hba";
308 di_node_t portNode;
309 int protocol = 0;
310 int *propIntData;
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.");
328 } else {
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) {
335 if (*propIntData) {
336 /* ignore a virtual port. */
337 portNode = di_sibling_node(portNode);
338 continue;
341 if (add_hba_port_info(portNode, hba_ptr, protocol)
342 == HBA_STATUS_ERROR) {
343 S_FREE(hba_ptr->first_port);
344 S_FREE(hba_ptr);
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
356 * to true.
357 * Without iport support, the devinfo node will represent one port hba.
358 * This routine assumes the locks have been taken.
360 HBA_STATUS
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;
368 int protocol = 0;
369 di_node_t portNode;
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;
388 hba_ptr != NULL;
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) !=
393 HBA_STATUS_OK) {
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);
419 /* Manufacturer */
420 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
421 "Manufacturer", (char **)&propdata)) == -1) {
422 (void) strlcpy(new_hba->adapter_attributes.Manufacturer,
423 SUN_MICROSYSTEMS,
424 sizeof (new_hba->adapter_attributes.Manufacturer));
425 } else {
426 (void) strlcpy(new_hba->adapter_attributes.Manufacturer,
427 propdata,
428 sizeof (new_hba->adapter_attributes.Manufacturer));
431 /* SerialNumber */
432 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
433 "SerialNumber", (char **)&propdata)) == -1) {
434 new_hba->adapter_attributes.SerialNumber[0] = '\0';
435 } else {
436 (void) strlcpy(new_hba->adapter_attributes.SerialNumber,
437 propdata,
438 sizeof (new_hba->adapter_attributes.SerialNumber));
441 /* Model */
442 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
443 "ModelName", (char **)&propdata)) == -1) {
444 new_hba->adapter_attributes.Model[0] = '\0';
445 } else {
446 (void) strlcpy(new_hba->adapter_attributes.Model,
447 propdata,
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);
457 } else {
458 (void) strlcpy(new_hba->adapter_attributes.FirmwareVersion,
459 propdata,
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);
469 } else {
470 (void) strlcpy(new_hba->adapter_attributes.HardwareVersion,
471 propdata,
472 sizeof (new_hba->adapter_attributes.HardwareVersion));
475 /* DriverVersion */
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);
481 } else {
482 (void) strlcpy(new_hba->adapter_attributes.DriverVersion,
483 propdata,
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.");
491 } else {
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,
503 hba_driver,
504 sizeof (new_hba->adapter_attributes.DriverName));
505 } else {
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
514 * among rebooting.
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;
542 } else {
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) {
551 if (*propIntData) {
552 /* ignore a virtual port. */
553 portNode = di_sibling_node(portNode);
554 continue;
557 if (add_hba_port_info(portNode, new_hba, protocol)
558 == HBA_STATUS_ERROR) {
559 S_FREE(new_hba->first_port);
560 S_FREE(new_hba);
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;
583 } else {
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
595 static int
596 lookup_smhba_sas_hba(di_node_t node, void *arg)
598 const char ROUTINE[] = "lookup_smhba_sas_hba";
599 int *propData, rval;
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);
610 if (rval >= 0) {
611 if (*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
630 HBA_STATUS
631 devtree_get_all_hbas(di_node_t root)
633 const char ROUTINE[] = "devtree_get_all_hbas";
634 int rv, ret = HBA_STATUS_ERROR;
635 walkarg_t wa;
637 wa.devpath = NULL;
638 if ((wa.flag = (boolean_t *)calloc(1,
639 sizeof (boolean_t))) == NULL) {
640 OUT_OF_MEMORY(ROUTINE);
641 return (HBA_STATUS_ERROR);
643 *wa.flag = B_FALSE;
644 rv = di_walk_node(root, DI_WALK_SIBFIRST, &wa, lookup_smhba_sas_hba);
646 if (rv == 0) {
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
659 * failures.
661 ret = HBA_STATUS_OK;
662 } else if (*(wa.flag)) {
663 /* We have no HBAs but have failures */
664 ret = HBA_STATUS_ERROR;
665 } else {
666 /* We have no HBAs and no failures */
667 ret = HBA_STATUS_OK;
672 S_FREE(wa.flag);
674 if (ret == HBA_STATUS_OK)
675 (void) registerSysevent();
677 return (ret);