xref: /onnv-gate/usr/src/lib/cfgadm_plugins/scsi/common/cfga_ctl.c (revision 10696:cd0f390dd9e2)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51957Sdnielsen  * Common Development and Distribution License (the "License").
61957Sdnielsen  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
211957Sdnielsen 
220Sstevel@tonic-gate /*
23*10696SDavid.Hollister@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #include "cfga_scsi.h"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate struct larg {
300Sstevel@tonic-gate 	int ndevs;
310Sstevel@tonic-gate 	int nelem;
320Sstevel@tonic-gate 	char *dev;
330Sstevel@tonic-gate 	char **dev_list;
340Sstevel@tonic-gate };
350Sstevel@tonic-gate 
360Sstevel@tonic-gate #define	ETC_VFSTAB	"/etc/vfstab"
370Sstevel@tonic-gate #define	SCFGA_LOCK	"/var/run/cfgadm_scsi"
380Sstevel@tonic-gate 
390Sstevel@tonic-gate /* Function prototypes */
400Sstevel@tonic-gate 
410Sstevel@tonic-gate static scfga_ret_t quiesce_confirm(apid_t *apidp,
420Sstevel@tonic-gate     msgid_t cmd_msg, prompt_t *pt, int *okp, int *quiesce, int *l_errnop);
430Sstevel@tonic-gate static scfga_ret_t dev_hotplug(apid_t *apidp,
440Sstevel@tonic-gate     prompt_t *pt, cfga_flags_t flags, int quiesce, char **errstring);
450Sstevel@tonic-gate static int disconnect(struct cfga_confirm *confp);
460Sstevel@tonic-gate static int critical_ctrlr(const char *hba_phys);
470Sstevel@tonic-gate static cfga_stat_t bus_devctl_to_recep_state(uint_t bus_dc_state);
480Sstevel@tonic-gate static int get_hba_children(char *bus_path, char *dev_excl, char ***dev_list);
490Sstevel@tonic-gate static char *get_node_path(char *minor_path);
500Sstevel@tonic-gate static void free_dev_list_elements(char **dev_list);
510Sstevel@tonic-gate static void free_dev_list(char **dev_list);
520Sstevel@tonic-gate static int alloc_dev_list(struct larg *largp);
530Sstevel@tonic-gate 
540Sstevel@tonic-gate /*
550Sstevel@tonic-gate  * Single thread all implicit quiesce operations
560Sstevel@tonic-gate  */
570Sstevel@tonic-gate static mutex_t	quiesce_mutex = DEFAULTMUTEX;
580Sstevel@tonic-gate 
590Sstevel@tonic-gate /*ARGSUSED*/
600Sstevel@tonic-gate scfga_ret_t
bus_change_state(cfga_cmd_t state_change_cmd,apid_t * apidp,struct cfga_confirm * confp,cfga_flags_t flags,char ** errstring)610Sstevel@tonic-gate bus_change_state(
620Sstevel@tonic-gate 	cfga_cmd_t state_change_cmd,
630Sstevel@tonic-gate 	apid_t *apidp,
640Sstevel@tonic-gate 	struct cfga_confirm *confp,
650Sstevel@tonic-gate 	cfga_flags_t flags,
660Sstevel@tonic-gate 	char **errstring)
670Sstevel@tonic-gate {
680Sstevel@tonic-gate 	int l_errno = 0, force;
690Sstevel@tonic-gate 	uint_t state = 0;
700Sstevel@tonic-gate 	cfga_stat_t bus_state;
710Sstevel@tonic-gate 	scfga_cmd_t cmd;
720Sstevel@tonic-gate 	msgid_t errid;
730Sstevel@tonic-gate 	cfga_stat_t prereq;
740Sstevel@tonic-gate 	scfga_ret_t ret;
750Sstevel@tonic-gate 	char **dev_list = NULL;
760Sstevel@tonic-gate 
770Sstevel@tonic-gate 	assert(apidp->path != NULL);
780Sstevel@tonic-gate 	assert(apidp->hba_phys != NULL);
790Sstevel@tonic-gate 
800Sstevel@tonic-gate 	/*
810Sstevel@tonic-gate 	 * No dynamic components allowed
820Sstevel@tonic-gate 	 */
830Sstevel@tonic-gate 	if (apidp->dyncomp != NULL) {
840Sstevel@tonic-gate 		cfga_err(errstring, 0, ERR_NOT_BUSAPID, 0);
850Sstevel@tonic-gate 		return (SCFGA_ERR);
860Sstevel@tonic-gate 	}
870Sstevel@tonic-gate 
880Sstevel@tonic-gate 	/* Get bus state */
890Sstevel@tonic-gate 	if (devctl_cmd(apidp->path, SCFGA_BUS_GETSTATE, &state,
900Sstevel@tonic-gate 	    &l_errno) != SCFGA_OK) {
910Sstevel@tonic-gate 		cfga_err(errstring, l_errno, ERR_BUS_GETSTATE, 0);
920Sstevel@tonic-gate 		return (SCFGA_ERR);
930Sstevel@tonic-gate 	}
940Sstevel@tonic-gate 
950Sstevel@tonic-gate 	bus_state = bus_devctl_to_recep_state(state);
960Sstevel@tonic-gate 	force = ((flags & CFGA_FLAG_FORCE) == CFGA_FLAG_FORCE) ? 1 : 0;
970Sstevel@tonic-gate 	assert(confp->confirm != NULL);
980Sstevel@tonic-gate 
990Sstevel@tonic-gate 	switch (state_change_cmd) {
1000Sstevel@tonic-gate 	case CFGA_CMD_DISCONNECT:	/* quiesce bus */
1010Sstevel@tonic-gate 		/*
1020Sstevel@tonic-gate 		 * If force flag not specified, check if controller is
1030Sstevel@tonic-gate 		 * critical.
1040Sstevel@tonic-gate 		 */
1050Sstevel@tonic-gate 		if (!force) {
1060Sstevel@tonic-gate 			/*
1070Sstevel@tonic-gate 			 * This check is not foolproof, get user confirmation
1080Sstevel@tonic-gate 			 * if test passes.
1090Sstevel@tonic-gate 			 */
1100Sstevel@tonic-gate 			if (critical_ctrlr(apidp->path)) {
1110Sstevel@tonic-gate 				cfga_err(errstring, 0, ERR_CTRLR_CRIT, 0);
1120Sstevel@tonic-gate 				ret = SCFGA_ERR;
1130Sstevel@tonic-gate 				break;
1140Sstevel@tonic-gate 			} else if (!disconnect(confp)) {
1150Sstevel@tonic-gate 				ret = SCFGA_NACK;
1160Sstevel@tonic-gate 				break;
1170Sstevel@tonic-gate 			}
1180Sstevel@tonic-gate 		}
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate 		cmd = SCFGA_BUS_QUIESCE;
1210Sstevel@tonic-gate 		errid = ERR_BUS_QUIESCE;
1220Sstevel@tonic-gate 		prereq = CFGA_STAT_CONNECTED;
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate 		goto common;
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate 	case CFGA_CMD_CONNECT:		/* unquiesce bus */
1270Sstevel@tonic-gate 		cmd = SCFGA_BUS_UNQUIESCE;
1280Sstevel@tonic-gate 		errid = ERR_BUS_UNQUIESCE;
1290Sstevel@tonic-gate 		prereq = CFGA_STAT_DISCONNECTED;
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate 		goto common;
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate 	case CFGA_CMD_CONFIGURE:
1340Sstevel@tonic-gate 		cmd = SCFGA_BUS_CONFIGURE;
1350Sstevel@tonic-gate 		errid = ERR_BUS_CONFIGURE;
1360Sstevel@tonic-gate 		prereq = CFGA_STAT_CONNECTED;
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate 		goto common;
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate 	case CFGA_CMD_UNCONFIGURE:
1410Sstevel@tonic-gate 		cmd = SCFGA_BUS_UNCONFIGURE;
1420Sstevel@tonic-gate 		errid = ERR_BUS_UNCONFIGURE;
1430Sstevel@tonic-gate 		prereq = CFGA_STAT_CONNECTED;
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate 		/* FALLTHROUGH */
1460Sstevel@tonic-gate 	common:
1470Sstevel@tonic-gate 		if (bus_state != prereq) {
1480Sstevel@tonic-gate 			cfga_err(errstring, 0,
1490Sstevel@tonic-gate 			    (prereq == CFGA_STAT_CONNECTED)
1500Sstevel@tonic-gate 			    ? ERR_BUS_NOTCONNECTED
1510Sstevel@tonic-gate 			    : ERR_BUS_CONNECTED, 0);
1520Sstevel@tonic-gate 			ret = SCFGA_ERR;
1530Sstevel@tonic-gate 			break;
1540Sstevel@tonic-gate 		}
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate 		/*
1570Sstevel@tonic-gate 		 * When quiescing or unconfiguring a bus, first suspend or
1580Sstevel@tonic-gate 		 * offline it through RCM.
1590Sstevel@tonic-gate 		 * For unquiescing, we simple build the dev_list for
1600Sstevel@tonic-gate 		 * resume notification.
1610Sstevel@tonic-gate 		 */
1620Sstevel@tonic-gate 		if (((apidp->flags & FLAG_DISABLE_RCM) == 0) &&
1630Sstevel@tonic-gate 		    ((cmd == SCFGA_BUS_QUIESCE) ||
1640Sstevel@tonic-gate 		    (cmd == SCFGA_BUS_UNQUIESCE) ||
1650Sstevel@tonic-gate 		    (cmd == SCFGA_BUS_UNCONFIGURE))) {
1660Sstevel@tonic-gate 			ret = get_hba_children(apidp->path, NULL, &dev_list);
1670Sstevel@tonic-gate 			if (ret != SCFGA_OK) {
1680Sstevel@tonic-gate 				break;
1690Sstevel@tonic-gate 			}
1700Sstevel@tonic-gate 			if (cmd == SCFGA_BUS_QUIESCE) {
1710Sstevel@tonic-gate 				if ((ret = scsi_rcm_suspend(dev_list,
1720Sstevel@tonic-gate 				    errstring, flags, 1)) != SCFGA_OK) {
1730Sstevel@tonic-gate 					break;
1740Sstevel@tonic-gate 				}
1750Sstevel@tonic-gate 			} else if (cmd == SCFGA_BUS_UNCONFIGURE) {
1760Sstevel@tonic-gate 				if ((ret = scsi_rcm_offline(dev_list,
1770Sstevel@tonic-gate 				    errstring, flags)) != SCFGA_OK) {
1780Sstevel@tonic-gate 					break;
1790Sstevel@tonic-gate 				}
1800Sstevel@tonic-gate 			}
1810Sstevel@tonic-gate 		}
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate 		ret = devctl_cmd(apidp->path, cmd, NULL, &l_errno);
1840Sstevel@tonic-gate 		if (ret != SCFGA_OK) {
1850Sstevel@tonic-gate 			/*
1860Sstevel@tonic-gate 			 * EIO when child devices are busy may confuse user.
1870Sstevel@tonic-gate 			 * So explain it.
1880Sstevel@tonic-gate 			 */
1890Sstevel@tonic-gate 			if (cmd == SCFGA_BUS_UNCONFIGURE && l_errno == EIO)
1900Sstevel@tonic-gate 				errid = ERR_MAYBE_BUSY;
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate 			cfga_err(errstring, l_errno, errid, 0);
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate 			/*
1950Sstevel@tonic-gate 			 * If the bus was suspended in RCM, then cancel the RCM
1960Sstevel@tonic-gate 			 * operation.  Discard RCM failures here because the
1970Sstevel@tonic-gate 			 * devctl's failure is what is most relevant.
1980Sstevel@tonic-gate 			 */
1990Sstevel@tonic-gate 			if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
2000Sstevel@tonic-gate 				if (cmd == SCFGA_BUS_QUIESCE)
2010Sstevel@tonic-gate 					(void) scsi_rcm_resume(dev_list,
2020Sstevel@tonic-gate 					    errstring,
2030Sstevel@tonic-gate 					    (flags & (~CFGA_FLAG_FORCE)), 1);
2040Sstevel@tonic-gate 				else if (cmd == SCFGA_BUS_UNCONFIGURE) {
2050Sstevel@tonic-gate 					(void) devctl_cmd(apidp->path,
2060Sstevel@tonic-gate 					    SCFGA_BUS_CONFIGURE, NULL,
2070Sstevel@tonic-gate 					    &l_errno);
2080Sstevel@tonic-gate 					(void) scsi_rcm_online(dev_list,
2090Sstevel@tonic-gate 					    errstring,
2100Sstevel@tonic-gate 					    (flags & (~CFGA_FLAG_FORCE)));
2110Sstevel@tonic-gate 				}
2120Sstevel@tonic-gate 			}
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 			break;
2150Sstevel@tonic-gate 		}
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 		/*
2180Sstevel@tonic-gate 		 * When unquiescing or configuring a bus, resume or online it
2190Sstevel@tonic-gate 		 * in RCM when the devctl command is complete.
2200Sstevel@tonic-gate 		 * When unconfiguring a bus, notify removal of devices.
2210Sstevel@tonic-gate 		 */
2220Sstevel@tonic-gate 		if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
2230Sstevel@tonic-gate 			if (cmd == SCFGA_BUS_UNQUIESCE) {
2240Sstevel@tonic-gate 				ret = scsi_rcm_resume(dev_list, errstring,
2250Sstevel@tonic-gate 				    (flags & (~CFGA_FLAG_FORCE)), 1);
2260Sstevel@tonic-gate 			} else if (cmd == SCFGA_BUS_UNCONFIGURE) {
2270Sstevel@tonic-gate 				ret = scsi_rcm_remove(dev_list, errstring,
2280Sstevel@tonic-gate 				    (flags & (~CFGA_FLAG_FORCE)));
2290Sstevel@tonic-gate 			}
2300Sstevel@tonic-gate 		}
2310Sstevel@tonic-gate 		break;
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate 	case CFGA_CMD_LOAD:
2340Sstevel@tonic-gate 	case CFGA_CMD_UNLOAD:
2350Sstevel@tonic-gate 		ret = SCFGA_OPNOTSUPP;
2360Sstevel@tonic-gate 		break;
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate 	default:
2390Sstevel@tonic-gate 		cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
2400Sstevel@tonic-gate 		ret = SCFGA_ERR;
2410Sstevel@tonic-gate 		break;
2420Sstevel@tonic-gate 	}
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate 	free_dev_list(dev_list);
2450Sstevel@tonic-gate 	return (ret);
2460Sstevel@tonic-gate }
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate scfga_ret_t
dev_change_state(cfga_cmd_t state_change_cmd,apid_t * apidp,cfga_flags_t flags,char ** errstring)2490Sstevel@tonic-gate dev_change_state(
2500Sstevel@tonic-gate 	cfga_cmd_t state_change_cmd,
2510Sstevel@tonic-gate 	apid_t *apidp,
2520Sstevel@tonic-gate 	cfga_flags_t flags,
2530Sstevel@tonic-gate 	char **errstring)
2540Sstevel@tonic-gate {
2550Sstevel@tonic-gate 	uint_t state = 0;
2560Sstevel@tonic-gate 	int l_errno = 0;
2570Sstevel@tonic-gate 	cfga_stat_t bus_state;
2580Sstevel@tonic-gate 	scfga_cmd_t cmd;
2590Sstevel@tonic-gate 	msgid_t errid;
2600Sstevel@tonic-gate 	scfga_ret_t ret;
2610Sstevel@tonic-gate 	char *dev_list[2] = {NULL};
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate 	assert(apidp->path != NULL);
2640Sstevel@tonic-gate 	assert(apidp->hba_phys != NULL);
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate 	/*
2670Sstevel@tonic-gate 	 * For a device, dynamic component must be present
2680Sstevel@tonic-gate 	 */
2690Sstevel@tonic-gate 	if (apidp->dyncomp == NULL) {
2700Sstevel@tonic-gate 		cfga_err(errstring, 0, ERR_APID_INVAL, 0);
2710Sstevel@tonic-gate 		return (SCFGA_ERR);
2720Sstevel@tonic-gate 	}
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 	/* Get bus state */
2750Sstevel@tonic-gate 	if (devctl_cmd(apidp->hba_phys, SCFGA_BUS_GETSTATE, &state,
2760Sstevel@tonic-gate 	    &l_errno) != SCFGA_OK) {
2770Sstevel@tonic-gate 		cfga_err(errstring, l_errno, ERR_BUS_GETSTATE, 0);
2780Sstevel@tonic-gate 		return (SCFGA_ERR);
2790Sstevel@tonic-gate 	}
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 	bus_state = bus_devctl_to_recep_state(state);
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	switch (state_change_cmd) {
2840Sstevel@tonic-gate 	case CFGA_CMD_CONFIGURE:		/* online device */
2850Sstevel@tonic-gate 		cmd = SCFGA_DEV_CONFIGURE;
2860Sstevel@tonic-gate 		errid = ERR_DEV_CONFIGURE;
2870Sstevel@tonic-gate 		goto common;
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	case CFGA_CMD_UNCONFIGURE:		/* offline device */
2900Sstevel@tonic-gate 		cmd = SCFGA_DEV_UNCONFIGURE;
2910Sstevel@tonic-gate 		errid = ERR_DEV_UNCONFIGURE;
2920Sstevel@tonic-gate 		/* FALLTHROUGH */
2930Sstevel@tonic-gate 	common:
2940Sstevel@tonic-gate 		if (bus_state != CFGA_STAT_CONNECTED) {
2950Sstevel@tonic-gate 			cfga_err(errstring, 0, ERR_BUS_NOTCONNECTED, 0);
2960Sstevel@tonic-gate 			ret = SCFGA_ERR;
2970Sstevel@tonic-gate 			break;
2980Sstevel@tonic-gate 		}
2990Sstevel@tonic-gate 
300*10696SDavid.Hollister@Sun.COM 		if (apidp->dyntype == PATH_APID) {
301*10696SDavid.Hollister@Sun.COM 			/* call a scsi_vhci ioctl to do online/offline path. */
302*10696SDavid.Hollister@Sun.COM 			ret = path_apid_state_change(apidp, cmd,
303*10696SDavid.Hollister@Sun.COM 			    flags, errstring, &l_errno, errid);
304*10696SDavid.Hollister@Sun.COM 		} else {
305*10696SDavid.Hollister@Sun.COM 			/*
306*10696SDavid.Hollister@Sun.COM 			 * When unconfiguring a device, first offline it
307*10696SDavid.Hollister@Sun.COM 			 * through RCM.
308*10696SDavid.Hollister@Sun.COM 			 */
309*10696SDavid.Hollister@Sun.COM 			if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
310*10696SDavid.Hollister@Sun.COM 				if (cmd == SCFGA_DEV_UNCONFIGURE) {
311*10696SDavid.Hollister@Sun.COM 					dev_list[0] =
312*10696SDavid.Hollister@Sun.COM 					    get_node_path(apidp->path);
313*10696SDavid.Hollister@Sun.COM 					if (dev_list[0] == NULL) {
314*10696SDavid.Hollister@Sun.COM 						ret = SCFGA_ERR;
315*10696SDavid.Hollister@Sun.COM 						break;
316*10696SDavid.Hollister@Sun.COM 					}
317*10696SDavid.Hollister@Sun.COM 					if ((ret = scsi_rcm_offline(dev_list,
318*10696SDavid.Hollister@Sun.COM 					    errstring, flags)) != SCFGA_OK) {
319*10696SDavid.Hollister@Sun.COM 						break;
320*10696SDavid.Hollister@Sun.COM 					}
3210Sstevel@tonic-gate 				}
3220Sstevel@tonic-gate 			}
3230Sstevel@tonic-gate 
324*10696SDavid.Hollister@Sun.COM 			ret = devctl_cmd(apidp->path, cmd, NULL, &l_errno);
325*10696SDavid.Hollister@Sun.COM 			if (ret != SCFGA_OK) {
326*10696SDavid.Hollister@Sun.COM 				cfga_err(errstring, l_errno, errid, 0);
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 			/*
3290Sstevel@tonic-gate 			 * If an unconfigure fails, cancel the RCM offline.
3300Sstevel@tonic-gate 			 * Discard any RCM failures so that the devctl
3310Sstevel@tonic-gate 			 * failure will still be reported.
3320Sstevel@tonic-gate 			 */
333*10696SDavid.Hollister@Sun.COM 				if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
334*10696SDavid.Hollister@Sun.COM 					if (cmd == SCFGA_DEV_UNCONFIGURE)
335*10696SDavid.Hollister@Sun.COM 						(void) scsi_rcm_online(dev_list,
336*10696SDavid.Hollister@Sun.COM 						    errstring, flags);
337*10696SDavid.Hollister@Sun.COM 				}
338*10696SDavid.Hollister@Sun.COM 				break;
339*10696SDavid.Hollister@Sun.COM 			}
3400Sstevel@tonic-gate 			if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
3410Sstevel@tonic-gate 			/*
3420Sstevel@tonic-gate 			 * Unconfigure succeeded, call the RCM notify_remove.
3430Sstevel@tonic-gate 			 */
344*10696SDavid.Hollister@Sun.COM 				if (cmd == SCFGA_DEV_UNCONFIGURE)
345*10696SDavid.Hollister@Sun.COM 					(void) scsi_rcm_remove(dev_list,
3460Sstevel@tonic-gate 					    errstring, flags);
347*10696SDavid.Hollister@Sun.COM 			}
3480Sstevel@tonic-gate 		}
3490Sstevel@tonic-gate 		break;
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate 	/*
3520Sstevel@tonic-gate 	 * Cannot disconnect/connect individual devices without affecting
3530Sstevel@tonic-gate 	 * other devices on the bus. So we don't support these ops.
3540Sstevel@tonic-gate 	 */
3550Sstevel@tonic-gate 	case CFGA_CMD_DISCONNECT:
3560Sstevel@tonic-gate 	case CFGA_CMD_CONNECT:
3570Sstevel@tonic-gate 		cfga_err(errstring, 0, ERR_NOT_DEVOP, 0);
3580Sstevel@tonic-gate 		ret = SCFGA_ERR;
3590Sstevel@tonic-gate 		break;
3600Sstevel@tonic-gate 	case CFGA_CMD_LOAD:
3610Sstevel@tonic-gate 	case CFGA_CMD_UNLOAD:
3620Sstevel@tonic-gate 		ret = SCFGA_OPNOTSUPP;
3630Sstevel@tonic-gate 		break;
3640Sstevel@tonic-gate 	default:
3650Sstevel@tonic-gate 		cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
3660Sstevel@tonic-gate 		ret = SCFGA_ERR;
3670Sstevel@tonic-gate 		break;
3680Sstevel@tonic-gate 	}
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 	free_dev_list_elements(dev_list);
3710Sstevel@tonic-gate 	return (ret);
3720Sstevel@tonic-gate }
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate /*ARGSUSED*/
3750Sstevel@tonic-gate scfga_ret_t
dev_remove(const char * func,scfga_cmd_t cmd,apid_t * apidp,prompt_t * prp,cfga_flags_t flags,char ** errstring)3760Sstevel@tonic-gate dev_remove(
3771957Sdnielsen 	const char *func,
3780Sstevel@tonic-gate 	scfga_cmd_t cmd,
3790Sstevel@tonic-gate 	apid_t *apidp,
3800Sstevel@tonic-gate 	prompt_t *prp,
3810Sstevel@tonic-gate 	cfga_flags_t flags,
3820Sstevel@tonic-gate 	char **errstring)
3830Sstevel@tonic-gate {
3840Sstevel@tonic-gate 	int proceed, l_errno = 0;
3850Sstevel@tonic-gate 	scfga_ret_t ret;
3860Sstevel@tonic-gate 	int do_quiesce;
3870Sstevel@tonic-gate 	char *dev_list[2] = {NULL};
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 	assert(apidp->hba_phys != NULL);
3900Sstevel@tonic-gate 	assert(apidp->path != NULL);
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 	/* device operation only */
3930Sstevel@tonic-gate 	if (apidp->dyncomp == NULL) {
3940Sstevel@tonic-gate 		cfga_err(errstring, 0, ERR_NOT_BUSOP, 0);
3950Sstevel@tonic-gate 		return (SCFGA_ERR);
3960Sstevel@tonic-gate 	}
3970Sstevel@tonic-gate 
3980Sstevel@tonic-gate 	proceed = 1;
3990Sstevel@tonic-gate 	ret = quiesce_confirm(apidp, MSG_RMDEV, prp, &proceed, &do_quiesce,
4000Sstevel@tonic-gate 	    &l_errno);
4010Sstevel@tonic-gate 	if (ret != SCFGA_OK) {
4020Sstevel@tonic-gate 		cfga_err(errstring, l_errno, ERR_DEV_REMOVE, 0);
4030Sstevel@tonic-gate 		return (ret);
4040Sstevel@tonic-gate 	}
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate 	if (!proceed) {
4070Sstevel@tonic-gate 		return (SCFGA_NACK);
4080Sstevel@tonic-gate 	}
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 	/*
4110Sstevel@tonic-gate 	 * Offline the device in RCM
4120Sstevel@tonic-gate 	 */
4130Sstevel@tonic-gate 	if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
4140Sstevel@tonic-gate 		dev_list[0] = get_node_path(apidp->path);
4150Sstevel@tonic-gate 		if (dev_list[0] == NULL)
4160Sstevel@tonic-gate 			return (SCFGA_ERR);
4170Sstevel@tonic-gate 		if ((ret = scsi_rcm_offline(dev_list, errstring, flags))
4180Sstevel@tonic-gate 		    != SCFGA_OK) {
4190Sstevel@tonic-gate 			free_dev_list_elements(dev_list);
4200Sstevel@tonic-gate 			return (ret);
4210Sstevel@tonic-gate 		}
4220Sstevel@tonic-gate 	}
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 	/*
4250Sstevel@tonic-gate 	 * Offline the device
4260Sstevel@tonic-gate 	 */
4270Sstevel@tonic-gate 	ret = devctl_cmd(apidp->path, SCFGA_DEV_UNCONFIGURE, NULL, &l_errno);
4280Sstevel@tonic-gate 	if (ret != SCFGA_OK) {
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 		cfga_err(errstring, l_errno, ERR_DEV_REMOVE, 0);
4310Sstevel@tonic-gate 
4320Sstevel@tonic-gate 		/*
4330Sstevel@tonic-gate 		 * Cancel the RCM offline.  Discard the RCM failures so that
4340Sstevel@tonic-gate 		 * the above devctl failure is still reported.
4350Sstevel@tonic-gate 		 */
4360Sstevel@tonic-gate 		if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
4370Sstevel@tonic-gate 			(void) scsi_rcm_online(dev_list, errstring, flags);
4380Sstevel@tonic-gate 		free_dev_list_elements(dev_list);
4390Sstevel@tonic-gate 		return (ret);
4400Sstevel@tonic-gate 	}
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate 	/* Do the physical removal */
4430Sstevel@tonic-gate 	ret = dev_hotplug(apidp, prp, flags, do_quiesce, errstring);
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 	if (ret == SCFGA_OK) {
4460Sstevel@tonic-gate 		/*
4470Sstevel@tonic-gate 		 * Complete the remove.
4480Sstevel@tonic-gate 		 * Since the device is already offlined, remove shouldn't
4490Sstevel@tonic-gate 		 * fail. Even if remove fails, there is no side effect.
4500Sstevel@tonic-gate 		 */
4510Sstevel@tonic-gate 		(void) devctl_cmd(apidp->path, SCFGA_DEV_REMOVE,
4520Sstevel@tonic-gate 		    NULL, &l_errno);
4530Sstevel@tonic-gate 		if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
4540Sstevel@tonic-gate 			ret = scsi_rcm_remove(dev_list, errstring, flags);
4550Sstevel@tonic-gate 	} else {
4560Sstevel@tonic-gate 		/*
4570Sstevel@tonic-gate 		 * Reconfigure the device and restore the device's RCM state.
4580Sstevel@tonic-gate 		 * If reconfigure succeeds, restore the state to online.
4590Sstevel@tonic-gate 		 * If reconfigure fails (e.g. a typo from user), we treat
4600Sstevel@tonic-gate 		 * the device as removed.
4610Sstevel@tonic-gate 		 */
4620Sstevel@tonic-gate 		if (devctl_cmd(apidp->path, SCFGA_DEV_CONFIGURE, NULL, &l_errno)
4630Sstevel@tonic-gate 		    == SCFGA_OK) {
4640Sstevel@tonic-gate 			if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
4650Sstevel@tonic-gate 				(void) scsi_rcm_online(dev_list, errstring,
4660Sstevel@tonic-gate 				    flags);
4670Sstevel@tonic-gate 		} else {
4680Sstevel@tonic-gate 			char *cp = strrchr(apidp->path, ':');
4690Sstevel@tonic-gate 			if (cp)
4700Sstevel@tonic-gate 				*cp = '\0';
4710Sstevel@tonic-gate 			cfga_err(errstring, l_errno, ERR_DEV_RECONFIGURE,
4720Sstevel@tonic-gate 			    apidp->path, 0);
4730Sstevel@tonic-gate 			if (cp)
4740Sstevel@tonic-gate 				*cp = ':';
4750Sstevel@tonic-gate 			if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
4760Sstevel@tonic-gate 				(void) scsi_rcm_remove(dev_list, errstring,
4770Sstevel@tonic-gate 				    flags);
4780Sstevel@tonic-gate 		}
4790Sstevel@tonic-gate 	}
4800Sstevel@tonic-gate 
4810Sstevel@tonic-gate 	free_dev_list_elements(dev_list);
4820Sstevel@tonic-gate 	return (ret);
4830Sstevel@tonic-gate }
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate /*ARGSUSED*/
4860Sstevel@tonic-gate scfga_ret_t
dev_insert(const char * func,scfga_cmd_t cmd,apid_t * apidp,prompt_t * prp,cfga_flags_t flags,char ** errstring)4870Sstevel@tonic-gate dev_insert(
4881957Sdnielsen 	const char *func,
4890Sstevel@tonic-gate 	scfga_cmd_t cmd,
4900Sstevel@tonic-gate 	apid_t *apidp,
4910Sstevel@tonic-gate 	prompt_t *prp,
4920Sstevel@tonic-gate 	cfga_flags_t flags,
4930Sstevel@tonic-gate 	char **errstring)
4940Sstevel@tonic-gate {
4950Sstevel@tonic-gate 	int proceed, l_errno = 0;
4960Sstevel@tonic-gate 	scfga_ret_t ret;
4970Sstevel@tonic-gate 	int do_quiesce;
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate 	assert(apidp->hba_phys != NULL);
5000Sstevel@tonic-gate 	assert(apidp->path != NULL);
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 	/* Currently, insert operation only allowed for bus */
5030Sstevel@tonic-gate 	if (apidp->dyncomp != NULL) {
5040Sstevel@tonic-gate 		cfga_err(errstring, 0, ERR_NOT_DEVOP, 0);
5050Sstevel@tonic-gate 		return (SCFGA_ERR);
5060Sstevel@tonic-gate 	}
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	proceed = 1;
5090Sstevel@tonic-gate 	ret = quiesce_confirm(apidp, MSG_INSDEV, prp, &proceed, &do_quiesce,
5100Sstevel@tonic-gate 	    &l_errno);
5110Sstevel@tonic-gate 	if (ret != SCFGA_OK) {
5120Sstevel@tonic-gate 		cfga_err(errstring, l_errno, ERR_DEV_INSERT, 0);
5130Sstevel@tonic-gate 		return (ret);
5140Sstevel@tonic-gate 	}
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 	if (!proceed) {
5170Sstevel@tonic-gate 		return (SCFGA_NACK);
5180Sstevel@tonic-gate 	}
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 	/* Do the physical addition */
5210Sstevel@tonic-gate 	ret = dev_hotplug(apidp, prp, flags, do_quiesce, errstring);
5220Sstevel@tonic-gate 	if (ret != SCFGA_OK) {
5230Sstevel@tonic-gate 		return (ret);
5240Sstevel@tonic-gate 	}
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 	/*
5270Sstevel@tonic-gate 	 * Configure bus to online new device(s).
5280Sstevel@tonic-gate 	 * Previously offlined devices will not be onlined.
5290Sstevel@tonic-gate 	 */
5300Sstevel@tonic-gate 	ret = devctl_cmd(apidp->hba_phys, SCFGA_BUS_CONFIGURE, NULL, &l_errno);
5310Sstevel@tonic-gate 	if (ret != SCFGA_OK) {
5320Sstevel@tonic-gate 		cfga_err(errstring, l_errno, ERR_DEV_INSERT, 0);
5330Sstevel@tonic-gate 		return (SCFGA_ERR);
5340Sstevel@tonic-gate 	}
5350Sstevel@tonic-gate 
5360Sstevel@tonic-gate 	return (SCFGA_OK);
5370Sstevel@tonic-gate }
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate /*ARGSUSED*/
5400Sstevel@tonic-gate scfga_ret_t
dev_replace(const char * func,scfga_cmd_t cmd,apid_t * apidp,prompt_t * prp,cfga_flags_t flags,char ** errstring)5410Sstevel@tonic-gate dev_replace(
5421957Sdnielsen 	const char *func,
5430Sstevel@tonic-gate 	scfga_cmd_t cmd,
5440Sstevel@tonic-gate 	apid_t *apidp,
5450Sstevel@tonic-gate 	prompt_t *prp,
5460Sstevel@tonic-gate 	cfga_flags_t flags,
5470Sstevel@tonic-gate 	char **errstring)
5480Sstevel@tonic-gate {
5490Sstevel@tonic-gate 	int proceed, l_errno = 0;
5500Sstevel@tonic-gate 	scfga_ret_t ret, ret2;
5510Sstevel@tonic-gate 	int do_quiesce;
5520Sstevel@tonic-gate 	char *dev_list[2] = {NULL};
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	assert(apidp->hba_phys != NULL);
5550Sstevel@tonic-gate 	assert(apidp->path != NULL);
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 	/* device operation only */
5580Sstevel@tonic-gate 	if (apidp->dyncomp == NULL) {
5590Sstevel@tonic-gate 		cfga_err(errstring, 0, ERR_NOT_BUSOP, 0);
5600Sstevel@tonic-gate 		return (SCFGA_ERR);
5610Sstevel@tonic-gate 	}
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 	proceed = 1;
5640Sstevel@tonic-gate 	ret = quiesce_confirm(apidp, MSG_REPLDEV, prp, &proceed, &do_quiesce,
5650Sstevel@tonic-gate 	    &l_errno);
5660Sstevel@tonic-gate 	if (ret != SCFGA_OK) {
5670Sstevel@tonic-gate 		cfga_err(errstring, l_errno, ERR_DEV_REPLACE, 0);
5680Sstevel@tonic-gate 		return (ret);
5690Sstevel@tonic-gate 	}
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 	if (!proceed) {
5720Sstevel@tonic-gate 		return (SCFGA_NACK);
5730Sstevel@tonic-gate 	}
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 	/* Offline the device in RCM */
5760Sstevel@tonic-gate 	if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
5770Sstevel@tonic-gate 		dev_list[0] = get_node_path(apidp->path);
5780Sstevel@tonic-gate 		if (dev_list[0] == NULL)
5790Sstevel@tonic-gate 			return (SCFGA_ERR);
5800Sstevel@tonic-gate 		if ((ret = scsi_rcm_offline(dev_list, errstring, flags))
5810Sstevel@tonic-gate 		    != SCFGA_OK) {
5820Sstevel@tonic-gate 			free_dev_list_elements(dev_list);
5830Sstevel@tonic-gate 			return (ret);
5840Sstevel@tonic-gate 		}
5850Sstevel@tonic-gate 	}
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 	ret = devctl_cmd(apidp->path, SCFGA_DEV_REMOVE, NULL, &l_errno);
5880Sstevel@tonic-gate 	if (ret != SCFGA_OK) {
5890Sstevel@tonic-gate 
5900Sstevel@tonic-gate 		/*
5910Sstevel@tonic-gate 		 * Cancel the RCM offline.  Discard any RCM failures so that
5920Sstevel@tonic-gate 		 * the devctl failure can still be reported.
5930Sstevel@tonic-gate 		 */
5940Sstevel@tonic-gate 		if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
5950Sstevel@tonic-gate 			(void) scsi_rcm_online(dev_list, errstring, flags);
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 		cfga_err(errstring, l_errno, ERR_DEV_REPLACE, 0);
5980Sstevel@tonic-gate 		free_dev_list_elements(dev_list);
5990Sstevel@tonic-gate 		return (ret);
6000Sstevel@tonic-gate 	}
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 	/* do the physical replace */
6030Sstevel@tonic-gate 	ret = dev_hotplug(apidp, prp, flags, do_quiesce, errstring);
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 	/* Online the replacement, or restore state on error */
6060Sstevel@tonic-gate 	ret2 = devctl_cmd(apidp->path, SCFGA_DEV_CONFIGURE, NULL, &l_errno);
6070Sstevel@tonic-gate 
6080Sstevel@tonic-gate 	if (ret2 != SCFGA_OK) {
6090Sstevel@tonic-gate 		cfga_err(errstring, l_errno, ERR_DEV_REPLACE, 0);
6100Sstevel@tonic-gate 	}
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 	/*
6130Sstevel@tonic-gate 	 * Remove the replaced device in RCM, or online the device in RCM
6140Sstevel@tonic-gate 	 * to recover.
6150Sstevel@tonic-gate 	 */
6160Sstevel@tonic-gate 	if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
6170Sstevel@tonic-gate 		if (ret == SCFGA_OK)
6180Sstevel@tonic-gate 			ret = scsi_rcm_remove(dev_list, errstring, flags);
6190Sstevel@tonic-gate 		else if (ret2 == SCFGA_OK)
6200Sstevel@tonic-gate 			ret2 = scsi_rcm_online(dev_list, errstring, flags);
6210Sstevel@tonic-gate 	}
6220Sstevel@tonic-gate 	free_dev_list_elements(dev_list);
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 	return (ret == SCFGA_OK ? ret2 : ret);
6250Sstevel@tonic-gate }
6260Sstevel@tonic-gate 
6271957Sdnielsen #pragma weak plat_dev_led
6281957Sdnielsen /*ARGSUSED*/
6291957Sdnielsen scfga_ret_t
dev_led(const char * func,scfga_cmd_t cmd,apid_t * apidp,prompt_t * prp,cfga_flags_t flags,char ** errstring)6301957Sdnielsen dev_led(
6311957Sdnielsen 	const char *func,
6321957Sdnielsen 	scfga_cmd_t cmd,
6331957Sdnielsen 	apid_t *apidp,
6341957Sdnielsen 	prompt_t *prp,
6351957Sdnielsen 	cfga_flags_t flags,
6361957Sdnielsen 	char **errstring)
6371957Sdnielsen {
6381957Sdnielsen 
6391957Sdnielsen 	/*
6401957Sdnielsen 	 * The implementation of the led command is platform-specific, so
6411957Sdnielsen 	 * the default behavior is to say that the functionality is not
6421957Sdnielsen 	 * available for this device.
6431957Sdnielsen 	 */
6441957Sdnielsen 	if (plat_dev_led) {
6451957Sdnielsen 		return (plat_dev_led(func, cmd, apidp, prp, flags, errstring));
6461957Sdnielsen 	}
6471957Sdnielsen 	cfga_err(errstring, 0, ERR_UNAVAILABLE, 0);
6481957Sdnielsen 	return (SCFGA_ERR);
6491957Sdnielsen }
6501957Sdnielsen 
6510Sstevel@tonic-gate /*ARGSUSED*/
6520Sstevel@tonic-gate scfga_ret_t
reset_common(const char * func,scfga_cmd_t cmd,apid_t * apidp,prompt_t * prp,cfga_flags_t flags,char ** errstring)6530Sstevel@tonic-gate reset_common(
6541957Sdnielsen 	const char *func,
6550Sstevel@tonic-gate 	scfga_cmd_t cmd,
6560Sstevel@tonic-gate 	apid_t *apidp,
6570Sstevel@tonic-gate 	prompt_t *prp,
6580Sstevel@tonic-gate 	cfga_flags_t flags,
6590Sstevel@tonic-gate 	char **errstring)
6600Sstevel@tonic-gate {
6610Sstevel@tonic-gate 	int l_errno = 0;
6620Sstevel@tonic-gate 	scfga_ret_t ret;
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 	assert(apidp->path != NULL);
6660Sstevel@tonic-gate 	assert(apidp->hba_phys != NULL);
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 	switch (cmd) {
6690Sstevel@tonic-gate 	case SCFGA_RESET_DEV:
6700Sstevel@tonic-gate 		if (apidp->dyncomp == NULL) {
6710Sstevel@tonic-gate 			cfga_err(errstring, 0, ERR_NOT_BUSOP, 0);
6720Sstevel@tonic-gate 			return (SCFGA_ERR);
6730Sstevel@tonic-gate 		}
6740Sstevel@tonic-gate 		break;
6750Sstevel@tonic-gate 
6760Sstevel@tonic-gate 	case SCFGA_RESET_BUS:
6770Sstevel@tonic-gate 	case SCFGA_RESET_ALL:
6780Sstevel@tonic-gate 		if (apidp->dyncomp != NULL) {
6790Sstevel@tonic-gate 			cfga_err(errstring, 0, ERR_NOT_DEVOP, 0);
6800Sstevel@tonic-gate 			return (SCFGA_ERR);
6810Sstevel@tonic-gate 		}
6820Sstevel@tonic-gate 		break;
6830Sstevel@tonic-gate 	default:
6840Sstevel@tonic-gate 		cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
6850Sstevel@tonic-gate 		return (SCFGA_ERR);
6860Sstevel@tonic-gate 	}
6870Sstevel@tonic-gate 
6880Sstevel@tonic-gate 	ret = devctl_cmd(apidp->path, cmd, NULL, &l_errno);
6890Sstevel@tonic-gate 	if (ret != SCFGA_OK) {
6900Sstevel@tonic-gate 		cfga_err(errstring, l_errno, ERR_RESET, 0);
6910Sstevel@tonic-gate 	}
6920Sstevel@tonic-gate 
6930Sstevel@tonic-gate 	return (ret);
6940Sstevel@tonic-gate }
6950Sstevel@tonic-gate 
6960Sstevel@tonic-gate static int
disconnect(struct cfga_confirm * confp)6970Sstevel@tonic-gate disconnect(struct cfga_confirm *confp)
6980Sstevel@tonic-gate {
6990Sstevel@tonic-gate 	int ans, append_newline;
7000Sstevel@tonic-gate 	char *cq;
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate 	append_newline = 0;
7030Sstevel@tonic-gate 	cq = cfga_str(append_newline, WARN_DISCONNECT, 0);
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate 	ans = confp->confirm(confp->appdata_ptr, cq);
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate 	S_FREE(cq);
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate 	return (ans == 1);
7100Sstevel@tonic-gate }
7110Sstevel@tonic-gate 
7120Sstevel@tonic-gate /*
7130Sstevel@tonic-gate  * Check for "scsi-no-quiesce" property
7140Sstevel@tonic-gate  * Return code: -1 error, 0 quiesce not required, 1 quiesce required
7150Sstevel@tonic-gate  */
7160Sstevel@tonic-gate static int
quiesce_required(apid_t * apidp,int * l_errnop)7170Sstevel@tonic-gate quiesce_required(apid_t *apidp, int *l_errnop)
7180Sstevel@tonic-gate {
7190Sstevel@tonic-gate 	di_node_t bus_node, dev_node;
7200Sstevel@tonic-gate 	char *bus_path, *bus_end;
7210Sstevel@tonic-gate 	char *dev_path, *dev_end;
7220Sstevel@tonic-gate 	int *propval;
7230Sstevel@tonic-gate 
7240Sstevel@tonic-gate 	/* take libdevinfo snapshot of subtree at hba */
7250Sstevel@tonic-gate 	bus_path = apidp->hba_phys + strlen(DEVICES_DIR);
7260Sstevel@tonic-gate 	bus_end = strrchr(bus_path, ':');
7270Sstevel@tonic-gate 	if (bus_end)
7280Sstevel@tonic-gate 		*bus_end = '\0';
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate 	bus_node = di_init(bus_path, DINFOSUBTREE|DINFOPROP);
7310Sstevel@tonic-gate 	if (bus_end)
7320Sstevel@tonic-gate 		*bus_end = ':';
7330Sstevel@tonic-gate 	if (bus_node == DI_NODE_NIL) {
7340Sstevel@tonic-gate 		*l_errnop = errno;
7350Sstevel@tonic-gate 		return (-1);	/* error */
7360Sstevel@tonic-gate 	}
7370Sstevel@tonic-gate 
7380Sstevel@tonic-gate 	/* check bus node for property */
7390Sstevel@tonic-gate 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, bus_node, SCSI_NO_QUIESCE,
7400Sstevel@tonic-gate 	    &propval) == 1) {
7410Sstevel@tonic-gate 		di_fini(bus_node);
7420Sstevel@tonic-gate 		return (0);	/* quiesce not required */
7430Sstevel@tonic-gate 	}
7440Sstevel@tonic-gate 
7450Sstevel@tonic-gate 	/* if this ap is HBA, return with quiesce required */
7460Sstevel@tonic-gate 	if (apidp->dyncomp == NULL) {
7470Sstevel@tonic-gate 		di_fini(bus_node);
7480Sstevel@tonic-gate 		return (1);
7490Sstevel@tonic-gate 	}
7500Sstevel@tonic-gate 
7510Sstevel@tonic-gate 	/* check device node for property */
7520Sstevel@tonic-gate 	dev_path = apidp->path + strlen(DEVICES_DIR);
7530Sstevel@tonic-gate 	dev_end = strrchr(dev_path, ':');
7540Sstevel@tonic-gate 	if (dev_end)
7550Sstevel@tonic-gate 		*dev_end = '\0';
7560Sstevel@tonic-gate 
7570Sstevel@tonic-gate 	dev_node = di_child_node(bus_node);
7580Sstevel@tonic-gate 	while (dev_node != DI_NODE_NIL) {
7590Sstevel@tonic-gate 		char *child_path;
7600Sstevel@tonic-gate 		child_path = di_devfs_path(dev_node);
7610Sstevel@tonic-gate 		if (strcmp(child_path, dev_path) == 0) {
7620Sstevel@tonic-gate 			di_devfs_path_free(child_path);
7630Sstevel@tonic-gate 			break;
7640Sstevel@tonic-gate 		}
7650Sstevel@tonic-gate 		di_devfs_path_free(child_path);
7660Sstevel@tonic-gate 		dev_node = di_sibling_node(dev_node);
7670Sstevel@tonic-gate 	}
7680Sstevel@tonic-gate 
7690Sstevel@tonic-gate 	if (dev_end)
7700Sstevel@tonic-gate 		*dev_end = ':';
7710Sstevel@tonic-gate 	if (dev_node == DI_NODE_NIL) {
7720Sstevel@tonic-gate 		di_fini(bus_node);
7730Sstevel@tonic-gate 		return (1);	/* dev not found (insert case) */
7740Sstevel@tonic-gate 	}
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 	/* check child node for property */
7770Sstevel@tonic-gate 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, dev_node, "scsi-no-quiesce",
7780Sstevel@tonic-gate 	    &propval) == 1) {
7790Sstevel@tonic-gate 		di_fini(bus_node);
7800Sstevel@tonic-gate 		return (0);	/* quiesce not required */
7810Sstevel@tonic-gate 	}
7820Sstevel@tonic-gate 	return (1);	/* quiesce required */
7830Sstevel@tonic-gate }
7840Sstevel@tonic-gate 
7850Sstevel@tonic-gate static scfga_ret_t
quiesce_confirm(apid_t * apidp,msgid_t cmd_msg,prompt_t * prp,int * okp,int * quiesce,int * l_errnop)7860Sstevel@tonic-gate quiesce_confirm(
7870Sstevel@tonic-gate 	apid_t *apidp,
7880Sstevel@tonic-gate 	msgid_t cmd_msg,
7890Sstevel@tonic-gate 	prompt_t *prp,
7900Sstevel@tonic-gate 	int *okp,
7910Sstevel@tonic-gate 	int *quiesce,
7920Sstevel@tonic-gate 	int *l_errnop)
7930Sstevel@tonic-gate {
7940Sstevel@tonic-gate 	char *buf = NULL, *hbap = NULL, *cq1 = NULL, *cq2 = NULL;
7950Sstevel@tonic-gate 	char *cp;
7960Sstevel@tonic-gate 	size_t len = 0;
7970Sstevel@tonic-gate 	int i = 0, append_newline;
7980Sstevel@tonic-gate 	scfga_ret_t ret;
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate 	assert(apidp->path != NULL);
8010Sstevel@tonic-gate 	assert(apidp->hba_phys != NULL);
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate 	*quiesce = quiesce_required(apidp, l_errnop);
8040Sstevel@tonic-gate 	if (*quiesce == -1)
8050Sstevel@tonic-gate 		return (SCFGA_ERR);
8060Sstevel@tonic-gate 	else if (*quiesce == 0)
8070Sstevel@tonic-gate 		return (SCFGA_OK);
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 	/*
8100Sstevel@tonic-gate 	 * Try to create HBA logical ap_id.
8110Sstevel@tonic-gate 	 * If that fails use physical path
8120Sstevel@tonic-gate 	 */
8130Sstevel@tonic-gate 	ret = make_hba_logid(apidp->hba_phys, &hbap, &i);
8140Sstevel@tonic-gate 	if (ret != SCFGA_OK) {
8150Sstevel@tonic-gate 		if ((hbap = get_node_path(apidp->hba_phys)) == NULL) {
8160Sstevel@tonic-gate 			*l_errnop = errno;
8170Sstevel@tonic-gate 			return (SCFGA_LIB_ERR);
8180Sstevel@tonic-gate 		}
8190Sstevel@tonic-gate 	}
8200Sstevel@tonic-gate 
8210Sstevel@tonic-gate 	assert(hbap != NULL);
8220Sstevel@tonic-gate 
8230Sstevel@tonic-gate 	append_newline = 0;
8240Sstevel@tonic-gate 	cq1 = cfga_str(append_newline, CONF_QUIESCE_1, hbap, 0);
8250Sstevel@tonic-gate 	cq2 = cfga_str(append_newline, CONF_QUIESCE_2, 0);
8260Sstevel@tonic-gate 	len = strlen(cq1) + strlen(cq2) + 1; /* Includes term. NULL */
8270Sstevel@tonic-gate 
8280Sstevel@tonic-gate 	if ((buf = calloc(1, len)) == NULL) {
8290Sstevel@tonic-gate 		*l_errnop = errno;
8300Sstevel@tonic-gate 		ret = SCFGA_LIB_ERR;
8310Sstevel@tonic-gate 		S_FREE(cq1);
8320Sstevel@tonic-gate 		S_FREE(cq2);
8330Sstevel@tonic-gate 		goto out;
8340Sstevel@tonic-gate 	}
8350Sstevel@tonic-gate 	(void) strcpy(buf, cq1);
8360Sstevel@tonic-gate 	(void) strcat(buf, cq2);
8370Sstevel@tonic-gate 
8380Sstevel@tonic-gate 	S_FREE(cq1);
8390Sstevel@tonic-gate 	S_FREE(cq2);
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate 	/* Remove minor name (if any) from phys path */
8430Sstevel@tonic-gate 	if ((cp = strrchr(apidp->path, ':')) != NULL) {
8440Sstevel@tonic-gate 		*cp = '\0';
8450Sstevel@tonic-gate 	}
8460Sstevel@tonic-gate 
8470Sstevel@tonic-gate 	/* describe operation being attempted */
8480Sstevel@tonic-gate 	cfga_msg(prp->msgp, cmd_msg, apidp->path, 0);
8490Sstevel@tonic-gate 
8500Sstevel@tonic-gate 	/* Restore minor name */
8510Sstevel@tonic-gate 	if (cp != NULL) {
8520Sstevel@tonic-gate 		*cp = ':';
8530Sstevel@tonic-gate 	}
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 	/* request permission to quiesce */
8560Sstevel@tonic-gate 	assert(prp->confp != NULL && prp->confp->confirm != NULL);
8570Sstevel@tonic-gate 	*okp = prp->confp->confirm(prp->confp->appdata_ptr, buf);
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate 	ret = SCFGA_OK;
8600Sstevel@tonic-gate 	/*FALLTHRU*/
8610Sstevel@tonic-gate out:
8620Sstevel@tonic-gate 	S_FREE(buf);
8630Sstevel@tonic-gate 	S_FREE(hbap);
8640Sstevel@tonic-gate 	return (ret);
8650Sstevel@tonic-gate }
8660Sstevel@tonic-gate 
8670Sstevel@tonic-gate static scfga_ret_t
suspend_in_rcm(apid_t * apidp,char *** suspend_list_ptr,char ** errstring,cfga_flags_t flags)8680Sstevel@tonic-gate suspend_in_rcm(
8690Sstevel@tonic-gate 	apid_t		*apidp,
8700Sstevel@tonic-gate 	char		***suspend_list_ptr,
8710Sstevel@tonic-gate 	char		**errstring,
8720Sstevel@tonic-gate 	cfga_flags_t	flags)
8730Sstevel@tonic-gate {
8740Sstevel@tonic-gate 	scfga_ret_t	ret;
8750Sstevel@tonic-gate 	char		*bus_path = NULL;
8760Sstevel@tonic-gate 	char		*dev_path = NULL;
8770Sstevel@tonic-gate 	char		**suspend_list = NULL;
8780Sstevel@tonic-gate 
8790Sstevel@tonic-gate 	*suspend_list_ptr = NULL;
8800Sstevel@tonic-gate 
8810Sstevel@tonic-gate 	/* Suspend the bus through RCM */
8820Sstevel@tonic-gate 	if (apidp->flags & FLAG_DISABLE_RCM)
8830Sstevel@tonic-gate 		return (SCFGA_OK);
8840Sstevel@tonic-gate 
8850Sstevel@tonic-gate 	/* The bus_path is the HBA path without its minor */
8860Sstevel@tonic-gate 	if ((bus_path = get_node_path(apidp->hba_phys)) == NULL)
8870Sstevel@tonic-gate 		return (SCFGA_ERR);
8880Sstevel@tonic-gate 
8890Sstevel@tonic-gate 	/*
8900Sstevel@tonic-gate 	 * The dev_path is already initialized to NULL.  If the AP Id
8910Sstevel@tonic-gate 	 * path differs from the HBA path, then the dev_path should
8920Sstevel@tonic-gate 	 * instead be set to the AP Id path without its minor.
8930Sstevel@tonic-gate 	 */
8940Sstevel@tonic-gate 	if (strcmp(apidp->hba_phys, apidp->path) != 0) {
8950Sstevel@tonic-gate 		if ((dev_path = get_node_path(apidp->path)) == NULL) {
8960Sstevel@tonic-gate 			ret = SCFGA_ERR;
8970Sstevel@tonic-gate 			goto out;
8980Sstevel@tonic-gate 		}
8990Sstevel@tonic-gate 	}
9000Sstevel@tonic-gate 
9010Sstevel@tonic-gate 	if ((ret = get_hba_children(bus_path, dev_path, &suspend_list))
9020Sstevel@tonic-gate 	    != SCFGA_OK) {
9030Sstevel@tonic-gate 		free_dev_list(suspend_list);
9040Sstevel@tonic-gate 		goto out;
9050Sstevel@tonic-gate 	}
9060Sstevel@tonic-gate 
9070Sstevel@tonic-gate 	if (scsi_rcm_suspend(suspend_list, errstring, flags, 0) != SCFGA_OK) {
9080Sstevel@tonic-gate 		ret = SCFGA_ERR;
9090Sstevel@tonic-gate 		free_dev_list(suspend_list);
9100Sstevel@tonic-gate 	} else {
9110Sstevel@tonic-gate 		ret = SCFGA_OK;
9120Sstevel@tonic-gate 		*suspend_list_ptr = suspend_list;
9130Sstevel@tonic-gate 	}
9140Sstevel@tonic-gate 	/*FALLTHROUGH*/
9150Sstevel@tonic-gate out:
9160Sstevel@tonic-gate 	S_FREE(bus_path);
9170Sstevel@tonic-gate 	S_FREE(dev_path);
9180Sstevel@tonic-gate 	return (ret);
9190Sstevel@tonic-gate }
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate /*
9220Sstevel@tonic-gate  * Resume the bus through RCM if it successfully
9230Sstevel@tonic-gate  * unquiesced.
9240Sstevel@tonic-gate  */
9250Sstevel@tonic-gate static void
resume_in_rcm(apid_t * apidp,char ** suspend_list,char ** errstring,cfga_flags_t flags)9260Sstevel@tonic-gate resume_in_rcm(
9270Sstevel@tonic-gate 	apid_t		*apidp,
9280Sstevel@tonic-gate 	char		**suspend_list,
9290Sstevel@tonic-gate 	char		**errstring,
9300Sstevel@tonic-gate 	cfga_flags_t	flags)
9310Sstevel@tonic-gate {
9320Sstevel@tonic-gate 	if (apidp->flags & FLAG_DISABLE_RCM)
9330Sstevel@tonic-gate 		return;
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate 	(void) scsi_rcm_resume(suspend_list, errstring, flags, 0);
9360Sstevel@tonic-gate 
9370Sstevel@tonic-gate 	free_dev_list(suspend_list);
9380Sstevel@tonic-gate }
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate static scfga_ret_t
wait_for_hotplug(prompt_t * pt,int msg)9410Sstevel@tonic-gate wait_for_hotplug(prompt_t *pt, int msg)
9420Sstevel@tonic-gate {
9430Sstevel@tonic-gate 	char		*cu = NULL;
9440Sstevel@tonic-gate 	int		append_newline = 0;
9450Sstevel@tonic-gate 	scfga_ret_t	ret;
9460Sstevel@tonic-gate 
9470Sstevel@tonic-gate 	cu = cfga_str(append_newline, msg, 0);
9480Sstevel@tonic-gate 	if (pt->confp->confirm(pt->confp->appdata_ptr, cu) != 1) {
9490Sstevel@tonic-gate 		ret = SCFGA_NACK;
9500Sstevel@tonic-gate 	} else {
9510Sstevel@tonic-gate 		ret = SCFGA_OK;
9520Sstevel@tonic-gate 	}
9530Sstevel@tonic-gate 	S_FREE(cu);
9540Sstevel@tonic-gate 	return (ret);
9550Sstevel@tonic-gate }
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate static scfga_ret_t
bus_quiesce(apid_t * apidp,prompt_t * pt,char ** errstring,cfga_flags_t flags)9580Sstevel@tonic-gate bus_quiesce(apid_t *apidp, prompt_t *pt, char **errstring, cfga_flags_t flags)
9590Sstevel@tonic-gate {
9600Sstevel@tonic-gate 	int		l_errno;
9610Sstevel@tonic-gate 	scfga_ret_t	ret;
9620Sstevel@tonic-gate 	scfga_ret_t	hpret;
9630Sstevel@tonic-gate 	char		**suspend_list = NULL;
9640Sstevel@tonic-gate 
9650Sstevel@tonic-gate 	ret = suspend_in_rcm(apidp, &suspend_list, errstring, flags);
9660Sstevel@tonic-gate 	if (ret != SCFGA_OK) {
9670Sstevel@tonic-gate 		return (ret);
9680Sstevel@tonic-gate 	}
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 	/*
9710Sstevel@tonic-gate 	 * If the quiesce fails, then cancel the RCM suspend.
9720Sstevel@tonic-gate 	 * Discard any RCM failures so that the devctl failure
9730Sstevel@tonic-gate 	 * can still be reported.
9740Sstevel@tonic-gate 	 */
9750Sstevel@tonic-gate 	l_errno = 0;
9760Sstevel@tonic-gate 	ret = devctl_cmd(apidp->hba_phys, SCFGA_BUS_QUIESCE, NULL, &l_errno);
9770Sstevel@tonic-gate 	if (ret != SCFGA_OK) {
9780Sstevel@tonic-gate 		resume_in_rcm(apidp, suspend_list, errstring, flags);
9790Sstevel@tonic-gate 		cfga_err(errstring, l_errno, ERR_BUS_QUIESCE, 0);
9800Sstevel@tonic-gate 		return (ret);
9810Sstevel@tonic-gate 	}
9820Sstevel@tonic-gate 
9830Sstevel@tonic-gate 	/*
9840Sstevel@tonic-gate 	 * Prompt user to proceed with physical hotplug
9850Sstevel@tonic-gate 	 * and wait until they are done.
9860Sstevel@tonic-gate 	 */
9870Sstevel@tonic-gate 	hpret = wait_for_hotplug(pt, CONF_UNQUIESCE);
9880Sstevel@tonic-gate 
9890Sstevel@tonic-gate 	/*
9900Sstevel@tonic-gate 	 * The unquiesce may fail with EALREADY (which is ok)
9910Sstevel@tonic-gate 	 * or some other error (which is not ok).
9920Sstevel@tonic-gate 	 */
9930Sstevel@tonic-gate 	l_errno = 0;
9940Sstevel@tonic-gate 	ret = devctl_cmd(apidp->hba_phys, SCFGA_BUS_UNQUIESCE, NULL, &l_errno);
9950Sstevel@tonic-gate 	if (ret != SCFGA_OK && l_errno != EALREADY) {
9960Sstevel@tonic-gate 		free_dev_list(suspend_list);
9970Sstevel@tonic-gate 		cfga_err(errstring, l_errno, ERR_BUS_UNQUIESCE, 0);
9980Sstevel@tonic-gate 		return (SCFGA_ERR);
9990Sstevel@tonic-gate 	}
10000Sstevel@tonic-gate 
10010Sstevel@tonic-gate 	resume_in_rcm(apidp, suspend_list, errstring, flags);
10020Sstevel@tonic-gate 
10030Sstevel@tonic-gate 	return (hpret);
10040Sstevel@tonic-gate }
10050Sstevel@tonic-gate 
10060Sstevel@tonic-gate #define	MAX_LOCK_TRIES		20
10070Sstevel@tonic-gate #define	MAX_UNLINK_TRIES	60
10080Sstevel@tonic-gate #define	s_getpid		(int)getpid	/* else lint causes problems */
10090Sstevel@tonic-gate 
10100Sstevel@tonic-gate static void
s_unlink(char * file)10110Sstevel@tonic-gate s_unlink(char *file)
10120Sstevel@tonic-gate {
10130Sstevel@tonic-gate 	int	count = 0;
10140Sstevel@tonic-gate 
10150Sstevel@tonic-gate retry:
10160Sstevel@tonic-gate 	if (unlink(file) == -1) {
10170Sstevel@tonic-gate 		if (errno != EINTR && errno != EAGAIN) {
10180Sstevel@tonic-gate 			CFGA_TRACE1((stdout, "s_unlink[%d]: unlink failed: "
10190Sstevel@tonic-gate 			    "%s: %s\n", s_getpid(), file, strerror(errno)));
10200Sstevel@tonic-gate 			return;
10210Sstevel@tonic-gate 		}
10220Sstevel@tonic-gate 
10230Sstevel@tonic-gate 		if (++count < MAX_UNLINK_TRIES) {
10240Sstevel@tonic-gate 			(void) sleep(1);
10250Sstevel@tonic-gate 			goto retry;
10260Sstevel@tonic-gate 		}
10270Sstevel@tonic-gate 		CFGA_TRACE1((stdout, "s_unlink[%d]: retry limit: %s\n",
10280Sstevel@tonic-gate 		    s_getpid(), file));
10290Sstevel@tonic-gate 	} else {
10300Sstevel@tonic-gate 		CFGA_TRACE3((stdout, "s_unlink[%d]: unlinked: %s\n",
10310Sstevel@tonic-gate 		    s_getpid(), file));
10320Sstevel@tonic-gate 	}
10330Sstevel@tonic-gate }
10340Sstevel@tonic-gate 
10350Sstevel@tonic-gate static scfga_ret_t
create_lock(int * fdp,struct cfga_msg * msgp,char ** errstring)10360Sstevel@tonic-gate create_lock(int *fdp, struct cfga_msg *msgp, char **errstring)
10370Sstevel@tonic-gate {
10380Sstevel@tonic-gate 	FILE			*fp;
10390Sstevel@tonic-gate 	int			count;
10400Sstevel@tonic-gate 	struct extmnttab	ent;
10410Sstevel@tonic-gate 	int			mnted;
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate 
10440Sstevel@tonic-gate 	*fdp = -1;
10450Sstevel@tonic-gate 
10460Sstevel@tonic-gate 	/*
10470Sstevel@tonic-gate 	 * Check that /var/run is mounted. In the unlikely event
10480Sstevel@tonic-gate 	 * that the lock file is left behind, we want it
10490Sstevel@tonic-gate 	 * cleared on the next reboot.
10500Sstevel@tonic-gate 	 */
10510Sstevel@tonic-gate 	errno = 0;
10520Sstevel@tonic-gate 	if ((fp = fopen(MNTTAB, "r")) == NULL) {
10530Sstevel@tonic-gate 		cfga_err(errstring, errno, ERRARG_OPEN, MNTTAB, 0);
10540Sstevel@tonic-gate 		return (SCFGA_LIB_ERR);
10550Sstevel@tonic-gate 	}
10560Sstevel@tonic-gate 
10570Sstevel@tonic-gate 	resetmnttab(fp);
10580Sstevel@tonic-gate 
10590Sstevel@tonic-gate 	mnted = 0;
10600Sstevel@tonic-gate 	while (getextmntent(fp, &ent, sizeof (ent)) == 0) {
10610Sstevel@tonic-gate 		if (strcmp(ent.mnt_mountp, "/var/run") == 0) {
10620Sstevel@tonic-gate 			mnted = 1;
10630Sstevel@tonic-gate 			break;
10640Sstevel@tonic-gate 		}
10650Sstevel@tonic-gate 	}
10660Sstevel@tonic-gate 
10670Sstevel@tonic-gate 	(void) fclose(fp);
10680Sstevel@tonic-gate 
10690Sstevel@tonic-gate 	if (!mnted) {
10700Sstevel@tonic-gate 		cfga_err(errstring, 0, ERR_VAR_RUN, 0);
10710Sstevel@tonic-gate 		return (SCFGA_LIB_ERR);
10720Sstevel@tonic-gate 	}
10730Sstevel@tonic-gate 
10740Sstevel@tonic-gate 	/*
10750Sstevel@tonic-gate 	 * Wait for a short period of time if we cannot O_EXCL create
10760Sstevel@tonic-gate 	 * lock file. If some other cfgadm process is finishing up, we
10770Sstevel@tonic-gate 	 * can get in. If the wait required is long however, just
10780Sstevel@tonic-gate 	 * return SYSTEM_BUSY to the user - a hotplug operation is
10790Sstevel@tonic-gate 	 * probably in progress.
10800Sstevel@tonic-gate 	 */
10810Sstevel@tonic-gate 	count = 0;
10820Sstevel@tonic-gate retry:
10830Sstevel@tonic-gate 	*fdp = open(SCFGA_LOCK, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
10840Sstevel@tonic-gate 	if (*fdp == -1 && errno == EEXIST) {
10850Sstevel@tonic-gate 		if (++count < MAX_LOCK_TRIES) {
10860Sstevel@tonic-gate 			if (count == 1)
10870Sstevel@tonic-gate 				cfga_msg(msgp, MSG_WAIT_LOCK, 0);
10880Sstevel@tonic-gate 			(void) sleep(1);
10890Sstevel@tonic-gate 			goto retry;
10900Sstevel@tonic-gate 		}
10910Sstevel@tonic-gate 	}
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate 	if (*fdp == -1 && errno == EEXIST) {
10940Sstevel@tonic-gate 		cfga_err(errstring, 0, ERRARG_QUIESCE_LOCK, SCFGA_LOCK, 0);
10950Sstevel@tonic-gate 		return (SCFGA_SYSTEM_BUSY);
10960Sstevel@tonic-gate 	} else if (*fdp == -1) {
10970Sstevel@tonic-gate 		cfga_err(errstring, errno, ERRARG_QUIESCE_LOCK, SCFGA_LOCK, 0);
10980Sstevel@tonic-gate 		return (SCFGA_LIB_ERR);
10990Sstevel@tonic-gate 	}
11000Sstevel@tonic-gate 
11010Sstevel@tonic-gate 	CFGA_TRACE3((stdout, "create_lock[%d]: created lockfile: %s\n",
11020Sstevel@tonic-gate 	    s_getpid(), SCFGA_LOCK));
11030Sstevel@tonic-gate 
11040Sstevel@tonic-gate 	return (SCFGA_OK);
11050Sstevel@tonic-gate }
11060Sstevel@tonic-gate 
11070Sstevel@tonic-gate static scfga_ret_t
syslock(int fd,char ** errstring)11080Sstevel@tonic-gate syslock(int fd, char **errstring)
11090Sstevel@tonic-gate {
11100Sstevel@tonic-gate 	struct flock	lock;
11110Sstevel@tonic-gate 	int		count;
11120Sstevel@tonic-gate 	int		rval;
11130Sstevel@tonic-gate 
11140Sstevel@tonic-gate 	assert(fd != -1);
11150Sstevel@tonic-gate 
11160Sstevel@tonic-gate 	CFGA_TRACE3((stdout, "syslock[%d]: trying lock: %s\n",
11170Sstevel@tonic-gate 	    s_getpid(), SCFGA_LOCK));
11180Sstevel@tonic-gate 
11190Sstevel@tonic-gate 	lock.l_type = F_WRLCK;
11200Sstevel@tonic-gate 	lock.l_whence = SEEK_SET;
11210Sstevel@tonic-gate 	lock.l_start = 0;
11220Sstevel@tonic-gate 	lock.l_len = 0;
11230Sstevel@tonic-gate 
11240Sstevel@tonic-gate 	count = 0;
11250Sstevel@tonic-gate 	while ((rval = fcntl(fd, F_SETLKW, &lock)) == -1 && errno == EINTR) {
11260Sstevel@tonic-gate 		if (++count >= MAX_LOCK_TRIES) {
11270Sstevel@tonic-gate 			CFGA_TRACE1((stdout, "syslock[%d]: retry limit: %s\n",
11280Sstevel@tonic-gate 			    s_getpid(), SCFGA_LOCK));
11290Sstevel@tonic-gate 			goto badlock;
11300Sstevel@tonic-gate 		}
11310Sstevel@tonic-gate 		(void) sleep(1);
11320Sstevel@tonic-gate 	}
11330Sstevel@tonic-gate 
11340Sstevel@tonic-gate 	if (rval != -1) {
11350Sstevel@tonic-gate 		CFGA_TRACE3((stdout, "syslock[%d]: locked file: %s\n",
11360Sstevel@tonic-gate 		    s_getpid(), SCFGA_LOCK));
11370Sstevel@tonic-gate 		return (SCFGA_OK);
11380Sstevel@tonic-gate 	}
11390Sstevel@tonic-gate 
11400Sstevel@tonic-gate 	/*FALLTHROUGH*/
11410Sstevel@tonic-gate badlock:
11420Sstevel@tonic-gate 	cfga_err(errstring, errno, ERRARG_LOCK, SCFGA_LOCK, 0);
11430Sstevel@tonic-gate 	/* trace message to display pid */
11440Sstevel@tonic-gate 	CFGA_TRACE1((stdout, "syslock[%d]: cannot lock %s\n",
11450Sstevel@tonic-gate 	    s_getpid(), SCFGA_LOCK));
11460Sstevel@tonic-gate 	return (SCFGA_LIB_ERR);
11470Sstevel@tonic-gate }
11480Sstevel@tonic-gate 
11490Sstevel@tonic-gate static void
wait_for_child(pid_t cpid)11500Sstevel@tonic-gate wait_for_child(pid_t cpid)
11510Sstevel@tonic-gate {
11520Sstevel@tonic-gate 	int	status;
11530Sstevel@tonic-gate 	pid_t	rval;
11540Sstevel@tonic-gate 
11550Sstevel@tonic-gate 	CFGA_TRACE2((stdout, "wait_for_child[%d]: child[%d]\n",
11560Sstevel@tonic-gate 	    s_getpid(), (int)cpid));
11570Sstevel@tonic-gate 
11580Sstevel@tonic-gate 	for (;;) {
11590Sstevel@tonic-gate 		while ((rval = waitpid(cpid, &status, 0)) != cpid) {
11600Sstevel@tonic-gate 			if (errno == ECHILD) {
11610Sstevel@tonic-gate 				CFGA_TRACE1((stdout, "waitpid[%d]: child[%d] "
11620Sstevel@tonic-gate 				    "doesn't exist\n", s_getpid(), (int)cpid));
11630Sstevel@tonic-gate 				return;
11640Sstevel@tonic-gate 			}
11650Sstevel@tonic-gate 
11660Sstevel@tonic-gate 			CFGA_TRACE3((stdout, "waitpid[%d]: returned: %d"
11670Sstevel@tonic-gate 			    ": errno: %s\n", s_getpid(), (int)rval,
11680Sstevel@tonic-gate 			    strerror(errno)));
11690Sstevel@tonic-gate 		}
11700Sstevel@tonic-gate 
11710Sstevel@tonic-gate 		if (WIFEXITED(status)) {
11720Sstevel@tonic-gate 			CFGA_TRACE2((stdout, "waitpid[%d]: child[%d]: "
11730Sstevel@tonic-gate 			    "normal exit\n", s_getpid(), (int)cpid));
11740Sstevel@tonic-gate 			return;
11750Sstevel@tonic-gate 		}
11760Sstevel@tonic-gate 
11770Sstevel@tonic-gate 		if (WIFSIGNALED(status)) {
11780Sstevel@tonic-gate 			CFGA_TRACE2((stdout, "waitpid[%d]: child[%d]: "
11790Sstevel@tonic-gate 			    "signal exit\n", s_getpid(), (int)cpid));
11800Sstevel@tonic-gate 			return;
11810Sstevel@tonic-gate 		}
11820Sstevel@tonic-gate 
11830Sstevel@tonic-gate 		/*
11840Sstevel@tonic-gate 		 * The child has not terminated. We received status
11850Sstevel@tonic-gate 		 * because the child was either stopped or continued.
11860Sstevel@tonic-gate 		 * Wait for child termination by calling waitpid() again.
11870Sstevel@tonic-gate 		 */
11880Sstevel@tonic-gate 	}
11890Sstevel@tonic-gate }
11900Sstevel@tonic-gate 
11910Sstevel@tonic-gate static void
wait_and_cleanup(int fd,apid_t * apidp)11920Sstevel@tonic-gate wait_and_cleanup(int fd, apid_t *apidp)
11930Sstevel@tonic-gate {
11940Sstevel@tonic-gate 	int		l_errno;
11950Sstevel@tonic-gate 	scfga_ret_t	ret;
11960Sstevel@tonic-gate 
11970Sstevel@tonic-gate 	/* This is the child */
11980Sstevel@tonic-gate 	CFGA_TRACE2((stdout, "child[%d]: Entering wait_cleanup\n", s_getpid()));
11990Sstevel@tonic-gate 
12000Sstevel@tonic-gate 	if (syslock(fd, NULL) != SCFGA_OK) {
12010Sstevel@tonic-gate 		CFGA_TRACE1((stdout, "child[%d]: lock failure "
12020Sstevel@tonic-gate 		    " - _exit(1)\n", s_getpid()));
12030Sstevel@tonic-gate 		/*
12040Sstevel@tonic-gate 		 * As a last resort, unlink the lock file. This is relatively
12050Sstevel@tonic-gate 		 * safe as the child doesn't unquiesce the bus in this case.
12060Sstevel@tonic-gate 		 */
12070Sstevel@tonic-gate 		s_unlink(SCFGA_LOCK);
12080Sstevel@tonic-gate 		_exit(1);
12090Sstevel@tonic-gate 	}
12100Sstevel@tonic-gate 
12110Sstevel@tonic-gate 	l_errno = 0;
12120Sstevel@tonic-gate 	ret = devctl_cmd(apidp->hba_phys, SCFGA_BUS_UNQUIESCE, NULL, &l_errno);
12130Sstevel@tonic-gate 	if (ret != SCFGA_OK) {
12140Sstevel@tonic-gate 		if (l_errno == EALREADY)
12150Sstevel@tonic-gate 			CFGA_TRACE3((stdout, "child[%d]: bus already "
12160Sstevel@tonic-gate 			    "unquiesced: %s\n", s_getpid(), apidp->hba_phys));
12170Sstevel@tonic-gate 		else
12180Sstevel@tonic-gate 			CFGA_TRACE1((stdout, "child[%d]: unquiesce failed: "
12190Sstevel@tonic-gate 			    "%s\n", s_getpid(), strerror(l_errno)));
12200Sstevel@tonic-gate 	} else {
12210Sstevel@tonic-gate 		CFGA_TRACE1((stdout, "child[%d]: unquiesced bus: %s\n",
12220Sstevel@tonic-gate 		    s_getpid(), apidp->hba_phys));
12230Sstevel@tonic-gate 	}
12240Sstevel@tonic-gate 
12250Sstevel@tonic-gate 	s_unlink(SCFGA_LOCK);
12260Sstevel@tonic-gate 
12270Sstevel@tonic-gate 	CFGA_TRACE2((stdout, "child[%d]: _exit(0)\n", s_getpid()));
12280Sstevel@tonic-gate 
12290Sstevel@tonic-gate 	_exit(0);
12300Sstevel@tonic-gate }
12310Sstevel@tonic-gate 
12320Sstevel@tonic-gate static void
sigblk(sigset_t * osp)12330Sstevel@tonic-gate sigblk(sigset_t *osp)
12340Sstevel@tonic-gate {
12350Sstevel@tonic-gate 	sigset_t set;
12360Sstevel@tonic-gate 
12370Sstevel@tonic-gate 	(void) sigemptyset(&set);
12380Sstevel@tonic-gate 	(void) sigemptyset(osp);
12390Sstevel@tonic-gate 	(void) sigaddset(&set, SIGHUP);
12400Sstevel@tonic-gate 	(void) sigaddset(&set, SIGINT);
12410Sstevel@tonic-gate 	(void) sigaddset(&set, SIGQUIT);
12420Sstevel@tonic-gate 	(void) sigaddset(&set, SIGTERM);
12430Sstevel@tonic-gate 	(void) sigaddset(&set, SIGUSR1);
12440Sstevel@tonic-gate 	(void) sigaddset(&set, SIGUSR2);
12450Sstevel@tonic-gate 	(void) sigprocmask(SIG_BLOCK, &set, osp);
12460Sstevel@tonic-gate }
12470Sstevel@tonic-gate 
12480Sstevel@tonic-gate static void
sigunblk(sigset_t * osp)12490Sstevel@tonic-gate sigunblk(sigset_t *osp)
12500Sstevel@tonic-gate {
12510Sstevel@tonic-gate 	(void) sigprocmask(SIG_SETMASK, osp, NULL);
12520Sstevel@tonic-gate }
12530Sstevel@tonic-gate 
12540Sstevel@tonic-gate /*
12550Sstevel@tonic-gate  * Here is the algorithm used to ensure that a SCSI bus is not
12560Sstevel@tonic-gate  * left in the quiesced state:
12570Sstevel@tonic-gate  *
12580Sstevel@tonic-gate  *	lock quiesce mutex	// single threads this code
12590Sstevel@tonic-gate  *	open(O_CREAT|O_EXCL) lock file	// only 1 process at a time
12600Sstevel@tonic-gate  *	exclusive record lock on lock file
12610Sstevel@tonic-gate  *	fork1()
12620Sstevel@tonic-gate  *	quiesce bus
12630Sstevel@tonic-gate  *	do the physical hotplug operation
12640Sstevel@tonic-gate  *	unquiesce bus
12650Sstevel@tonic-gate  *	unlock record lock
12660Sstevel@tonic-gate  *		-> *child*
12670Sstevel@tonic-gate  *		-> wait for record lock
12680Sstevel@tonic-gate  *		-> unconditionally unquiesce bus
12690Sstevel@tonic-gate  *		-> unlink lock file
12700Sstevel@tonic-gate  *		-> exit
12710Sstevel@tonic-gate  *	wait for child to exit
12720Sstevel@tonic-gate  *	unlock quiesce mutex
12730Sstevel@tonic-gate  *
12740Sstevel@tonic-gate  * NOTE1: since record locks are per-process and a close() can
12750Sstevel@tonic-gate  * release a lock, to keep things MT-safe we need a quiesce mutex.
12760Sstevel@tonic-gate  *
12770Sstevel@tonic-gate  * NOTE2: To ensure that the child does not unquiesce a bus quiesced
12780Sstevel@tonic-gate  * by an unrelated cfgadm_scsi operation, exactly 1 process in the
12790Sstevel@tonic-gate  * system can be doing an implicit quiesce operation  The exclusive
12800Sstevel@tonic-gate  * creation of the lock file guarantees this.
12810Sstevel@tonic-gate  *
12820Sstevel@tonic-gate  * NOTE3: This works even if the parent process dumps core and/or is
12830Sstevel@tonic-gate  * abnormally terminated. If the parent dies before the child is
12840Sstevel@tonic-gate  * forked, the bus is not quiesced. If the parent dies after the
12850Sstevel@tonic-gate  * bus is quiesced, the child process will ensure that the bus is
12860Sstevel@tonic-gate  * unquiesced.
12870Sstevel@tonic-gate  */
12880Sstevel@tonic-gate static scfga_ret_t
dev_hotplug(apid_t * apidp,prompt_t * pt,cfga_flags_t flags,int do_quiesce,char ** errstring)12890Sstevel@tonic-gate dev_hotplug(
12900Sstevel@tonic-gate 	apid_t *apidp,
12910Sstevel@tonic-gate 	prompt_t *pt,
12920Sstevel@tonic-gate 	cfga_flags_t flags,
12930Sstevel@tonic-gate 	int do_quiesce,
12940Sstevel@tonic-gate 	char **errstring)
12950Sstevel@tonic-gate {
12960Sstevel@tonic-gate 	scfga_ret_t	ret;
12970Sstevel@tonic-gate 	pid_t		cpid;
12980Sstevel@tonic-gate 	int		fd;
12990Sstevel@tonic-gate 	sigset_t	oset;
13000Sstevel@tonic-gate 
13010Sstevel@tonic-gate 	assert(apidp->hba_phys != NULL);
13020Sstevel@tonic-gate 	assert(apidp->path != NULL);
13030Sstevel@tonic-gate 
13040Sstevel@tonic-gate 	/* If no quiesce required, prompt the user to do the operation */
13050Sstevel@tonic-gate 	if (!do_quiesce)
13060Sstevel@tonic-gate 		return (wait_for_hotplug(pt, CONF_NO_QUIESCE));
13070Sstevel@tonic-gate 
13080Sstevel@tonic-gate 	(void) mutex_lock(&quiesce_mutex);
13090Sstevel@tonic-gate 
13100Sstevel@tonic-gate 	ret = create_lock(&fd, pt->msgp, errstring);
13110Sstevel@tonic-gate 	if (ret != SCFGA_OK) {
13120Sstevel@tonic-gate 		(void) mutex_unlock(&quiesce_mutex);
13130Sstevel@tonic-gate 		return (ret);
13140Sstevel@tonic-gate 	}
13150Sstevel@tonic-gate 
13160Sstevel@tonic-gate 	ret = syslock(fd, errstring);
13170Sstevel@tonic-gate 	if (ret != SCFGA_OK) {
13180Sstevel@tonic-gate 		goto bad;
13190Sstevel@tonic-gate 	}
13200Sstevel@tonic-gate 
13210Sstevel@tonic-gate 	/*
13220Sstevel@tonic-gate 	 * block signals in the child. Parent may
13230Sstevel@tonic-gate 	 * exit, causing signal to be sent to child.
13240Sstevel@tonic-gate 	 */
13250Sstevel@tonic-gate 	sigblk(&oset);
13260Sstevel@tonic-gate 
13270Sstevel@tonic-gate 	switch (cpid = fork1()) {
13280Sstevel@tonic-gate 		case 0:
13290Sstevel@tonic-gate 			/* child */
13300Sstevel@tonic-gate 			wait_and_cleanup(fd, apidp);
13310Sstevel@tonic-gate 			_exit(0); /* paranoia */
13320Sstevel@tonic-gate 			/*NOTREACHED*/
13330Sstevel@tonic-gate 		case -1:
13340Sstevel@tonic-gate 			cfga_err(errstring, errno, ERR_FORK, 0);
13350Sstevel@tonic-gate 			sigunblk(&oset);
13360Sstevel@tonic-gate 			ret = SCFGA_LIB_ERR;
13370Sstevel@tonic-gate 			goto bad;
13380Sstevel@tonic-gate 		default:
13390Sstevel@tonic-gate 			/* parent */
13400Sstevel@tonic-gate 			break;
13410Sstevel@tonic-gate 	}
13420Sstevel@tonic-gate 
13430Sstevel@tonic-gate 	sigunblk(&oset);
13440Sstevel@tonic-gate 
13450Sstevel@tonic-gate 	/* We have forked successfully - this is the parent */
13460Sstevel@tonic-gate 	ret = bus_quiesce(apidp, pt, errstring, flags);
13470Sstevel@tonic-gate 
13480Sstevel@tonic-gate 	(void) close(fd);	/* also unlocks */
13490Sstevel@tonic-gate 
13500Sstevel@tonic-gate 	wait_for_child(cpid);
13510Sstevel@tonic-gate 
13520Sstevel@tonic-gate 	(void) mutex_unlock(&quiesce_mutex);
13530Sstevel@tonic-gate 
13540Sstevel@tonic-gate 	return (ret);
13550Sstevel@tonic-gate bad:
13560Sstevel@tonic-gate 	(void) close(fd);
13570Sstevel@tonic-gate 	s_unlink(SCFGA_LOCK);
13580Sstevel@tonic-gate 	(void) mutex_unlock(&quiesce_mutex);
13590Sstevel@tonic-gate 	return (ret);
13600Sstevel@tonic-gate }
13610Sstevel@tonic-gate 
13620Sstevel@tonic-gate /*
13630Sstevel@tonic-gate  * Checks if HBA controls a critical file-system (/, /usr or swap)
13640Sstevel@tonic-gate  * This routine reads /etc/vfstab and is NOT foolproof.
13650Sstevel@tonic-gate  * If an error occurs, assumes that controller is NOT critical.
13660Sstevel@tonic-gate  */
13670Sstevel@tonic-gate static int
critical_ctrlr(const char * hba_phys)13680Sstevel@tonic-gate critical_ctrlr(const char *hba_phys)
13690Sstevel@tonic-gate {
13700Sstevel@tonic-gate 	FILE *fp;
13710Sstevel@tonic-gate 	struct vfstab vfst;
13720Sstevel@tonic-gate 	int vfsret = 1, rv = -1;
13730Sstevel@tonic-gate 	char *bufp;
13740Sstevel@tonic-gate 	const size_t buflen = PATH_MAX;
13750Sstevel@tonic-gate 	char mount[MAXPATHLEN], fstype[MAXPATHLEN], spec[MAXPATHLEN];
13760Sstevel@tonic-gate 
13770Sstevel@tonic-gate 
13780Sstevel@tonic-gate 	if ((bufp = calloc(1, buflen)) == NULL) {
13790Sstevel@tonic-gate 		return (0);
13800Sstevel@tonic-gate 	}
13810Sstevel@tonic-gate 
13820Sstevel@tonic-gate 	fp = NULL;
13830Sstevel@tonic-gate 	if ((fp = fopen(ETC_VFSTAB, "r")) == NULL) {
13840Sstevel@tonic-gate 		rv = 0;
13850Sstevel@tonic-gate 		goto out;
13860Sstevel@tonic-gate 	}
13870Sstevel@tonic-gate 
13880Sstevel@tonic-gate 	while ((vfsret = getvfsent(fp, &vfst)) == 0) {
13890Sstevel@tonic-gate 
13900Sstevel@tonic-gate 		(void) strcpy(mount, S_STR(vfst.vfs_mountp));
13910Sstevel@tonic-gate 		(void) strcpy(fstype, S_STR(vfst.vfs_fstype));
13920Sstevel@tonic-gate 		(void) strcpy(spec, S_STR(vfst.vfs_special));
13930Sstevel@tonic-gate 
13940Sstevel@tonic-gate 		/* Ignore non-critical entries */
13950Sstevel@tonic-gate 		if (strcmp(mount, "/") && strcmp(mount, "/usr") &&
13960Sstevel@tonic-gate 		    strcmp(fstype, "swap")) {
13970Sstevel@tonic-gate 			continue;
13980Sstevel@tonic-gate 		}
13990Sstevel@tonic-gate 
14000Sstevel@tonic-gate 		/* get physical path */
14010Sstevel@tonic-gate 		if (realpath(spec, bufp) == NULL) {
14020Sstevel@tonic-gate 			continue;
14030Sstevel@tonic-gate 		}
14040Sstevel@tonic-gate 
14050Sstevel@tonic-gate 		/* Check if critical partition is on the HBA */
14060Sstevel@tonic-gate 		if (!(rv = hba_dev_cmp(hba_phys, bufp))) {
14070Sstevel@tonic-gate 			break;
14080Sstevel@tonic-gate 		}
14090Sstevel@tonic-gate 	}
14100Sstevel@tonic-gate 
14110Sstevel@tonic-gate 	rv = !vfsret;
14120Sstevel@tonic-gate 
14130Sstevel@tonic-gate 	/*FALLTHRU*/
14140Sstevel@tonic-gate out:
14150Sstevel@tonic-gate 	S_FREE(bufp);
14161957Sdnielsen 	if (fp != NULL) {
14171957Sdnielsen 		(void) fclose(fp);
14181957Sdnielsen 	}
14190Sstevel@tonic-gate 	return (rv);
14200Sstevel@tonic-gate }
14210Sstevel@tonic-gate 
14220Sstevel@tonic-gate /*
14230Sstevel@tonic-gate  * Convert bus state to receptacle state
14240Sstevel@tonic-gate  */
14250Sstevel@tonic-gate static cfga_stat_t
bus_devctl_to_recep_state(uint_t bus_dc_state)14260Sstevel@tonic-gate bus_devctl_to_recep_state(uint_t bus_dc_state)
14270Sstevel@tonic-gate {
14280Sstevel@tonic-gate 	cfga_stat_t rs;
14290Sstevel@tonic-gate 
14300Sstevel@tonic-gate 	switch (bus_dc_state) {
14310Sstevel@tonic-gate 	case BUS_ACTIVE:
14320Sstevel@tonic-gate 		rs = CFGA_STAT_CONNECTED;
14330Sstevel@tonic-gate 		break;
14340Sstevel@tonic-gate 	case BUS_QUIESCED:
14350Sstevel@tonic-gate 	case BUS_SHUTDOWN:
14360Sstevel@tonic-gate 		rs = CFGA_STAT_DISCONNECTED;
14370Sstevel@tonic-gate 		break;
14380Sstevel@tonic-gate 	default:
14390Sstevel@tonic-gate 		rs = CFGA_STAT_NONE;
14400Sstevel@tonic-gate 		break;
14410Sstevel@tonic-gate 	}
14420Sstevel@tonic-gate 
14430Sstevel@tonic-gate 	return (rs);
14440Sstevel@tonic-gate }
14450Sstevel@tonic-gate 
14460Sstevel@tonic-gate static int
add_dev(di_node_t node,void * arg)14470Sstevel@tonic-gate add_dev(di_node_t node, void *arg)
14480Sstevel@tonic-gate {
14490Sstevel@tonic-gate 	int ndevs, len;
14500Sstevel@tonic-gate 	char *path, *p;
14510Sstevel@tonic-gate 	struct larg *largp = (struct larg *)arg;
14520Sstevel@tonic-gate 
14530Sstevel@tonic-gate 	/* ignore hba itself and all detached nodes */
14540Sstevel@tonic-gate 	if (di_parent_node(node) == DI_NODE_NIL ||
14550Sstevel@tonic-gate 	    di_node_state(node) < DS_ATTACHED)
14560Sstevel@tonic-gate 		return (DI_WALK_CONTINUE);
14570Sstevel@tonic-gate 
14580Sstevel@tonic-gate 	if ((path = di_devfs_path(node)) == NULL) {
14590Sstevel@tonic-gate 		largp->ndevs = -1;
14600Sstevel@tonic-gate 		return (DI_WALK_TERMINATE);
14610Sstevel@tonic-gate 	}
14620Sstevel@tonic-gate 
14630Sstevel@tonic-gate 	/* sizeof (DEVICES_DIR) includes the null terminator */
14640Sstevel@tonic-gate 	len = strlen(path) + sizeof (DEVICES_DIR);
14650Sstevel@tonic-gate 	if ((p = malloc(len)) == NULL) {
14660Sstevel@tonic-gate 		di_devfs_path_free(path);
14670Sstevel@tonic-gate 		largp->ndevs = -1;
14680Sstevel@tonic-gate 		return (DI_WALK_TERMINATE);
14690Sstevel@tonic-gate 	}
14700Sstevel@tonic-gate 	(void) snprintf(p, len, "%s%s", DEVICES_DIR, path);
14710Sstevel@tonic-gate 	di_devfs_path_free(path);
14720Sstevel@tonic-gate 
14730Sstevel@tonic-gate 	/* ignore device to be excluded */
14740Sstevel@tonic-gate 	if (largp->dev && strcmp(largp->dev, p) == 0) {
14750Sstevel@tonic-gate 		free(p);
14760Sstevel@tonic-gate 		return (DI_WALK_CONTINUE);
14770Sstevel@tonic-gate 	}
14780Sstevel@tonic-gate 
14790Sstevel@tonic-gate 	/* grow dev_list to allow room for one more device */
14800Sstevel@tonic-gate 	if (alloc_dev_list(largp) != 0) {
14810Sstevel@tonic-gate 		free(p);
14820Sstevel@tonic-gate 		return (DI_WALK_TERMINATE);
14830Sstevel@tonic-gate 	}
14840Sstevel@tonic-gate 	ndevs = largp->ndevs;
14850Sstevel@tonic-gate 	largp->ndevs++;
14860Sstevel@tonic-gate 	largp->dev_list[ndevs] = p;
14870Sstevel@tonic-gate 	largp->dev_list[ndevs + 1] = NULL;
14880Sstevel@tonic-gate 	return (DI_WALK_CONTINUE);
14890Sstevel@tonic-gate }
14900Sstevel@tonic-gate 
14910Sstevel@tonic-gate /*
14920Sstevel@tonic-gate  * Get list of children excluding dev_excl (if not null).
14930Sstevel@tonic-gate  */
14940Sstevel@tonic-gate static int
get_hba_children(char * bus_path,char * dev_excl,char *** dev_listp)14950Sstevel@tonic-gate get_hba_children(char *bus_path, char *dev_excl, char ***dev_listp)
14960Sstevel@tonic-gate {
14970Sstevel@tonic-gate 	int err, ret;
14980Sstevel@tonic-gate 	walkarg_t u;
14990Sstevel@tonic-gate 	struct larg larg;
15000Sstevel@tonic-gate 
15010Sstevel@tonic-gate 	*dev_listp = NULL;
15020Sstevel@tonic-gate 
15030Sstevel@tonic-gate 	u.node_args.flags = DI_WALK_CLDFIRST;
15040Sstevel@tonic-gate 	u.node_args.fcn = add_dev;
15050Sstevel@tonic-gate 
15060Sstevel@tonic-gate 	larg.ndevs = 0;
15070Sstevel@tonic-gate 	larg.nelem = 0;
15080Sstevel@tonic-gate 	larg.dev = dev_excl;
15090Sstevel@tonic-gate 	larg.dev_list = NULL;
15100Sstevel@tonic-gate 
15110Sstevel@tonic-gate 	ret = walk_tree(bus_path, &larg, DINFOSUBTREE, &u, SCFGA_WALK_NODE,
15120Sstevel@tonic-gate 	    &err);
15130Sstevel@tonic-gate 	if (larg.ndevs == -1) {
15140Sstevel@tonic-gate 		free_dev_list(larg.dev_list);
15150Sstevel@tonic-gate 		return (SCFGA_ERR);
15160Sstevel@tonic-gate 	}
15170Sstevel@tonic-gate 	*dev_listp = larg.dev_list;
15180Sstevel@tonic-gate 	return (ret);
15190Sstevel@tonic-gate }
15200Sstevel@tonic-gate 
15210Sstevel@tonic-gate static char *
get_node_path(char * minor_path)15220Sstevel@tonic-gate get_node_path(char *minor_path)
15230Sstevel@tonic-gate {
15240Sstevel@tonic-gate 	char *path, *cp;
15250Sstevel@tonic-gate 
15260Sstevel@tonic-gate 	if ((path = strdup(minor_path)) == NULL)
15270Sstevel@tonic-gate 		return (NULL);
15280Sstevel@tonic-gate 	if ((cp = strrchr(path, ':')) != NULL)
15290Sstevel@tonic-gate 		*cp = '\0';
15300Sstevel@tonic-gate 	return (path);
15310Sstevel@tonic-gate }
15320Sstevel@tonic-gate 
15330Sstevel@tonic-gate /*
15340Sstevel@tonic-gate  * Ensure largp->dev_list has room for one more device.
15350Sstevel@tonic-gate  * Returns 0 on success, -1 on failure.
15360Sstevel@tonic-gate  */
15370Sstevel@tonic-gate static int
alloc_dev_list(struct larg * largp)15380Sstevel@tonic-gate alloc_dev_list(struct larg *largp)
15390Sstevel@tonic-gate {
15400Sstevel@tonic-gate 	int nelem;
15410Sstevel@tonic-gate 	char **p;
15420Sstevel@tonic-gate 
15430Sstevel@tonic-gate 	if (largp->nelem > largp->ndevs + 2)	/* +1 for NULL termination */
15440Sstevel@tonic-gate 		return (0);
15450Sstevel@tonic-gate 
15460Sstevel@tonic-gate 	nelem =  largp->nelem + 16;
15470Sstevel@tonic-gate 	p = realloc(largp->dev_list, nelem * sizeof (char *));
15480Sstevel@tonic-gate 	if (p == NULL)
15490Sstevel@tonic-gate 		return (-1);
15500Sstevel@tonic-gate 
15510Sstevel@tonic-gate 	largp->dev_list = p;
15520Sstevel@tonic-gate 	largp->nelem = nelem;
15530Sstevel@tonic-gate 	return (0);
15540Sstevel@tonic-gate }
15550Sstevel@tonic-gate 
15560Sstevel@tonic-gate static void
free_dev_list_elements(char ** dev_list)15570Sstevel@tonic-gate free_dev_list_elements(char **dev_list)
15580Sstevel@tonic-gate {
15590Sstevel@tonic-gate 	while (*dev_list) {
15600Sstevel@tonic-gate 		free(*dev_list);
15610Sstevel@tonic-gate 		dev_list++;
15620Sstevel@tonic-gate 	}
15630Sstevel@tonic-gate }
15640Sstevel@tonic-gate 
15650Sstevel@tonic-gate static void
free_dev_list(char ** dev_list)15660Sstevel@tonic-gate free_dev_list(char **dev_list)
15670Sstevel@tonic-gate {
15680Sstevel@tonic-gate 	if (dev_list == NULL)
15690Sstevel@tonic-gate 		return;
15700Sstevel@tonic-gate 
15710Sstevel@tonic-gate 	free_dev_list_elements(dev_list);
15720Sstevel@tonic-gate 	free(dev_list);
15730Sstevel@tonic-gate }
1574