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