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#include "cfga_scsi.h"
28
29struct larg {
30	int ndevs;
31	int nelem;
32	char *dev;
33	char **dev_list;
34};
35
36#define	ETC_VFSTAB	"/etc/vfstab"
37#define	SCFGA_LOCK	"/var/run/cfgadm_scsi"
38
39/* Function prototypes */
40
41static scfga_ret_t quiesce_confirm(apid_t *apidp,
42    msgid_t cmd_msg, prompt_t *pt, int *okp, int *quiesce, int *l_errnop);
43static scfga_ret_t dev_hotplug(apid_t *apidp,
44    prompt_t *pt, cfga_flags_t flags, int quiesce, char **errstring);
45static int disconnect(struct cfga_confirm *confp);
46static int critical_ctrlr(const char *hba_phys);
47static cfga_stat_t bus_devctl_to_recep_state(uint_t bus_dc_state);
48static int get_hba_children(char *bus_path, char *dev_excl, char ***dev_list);
49static char *get_node_path(char *minor_path);
50static void free_dev_list_elements(char **dev_list);
51static void free_dev_list(char **dev_list);
52static int alloc_dev_list(struct larg *largp);
53
54/*
55 * Single thread all implicit quiesce operations
56 */
57static mutex_t	quiesce_mutex = DEFAULTMUTEX;
58
59/*ARGSUSED*/
60scfga_ret_t
61bus_change_state(
62	cfga_cmd_t state_change_cmd,
63	apid_t *apidp,
64	struct cfga_confirm *confp,
65	cfga_flags_t flags,
66	char **errstring)
67{
68	int l_errno = 0, force;
69	uint_t state = 0;
70	cfga_stat_t bus_state;
71	scfga_cmd_t cmd;
72	msgid_t errid;
73	cfga_stat_t prereq;
74	scfga_ret_t ret;
75	char **dev_list = NULL;
76
77	assert(apidp->path != NULL);
78	assert(apidp->hba_phys != NULL);
79
80	/*
81	 * No dynamic components allowed
82	 */
83	if (apidp->dyncomp != NULL) {
84		cfga_err(errstring, 0, ERR_NOT_BUSAPID, 0);
85		return (SCFGA_ERR);
86	}
87
88	/* Get bus state */
89	if (devctl_cmd(apidp->path, SCFGA_BUS_GETSTATE, &state,
90	    &l_errno) != SCFGA_OK) {
91		cfga_err(errstring, l_errno, ERR_BUS_GETSTATE, 0);
92		return (SCFGA_ERR);
93	}
94
95	bus_state = bus_devctl_to_recep_state(state);
96	force = ((flags & CFGA_FLAG_FORCE) == CFGA_FLAG_FORCE) ? 1 : 0;
97	assert(confp->confirm != NULL);
98
99	switch (state_change_cmd) {
100	case CFGA_CMD_DISCONNECT:	/* quiesce bus */
101		/*
102		 * If force flag not specified, check if controller is
103		 * critical.
104		 */
105		if (!force) {
106			/*
107			 * This check is not foolproof, get user confirmation
108			 * if test passes.
109			 */
110			if (critical_ctrlr(apidp->path)) {
111				cfga_err(errstring, 0, ERR_CTRLR_CRIT, 0);
112				ret = SCFGA_ERR;
113				break;
114			} else if (!disconnect(confp)) {
115				ret = SCFGA_NACK;
116				break;
117			}
118		}
119
120		cmd = SCFGA_BUS_QUIESCE;
121		errid = ERR_BUS_QUIESCE;
122		prereq = CFGA_STAT_CONNECTED;
123
124		goto common;
125
126	case CFGA_CMD_CONNECT:		/* unquiesce bus */
127		cmd = SCFGA_BUS_UNQUIESCE;
128		errid = ERR_BUS_UNQUIESCE;
129		prereq = CFGA_STAT_DISCONNECTED;
130
131		goto common;
132
133	case CFGA_CMD_CONFIGURE:
134		cmd = SCFGA_BUS_CONFIGURE;
135		errid = ERR_BUS_CONFIGURE;
136		prereq = CFGA_STAT_CONNECTED;
137
138		goto common;
139
140	case CFGA_CMD_UNCONFIGURE:
141		cmd = SCFGA_BUS_UNCONFIGURE;
142		errid = ERR_BUS_UNCONFIGURE;
143		prereq = CFGA_STAT_CONNECTED;
144
145		/* FALLTHROUGH */
146	common:
147		if (bus_state != prereq) {
148			cfga_err(errstring, 0,
149			    (prereq == CFGA_STAT_CONNECTED)
150			    ? ERR_BUS_NOTCONNECTED
151			    : ERR_BUS_CONNECTED, 0);
152			ret = SCFGA_ERR;
153			break;
154		}
155
156		/*
157		 * When quiescing or unconfiguring a bus, first suspend or
158		 * offline it through RCM.
159		 * For unquiescing, we simple build the dev_list for
160		 * resume notification.
161		 */
162		if (((apidp->flags & FLAG_DISABLE_RCM) == 0) &&
163		    ((cmd == SCFGA_BUS_QUIESCE) ||
164		    (cmd == SCFGA_BUS_UNQUIESCE) ||
165		    (cmd == SCFGA_BUS_UNCONFIGURE))) {
166			ret = get_hba_children(apidp->path, NULL, &dev_list);
167			if (ret != SCFGA_OK) {
168				break;
169			}
170			if (cmd == SCFGA_BUS_QUIESCE) {
171				if ((ret = scsi_rcm_suspend(dev_list,
172				    errstring, flags, 1)) != SCFGA_OK) {
173					break;
174				}
175			} else if (cmd == SCFGA_BUS_UNCONFIGURE) {
176				if ((ret = scsi_rcm_offline(dev_list,
177				    errstring, flags)) != SCFGA_OK) {
178					break;
179				}
180			}
181		}
182
183		ret = devctl_cmd(apidp->path, cmd, NULL, &l_errno);
184		if (ret != SCFGA_OK) {
185			/*
186			 * EIO when child devices are busy may confuse user.
187			 * So explain it.
188			 */
189			if (cmd == SCFGA_BUS_UNCONFIGURE && l_errno == EIO)
190				errid = ERR_MAYBE_BUSY;
191
192			cfga_err(errstring, l_errno, errid, 0);
193
194			/*
195			 * If the bus was suspended in RCM, then cancel the RCM
196			 * operation.  Discard RCM failures here because the
197			 * devctl's failure is what is most relevant.
198			 */
199			if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
200				if (cmd == SCFGA_BUS_QUIESCE)
201					(void) scsi_rcm_resume(dev_list,
202					    errstring,
203					    (flags & (~CFGA_FLAG_FORCE)), 1);
204				else if (cmd == SCFGA_BUS_UNCONFIGURE) {
205					(void) devctl_cmd(apidp->path,
206					    SCFGA_BUS_CONFIGURE, NULL,
207					    &l_errno);
208					(void) scsi_rcm_online(dev_list,
209					    errstring,
210					    (flags & (~CFGA_FLAG_FORCE)));
211				}
212			}
213
214			break;
215		}
216
217		/*
218		 * When unquiescing or configuring a bus, resume or online it
219		 * in RCM when the devctl command is complete.
220		 * When unconfiguring a bus, notify removal of devices.
221		 */
222		if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
223			if (cmd == SCFGA_BUS_UNQUIESCE) {
224				ret = scsi_rcm_resume(dev_list, errstring,
225				    (flags & (~CFGA_FLAG_FORCE)), 1);
226			} else if (cmd == SCFGA_BUS_UNCONFIGURE) {
227				ret = scsi_rcm_remove(dev_list, errstring,
228				    (flags & (~CFGA_FLAG_FORCE)));
229			}
230		}
231		break;
232
233	case CFGA_CMD_LOAD:
234	case CFGA_CMD_UNLOAD:
235		ret = SCFGA_OPNOTSUPP;
236		break;
237
238	default:
239		cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
240		ret = SCFGA_ERR;
241		break;
242	}
243
244	free_dev_list(dev_list);
245	return (ret);
246}
247
248scfga_ret_t
249dev_change_state(
250	cfga_cmd_t state_change_cmd,
251	apid_t *apidp,
252	cfga_flags_t flags,
253	char **errstring)
254{
255	uint_t state = 0;
256	int l_errno = 0;
257	cfga_stat_t bus_state;
258	scfga_cmd_t cmd;
259	msgid_t errid;
260	scfga_ret_t ret;
261	char *dev_list[2] = {NULL};
262
263	assert(apidp->path != NULL);
264	assert(apidp->hba_phys != NULL);
265
266	/*
267	 * For a device, dynamic component must be present
268	 */
269	if (apidp->dyncomp == NULL) {
270		cfga_err(errstring, 0, ERR_APID_INVAL, 0);
271		return (SCFGA_ERR);
272	}
273
274	/* Get bus state */
275	if (devctl_cmd(apidp->hba_phys, SCFGA_BUS_GETSTATE, &state,
276	    &l_errno) != SCFGA_OK) {
277		cfga_err(errstring, l_errno, ERR_BUS_GETSTATE, 0);
278		return (SCFGA_ERR);
279	}
280
281	bus_state = bus_devctl_to_recep_state(state);
282
283	switch (state_change_cmd) {
284	case CFGA_CMD_CONFIGURE:		/* online device */
285		cmd = SCFGA_DEV_CONFIGURE;
286		errid = ERR_DEV_CONFIGURE;
287		goto common;
288
289	case CFGA_CMD_UNCONFIGURE:		/* offline device */
290		cmd = SCFGA_DEV_UNCONFIGURE;
291		errid = ERR_DEV_UNCONFIGURE;
292		/* FALLTHROUGH */
293	common:
294		if (bus_state != CFGA_STAT_CONNECTED) {
295			cfga_err(errstring, 0, ERR_BUS_NOTCONNECTED, 0);
296			ret = SCFGA_ERR;
297			break;
298		}
299
300		if (apidp->dyntype == PATH_APID) {
301			/* call a scsi_vhci ioctl to do online/offline path. */
302			ret = path_apid_state_change(apidp, cmd,
303			    flags, errstring, &l_errno, errid);
304		} else {
305			/*
306			 * When unconfiguring a device, first offline it
307			 * through RCM.
308			 */
309			if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
310				if (cmd == SCFGA_DEV_UNCONFIGURE) {
311					dev_list[0] =
312					    get_node_path(apidp->path);
313					if (dev_list[0] == NULL) {
314						ret = SCFGA_ERR;
315						break;
316					}
317					if ((ret = scsi_rcm_offline(dev_list,
318					    errstring, flags)) != SCFGA_OK) {
319						break;
320					}
321				}
322			}
323
324			ret = devctl_cmd(apidp->path, cmd, NULL, &l_errno);
325			if (ret != SCFGA_OK) {
326				cfga_err(errstring, l_errno, errid, 0);
327
328			/*
329			 * If an unconfigure fails, cancel the RCM offline.
330			 * Discard any RCM failures so that the devctl
331			 * failure will still be reported.
332			 */
333				if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
334					if (cmd == SCFGA_DEV_UNCONFIGURE)
335						(void) scsi_rcm_online(dev_list,
336						    errstring, flags);
337				}
338				break;
339			}
340			if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
341			/*
342			 * Unconfigure succeeded, call the RCM notify_remove.
343			 */
344				if (cmd == SCFGA_DEV_UNCONFIGURE)
345					(void) scsi_rcm_remove(dev_list,
346					    errstring, flags);
347			}
348		}
349		break;
350
351	/*
352	 * Cannot disconnect/connect individual devices without affecting
353	 * other devices on the bus. So we don't support these ops.
354	 */
355	case CFGA_CMD_DISCONNECT:
356	case CFGA_CMD_CONNECT:
357		cfga_err(errstring, 0, ERR_NOT_DEVOP, 0);
358		ret = SCFGA_ERR;
359		break;
360	case CFGA_CMD_LOAD:
361	case CFGA_CMD_UNLOAD:
362		ret = SCFGA_OPNOTSUPP;
363		break;
364	default:
365		cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
366		ret = SCFGA_ERR;
367		break;
368	}
369
370	free_dev_list_elements(dev_list);
371	return (ret);
372}
373
374/*ARGSUSED*/
375scfga_ret_t
376dev_remove(
377	const char *func,
378	scfga_cmd_t cmd,
379	apid_t *apidp,
380	prompt_t *prp,
381	cfga_flags_t flags,
382	char **errstring)
383{
384	int proceed, l_errno = 0;
385	scfga_ret_t ret;
386	int do_quiesce;
387	char *dev_list[2] = {NULL};
388
389	assert(apidp->hba_phys != NULL);
390	assert(apidp->path != NULL);
391
392	/* device operation only */
393	if (apidp->dyncomp == NULL) {
394		cfga_err(errstring, 0, ERR_NOT_BUSOP, 0);
395		return (SCFGA_ERR);
396	}
397
398	proceed = 1;
399	ret = quiesce_confirm(apidp, MSG_RMDEV, prp, &proceed, &do_quiesce,
400	    &l_errno);
401	if (ret != SCFGA_OK) {
402		cfga_err(errstring, l_errno, ERR_DEV_REMOVE, 0);
403		return (ret);
404	}
405
406	if (!proceed) {
407		return (SCFGA_NACK);
408	}
409
410	/*
411	 * Offline the device in RCM
412	 */
413	if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
414		dev_list[0] = get_node_path(apidp->path);
415		if (dev_list[0] == NULL)
416			return (SCFGA_ERR);
417		if ((ret = scsi_rcm_offline(dev_list, errstring, flags))
418		    != SCFGA_OK) {
419			free_dev_list_elements(dev_list);
420			return (ret);
421		}
422	}
423
424	/*
425	 * Offline the device
426	 */
427	ret = devctl_cmd(apidp->path, SCFGA_DEV_UNCONFIGURE, NULL, &l_errno);
428	if (ret != SCFGA_OK) {
429
430		cfga_err(errstring, l_errno, ERR_DEV_REMOVE, 0);
431
432		/*
433		 * Cancel the RCM offline.  Discard the RCM failures so that
434		 * the above devctl failure is still reported.
435		 */
436		if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
437			(void) scsi_rcm_online(dev_list, errstring, flags);
438		free_dev_list_elements(dev_list);
439		return (ret);
440	}
441
442	/* Do the physical removal */
443	ret = dev_hotplug(apidp, prp, flags, do_quiesce, errstring);
444
445	if (ret == SCFGA_OK) {
446		/*
447		 * Complete the remove.
448		 * Since the device is already offlined, remove shouldn't
449		 * fail. Even if remove fails, there is no side effect.
450		 */
451		(void) devctl_cmd(apidp->path, SCFGA_DEV_REMOVE,
452		    NULL, &l_errno);
453		if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
454			ret = scsi_rcm_remove(dev_list, errstring, flags);
455	} else {
456		/*
457		 * Reconfigure the device and restore the device's RCM state.
458		 * If reconfigure succeeds, restore the state to online.
459		 * If reconfigure fails (e.g. a typo from user), we treat
460		 * the device as removed.
461		 */
462		if (devctl_cmd(apidp->path, SCFGA_DEV_CONFIGURE, NULL, &l_errno)
463		    == SCFGA_OK) {
464			if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
465				(void) scsi_rcm_online(dev_list, errstring,
466				    flags);
467		} else {
468			char *cp = strrchr(apidp->path, ':');
469			if (cp)
470				*cp = '\0';
471			cfga_err(errstring, l_errno, ERR_DEV_RECONFIGURE,
472			    apidp->path, 0);
473			if (cp)
474				*cp = ':';
475			if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
476				(void) scsi_rcm_remove(dev_list, errstring,
477				    flags);
478		}
479	}
480
481	free_dev_list_elements(dev_list);
482	return (ret);
483}
484
485/*ARGSUSED*/
486scfga_ret_t
487dev_insert(
488	const char *func,
489	scfga_cmd_t cmd,
490	apid_t *apidp,
491	prompt_t *prp,
492	cfga_flags_t flags,
493	char **errstring)
494{
495	int proceed, l_errno = 0;
496	scfga_ret_t ret;
497	int do_quiesce;
498
499	assert(apidp->hba_phys != NULL);
500	assert(apidp->path != NULL);
501
502	/* Currently, insert operation only allowed for bus */
503	if (apidp->dyncomp != NULL) {
504		cfga_err(errstring, 0, ERR_NOT_DEVOP, 0);
505		return (SCFGA_ERR);
506	}
507
508	proceed = 1;
509	ret = quiesce_confirm(apidp, MSG_INSDEV, prp, &proceed, &do_quiesce,
510	    &l_errno);
511	if (ret != SCFGA_OK) {
512		cfga_err(errstring, l_errno, ERR_DEV_INSERT, 0);
513		return (ret);
514	}
515
516	if (!proceed) {
517		return (SCFGA_NACK);
518	}
519
520	/* Do the physical addition */
521	ret = dev_hotplug(apidp, prp, flags, do_quiesce, errstring);
522	if (ret != SCFGA_OK) {
523		return (ret);
524	}
525
526	/*
527	 * Configure bus to online new device(s).
528	 * Previously offlined devices will not be onlined.
529	 */
530	ret = devctl_cmd(apidp->hba_phys, SCFGA_BUS_CONFIGURE, NULL, &l_errno);
531	if (ret != SCFGA_OK) {
532		cfga_err(errstring, l_errno, ERR_DEV_INSERT, 0);
533		return (SCFGA_ERR);
534	}
535
536	return (SCFGA_OK);
537}
538
539/*ARGSUSED*/
540scfga_ret_t
541dev_replace(
542	const char *func,
543	scfga_cmd_t cmd,
544	apid_t *apidp,
545	prompt_t *prp,
546	cfga_flags_t flags,
547	char **errstring)
548{
549	int proceed, l_errno = 0;
550	scfga_ret_t ret, ret2;
551	int do_quiesce;
552	char *dev_list[2] = {NULL};
553
554	assert(apidp->hba_phys != NULL);
555	assert(apidp->path != NULL);
556
557	/* device operation only */
558	if (apidp->dyncomp == NULL) {
559		cfga_err(errstring, 0, ERR_NOT_BUSOP, 0);
560		return (SCFGA_ERR);
561	}
562
563	proceed = 1;
564	ret = quiesce_confirm(apidp, MSG_REPLDEV, prp, &proceed, &do_quiesce,
565	    &l_errno);
566	if (ret != SCFGA_OK) {
567		cfga_err(errstring, l_errno, ERR_DEV_REPLACE, 0);
568		return (ret);
569	}
570
571	if (!proceed) {
572		return (SCFGA_NACK);
573	}
574
575	/* Offline the device in RCM */
576	if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
577		dev_list[0] = get_node_path(apidp->path);
578		if (dev_list[0] == NULL)
579			return (SCFGA_ERR);
580		if ((ret = scsi_rcm_offline(dev_list, errstring, flags))
581		    != SCFGA_OK) {
582			free_dev_list_elements(dev_list);
583			return (ret);
584		}
585	}
586
587	ret = devctl_cmd(apidp->path, SCFGA_DEV_REMOVE, NULL, &l_errno);
588	if (ret != SCFGA_OK) {
589
590		/*
591		 * Cancel the RCM offline.  Discard any RCM failures so that
592		 * the devctl failure can still be reported.
593		 */
594		if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
595			(void) scsi_rcm_online(dev_list, errstring, flags);
596
597		cfga_err(errstring, l_errno, ERR_DEV_REPLACE, 0);
598		free_dev_list_elements(dev_list);
599		return (ret);
600	}
601
602	/* do the physical replace */
603	ret = dev_hotplug(apidp, prp, flags, do_quiesce, errstring);
604
605	/* Online the replacement, or restore state on error */
606	ret2 = devctl_cmd(apidp->path, SCFGA_DEV_CONFIGURE, NULL, &l_errno);
607
608	if (ret2 != SCFGA_OK) {
609		cfga_err(errstring, l_errno, ERR_DEV_REPLACE, 0);
610	}
611
612	/*
613	 * Remove the replaced device in RCM, or online the device in RCM
614	 * to recover.
615	 */
616	if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
617		if (ret == SCFGA_OK)
618			ret = scsi_rcm_remove(dev_list, errstring, flags);
619		else if (ret2 == SCFGA_OK)
620			ret2 = scsi_rcm_online(dev_list, errstring, flags);
621	}
622	free_dev_list_elements(dev_list);
623
624	return (ret == SCFGA_OK ? ret2 : ret);
625}
626
627#pragma weak plat_dev_led
628/*ARGSUSED*/
629scfga_ret_t
630dev_led(
631	const char *func,
632	scfga_cmd_t cmd,
633	apid_t *apidp,
634	prompt_t *prp,
635	cfga_flags_t flags,
636	char **errstring)
637{
638
639	/*
640	 * The implementation of the led command is platform-specific, so
641	 * the default behavior is to say that the functionality is not
642	 * available for this device.
643	 */
644	if (plat_dev_led) {
645		return (plat_dev_led(func, cmd, apidp, prp, flags, errstring));
646	}
647	cfga_err(errstring, 0, ERR_UNAVAILABLE, 0);
648	return (SCFGA_ERR);
649}
650
651/*ARGSUSED*/
652scfga_ret_t
653reset_common(
654	const char *func,
655	scfga_cmd_t cmd,
656	apid_t *apidp,
657	prompt_t *prp,
658	cfga_flags_t flags,
659	char **errstring)
660{
661	int l_errno = 0;
662	scfga_ret_t ret;
663
664
665	assert(apidp->path != NULL);
666	assert(apidp->hba_phys != NULL);
667
668	switch (cmd) {
669	case SCFGA_RESET_DEV:
670		if (apidp->dyncomp == NULL) {
671			cfga_err(errstring, 0, ERR_NOT_BUSOP, 0);
672			return (SCFGA_ERR);
673		}
674		break;
675
676	case SCFGA_RESET_BUS:
677	case SCFGA_RESET_ALL:
678		if (apidp->dyncomp != NULL) {
679			cfga_err(errstring, 0, ERR_NOT_DEVOP, 0);
680			return (SCFGA_ERR);
681		}
682		break;
683	default:
684		cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
685		return (SCFGA_ERR);
686	}
687
688	ret = devctl_cmd(apidp->path, cmd, NULL, &l_errno);
689	if (ret != SCFGA_OK) {
690		cfga_err(errstring, l_errno, ERR_RESET, 0);
691	}
692
693	return (ret);
694}
695
696static int
697disconnect(struct cfga_confirm *confp)
698{
699	int ans, append_newline;
700	char *cq;
701
702	append_newline = 0;
703	cq = cfga_str(append_newline, WARN_DISCONNECT, 0);
704
705	ans = confp->confirm(confp->appdata_ptr, cq);
706
707	S_FREE(cq);
708
709	return (ans == 1);
710}
711
712/*
713 * Check for "scsi-no-quiesce" property
714 * Return code: -1 error, 0 quiesce not required, 1 quiesce required
715 */
716static int
717quiesce_required(apid_t *apidp, int *l_errnop)
718{
719	di_node_t bus_node, dev_node;
720	char *bus_path, *bus_end;
721	char *dev_path, *dev_end;
722	int *propval;
723
724	/* take libdevinfo snapshot of subtree at hba */
725	bus_path = apidp->hba_phys + strlen(DEVICES_DIR);
726	bus_end = strrchr(bus_path, ':');
727	if (bus_end)
728		*bus_end = '\0';
729
730	bus_node = di_init(bus_path, DINFOSUBTREE|DINFOPROP);
731	if (bus_end)
732		*bus_end = ':';
733	if (bus_node == DI_NODE_NIL) {
734		*l_errnop = errno;
735		return (-1);	/* error */
736	}
737
738	/* check bus node for property */
739	if (di_prop_lookup_ints(DDI_DEV_T_ANY, bus_node, SCSI_NO_QUIESCE,
740	    &propval) == 1) {
741		di_fini(bus_node);
742		return (0);	/* quiesce not required */
743	}
744
745	/* if this ap is HBA, return with quiesce required */
746	if (apidp->dyncomp == NULL) {
747		di_fini(bus_node);
748		return (1);
749	}
750
751	/* check device node for property */
752	dev_path = apidp->path + strlen(DEVICES_DIR);
753	dev_end = strrchr(dev_path, ':');
754	if (dev_end)
755		*dev_end = '\0';
756
757	dev_node = di_child_node(bus_node);
758	while (dev_node != DI_NODE_NIL) {
759		char *child_path;
760		child_path = di_devfs_path(dev_node);
761		if (strcmp(child_path, dev_path) == 0) {
762			di_devfs_path_free(child_path);
763			break;
764		}
765		di_devfs_path_free(child_path);
766		dev_node = di_sibling_node(dev_node);
767	}
768
769	if (dev_end)
770		*dev_end = ':';
771	if (dev_node == DI_NODE_NIL) {
772		di_fini(bus_node);
773		return (1);	/* dev not found (insert case) */
774	}
775
776	/* check child node for property */
777	if (di_prop_lookup_ints(DDI_DEV_T_ANY, dev_node, "scsi-no-quiesce",
778	    &propval) == 1) {
779		di_fini(bus_node);
780		return (0);	/* quiesce not required */
781	}
782	return (1);	/* quiesce required */
783}
784
785static scfga_ret_t
786quiesce_confirm(
787	apid_t *apidp,
788	msgid_t cmd_msg,
789	prompt_t *prp,
790	int *okp,
791	int *quiesce,
792	int *l_errnop)
793{
794	char *buf = NULL, *hbap = NULL, *cq1 = NULL, *cq2 = NULL;
795	char *cp;
796	size_t len = 0;
797	int i = 0, append_newline;
798	scfga_ret_t ret;
799
800	assert(apidp->path != NULL);
801	assert(apidp->hba_phys != NULL);
802
803	*quiesce = quiesce_required(apidp, l_errnop);
804	if (*quiesce == -1)
805		return (SCFGA_ERR);
806	else if (*quiesce == 0)
807		return (SCFGA_OK);
808
809	/*
810	 * Try to create HBA logical ap_id.
811	 * If that fails use physical path
812	 */
813	ret = make_hba_logid(apidp->hba_phys, &hbap, &i);
814	if (ret != SCFGA_OK) {
815		if ((hbap = get_node_path(apidp->hba_phys)) == NULL) {
816			*l_errnop = errno;
817			return (SCFGA_LIB_ERR);
818		}
819	}
820
821	assert(hbap != NULL);
822
823	append_newline = 0;
824	cq1 = cfga_str(append_newline, CONF_QUIESCE_1, hbap, 0);
825	cq2 = cfga_str(append_newline, CONF_QUIESCE_2, 0);
826	len = strlen(cq1) + strlen(cq2) + 1; /* Includes term. NULL */
827
828	if ((buf = calloc(1, len)) == NULL) {
829		*l_errnop = errno;
830		ret = SCFGA_LIB_ERR;
831		S_FREE(cq1);
832		S_FREE(cq2);
833		goto out;
834	}
835	(void) strcpy(buf, cq1);
836	(void) strcat(buf, cq2);
837
838	S_FREE(cq1);
839	S_FREE(cq2);
840
841
842	/* Remove minor name (if any) from phys path */
843	if ((cp = strrchr(apidp->path, ':')) != NULL) {
844		*cp = '\0';
845	}
846
847	/* describe operation being attempted */
848	cfga_msg(prp->msgp, cmd_msg, apidp->path, 0);
849
850	/* Restore minor name */
851	if (cp != NULL) {
852		*cp = ':';
853	}
854
855	/* request permission to quiesce */
856	assert(prp->confp != NULL && prp->confp->confirm != NULL);
857	*okp = prp->confp->confirm(prp->confp->appdata_ptr, buf);
858
859	ret = SCFGA_OK;
860	/*FALLTHRU*/
861out:
862	S_FREE(buf);
863	S_FREE(hbap);
864	return (ret);
865}
866
867static scfga_ret_t
868suspend_in_rcm(
869	apid_t		*apidp,
870	char		***suspend_list_ptr,
871	char		**errstring,
872	cfga_flags_t	flags)
873{
874	scfga_ret_t	ret;
875	char		*bus_path = NULL;
876	char		*dev_path = NULL;
877	char		**suspend_list = NULL;
878
879	*suspend_list_ptr = NULL;
880
881	/* Suspend the bus through RCM */
882	if (apidp->flags & FLAG_DISABLE_RCM)
883		return (SCFGA_OK);
884
885	/* The bus_path is the HBA path without its minor */
886	if ((bus_path = get_node_path(apidp->hba_phys)) == NULL)
887		return (SCFGA_ERR);
888
889	/*
890	 * The dev_path is already initialized to NULL.  If the AP Id
891	 * path differs from the HBA path, then the dev_path should
892	 * instead be set to the AP Id path without its minor.
893	 */
894	if (strcmp(apidp->hba_phys, apidp->path) != 0) {
895		if ((dev_path = get_node_path(apidp->path)) == NULL) {
896			ret = SCFGA_ERR;
897			goto out;
898		}
899	}
900
901	if ((ret = get_hba_children(bus_path, dev_path, &suspend_list))
902	    != SCFGA_OK) {
903		free_dev_list(suspend_list);
904		goto out;
905	}
906
907	if (scsi_rcm_suspend(suspend_list, errstring, flags, 0) != SCFGA_OK) {
908		ret = SCFGA_ERR;
909		free_dev_list(suspend_list);
910	} else {
911		ret = SCFGA_OK;
912		*suspend_list_ptr = suspend_list;
913	}
914	/*FALLTHROUGH*/
915out:
916	S_FREE(bus_path);
917	S_FREE(dev_path);
918	return (ret);
919}
920
921/*
922 * Resume the bus through RCM if it successfully
923 * unquiesced.
924 */
925static void
926resume_in_rcm(
927	apid_t		*apidp,
928	char		**suspend_list,
929	char		**errstring,
930	cfga_flags_t	flags)
931{
932	if (apidp->flags & FLAG_DISABLE_RCM)
933		return;
934
935	(void) scsi_rcm_resume(suspend_list, errstring, flags, 0);
936
937	free_dev_list(suspend_list);
938}
939
940static scfga_ret_t
941wait_for_hotplug(prompt_t *pt, int msg)
942{
943	char		*cu = NULL;
944	int		append_newline = 0;
945	scfga_ret_t	ret;
946
947	cu = cfga_str(append_newline, msg, 0);
948	if (pt->confp->confirm(pt->confp->appdata_ptr, cu) != 1) {
949		ret = SCFGA_NACK;
950	} else {
951		ret = SCFGA_OK;
952	}
953	S_FREE(cu);
954	return (ret);
955}
956
957static scfga_ret_t
958bus_quiesce(apid_t *apidp, prompt_t *pt, char **errstring, cfga_flags_t flags)
959{
960	int		l_errno;
961	scfga_ret_t	ret;
962	scfga_ret_t	hpret;
963	char		**suspend_list = NULL;
964
965	ret = suspend_in_rcm(apidp, &suspend_list, errstring, flags);
966	if (ret != SCFGA_OK) {
967		return (ret);
968	}
969
970	/*
971	 * If the quiesce fails, then cancel the RCM suspend.
972	 * Discard any RCM failures so that the devctl failure
973	 * can still be reported.
974	 */
975	l_errno = 0;
976	ret = devctl_cmd(apidp->hba_phys, SCFGA_BUS_QUIESCE, NULL, &l_errno);
977	if (ret != SCFGA_OK) {
978		resume_in_rcm(apidp, suspend_list, errstring, flags);
979		cfga_err(errstring, l_errno, ERR_BUS_QUIESCE, 0);
980		return (ret);
981	}
982
983	/*
984	 * Prompt user to proceed with physical hotplug
985	 * and wait until they are done.
986	 */
987	hpret = wait_for_hotplug(pt, CONF_UNQUIESCE);
988
989	/*
990	 * The unquiesce may fail with EALREADY (which is ok)
991	 * or some other error (which is not ok).
992	 */
993	l_errno = 0;
994	ret = devctl_cmd(apidp->hba_phys, SCFGA_BUS_UNQUIESCE, NULL, &l_errno);
995	if (ret != SCFGA_OK && l_errno != EALREADY) {
996		free_dev_list(suspend_list);
997		cfga_err(errstring, l_errno, ERR_BUS_UNQUIESCE, 0);
998		return (SCFGA_ERR);
999	}
1000
1001	resume_in_rcm(apidp, suspend_list, errstring, flags);
1002
1003	return (hpret);
1004}
1005
1006#define	MAX_LOCK_TRIES		20
1007#define	MAX_UNLINK_TRIES	60
1008#define	s_getpid		(int)getpid	/* else lint causes problems */
1009
1010static void
1011s_unlink(char *file)
1012{
1013	int	count = 0;
1014
1015retry:
1016	if (unlink(file) == -1) {
1017		if (errno != EINTR && errno != EAGAIN) {
1018			CFGA_TRACE1((stdout, "s_unlink[%d]: unlink failed: "
1019			    "%s: %s\n", s_getpid(), file, strerror(errno)));
1020			return;
1021		}
1022
1023		if (++count < MAX_UNLINK_TRIES) {
1024			(void) sleep(1);
1025			goto retry;
1026		}
1027		CFGA_TRACE1((stdout, "s_unlink[%d]: retry limit: %s\n",
1028		    s_getpid(), file));
1029	} else {
1030		CFGA_TRACE3((stdout, "s_unlink[%d]: unlinked: %s\n",
1031		    s_getpid(), file));
1032	}
1033}
1034
1035static scfga_ret_t
1036create_lock(int *fdp, struct cfga_msg *msgp, char **errstring)
1037{
1038	FILE			*fp;
1039	int			count;
1040	struct extmnttab	ent;
1041	int			mnted;
1042
1043
1044	*fdp = -1;
1045
1046	/*
1047	 * Check that /var/run is mounted. In the unlikely event
1048	 * that the lock file is left behind, we want it
1049	 * cleared on the next reboot.
1050	 */
1051	errno = 0;
1052	if ((fp = fopen(MNTTAB, "r")) == NULL) {
1053		cfga_err(errstring, errno, ERRARG_OPEN, MNTTAB, 0);
1054		return (SCFGA_LIB_ERR);
1055	}
1056
1057	resetmnttab(fp);
1058
1059	mnted = 0;
1060	while (getextmntent(fp, &ent, sizeof (ent)) == 0) {
1061		if (strcmp(ent.mnt_mountp, "/var/run") == 0) {
1062			mnted = 1;
1063			break;
1064		}
1065	}
1066
1067	(void) fclose(fp);
1068
1069	if (!mnted) {
1070		cfga_err(errstring, 0, ERR_VAR_RUN, 0);
1071		return (SCFGA_LIB_ERR);
1072	}
1073
1074	/*
1075	 * Wait for a short period of time if we cannot O_EXCL create
1076	 * lock file. If some other cfgadm process is finishing up, we
1077	 * can get in. If the wait required is long however, just
1078	 * return SYSTEM_BUSY to the user - a hotplug operation is
1079	 * probably in progress.
1080	 */
1081	count = 0;
1082retry:
1083	*fdp = open(SCFGA_LOCK, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
1084	if (*fdp == -1 && errno == EEXIST) {
1085		if (++count < MAX_LOCK_TRIES) {
1086			if (count == 1)
1087				cfga_msg(msgp, MSG_WAIT_LOCK, 0);
1088			(void) sleep(1);
1089			goto retry;
1090		}
1091	}
1092
1093	if (*fdp == -1 && errno == EEXIST) {
1094		cfga_err(errstring, 0, ERRARG_QUIESCE_LOCK, SCFGA_LOCK, 0);
1095		return (SCFGA_SYSTEM_BUSY);
1096	} else if (*fdp == -1) {
1097		cfga_err(errstring, errno, ERRARG_QUIESCE_LOCK, SCFGA_LOCK, 0);
1098		return (SCFGA_LIB_ERR);
1099	}
1100
1101	CFGA_TRACE3((stdout, "create_lock[%d]: created lockfile: %s\n",
1102	    s_getpid(), SCFGA_LOCK));
1103
1104	return (SCFGA_OK);
1105}
1106
1107static scfga_ret_t
1108syslock(int fd, char **errstring)
1109{
1110	struct flock	lock;
1111	int		count;
1112	int		rval;
1113
1114	assert(fd != -1);
1115
1116	CFGA_TRACE3((stdout, "syslock[%d]: trying lock: %s\n",
1117	    s_getpid(), SCFGA_LOCK));
1118
1119	lock.l_type = F_WRLCK;
1120	lock.l_whence = SEEK_SET;
1121	lock.l_start = 0;
1122	lock.l_len = 0;
1123
1124	count = 0;
1125	while ((rval = fcntl(fd, F_SETLKW, &lock)) == -1 && errno == EINTR) {
1126		if (++count >= MAX_LOCK_TRIES) {
1127			CFGA_TRACE1((stdout, "syslock[%d]: retry limit: %s\n",
1128			    s_getpid(), SCFGA_LOCK));
1129			goto badlock;
1130		}
1131		(void) sleep(1);
1132	}
1133
1134	if (rval != -1) {
1135		CFGA_TRACE3((stdout, "syslock[%d]: locked file: %s\n",
1136		    s_getpid(), SCFGA_LOCK));
1137		return (SCFGA_OK);
1138	}
1139
1140	/*FALLTHROUGH*/
1141badlock:
1142	cfga_err(errstring, errno, ERRARG_LOCK, SCFGA_LOCK, 0);
1143	/* trace message to display pid */
1144	CFGA_TRACE1((stdout, "syslock[%d]: cannot lock %s\n",
1145	    s_getpid(), SCFGA_LOCK));
1146	return (SCFGA_LIB_ERR);
1147}
1148
1149static void
1150wait_for_child(pid_t cpid)
1151{
1152	int	status;
1153	pid_t	rval;
1154
1155	CFGA_TRACE2((stdout, "wait_for_child[%d]: child[%d]\n",
1156	    s_getpid(), (int)cpid));
1157
1158	for (;;) {
1159		while ((rval = waitpid(cpid, &status, 0)) != cpid) {
1160			if (errno == ECHILD) {
1161				CFGA_TRACE1((stdout, "waitpid[%d]: child[%d] "
1162				    "doesn't exist\n", s_getpid(), (int)cpid));
1163				return;
1164			}
1165
1166			CFGA_TRACE3((stdout, "waitpid[%d]: returned: %d"
1167			    ": errno: %s\n", s_getpid(), (int)rval,
1168			    strerror(errno)));
1169		}
1170
1171		if (WIFEXITED(status)) {
1172			CFGA_TRACE2((stdout, "waitpid[%d]: child[%d]: "
1173			    "normal exit\n", s_getpid(), (int)cpid));
1174			return;
1175		}
1176
1177		if (WIFSIGNALED(status)) {
1178			CFGA_TRACE2((stdout, "waitpid[%d]: child[%d]: "
1179			    "signal exit\n", s_getpid(), (int)cpid));
1180			return;
1181		}
1182
1183		/*
1184		 * The child has not terminated. We received status
1185		 * because the child was either stopped or continued.
1186		 * Wait for child termination by calling waitpid() again.
1187		 */
1188	}
1189}
1190
1191static void
1192wait_and_cleanup(int fd, apid_t *apidp)
1193{
1194	int		l_errno;
1195	scfga_ret_t	ret;
1196
1197	/* This is the child */
1198	CFGA_TRACE2((stdout, "child[%d]: Entering wait_cleanup\n", s_getpid()));
1199
1200	if (syslock(fd, NULL) != SCFGA_OK) {
1201		CFGA_TRACE1((stdout, "child[%d]: lock failure "
1202		    " - _exit(1)\n", s_getpid()));
1203		/*
1204		 * As a last resort, unlink the lock file. This is relatively
1205		 * safe as the child doesn't unquiesce the bus in this case.
1206		 */
1207		s_unlink(SCFGA_LOCK);
1208		_exit(1);
1209	}
1210
1211	l_errno = 0;
1212	ret = devctl_cmd(apidp->hba_phys, SCFGA_BUS_UNQUIESCE, NULL, &l_errno);
1213	if (ret != SCFGA_OK) {
1214		if (l_errno == EALREADY)
1215			CFGA_TRACE3((stdout, "child[%d]: bus already "
1216			    "unquiesced: %s\n", s_getpid(), apidp->hba_phys));
1217		else
1218			CFGA_TRACE1((stdout, "child[%d]: unquiesce failed: "
1219			    "%s\n", s_getpid(), strerror(l_errno)));
1220	} else {
1221		CFGA_TRACE1((stdout, "child[%d]: unquiesced bus: %s\n",
1222		    s_getpid(), apidp->hba_phys));
1223	}
1224
1225	s_unlink(SCFGA_LOCK);
1226
1227	CFGA_TRACE2((stdout, "child[%d]: _exit(0)\n", s_getpid()));
1228
1229	_exit(0);
1230}
1231
1232static void
1233sigblk(sigset_t *osp)
1234{
1235	sigset_t set;
1236
1237	(void) sigemptyset(&set);
1238	(void) sigemptyset(osp);
1239	(void) sigaddset(&set, SIGHUP);
1240	(void) sigaddset(&set, SIGINT);
1241	(void) sigaddset(&set, SIGQUIT);
1242	(void) sigaddset(&set, SIGTERM);
1243	(void) sigaddset(&set, SIGUSR1);
1244	(void) sigaddset(&set, SIGUSR2);
1245	(void) sigprocmask(SIG_BLOCK, &set, osp);
1246}
1247
1248static void
1249sigunblk(sigset_t *osp)
1250{
1251	(void) sigprocmask(SIG_SETMASK, osp, NULL);
1252}
1253
1254/*
1255 * Here is the algorithm used to ensure that a SCSI bus is not
1256 * left in the quiesced state:
1257 *
1258 *	lock quiesce mutex	// single threads this code
1259 *	open(O_CREAT|O_EXCL) lock file	// only 1 process at a time
1260 *	exclusive record lock on lock file
1261 *	fork1()
1262 *	quiesce bus
1263 *	do the physical hotplug operation
1264 *	unquiesce bus
1265 *	unlock record lock
1266 *		-> *child*
1267 *		-> wait for record lock
1268 *		-> unconditionally unquiesce bus
1269 *		-> unlink lock file
1270 *		-> exit
1271 *	wait for child to exit
1272 *	unlock quiesce mutex
1273 *
1274 * NOTE1: since record locks are per-process and a close() can
1275 * release a lock, to keep things MT-safe we need a quiesce mutex.
1276 *
1277 * NOTE2: To ensure that the child does not unquiesce a bus quiesced
1278 * by an unrelated cfgadm_scsi operation, exactly 1 process in the
1279 * system can be doing an implicit quiesce operation  The exclusive
1280 * creation of the lock file guarantees this.
1281 *
1282 * NOTE3: This works even if the parent process dumps core and/or is
1283 * abnormally terminated. If the parent dies before the child is
1284 * forked, the bus is not quiesced. If the parent dies after the
1285 * bus is quiesced, the child process will ensure that the bus is
1286 * unquiesced.
1287 */
1288static scfga_ret_t
1289dev_hotplug(
1290	apid_t *apidp,
1291	prompt_t *pt,
1292	cfga_flags_t flags,
1293	int do_quiesce,
1294	char **errstring)
1295{
1296	scfga_ret_t	ret;
1297	pid_t		cpid;
1298	int		fd;
1299	sigset_t	oset;
1300
1301	assert(apidp->hba_phys != NULL);
1302	assert(apidp->path != NULL);
1303
1304	/* If no quiesce required, prompt the user to do the operation */
1305	if (!do_quiesce)
1306		return (wait_for_hotplug(pt, CONF_NO_QUIESCE));
1307
1308	(void) mutex_lock(&quiesce_mutex);
1309
1310	ret = create_lock(&fd, pt->msgp, errstring);
1311	if (ret != SCFGA_OK) {
1312		(void) mutex_unlock(&quiesce_mutex);
1313		return (ret);
1314	}
1315
1316	ret = syslock(fd, errstring);
1317	if (ret != SCFGA_OK) {
1318		goto bad;
1319	}
1320
1321	/*
1322	 * block signals in the child. Parent may
1323	 * exit, causing signal to be sent to child.
1324	 */
1325	sigblk(&oset);
1326
1327	switch (cpid = fork1()) {
1328		case 0:
1329			/* child */
1330			wait_and_cleanup(fd, apidp);
1331			_exit(0); /* paranoia */
1332			/*NOTREACHED*/
1333		case -1:
1334			cfga_err(errstring, errno, ERR_FORK, 0);
1335			sigunblk(&oset);
1336			ret = SCFGA_LIB_ERR;
1337			goto bad;
1338		default:
1339			/* parent */
1340			break;
1341	}
1342
1343	sigunblk(&oset);
1344
1345	/* We have forked successfully - this is the parent */
1346	ret = bus_quiesce(apidp, pt, errstring, flags);
1347
1348	(void) close(fd);	/* also unlocks */
1349
1350	wait_for_child(cpid);
1351
1352	(void) mutex_unlock(&quiesce_mutex);
1353
1354	return (ret);
1355bad:
1356	(void) close(fd);
1357	s_unlink(SCFGA_LOCK);
1358	(void) mutex_unlock(&quiesce_mutex);
1359	return (ret);
1360}
1361
1362/*
1363 * Checks if HBA controls a critical file-system (/, /usr or swap)
1364 * This routine reads /etc/vfstab and is NOT foolproof.
1365 * If an error occurs, assumes that controller is NOT critical.
1366 */
1367static int
1368critical_ctrlr(const char *hba_phys)
1369{
1370	FILE *fp;
1371	struct vfstab vfst;
1372	int vfsret = 1, rv = -1;
1373	char *bufp;
1374	const size_t buflen = PATH_MAX;
1375	char mount[MAXPATHLEN], fstype[MAXPATHLEN], spec[MAXPATHLEN];
1376
1377
1378	if ((bufp = calloc(1, buflen)) == NULL) {
1379		return (0);
1380	}
1381
1382	fp = NULL;
1383	if ((fp = fopen(ETC_VFSTAB, "r")) == NULL) {
1384		rv = 0;
1385		goto out;
1386	}
1387
1388	while ((vfsret = getvfsent(fp, &vfst)) == 0) {
1389
1390		(void) strcpy(mount, S_STR(vfst.vfs_mountp));
1391		(void) strcpy(fstype, S_STR(vfst.vfs_fstype));
1392		(void) strcpy(spec, S_STR(vfst.vfs_special));
1393
1394		/* Ignore non-critical entries */
1395		if (strcmp(mount, "/") && strcmp(mount, "/usr") &&
1396		    strcmp(fstype, "swap")) {
1397			continue;
1398		}
1399
1400		/* get physical path */
1401		if (realpath(spec, bufp) == NULL) {
1402			continue;
1403		}
1404
1405		/* Check if critical partition is on the HBA */
1406		if (!(rv = hba_dev_cmp(hba_phys, bufp))) {
1407			break;
1408		}
1409	}
1410
1411	rv = !vfsret;
1412
1413	/*FALLTHRU*/
1414out:
1415	S_FREE(bufp);
1416	if (fp != NULL) {
1417		(void) fclose(fp);
1418	}
1419	return (rv);
1420}
1421
1422/*
1423 * Convert bus state to receptacle state
1424 */
1425static cfga_stat_t
1426bus_devctl_to_recep_state(uint_t bus_dc_state)
1427{
1428	cfga_stat_t rs;
1429
1430	switch (bus_dc_state) {
1431	case BUS_ACTIVE:
1432		rs = CFGA_STAT_CONNECTED;
1433		break;
1434	case BUS_QUIESCED:
1435	case BUS_SHUTDOWN:
1436		rs = CFGA_STAT_DISCONNECTED;
1437		break;
1438	default:
1439		rs = CFGA_STAT_NONE;
1440		break;
1441	}
1442
1443	return (rs);
1444}
1445
1446static int
1447add_dev(di_node_t node, void *arg)
1448{
1449	int ndevs, len;
1450	char *path, *p;
1451	struct larg *largp = (struct larg *)arg;
1452
1453	/* ignore hba itself and all detached nodes */
1454	if (di_parent_node(node) == DI_NODE_NIL ||
1455	    di_node_state(node) < DS_ATTACHED)
1456		return (DI_WALK_CONTINUE);
1457
1458	if ((path = di_devfs_path(node)) == NULL) {
1459		largp->ndevs = -1;
1460		return (DI_WALK_TERMINATE);
1461	}
1462
1463	/* sizeof (DEVICES_DIR) includes the null terminator */
1464	len = strlen(path) + sizeof (DEVICES_DIR);
1465	if ((p = malloc(len)) == NULL) {
1466		di_devfs_path_free(path);
1467		largp->ndevs = -1;
1468		return (DI_WALK_TERMINATE);
1469	}
1470	(void) snprintf(p, len, "%s%s", DEVICES_DIR, path);
1471	di_devfs_path_free(path);
1472
1473	/* ignore device to be excluded */
1474	if (largp->dev && strcmp(largp->dev, p) == 0) {
1475		free(p);
1476		return (DI_WALK_CONTINUE);
1477	}
1478
1479	/* grow dev_list to allow room for one more device */
1480	if (alloc_dev_list(largp) != 0) {
1481		free(p);
1482		return (DI_WALK_TERMINATE);
1483	}
1484	ndevs = largp->ndevs;
1485	largp->ndevs++;
1486	largp->dev_list[ndevs] = p;
1487	largp->dev_list[ndevs + 1] = NULL;
1488	return (DI_WALK_CONTINUE);
1489}
1490
1491/*
1492 * Get list of children excluding dev_excl (if not null).
1493 */
1494static int
1495get_hba_children(char *bus_path, char *dev_excl, char ***dev_listp)
1496{
1497	int err, ret;
1498	walkarg_t u;
1499	struct larg larg;
1500
1501	*dev_listp = NULL;
1502
1503	u.node_args.flags = DI_WALK_CLDFIRST;
1504	u.node_args.fcn = add_dev;
1505
1506	larg.ndevs = 0;
1507	larg.nelem = 0;
1508	larg.dev = dev_excl;
1509	larg.dev_list = NULL;
1510
1511	ret = walk_tree(bus_path, &larg, DINFOSUBTREE, &u, SCFGA_WALK_NODE,
1512	    &err);
1513	if (larg.ndevs == -1) {
1514		free_dev_list(larg.dev_list);
1515		return (SCFGA_ERR);
1516	}
1517	*dev_listp = larg.dev_list;
1518	return (ret);
1519}
1520
1521static char *
1522get_node_path(char *minor_path)
1523{
1524	char *path, *cp;
1525
1526	if ((path = strdup(minor_path)) == NULL)
1527		return (NULL);
1528	if ((cp = strrchr(path, ':')) != NULL)
1529		*cp = '\0';
1530	return (path);
1531}
1532
1533/*
1534 * Ensure largp->dev_list has room for one more device.
1535 * Returns 0 on success, -1 on failure.
1536 */
1537static int
1538alloc_dev_list(struct larg *largp)
1539{
1540	int nelem;
1541	char **p;
1542
1543	if (largp->nelem > largp->ndevs + 2)	/* +1 for NULL termination */
1544		return (0);
1545
1546	nelem =  largp->nelem + 16;
1547	p = realloc(largp->dev_list, nelem * sizeof (char *));
1548	if (p == NULL)
1549		return (-1);
1550
1551	largp->dev_list = p;
1552	largp->nelem = nelem;
1553	return (0);
1554}
1555
1556static void
1557free_dev_list_elements(char **dev_list)
1558{
1559	while (*dev_list) {
1560		free(*dev_list);
1561		dev_list++;
1562	}
1563}
1564
1565static void
1566free_dev_list(char **dev_list)
1567{
1568	if (dev_list == NULL)
1569		return;
1570
1571	free_dev_list_elements(dev_list);
1572	free(dev_list);
1573}
1574