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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
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>
32 
33 /* free hba port info for the given hba */
34 static void
35 free_hba_port(struct sun_sas_hba *hba_ptr)
36 {
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;
45 
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);
58 			}
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);
64 		}
65 		hba_port->first_attached_port = NULL;
66 
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);
72 		}
73 		hba_port->first_phy = NULL;
74 
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);
80 	}
81 
82 	hba_ptr->first_port = NULL;
83 }
84 
85 /*
86  * Internal routine for adding an HBA port
87  */
88 static HBA_STATUS
89 add_hba_port_info(di_node_t portNode, struct sun_sas_hba *hba_ptr, int protocol)
90 {
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;
101 
102 	if (hba_ptr == NULL) {
103 		log(LOG_DEBUG, ROUTINE,
104 		    "Sun_sas handle ptr set to NULL.");
105 		return (HBA_STATUS_ERROR_ARG);
106 	}
107 
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);
112 	}
113 
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);
119 	}
120 
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);
127 	}
128 
129 	/*
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.
136 	 */
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);
147 	}
148 
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;
158 	}
159 
160 	port_ptr->port_attributes.PortType = HBA_PORTTYPE_SASDEVICE;
161 
162 	(void) strlcpy(port_ptr->device_path, portDevpath, MAXPATHLEN + 1);
163 
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++;
170 		}
171 		if (charptr[0] ==  'c') {
172 			port_ptr->cntlNumber = atoi(++charptr);
173 		} else {
174 			port_ptr->cntlNumber = -1;
175 		}
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);
180 	}
181 
182 	di_devfs_path_free(portDevpath);
183 
184 	port_ptr->port_attributes.PortSpecificAttribute.
185 	    SASPort->PortProtocol = protocol;
186 
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;
201 			}
202 		}
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);
212 		}
213 	}
214 
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;
229 			}
230 		}
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));
244 		}
245 	}
246 
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;
260 	}
261 
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);
273 		}
274 	}
275 
276 	/* now done with prop checking. remove branchNode. */
277 	di_fini(branchNode);
278 
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);
287 	}
288 
289 	fillDomainPortWWN(port_ptr);
290 
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;
299 	}
300 
301 	return (HBA_STATUS_OK);
302 }
303 
304 HBA_STATUS
305 refresh_hba(di_node_t hbaNode, struct sun_sas_hba *hba_ptr)
306 {
307 	const char	ROUTINE[] = "refresh_hba";
308 	di_node_t	portNode;
309 	int		protocol = 0;
310 	int		*propIntData;
311 
312 	/*
313 	 * clean up existing hba port, discovered target, phy info.
314 	 * leave open handles intact.
315 	 */
316 	free_hba_port(hba_ptr);
317 
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);
322 	}
323 
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;
330 	}
331 
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;
339 			}
340 		}
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);
346 		}
347 		portNode = di_sibling_node(portNode);
348 	}
349 
350 	return (HBA_STATUS_OK);
351 }
352 
353 /*
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.
359  */
360 HBA_STATUS
361 devtree_get_one_hba(di_node_t hbaNode)
362 {
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;
371 
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);
377 	}
378 
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);
383 	}
384 
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);
396 				}
397 				di_devfs_path_free(hbaDevpath);
398 				return (HBA_STATUS_OK);
399 			}
400 		}
401 	}
402 
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);
409 	}
410 
411 	(void) strlcpy(new_hba->device_path, hbaDevpath,
412 	    sizeof (new_hba->device_path));
413 	di_devfs_path_free(hbaDevpath);
414 
415 	(void) snprintf(new_hba->adapter_attributes.HBASymbolicName,
416 	    sizeof (new_hba->adapter_attributes.HBASymbolicName),
417 	    "%s%s", DEVICES_DIR, new_hba->device_path);
418 
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));
429 	}
430 
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));
439 	}
440 
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));
449 	}
450 
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));
461 	}
462 
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));
473 	}
474 
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));
485 	}
486 
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;
493 	}
494 
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;
500 
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);
509 	}
510 
511 	/*
512 	 * Name the adapter: like SUNW-pmcs-1
513 	 * Using di_instance number as the suffix for the name for persistent
514 	 * among rebooting.
515 	 */
516 	(void) snprintf(new_hba->handle_name, HANDLE_NAME_LENGTH, "%s-%s-%d",
517 	    "SUNW", new_hba->adapter_attributes.DriverName, hba_instance);
518 
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++;
525 
526 		/*
527 		 * add newly created handle into global_hba_head list
528 		 */
529 		if (global_hba_head != NULL) {
530 			/*
531 			 * Make sure to move the open_handles list to back to
532 			 * the head if it's there (for refresh scenario)
533 			 */
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;
538 			}
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;
544 		}
545 		return (HBA_STATUS_OK);
546 	}
547 
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;
555 			}
556 		}
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);
562 		}
563 		portNode = di_sibling_node(portNode);
564 	}
565 
566 	new_hba->index = hba_count++;
567 
568 	/*
569 	 * add newly created handle into global_hba_head list
570 	 */
571 	if (global_hba_head != NULL) {
572 		/*
573 		 * Make sure to move the open_handles list to back to the
574 		 * head if it's there (for refresh scenario)
575 		 */
576 		if (global_hba_head->open_handles) {
577 			new_hba->open_handles = global_hba_head->open_handles;
578 			global_hba_head->open_handles = NULL;
579 		}
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;
585 	}
586 
587 	return (HBA_STATUS_OK);
588 }
589 
590 /*
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
594  */
595 static int
596 lookup_smhba_sas_hba(di_node_t node, void *arg)
597 {
598 	const char	ROUTINE[] = "lookup_smhba_sas_hba";
599 	int 		*propData, rval;
600 	walkarg_t 	*wa = (walkarg_t *)arg;
601 
602 	/* Skip stub(instance -1) nodes */
603 	if (IS_STUB_NODE(node)) {
604 		log(LOG_DEBUG, ROUTINE, "Walk continue");
605 		return (DI_WALK_CONTINUE);
606 	}
607 
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;
615 			}
616 			/* Found a node. No need to walk the child. */
617 			log(LOG_DEBUG, ROUTINE, "Walk prunechild");
618 			return (DI_WALK_PRUNECHILD);
619 		}
620 	}
621 
622 	return (DI_WALK_CONTINUE);
623 }
624 
625 /*
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
629  */
630 HBA_STATUS
631 devtree_get_all_hbas(di_node_t root)
632 {
633 	const char	ROUTINE[] = "devtree_get_all_hbas";
634 	int		rv, ret = HBA_STATUS_ERROR;
635 	walkarg_t	wa;
636 
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);
642 	}
643 	*wa.flag = B_FALSE;
644 	rv = di_walk_node(root, DI_WALK_SIBFIRST, &wa, lookup_smhba_sas_hba);
645 
646 	if (rv == 0) {
647 		/*
648 		 * Now determine what status code to return, taking
649 		 * partial failure scenarios into consideration.
650 		 *
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.
655 		 */
656 		if (global_hba_head) {
657 			/*
658 			 * We've got at least one HBA and possibly some
659 			 * failures.
660 			 */
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;
668 		}
669 	}
670 
671 
672 	S_FREE(wa.flag);
673 
674 	if (ret == HBA_STATUS_OK)
675 		(void) registerSysevent();
676 
677 	return (ret);
678 }
679