xref: /illumos-gate/usr/src/lib/sun_sas/common/devtree_hba_disco.c (revision dd72704bd9e794056c558153663c739e2012d721)
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  * Copyright 2019 Joyent, Inc.
27  */
28 
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>
35 
36 struct sun_sas_hba *global_hba_head;
37 
38 /* free hba port info for the given hba */
39 static void
40 free_hba_port(struct sun_sas_hba *hba_ptr)
41 {
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;
50 
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);
63 			}
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);
69 		}
70 		hba_port->first_attached_port = NULL;
71 
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);
77 		}
78 		hba_port->first_phy = NULL;
79 
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);
85 	}
86 
87 	hba_ptr->first_port = NULL;
88 }
89 
90 /*
91  * Internal routine for adding an HBA port
92  */
93 static HBA_STATUS
94 add_hba_port_info(di_node_t portNode, struct sun_sas_hba *hba_ptr, int protocol)
95 {
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;
106 
107 	if (hba_ptr == NULL) {
108 		log(LOG_DEBUG, ROUTINE,
109 		    "Sun_sas handle ptr set to NULL.");
110 		return (HBA_STATUS_ERROR_ARG);
111 	}
112 
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);
117 	}
118 
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);
124 	}
125 
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);
132 	}
133 
134 	/*
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.
141 	 */
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);
152 	}
153 
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;
163 	}
164 
165 	port_ptr->port_attributes.PortType = HBA_PORTTYPE_SASDEVICE;
166 
167 	(void) strlcpy(port_ptr->device_path, portDevpath, MAXPATHLEN + 1);
168 
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++;
175 		}
176 		if (charptr[0] ==  'c') {
177 			port_ptr->cntlNumber = atoi(++charptr);
178 		} else {
179 			port_ptr->cntlNumber = -1;
180 		}
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);
185 	}
186 
187 	di_devfs_path_free(portDevpath);
188 
189 	port_ptr->port_attributes.PortSpecificAttribute.
190 	    SASPort->PortProtocol = protocol;
191 
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;
206 			}
207 		}
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);
217 		}
218 	}
219 
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;
234 			}
235 		}
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));
249 		}
250 	}
251 
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;
265 	}
266 
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);
278 		}
279 	}
280 
281 	/* now done with prop checking. remove branchNode. */
282 	di_fini(branchNode);
283 
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);
292 	}
293 
294 	fillDomainPortWWN(port_ptr);
295 
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;
304 	}
305 
306 	return (HBA_STATUS_OK);
307 }
308 
309 HBA_STATUS
310 refresh_hba(di_node_t hbaNode, struct sun_sas_hba *hba_ptr)
311 {
312 	const char	ROUTINE[] = "refresh_hba";
313 	di_node_t	portNode;
314 	int		protocol = 0;
315 	int		*propIntData;
316 
317 	/*
318 	 * clean up existing hba port, discovered target, phy info.
319 	 * leave open handles intact.
320 	 */
321 	free_hba_port(hba_ptr);
322 
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);
327 	}
328 
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;
335 	}
336 
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;
344 			}
345 		}
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);
351 		}
352 		portNode = di_sibling_node(portNode);
353 	}
354 
355 	return (HBA_STATUS_OK);
356 }
357 
358 /*
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.
364  */
365 HBA_STATUS
366 devtree_get_one_hba(di_node_t hbaNode)
367 {
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;
376 
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);
382 	}
383 
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);
388 	}
389 
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);
401 				}
402 				di_devfs_path_free(hbaDevpath);
403 				return (HBA_STATUS_OK);
404 			}
405 		}
406 	}
407 
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);
414 	}
415 
416 	(void) strlcpy(new_hba->device_path, hbaDevpath,
417 	    sizeof (new_hba->device_path));
418 	di_devfs_path_free(hbaDevpath);
419 
420 	(void) snprintf(new_hba->adapter_attributes.HBASymbolicName,
421 	    sizeof (new_hba->adapter_attributes.HBASymbolicName),
422 	    "%s%s", DEVICES_DIR, new_hba->device_path);
423 
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));
434 	}
435 
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));
444 	}
445 
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));
454 	}
455 
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));
466 	}
467 
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));
478 	}
479 
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));
490 	}
491 
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;
498 	}
499 
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;
505 
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);
514 	}
515 
516 	/*
517 	 * Name the adapter: like SUNW-pmcs-1
518 	 * Using di_instance number as the suffix for the name for persistent
519 	 * among rebooting.
520 	 */
521 	(void) snprintf(new_hba->handle_name, HANDLE_NAME_LENGTH, "%s-%s-%d",
522 	    "SUNW", new_hba->adapter_attributes.DriverName, hba_instance);
523 
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++;
530 
531 		/*
532 		 * add newly created handle into global_hba_head list
533 		 */
534 		if (global_hba_head != NULL) {
535 			/*
536 			 * Make sure to move the open_handles list to back to
537 			 * the head if it's there (for refresh scenario)
538 			 */
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;
543 			}
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;
549 		}
550 		return (HBA_STATUS_OK);
551 	}
552 
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;
560 			}
561 		}
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);
567 		}
568 		portNode = di_sibling_node(portNode);
569 	}
570 
571 	new_hba->index = hba_count++;
572 
573 	/*
574 	 * add newly created handle into global_hba_head list
575 	 */
576 	if (global_hba_head != NULL) {
577 		/*
578 		 * Make sure to move the open_handles list to back to the
579 		 * head if it's there (for refresh scenario)
580 		 */
581 		if (global_hba_head->open_handles) {
582 			new_hba->open_handles = global_hba_head->open_handles;
583 			global_hba_head->open_handles = NULL;
584 		}
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;
590 	}
591 
592 	return (HBA_STATUS_OK);
593 }
594 
595 /*
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
599  */
600 static int
601 lookup_smhba_sas_hba(di_node_t node, void *arg)
602 {
603 	const char ROUTINE[] = "lookup_smhba_sas_hba";
604 	int *propData, rval;
605 	walkarg_t *wa = (walkarg_t *)arg;
606 
607 	/* Skip stub(instance -1) nodes */
608 	if (IS_STUB_NODE(node)) {
609 		log(LOG_DEBUG, ROUTINE, "Walk continue");
610 		return (DI_WALK_CONTINUE);
611 	}
612 
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;
620 			}
621 			/* Found a node. No need to walk the child. */
622 			log(LOG_DEBUG, ROUTINE, "Walk prunechild");
623 			return (DI_WALK_PRUNECHILD);
624 		}
625 	}
626 
627 	return (DI_WALK_CONTINUE);
628 }
629 
630 /*
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
634  */
635 HBA_STATUS
636 devtree_get_all_hbas(di_node_t root)
637 {
638 	const char	ROUTINE[] = "devtree_get_all_hbas";
639 	int		rv, ret = HBA_STATUS_ERROR;
640 	walkarg_t	wa;
641 
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);
647 	}
648 	*wa.flag = B_FALSE;
649 	rv = di_walk_node(root, DI_WALK_SIBFIRST, &wa, lookup_smhba_sas_hba);
650 
651 	if (rv == 0) {
652 		/*
653 		 * Now determine what status code to return, taking
654 		 * partial failure scenarios into consideration.
655 		 *
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.
660 		 */
661 		if (global_hba_head) {
662 			/*
663 			 * We've got at least one HBA and possibly some
664 			 * failures.
665 			 */
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;
673 		}
674 	}
675 
676 
677 	S_FREE(wa.flag);
678 
679 	if (ret == HBA_STATUS_OK)
680 		(void) registerSysevent();
681 
682 	return (ret);
683 }
684