xref: /illumos-gate/usr/src/lib/sun_sas/common/devtree_hba_disco.c (revision 915894ef19890baaed00080f85f6b69e225cda98)
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 /* free hba port info for the given hba */
37 static void
38 free_hba_port(struct sun_sas_hba *hba_ptr)
39 {
40 	struct sun_sas_port	*hba_port = NULL;
41 	struct sun_sas_port	*last_hba_port = NULL;
42 	struct sun_sas_port	*tgt_port = NULL;
43 	struct sun_sas_port	*last_tgt_port = NULL;
44 	struct ScsiEntryList	*scsi_info = NULL;
45 	struct ScsiEntryList	*last_scsi_info = NULL;
46 	struct phy_info		*phy_ptr = NULL;
47 	struct phy_info		*last_phy = NULL;
48 
49 	/* Free the nested structures (port and attached port) */
50 	hba_port = hba_ptr->first_port;
51 	while (hba_port != NULL) {
52 		/* Free discovered port structure list. */
53 		tgt_port = hba_port->first_attached_port;
54 		while (tgt_port != NULL) {
55 			/* Free target mapping data list first. */
56 			scsi_info = tgt_port->scsiInfo;
57 			while (scsi_info != NULL) {
58 				last_scsi_info = scsi_info;
59 				scsi_info = scsi_info->next;
60 				free(last_scsi_info);
61 			}
62 			last_tgt_port = tgt_port;
63 			tgt_port = tgt_port->next;
64 			free(last_tgt_port->port_attributes.\
65 			    PortSpecificAttribute.SASPort);
66 			free(last_tgt_port);
67 		}
68 		hba_port->first_attached_port = NULL;
69 
70 		phy_ptr = hba_port->first_phy;
71 		while (phy_ptr != NULL) {
72 			last_phy = phy_ptr;
73 			phy_ptr = phy_ptr->next;
74 			free(last_phy);
75 		}
76 		hba_port->first_phy = NULL;
77 
78 		last_hba_port = hba_port;
79 		hba_port = hba_port->next;
80 		free(last_hba_port->port_attributes.\
81 		    PortSpecificAttribute.SASPort);
82 		free(last_hba_port);
83 	}
84 
85 	hba_ptr->first_port = NULL;
86 }
87 
88 /*
89  * Internal routine for adding an HBA port
90  */
91 static HBA_STATUS
92 add_hba_port_info(di_node_t portNode, struct sun_sas_hba *hba_ptr, int protocol)
93 {
94 	const char		    ROUTINE[] = "add_hba_port_info";
95 	struct sun_sas_port	    *port_ptr;
96 	char			    *portDevpath;
97 	int			    *propIntData;
98 	char			    *propStringData;
99 	uint64_t		    tmpAddr;
100 	char			    *charptr, cntlLink[MAXPATHLEN] = {'\0'};
101 	int			    rval;
102 	di_node_t		    branchNode;
103 	uint_t			    state = HBA_PORTSTATE_UNKNOWN;
104 
105 	if (hba_ptr == NULL) {
106 		log(LOG_DEBUG, ROUTINE,
107 		    "Sun_sas handle ptr set to NULL.");
108 		return (HBA_STATUS_ERROR_ARG);
109 	}
110 
111 	if ((port_ptr = (struct sun_sas_port *)calloc(1,
112 	    sizeof (struct sun_sas_port))) == NULL) {
113 		OUT_OF_MEMORY(ROUTINE);
114 		return (HBA_STATUS_ERROR);
115 	}
116 
117 	if ((port_ptr->port_attributes.PortSpecificAttribute.SASPort =
118 	    (struct SMHBA_SAS_Port *)calloc(1, sizeof (struct SMHBA_SAS_Port)))
119 	    == NULL) {
120 		OUT_OF_MEMORY(ROUTINE);
121 		return (HBA_STATUS_ERROR);
122 	}
123 
124 	if ((portDevpath = di_devfs_path(portNode)) == NULL) {
125 		log(LOG_DEBUG, ROUTINE,
126 		    "Unable to get device path from HBA Port Node.");
127 		S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
128 		S_FREE(port_ptr);
129 		return (HBA_STATUS_ERROR);
130 	}
131 
132 	/*
133 	 * Let's take a branch snap shot for pulling attributes.
134 	 * The attribute change doesn't invalidate devinfo cache snapshot.
135 	 * Phy info prop and num-phys can be obsolate when the same hba
136 	 * connected to the same expander(SIM) thus phy numbers are increased.
137 	 * Also the phy number may get decreased when a connection is removed
138 	 * while the iport still exist through another connection.
139 	 */
140 	branchNode = di_init(portDevpath, DINFOPROP);
141 	if (branchNode == DI_NODE_NIL) {
142 		/* something is wrong here. */
143 		di_fini(branchNode);
144 		log(LOG_DEBUG, ROUTINE,
145 		    "Unable to take devinfoi branch snapshot on HBA port \"%s\""
146 		    " due to %s", portDevpath, strerror(errno));
147 		S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
148 		S_FREE(port_ptr);
149 		return (HBA_STATUS_ERROR);
150 	}
151 
152 	state = di_state(portNode);
153 	if (((state & DI_DRIVER_DETACHED) == DI_DRIVER_DETACHED) ||
154 	    ((state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE)) {
155 		log(LOG_DEBUG, ROUTINE,
156 		    "HBA port node %s is either OFFLINE or DETACHED",
157 		    portDevpath);
158 		port_ptr->port_attributes.PortState = HBA_PORTSTATE_OFFLINE;
159 	} else {
160 		port_ptr->port_attributes.PortState = HBA_PORTSTATE_ONLINE;
161 	}
162 
163 	port_ptr->port_attributes.PortType = HBA_PORTTYPE_SASDEVICE;
164 
165 	(void) strlcpy(port_ptr->device_path, portDevpath, MAXPATHLEN + 1);
166 
167 	if (lookupControllerLink(portDevpath, (char *)cntlLink) ==
168 	    HBA_STATUS_OK) {
169 		(void) strlcpy(port_ptr->port_attributes.OSDeviceName, cntlLink,
170 		    sizeof (port_ptr->port_attributes.OSDeviceName));
171 		if ((charptr = strrchr(cntlLink, '/')) != NULL) {
172 			charptr++;
173 		}
174 		if (charptr[0] ==  'c') {
175 			port_ptr->cntlNumber = atoi(++charptr);
176 		} else {
177 			port_ptr->cntlNumber = -1;
178 		}
179 	} else {
180 		(void) snprintf(port_ptr->port_attributes.OSDeviceName,
181 		    sizeof (port_ptr->port_attributes.OSDeviceName),
182 		    "%s%s%s", DEVICES_DIR, portDevpath, SCSI_SUFFIX);
183 	}
184 
185 	di_devfs_path_free(portDevpath);
186 
187 	port_ptr->port_attributes.PortSpecificAttribute.
188 	    SASPort->PortProtocol = protocol;
189 
190 	rval = di_prop_lookup_strings(DDI_DEV_T_ANY, branchNode,
191 	    "initiator-port", &propStringData);
192 	if (rval < 0) {
193 		log(LOG_DEBUG, ROUTINE,
194 		    "Unable to get initiator-port from HBA port node %s.",
195 		    port_ptr->port_attributes.OSDeviceName);
196 		di_fini(branchNode);
197 		S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
198 		S_FREE(port_ptr);
199 		return (HBA_STATUS_ERROR);
200 	} else {
201 		for (charptr = propStringData; *charptr != '\0'; charptr++) {
202 			if (isxdigit(*charptr)) {
203 				break;
204 			}
205 		}
206 		if (*charptr != '\0') {
207 			tmpAddr = htonll(strtoll(charptr, NULL, 16));
208 			(void) memcpy(port_ptr->port_attributes.
209 			    PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
210 			    &tmpAddr, 8);
211 		} else {
212 			log(LOG_DEBUG, ROUTINE,
213 			    "No proper intiator-port prop value on HBA port %s",
214 			    port_ptr->port_attributes.OSDeviceName);
215 		}
216 	}
217 
218 	rval = di_prop_lookup_strings(DDI_DEV_T_ANY, branchNode,
219 	    "attached-port", &propStringData);
220 	if (rval < 0) {
221 		log(LOG_DEBUG, ROUTINE,
222 		    "Unable to get attached-port from HBA port node %s.",
223 		    port_ptr->port_attributes.OSDeviceName);
224 		di_fini(branchNode);
225 		S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
226 		S_FREE(port_ptr);
227 		return (HBA_STATUS_ERROR);
228 	} else {
229 		for (charptr = propStringData; *charptr != '\0'; charptr++) {
230 			if (isxdigit(*charptr)) {
231 				break;
232 			}
233 		}
234 		if (*charptr != '\0') {
235 			tmpAddr = htonll(strtoll(charptr, NULL, 16));
236 			(void) memcpy(port_ptr->port_attributes.
237 			    PortSpecificAttribute.SASPort->
238 			    AttachedSASAddress.wwn, &tmpAddr, 8);
239 		} else {
240 			/* continue even if the attached port is NULL. */
241 			log(LOG_DEBUG, ROUTINE,
242 			    "No proper attached-port prop value: "
243 			    "HBA port Local SAS Address(%016llx)",
244 			    wwnConversion(port_ptr->port_attributes.
245 			    PortSpecificAttribute.
246 			    SASPort->LocalSASAddress.wwn));
247 		}
248 	}
249 
250 	rval = di_prop_lookup_ints(DDI_DEV_T_ANY, branchNode,
251 	    "num-phys", &propIntData);
252 	if (rval < 0) {
253 		log(LOG_DEBUG, ROUTINE,
254 		    "Unable to get NumberofPhys from HBA port %s.",
255 		    port_ptr->port_attributes.OSDeviceName);
256 		di_fini(branchNode);
257 		S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
258 		S_FREE(port_ptr);
259 		return (HBA_STATUS_ERROR);
260 	} else {
261 		port_ptr->port_attributes.PortSpecificAttribute.\
262 		    SASPort->NumberofPhys = *propIntData;
263 	}
264 
265 	if (port_ptr->port_attributes.PortSpecificAttribute.\
266 	    SASPort->NumberofPhys > 0) {
267 		if (get_phy_info(branchNode, port_ptr) != HBA_STATUS_OK) {
268 			log(LOG_DEBUG, ROUTINE,
269 			    "Failed to get phy info on HBA port %s.",
270 			    port_ptr->port_attributes.OSDeviceName);
271 			di_fini(branchNode);
272 			S_FREE(port_ptr->port_attributes.
273 			    PortSpecificAttribute.SASPort);
274 			S_FREE(port_ptr);
275 			return (HBA_STATUS_ERROR);
276 		}
277 	}
278 
279 	/* now done with prop checking. remove branchNode. */
280 	di_fini(branchNode);
281 
282 	/* Construct discovered target port. */
283 	if (devtree_attached_devices(portNode, port_ptr) != HBA_STATUS_OK) {
284 		log(LOG_DEBUG, ROUTINE,
285 		    "Failed to get attached device info HBA port %s.",
286 		    port_ptr->port_attributes.OSDeviceName);
287 		S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
288 		S_FREE(port_ptr);
289 		return (HBA_STATUS_ERROR);
290 	}
291 
292 	fillDomainPortWWN(port_ptr);
293 
294 	/* add new port onto hba handle list */
295 	if (hba_ptr->first_port == NULL) {
296 		port_ptr->index = 0;
297 		hba_ptr->first_port = port_ptr;
298 	} else {
299 		port_ptr->index = hba_ptr->first_port->index + 1;
300 		port_ptr->next = hba_ptr->first_port;
301 		hba_ptr->first_port = port_ptr;
302 	}
303 
304 	return (HBA_STATUS_OK);
305 }
306 
307 HBA_STATUS
308 refresh_hba(di_node_t hbaNode, struct sun_sas_hba *hba_ptr)
309 {
310 	const char	ROUTINE[] = "refresh_hba";
311 	di_node_t	portNode;
312 	int		protocol = 0;
313 	int		*propIntData;
314 
315 	/*
316 	 * clean up existing hba port, discovered target, phy info.
317 	 * leave open handles intact.
318 	 */
319 	free_hba_port(hba_ptr);
320 
321 	if ((portNode = di_child_node(hbaNode)) == NULL) {
322 		log(LOG_DEBUG, ROUTINE,
323 		    "HBA node doesn't have iport child.");
324 		return (HBA_STATUS_ERROR);
325 	}
326 
327 	if ((di_prop_lookup_ints(DDI_DEV_T_ANY, hbaNode,
328 	    "supported-protocol", &propIntData)) == -1) {
329 		log(LOG_DEBUG, ROUTINE,
330 		    "Unable to get supported-protocol from HBA node.");
331 	} else {
332 		protocol = *propIntData;
333 	}
334 
335 	while (portNode != DI_NODE_NIL) {
336 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, portNode,
337 		    "virtual-port", &propIntData) >= 0) {
338 			if (*propIntData) {
339 				/* ignore a virtual port. */
340 				portNode = di_sibling_node(portNode);
341 				continue;
342 			}
343 		}
344 		if (add_hba_port_info(portNode, hba_ptr, protocol)
345 		    == HBA_STATUS_ERROR) {
346 			S_FREE(hba_ptr->first_port);
347 			S_FREE(hba_ptr);
348 			return (HBA_STATUS_ERROR);
349 		}
350 		portNode = di_sibling_node(portNode);
351 	}
352 
353 	return (HBA_STATUS_OK);
354 }
355 
356 /*
357  * Discover information for one HBA in the device tree.
358  * The di_node_t argument should be a node with smhba-supported prop set
359  * to true.
360  * Without iport support, the devinfo node will represent one port hba.
361  * This routine assumes the locks have been taken.
362  */
363 HBA_STATUS
364 devtree_get_one_hba(di_node_t hbaNode)
365 {
366 	const char		ROUTINE[] = "devtree_get_one_hba";
367 	char			*propdata = NULL;
368 	int			*propIntData = NULL;
369 	struct sun_sas_hba	*new_hba, *hba_ptr;
370 	char			*hbaDevpath, *hba_driver;
371 	int			protocol = 0;
372 	di_node_t		portNode;
373 	int			hba_instance = -1;
374 
375 	hba_instance = di_instance(hbaNode);
376 	if (hba_instance == -1) {
377 		log(LOG_DEBUG, ROUTINE,
378 		    "portNode has instance of -1");
379 		return (DI_WALK_CONTINUE);
380 	}
381 
382 	if ((hbaDevpath = di_devfs_path(hbaNode)) == NULL) {
383 		log(LOG_DEBUG, ROUTINE, "Unable to get "
384 		    "device path from hbaNode");
385 		return (HBA_STATUS_ERROR);
386 	}
387 
388 	/* check to see if this is a repeat HBA */
389 	if (global_hba_head) {
390 		for (hba_ptr = global_hba_head;
391 		    hba_ptr != NULL;
392 		    hba_ptr = hba_ptr->next) {
393 			if ((strncmp(hba_ptr->device_path, hbaDevpath,
394 			    strlen(hbaDevpath))) == 0) {
395 				if (refresh_hba(hbaNode, hba_ptr) !=
396 				    HBA_STATUS_OK) {
397 					log(LOG_DEBUG, ROUTINE, "Refresh failed"
398 					    " on hbaNode %s", hbaDevpath);
399 				}
400 				di_devfs_path_free(hbaDevpath);
401 				return (HBA_STATUS_OK);
402 			}
403 		}
404 	}
405 
406 	/* this is a new hba */
407 	if ((new_hba = (struct sun_sas_hba *)calloc(1,
408 	    sizeof (struct sun_sas_hba))) == NULL) {
409 		OUT_OF_MEMORY(ROUTINE);
410 		di_devfs_path_free(hbaDevpath);
411 		return (HBA_STATUS_ERROR);
412 	}
413 
414 	(void) strlcpy(new_hba->device_path, hbaDevpath,
415 	    sizeof (new_hba->device_path));
416 	di_devfs_path_free(hbaDevpath);
417 
418 	(void) snprintf(new_hba->adapter_attributes.HBASymbolicName,
419 	    sizeof (new_hba->adapter_attributes.HBASymbolicName),
420 	    "%s%s", DEVICES_DIR, new_hba->device_path);
421 
422 	/* Manufacturer */
423 	if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
424 	    "Manufacturer", (char **)&propdata)) == -1) {
425 		(void) strlcpy(new_hba->adapter_attributes.Manufacturer,
426 		    SUN_MICROSYSTEMS,
427 		    sizeof (new_hba->adapter_attributes.Manufacturer));
428 	} else {
429 		(void) strlcpy(new_hba->adapter_attributes.Manufacturer,
430 		    propdata,
431 		    sizeof (new_hba->adapter_attributes.Manufacturer));
432 	}
433 
434 	/* SerialNumber */
435 	if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
436 	    "SerialNumber", (char **)&propdata)) == -1) {
437 		new_hba->adapter_attributes.SerialNumber[0] = '\0';
438 	} else {
439 		(void) strlcpy(new_hba->adapter_attributes.SerialNumber,
440 		    propdata,
441 		    sizeof (new_hba->adapter_attributes.SerialNumber));
442 	}
443 
444 	/* Model */
445 	if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
446 	    "ModelName", (char **)&propdata)) == -1) {
447 		new_hba->adapter_attributes.Model[0] = '\0';
448 	} else {
449 		(void) strlcpy(new_hba->adapter_attributes.Model,
450 		    propdata,
451 		    sizeof (new_hba->adapter_attributes.Model));
452 	}
453 
454 	/* FirmwareVersion */
455 	if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
456 	    "firmware-version", (char **)&propdata)) == -1) {
457 		log(LOG_DEBUG, ROUTINE,
458 		    "Property \"%s\" not found for device \"%s\"",
459 		    "firmware-version", new_hba->device_path);
460 	} else {
461 		(void) strlcpy(new_hba->adapter_attributes.FirmwareVersion,
462 		    propdata,
463 		    sizeof (new_hba->adapter_attributes.FirmwareVersion));
464 	}
465 
466 	/* HardwareVersion */
467 	if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
468 	    "hardware-version", (char **)&propdata)) == -1) {
469 		log(LOG_DEBUG, ROUTINE,
470 		    "Property \"%s\" not found for device \"%s\"",
471 		    "hardware-version", new_hba->device_path);
472 	} else {
473 		(void) strlcpy(new_hba->adapter_attributes.HardwareVersion,
474 		    propdata,
475 		    sizeof (new_hba->adapter_attributes.HardwareVersion));
476 	}
477 
478 	/* DriverVersion */
479 	if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
480 	    "driver-version", (char **)&propdata)) == -1) {
481 		log(LOG_DEBUG, ROUTINE,
482 		    "Property \"%s\" not found for device \"%s\"",
483 		    "driver-version", new_hba->device_path);
484 	} else {
485 		(void) strlcpy(new_hba->adapter_attributes.DriverVersion,
486 		    propdata,
487 		    sizeof (new_hba->adapter_attributes.DriverVersion));
488 	}
489 
490 	if ((di_prop_lookup_ints(DDI_DEV_T_ANY, hbaNode,
491 	    "supported-protocol", &propIntData)) == -1) {
492 		log(LOG_DEBUG, ROUTINE,
493 		    "Unable to get supported-protocol from HBA node.");
494 	} else {
495 		protocol = *propIntData;
496 	}
497 
498 	/* We don't use these */
499 	new_hba->adapter_attributes.OptionROMVersion[0] = '\0';
500 	new_hba->adapter_attributes.RedundantOptionROMVersion[0] = '\0';
501 	new_hba->adapter_attributes.RedundantFirmwareVersion[0] = '\0';
502 	new_hba->adapter_attributes.VendorSpecificID = 0;
503 
504 	if ((hba_driver = di_driver_name(hbaNode)) != NULL) {
505 		(void) strlcpy(new_hba->adapter_attributes.DriverName,
506 		    hba_driver,
507 		    sizeof (new_hba->adapter_attributes.DriverName));
508 	} else {
509 		log(LOG_DEBUG, ROUTINE,
510 		    "HBA driver name not found for device \"%s\"",
511 		    new_hba->device_path);
512 	}
513 
514 	/*
515 	 * Name the adapter: like SUNW-pmcs-1
516 	 * Using di_instance number as the suffix for the name for persistent
517 	 * among rebooting.
518 	 */
519 	(void) snprintf(new_hba->handle_name, HANDLE_NAME_LENGTH, "%s-%s-%d",
520 	    "SUNW", new_hba->adapter_attributes.DriverName, hba_instance);
521 
522 	if ((portNode = di_child_node(hbaNode)) == NULL) {
523 		log(LOG_DEBUG, ROUTINE,
524 		    "HBA driver doesn't have iport child. \"%s\"",
525 		    new_hba->device_path);
526 		/* continue on with an hba without any port. */
527 		new_hba->index = hba_count++;
528 
529 		/*
530 		 * add newly created handle into global_hba_head list
531 		 */
532 		if (global_hba_head != NULL) {
533 			/*
534 			 * Make sure to move the open_handles list to back to
535 			 * the head if it's there (for refresh scenario)
536 			 */
537 			if (global_hba_head->open_handles) {
538 				new_hba->open_handles =
539 				    global_hba_head->open_handles;
540 				global_hba_head->open_handles = NULL;
541 			}
542 			/* Now bump the new one to the head of the list */
543 			new_hba->next = global_hba_head;
544 			global_hba_head = new_hba;
545 		} else {
546 			global_hba_head = new_hba;
547 		}
548 		return (HBA_STATUS_OK);
549 	}
550 
551 	while (portNode != DI_NODE_NIL) {
552 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, portNode,
553 		    "virtual-port", &propIntData) >= 0) {
554 			if (*propIntData) {
555 				/* ignore a virtual port. */
556 				portNode = di_sibling_node(portNode);
557 				continue;
558 			}
559 		}
560 		if (add_hba_port_info(portNode, new_hba, protocol)
561 		    == HBA_STATUS_ERROR) {
562 			S_FREE(new_hba->first_port);
563 			S_FREE(new_hba);
564 			return (HBA_STATUS_ERROR);
565 		}
566 		portNode = di_sibling_node(portNode);
567 	}
568 
569 	new_hba->index = hba_count++;
570 
571 	/*
572 	 * add newly created handle into global_hba_head list
573 	 */
574 	if (global_hba_head != NULL) {
575 		/*
576 		 * Make sure to move the open_handles list to back to the
577 		 * head if it's there (for refresh scenario)
578 		 */
579 		if (global_hba_head->open_handles) {
580 			new_hba->open_handles = global_hba_head->open_handles;
581 			global_hba_head->open_handles = NULL;
582 		}
583 		/* Now bump the new one to the head of the list */
584 		new_hba->next = global_hba_head;
585 		global_hba_head = new_hba;
586 	} else {
587 		global_hba_head = new_hba;
588 	}
589 
590 	return (HBA_STATUS_OK);
591 }
592 
593 /*
594  * Discover information for all HBAs found on the system.
595  * The di_node_t argument should be the root of the device tree.
596  * This routine assumes the locks have been taken
597  */
598 static int
599 lookup_smhba_sas_hba(di_node_t node, void *arg)
600 {
601 	const char ROUTINE[] = "lookup_smhba_sas_hba";
602 	int *propData, rval;
603 	walkarg_t *wa = (walkarg_t *)arg;
604 
605 	/* Skip stub(instance -1) nodes */
606 	if (IS_STUB_NODE(node)) {
607 		log(LOG_DEBUG, ROUTINE, "Walk continue");
608 		return (DI_WALK_CONTINUE);
609 	}
610 
611 	rval = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
612 	    "sm-hba-supported", &propData);
613 	if (rval >= 0) {
614 		if (*propData) {
615 			/* add the hba to the hba list */
616 			if (devtree_get_one_hba(node) != HBA_STATUS_OK) {
617 				*(wa->flag) = B_TRUE;
618 			}
619 			/* Found a node. No need to walk the child. */
620 			log(LOG_DEBUG, ROUTINE, "Walk prunechild");
621 			return (DI_WALK_PRUNECHILD);
622 		}
623 	}
624 
625 	return (DI_WALK_CONTINUE);
626 }
627 
628 /*
629  * Discover information for all HBAs found on the system.
630  * The di_node_t argument should be the root of the device tree.
631  * This routine assumes the locks have been taken
632  */
633 HBA_STATUS
634 devtree_get_all_hbas(di_node_t root)
635 {
636 	const char	ROUTINE[] = "devtree_get_all_hbas";
637 	int		rv, ret = HBA_STATUS_ERROR;
638 	walkarg_t	wa;
639 
640 	wa.devpath = NULL;
641 	if ((wa.flag = (boolean_t *)calloc(1,
642 	    sizeof (boolean_t))) == NULL) {
643 		OUT_OF_MEMORY(ROUTINE);
644 		return (HBA_STATUS_ERROR);
645 	}
646 	*wa.flag = B_FALSE;
647 	rv = di_walk_node(root, DI_WALK_SIBFIRST, &wa, lookup_smhba_sas_hba);
648 
649 	if (rv == 0) {
650 		/*
651 		 * Now determine what status code to return, taking
652 		 * partial failure scenarios into consideration.
653 		 *
654 		 * If we have at least one working HBA, then we return an
655 		 * OK status.  If we have no good HBAs, but at least one
656 		 * failed HBA, we return an ERROR status.  If we have
657 		 * no HBAs and no failures, we return OK.
658 		 */
659 		if (global_hba_head) {
660 			/*
661 			 * We've got at least one HBA and possibly some
662 			 * failures.
663 			 */
664 			ret = HBA_STATUS_OK;
665 		} else if (*(wa.flag)) {
666 			/* We have no HBAs but have failures */
667 			ret = HBA_STATUS_ERROR;
668 		} else {
669 			/* We have no HBAs and no failures */
670 			ret = HBA_STATUS_OK;
671 		}
672 	}
673 
674 
675 	S_FREE(wa.flag);
676 
677 	if (ret == HBA_STATUS_OK)
678 		(void) registerSysevent();
679 
680 	return (ret);
681 }
682