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