xref: /onnv-gate/usr/src/uts/sun4u/ngdr/io/dr_quiesce.c (revision 930:1b624a2ec4bc)
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
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
23463Sjesusm  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * A CPR derivative specifically for starfire/starcat
310Sstevel@tonic-gate  */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate #include <sys/types.h>
340Sstevel@tonic-gate #include <sys/systm.h>
350Sstevel@tonic-gate #include <sys/machparam.h>
360Sstevel@tonic-gate #include <sys/machsystm.h>
370Sstevel@tonic-gate #include <sys/ddi.h>
380Sstevel@tonic-gate #define	SUNDDI_IMPL
390Sstevel@tonic-gate #include <sys/sunddi.h>
400Sstevel@tonic-gate #include <sys/sunndi.h>
410Sstevel@tonic-gate #include <sys/devctl.h>
420Sstevel@tonic-gate #include <sys/time.h>
430Sstevel@tonic-gate #include <sys/kmem.h>
440Sstevel@tonic-gate #include <nfs/lm.h>
450Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
460Sstevel@tonic-gate #include <sys/ndi_impldefs.h>
470Sstevel@tonic-gate #include <sys/obpdefs.h>
480Sstevel@tonic-gate #include <sys/cmn_err.h>
490Sstevel@tonic-gate #include <sys/debug.h>
500Sstevel@tonic-gate #include <sys/errno.h>
510Sstevel@tonic-gate #include <sys/callb.h>
520Sstevel@tonic-gate #include <sys/clock.h>
530Sstevel@tonic-gate #include <sys/x_call.h>
540Sstevel@tonic-gate #include <sys/cpuvar.h>
550Sstevel@tonic-gate #include <sys/epm.h>
560Sstevel@tonic-gate #include <sys/vfs.h>
570Sstevel@tonic-gate 
580Sstevel@tonic-gate #include <sys/cpu_sgnblk_defs.h>
590Sstevel@tonic-gate #include <sys/dr.h>
600Sstevel@tonic-gate #include <sys/dr_util.h>
610Sstevel@tonic-gate 
620Sstevel@tonic-gate #include <sys/promif.h>
630Sstevel@tonic-gate #include <sys/conf.h>
640Sstevel@tonic-gate #include <sys/cyclic.h>
650Sstevel@tonic-gate 
660Sstevel@tonic-gate extern void	e_ddi_enter_driver_list(struct devnames *dnp, int *listcnt);
670Sstevel@tonic-gate extern void	e_ddi_exit_driver_list(struct devnames *dnp, int listcnt);
680Sstevel@tonic-gate extern int	is_pseudo_device(dev_info_t *dip);
690Sstevel@tonic-gate 
700Sstevel@tonic-gate extern kmutex_t	cpu_lock;
710Sstevel@tonic-gate extern dr_unsafe_devs_t dr_unsafe_devs;
720Sstevel@tonic-gate 
730Sstevel@tonic-gate static int		dr_is_real_device(dev_info_t *dip);
740Sstevel@tonic-gate static int		dr_is_unsafe_major(major_t major);
750Sstevel@tonic-gate static int		dr_bypass_device(char *dname);
760Sstevel@tonic-gate static int		dr_check_dip(dev_info_t *dip, void *arg, uint_t ref);
770Sstevel@tonic-gate static int		dr_resolve_devname(dev_info_t *dip, char *buffer,
780Sstevel@tonic-gate 				char *alias);
790Sstevel@tonic-gate static sbd_error_t	*drerr_int(int e_code, uint64_t *arr, int idx,
800Sstevel@tonic-gate 				int majors);
810Sstevel@tonic-gate static int		dr_add_int(uint64_t *arr, int idx, int len,
820Sstevel@tonic-gate 				uint64_t val);
830Sstevel@tonic-gate 
840Sstevel@tonic-gate int dr_pt_test_suspend(dr_handle_t *hp);
850Sstevel@tonic-gate 
860Sstevel@tonic-gate /*
870Sstevel@tonic-gate  * dr_quiesce.c interface
880Sstevel@tonic-gate  * NOTE: states used internally by dr_suspend and dr_resume
890Sstevel@tonic-gate  */
900Sstevel@tonic-gate typedef enum dr_suspend_state {
910Sstevel@tonic-gate 	DR_SRSTATE_BEGIN = 0,
920Sstevel@tonic-gate 	DR_SRSTATE_USER,
930Sstevel@tonic-gate 	DR_SRSTATE_DRIVER,
940Sstevel@tonic-gate 	DR_SRSTATE_FULL
950Sstevel@tonic-gate } suspend_state_t;
960Sstevel@tonic-gate 
970Sstevel@tonic-gate struct dr_sr_handle {
980Sstevel@tonic-gate 	dr_handle_t		*sr_dr_handlep;
990Sstevel@tonic-gate 	dev_info_t		*sr_failed_dip;
1000Sstevel@tonic-gate 	suspend_state_t		sr_suspend_state;
1010Sstevel@tonic-gate 	uint_t			sr_flags;
1020Sstevel@tonic-gate 	uint64_t		sr_err_ints[DR_MAX_ERR_INT];
1030Sstevel@tonic-gate 	int			sr_err_idx;
1040Sstevel@tonic-gate };
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate #define	SR_FLAG_WATCHDOG	0x1
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate /*
1090Sstevel@tonic-gate  * XXX
1100Sstevel@tonic-gate  * This hack will go away before RTI.  Just for testing.
1110Sstevel@tonic-gate  * List of drivers to bypass when performing a suspend.
1120Sstevel@tonic-gate  */
1130Sstevel@tonic-gate static char *dr_bypass_list[] = {
1140Sstevel@tonic-gate 	""
1150Sstevel@tonic-gate };
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate #define		SKIP_SYNC	/* bypass sync ops in dr_suspend */
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate /*
1210Sstevel@tonic-gate  * dr_skip_user_threads is used to control if user threads should
1220Sstevel@tonic-gate  * be suspended.  If dr_skip_user_threads is true, the rest of the
1230Sstevel@tonic-gate  * flags are not used; if it is false, dr_check_user_stop_result
1240Sstevel@tonic-gate  * will be used to control whether or not we need to check suspend
1250Sstevel@tonic-gate  * result, and dr_allow_blocked_threads will be used to control
1260Sstevel@tonic-gate  * whether or not we allow suspend to continue if there are blocked
1270Sstevel@tonic-gate  * threads.  We allow all combinations of dr_check_user_stop_result
1280Sstevel@tonic-gate  * and dr_allow_block_threads, even though it might not make much
1290Sstevel@tonic-gate  * sense to not allow block threads when we don't even check stop
1300Sstevel@tonic-gate  * result.
1310Sstevel@tonic-gate  */
1320Sstevel@tonic-gate static int	dr_skip_user_threads = 0;	/* default to FALSE */
1330Sstevel@tonic-gate static int	dr_check_user_stop_result = 1;	/* default to TRUE */
1340Sstevel@tonic-gate static int	dr_allow_blocked_threads = 1;	/* default to TRUE */
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate #define	DR_CPU_LOOP_MSEC	1000
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate static void
1390Sstevel@tonic-gate dr_stop_intr(void)
1400Sstevel@tonic-gate {
1410Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&cpu_lock));
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 	kpreempt_disable();
1440Sstevel@tonic-gate 	cyclic_suspend();
1450Sstevel@tonic-gate }
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate static void
1480Sstevel@tonic-gate dr_enable_intr(void)
1490Sstevel@tonic-gate {
1500Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&cpu_lock));
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate 	cyclic_resume();
1530Sstevel@tonic-gate 	kpreempt_enable();
1540Sstevel@tonic-gate }
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate dr_sr_handle_t *
1570Sstevel@tonic-gate dr_get_sr_handle(dr_handle_t *hp)
1580Sstevel@tonic-gate {
1590Sstevel@tonic-gate 	dr_sr_handle_t *srh;
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate 	srh = GETSTRUCT(dr_sr_handle_t, 1);
1620Sstevel@tonic-gate 	srh->sr_dr_handlep = hp;
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 	return (srh);
1650Sstevel@tonic-gate }
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate void
1680Sstevel@tonic-gate dr_release_sr_handle(dr_sr_handle_t *srh)
1690Sstevel@tonic-gate {
1700Sstevel@tonic-gate 	ASSERT(srh->sr_failed_dip == NULL);
1710Sstevel@tonic-gate 	FREESTRUCT(srh, dr_sr_handle_t, 1);
1720Sstevel@tonic-gate }
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate static int
1750Sstevel@tonic-gate dr_is_real_device(dev_info_t *dip)
1760Sstevel@tonic-gate {
1770Sstevel@tonic-gate 	struct regspec *regbuf = NULL;
1780Sstevel@tonic-gate 	int length = 0;
1790Sstevel@tonic-gate 	int rc;
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate 	if (ddi_get_driver(dip) == NULL)
1820Sstevel@tonic-gate 		return (0);
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_flags & (PMC_NEEDS_SR|PMC_PARENTAL_SR))
1850Sstevel@tonic-gate 		return (1);
1860Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_flags & PMC_NO_SR)
1870Sstevel@tonic-gate 		return (0);
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate 	/*
1900Sstevel@tonic-gate 	 * now the general case
1910Sstevel@tonic-gate 	 */
192506Scth 	rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
1930Sstevel@tonic-gate 		(caddr_t)&regbuf, &length);
1940Sstevel@tonic-gate 	ASSERT(rc != DDI_PROP_NO_MEMORY);
1950Sstevel@tonic-gate 	if (rc != DDI_PROP_SUCCESS) {
1960Sstevel@tonic-gate 		return (0);
1970Sstevel@tonic-gate 	} else {
1980Sstevel@tonic-gate 		if ((length > 0) && (regbuf != NULL))
1990Sstevel@tonic-gate 			kmem_free(regbuf, length);
2000Sstevel@tonic-gate 		return (1);
2010Sstevel@tonic-gate 	}
2020Sstevel@tonic-gate }
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate static int
2050Sstevel@tonic-gate dr_is_unsafe_major(major_t major)
2060Sstevel@tonic-gate {
2070Sstevel@tonic-gate 	char    *dname, **cpp;
2080Sstevel@tonic-gate 	int	i, ndevs;
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 	if ((dname = ddi_major_to_name(major)) == NULL) {
2110Sstevel@tonic-gate 		PR_QR("dr_is_unsafe_major: invalid major # %d\n", major);
2120Sstevel@tonic-gate 		return (0);
2130Sstevel@tonic-gate 	}
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate 	ndevs = dr_unsafe_devs.ndevs;
2160Sstevel@tonic-gate 	for (i = 0, cpp = dr_unsafe_devs.devnames; i < ndevs; i++) {
2170Sstevel@tonic-gate 		if (strcmp(dname, *cpp++) == 0)
2180Sstevel@tonic-gate 			return (1);
2190Sstevel@tonic-gate 	}
2200Sstevel@tonic-gate 	return (0);
2210Sstevel@tonic-gate }
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate static int
2240Sstevel@tonic-gate dr_bypass_device(char *dname)
2250Sstevel@tonic-gate {
2260Sstevel@tonic-gate 	int i;
2270Sstevel@tonic-gate 	char **lname;
2280Sstevel@tonic-gate 	/* check the bypass list */
2290Sstevel@tonic-gate 	for (i = 0, lname = &dr_bypass_list[i]; **lname != '\0'; lname++) {
2300Sstevel@tonic-gate 		if (strcmp(dname, dr_bypass_list[i++]) == 0)
2310Sstevel@tonic-gate 			return (1);
2320Sstevel@tonic-gate 	}
2330Sstevel@tonic-gate 	return (0);
2340Sstevel@tonic-gate }
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate static int
2370Sstevel@tonic-gate dr_resolve_devname(dev_info_t *dip, char *buffer, char *alias)
2380Sstevel@tonic-gate {
2390Sstevel@tonic-gate 	major_t	devmajor;
2400Sstevel@tonic-gate 	char	*aka, *name;
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	*buffer = *alias = 0;
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate 	if (dip == NULL)
2450Sstevel@tonic-gate 		return (-1);
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 	if ((name = ddi_get_name(dip)) == NULL)
2480Sstevel@tonic-gate 		name = "<null name>";
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate 	aka = name;
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate 	if ((devmajor = ddi_name_to_major(aka)) != -1)
2530Sstevel@tonic-gate 		aka = ddi_major_to_name(devmajor);
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 	strcpy(buffer, name);
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	if (strcmp(name, aka))
2580Sstevel@tonic-gate 		strcpy(alias, aka);
2590Sstevel@tonic-gate 	else
2600Sstevel@tonic-gate 		*alias = 0;
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 	return (0);
2630Sstevel@tonic-gate }
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate struct dr_ref {
2660Sstevel@tonic-gate 	int		*refcount;
2670Sstevel@tonic-gate 	uint64_t	*arr;
2680Sstevel@tonic-gate 	int		*idx;
2690Sstevel@tonic-gate 	int		len;
2700Sstevel@tonic-gate };
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate /* ARGSUSED */
2730Sstevel@tonic-gate static int
2740Sstevel@tonic-gate dr_check_dip(dev_info_t *dip, void *arg, uint_t ref)
2750Sstevel@tonic-gate {
2760Sstevel@tonic-gate 	major_t		major;
2770Sstevel@tonic-gate 	char		*dname;
2780Sstevel@tonic-gate 	struct dr_ref	*rp = (struct dr_ref *)arg;
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 	if (dip == NULL)
2810Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	if (!dr_is_real_device(dip))
2840Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate 	dname = ddi_binding_name(dip);
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	if (dr_bypass_device(dname))
2890Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate 	if (dname && ((major = ddi_name_to_major(dname)) != (major_t)-1)) {
2920Sstevel@tonic-gate 		if (ref && rp->refcount) {
2930Sstevel@tonic-gate 			*rp->refcount += ref;
2940Sstevel@tonic-gate 			PR_QR("\n  %s (major# %d) is referenced(%u)\n",
2950Sstevel@tonic-gate 				dname, major, ref);
2960Sstevel@tonic-gate 		}
2970Sstevel@tonic-gate 		if (dr_is_unsafe_major(major) &&
2980Sstevel@tonic-gate 		    i_ddi_node_state(dip) >= DS_ATTACHED) {
2990Sstevel@tonic-gate 			PR_QR("\n  %s (major# %d) not hotpluggable\n",
3000Sstevel@tonic-gate 				dname, major);
3010Sstevel@tonic-gate 			if (rp->arr != NULL && rp->idx != NULL)
3020Sstevel@tonic-gate 				*rp->idx = dr_add_int(rp->arr, *rp->idx,
3030Sstevel@tonic-gate 					rp->len, (uint64_t)major);
3040Sstevel@tonic-gate 		}
3050Sstevel@tonic-gate 	}
3060Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
3070Sstevel@tonic-gate }
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate static int
3100Sstevel@tonic-gate dr_check_unsafe_major(dev_info_t *dip, void *arg)
3110Sstevel@tonic-gate {
3120Sstevel@tonic-gate 	return (dr_check_dip(dip, arg, 0));
3130Sstevel@tonic-gate }
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate /*ARGSUSED*/
3170Sstevel@tonic-gate void
3180Sstevel@tonic-gate dr_check_devices(dev_info_t *dip, int *refcount, dr_handle_t *handle,
3190Sstevel@tonic-gate     uint64_t *arr, int *idx, int len)
3200Sstevel@tonic-gate {
3210Sstevel@tonic-gate 	struct dr_ref bref = {0};
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 	if (dip == NULL)
3240Sstevel@tonic-gate 		return;
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 	bref.refcount = refcount;
3270Sstevel@tonic-gate 	bref.arr = arr;
3280Sstevel@tonic-gate 	bref.idx = idx;
3290Sstevel@tonic-gate 	bref.len = len;
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 	ASSERT(e_ddi_branch_held(dip));
3320Sstevel@tonic-gate 	(void) e_ddi_branch_referenced(dip, dr_check_dip, &bref);
3330Sstevel@tonic-gate }
3340Sstevel@tonic-gate 
3350Sstevel@tonic-gate /*
3360Sstevel@tonic-gate  * The "dip" argument's parent (if it exists) must be held busy.
3370Sstevel@tonic-gate  */
3380Sstevel@tonic-gate static int
3390Sstevel@tonic-gate dr_suspend_devices(dev_info_t *dip, dr_sr_handle_t *srh)
3400Sstevel@tonic-gate {
3410Sstevel@tonic-gate 	dr_handle_t	*handle;
3420Sstevel@tonic-gate 	major_t		major;
3430Sstevel@tonic-gate 	char		*dname;
3440Sstevel@tonic-gate 	int		circ;
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 	/*
3470Sstevel@tonic-gate 	 * If dip is the root node, it has no siblings and it is
3480Sstevel@tonic-gate 	 * always held. If dip is not the root node, dr_suspend_devices()
3490Sstevel@tonic-gate 	 * will be invoked with the parent held busy.
3500Sstevel@tonic-gate 	 */
3510Sstevel@tonic-gate 	for (; dip != NULL; dip = ddi_get_next_sibling(dip)) {
3520Sstevel@tonic-gate 		char	d_name[40], d_alias[40], *d_info;
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate 		ndi_devi_enter(dip, &circ);
3550Sstevel@tonic-gate 		if (dr_suspend_devices(ddi_get_child(dip), srh)) {
3560Sstevel@tonic-gate 			ndi_devi_exit(dip, circ);
3570Sstevel@tonic-gate 			return (ENXIO);
3580Sstevel@tonic-gate 		}
3590Sstevel@tonic-gate 		ndi_devi_exit(dip, circ);
3600Sstevel@tonic-gate 
3610Sstevel@tonic-gate 		if (!dr_is_real_device(dip))
3620Sstevel@tonic-gate 			continue;
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 		major = (major_t)-1;
3650Sstevel@tonic-gate 		if ((dname = ddi_binding_name(dip)) != NULL)
3660Sstevel@tonic-gate 			major = ddi_name_to_major(dname);
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 		if (dr_bypass_device(dname)) {
3690Sstevel@tonic-gate 			PR_QR(" bypassed suspend of %s (major# %d)\n", dname,
3700Sstevel@tonic-gate 				major);
3710Sstevel@tonic-gate 			continue;
3720Sstevel@tonic-gate 		}
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 		if (drmach_verify_sr(dip, 1)) {
3750Sstevel@tonic-gate 			PR_QR(" bypassed suspend of %s (major# %d)\n", dname,
3760Sstevel@tonic-gate 				major);
3770Sstevel@tonic-gate 			continue;
3780Sstevel@tonic-gate 		}
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 		if ((d_info = ddi_get_name_addr(dip)) == NULL)
3810Sstevel@tonic-gate 			d_info = "<null>";
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate 		d_name[0] = 0;
3840Sstevel@tonic-gate 		if (dr_resolve_devname(dip, d_name, d_alias) == 0) {
3850Sstevel@tonic-gate 			if (d_alias[0] != 0) {
3860Sstevel@tonic-gate 				prom_printf("\tsuspending %s@%s (aka %s)\n",
3870Sstevel@tonic-gate 					d_name, d_info, d_alias);
3880Sstevel@tonic-gate 			} else {
3890Sstevel@tonic-gate 				prom_printf("\tsuspending %s@%s\n",
3900Sstevel@tonic-gate 					d_name, d_info);
3910Sstevel@tonic-gate 			}
3920Sstevel@tonic-gate 		} else {
3930Sstevel@tonic-gate 			prom_printf("\tsuspending %s@%s\n", dname, d_info);
3940Sstevel@tonic-gate 		}
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 		if (devi_detach(dip, DDI_SUSPEND) != DDI_SUCCESS) {
3970Sstevel@tonic-gate 			prom_printf("\tFAILED to suspend %s@%s\n",
3980Sstevel@tonic-gate 				d_name[0] ? d_name : dname, d_info);
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate 			srh->sr_err_idx = dr_add_int(srh->sr_err_ints,
4010Sstevel@tonic-gate 				srh->sr_err_idx, DR_MAX_ERR_INT,
4020Sstevel@tonic-gate 				(uint64_t)major);
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate 			ndi_hold_devi(dip);
4050Sstevel@tonic-gate 			srh->sr_failed_dip = dip;
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 			handle = srh->sr_dr_handlep;
4080Sstevel@tonic-gate 			dr_op_err(CE_IGNORE, handle, ESBD_SUSPEND, "%s@%s",
4090Sstevel@tonic-gate 				d_name[0] ? d_name : dname, d_info);
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate 			return (DDI_FAILURE);
4120Sstevel@tonic-gate 		}
4130Sstevel@tonic-gate 	}
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 	return (DDI_SUCCESS);
4160Sstevel@tonic-gate }
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate static void
4190Sstevel@tonic-gate dr_resume_devices(dev_info_t *start, dr_sr_handle_t *srh)
4200Sstevel@tonic-gate {
4210Sstevel@tonic-gate 	dr_handle_t	*handle;
4220Sstevel@tonic-gate 	dev_info_t	*dip, *next, *last = NULL;
4230Sstevel@tonic-gate 	major_t		major;
4240Sstevel@tonic-gate 	char		*bn;
4250Sstevel@tonic-gate 	int		circ;
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 	major = (major_t)-1;
4280Sstevel@tonic-gate 
4290Sstevel@tonic-gate 	/* attach in reverse device tree order */
4300Sstevel@tonic-gate 	while (last != start) {
4310Sstevel@tonic-gate 		dip = start;
4320Sstevel@tonic-gate 		next = ddi_get_next_sibling(dip);
4330Sstevel@tonic-gate 		while (next != last && dip != srh->sr_failed_dip) {
4340Sstevel@tonic-gate 			dip = next;
4350Sstevel@tonic-gate 			next = ddi_get_next_sibling(dip);
4360Sstevel@tonic-gate 		}
4370Sstevel@tonic-gate 		if (dip == srh->sr_failed_dip) {
4380Sstevel@tonic-gate 			/* release hold acquired in dr_suspend_devices() */
4390Sstevel@tonic-gate 			srh->sr_failed_dip = NULL;
4400Sstevel@tonic-gate 			ndi_rele_devi(dip);
4410Sstevel@tonic-gate 		} else if (dr_is_real_device(dip) &&
4420Sstevel@tonic-gate 				srh->sr_failed_dip == NULL) {
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate 			if ((bn = ddi_binding_name(dip)) != NULL) {
4450Sstevel@tonic-gate 				major = ddi_name_to_major(bn);
4460Sstevel@tonic-gate 			} else {
4470Sstevel@tonic-gate 				bn = "<null>";
4480Sstevel@tonic-gate 			}
4490Sstevel@tonic-gate 			if (!dr_bypass_device(bn) &&
4500Sstevel@tonic-gate 				!drmach_verify_sr(dip, 0)) {
4510Sstevel@tonic-gate 				char	d_name[40], d_alias[40], *d_info;
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate 				d_name[0] = 0;
4540Sstevel@tonic-gate 				d_info = ddi_get_name_addr(dip);
4550Sstevel@tonic-gate 				if (d_info == NULL)
4560Sstevel@tonic-gate 					d_info = "<null>";
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate 				if (!dr_resolve_devname(dip, d_name,
4590Sstevel@tonic-gate 								d_alias)) {
4600Sstevel@tonic-gate 					if (d_alias[0] != 0) {
4610Sstevel@tonic-gate 						prom_printf("\tresuming "
4620Sstevel@tonic-gate 							"%s@%s (aka %s)\n",
4630Sstevel@tonic-gate 							d_name, d_info,
4640Sstevel@tonic-gate 							d_alias);
4650Sstevel@tonic-gate 					} else {
4660Sstevel@tonic-gate 						prom_printf("\tresuming "
4670Sstevel@tonic-gate 							"%s@%s\n",
4680Sstevel@tonic-gate 							d_name, d_info);
4690Sstevel@tonic-gate 					}
4700Sstevel@tonic-gate 				} else {
4710Sstevel@tonic-gate 					prom_printf("\tresuming %s@%s\n",
4720Sstevel@tonic-gate 						bn, d_info);
4730Sstevel@tonic-gate 				}
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate 				if (devi_attach(dip, DDI_RESUME) !=
4760Sstevel@tonic-gate 							DDI_SUCCESS) {
4770Sstevel@tonic-gate 					/*
4780Sstevel@tonic-gate 					 * Print a console warning,
4790Sstevel@tonic-gate 					 * set an e_code of ESBD_RESUME,
4800Sstevel@tonic-gate 					 * and save the driver major
4810Sstevel@tonic-gate 					 * number in the e_rsc.
4820Sstevel@tonic-gate 					 */
4830Sstevel@tonic-gate 					prom_printf("\tFAILED to resume %s@%s",
4840Sstevel@tonic-gate 					    d_name[0] ? d_name : bn, d_info);
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate 					srh->sr_err_idx =
4870Sstevel@tonic-gate 						dr_add_int(srh->sr_err_ints,
4880Sstevel@tonic-gate 						srh->sr_err_idx, DR_MAX_ERR_INT,
4890Sstevel@tonic-gate 						(uint64_t)major);
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate 					handle = srh->sr_dr_handlep;
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate 					dr_op_err(CE_IGNORE, handle,
4940Sstevel@tonic-gate 					    ESBD_RESUME, "%s@%s",
4950Sstevel@tonic-gate 					    d_name[0] ? d_name : bn, d_info);
4960Sstevel@tonic-gate 				}
4970Sstevel@tonic-gate 			}
4980Sstevel@tonic-gate 		}
4990Sstevel@tonic-gate 
5000Sstevel@tonic-gate 		/* Hold parent busy while walking its children */
5010Sstevel@tonic-gate 		ndi_devi_enter(dip, &circ);
5020Sstevel@tonic-gate 		dr_resume_devices(ddi_get_child(dip), srh);
5030Sstevel@tonic-gate 		ndi_devi_exit(dip, circ);
5040Sstevel@tonic-gate 		last = dip;
5050Sstevel@tonic-gate 	}
5060Sstevel@tonic-gate }
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate /*
5090Sstevel@tonic-gate  * True if thread is virtually stopped.  Similar to CPR_VSTOPPED
5100Sstevel@tonic-gate  * but from DR point of view.  These user threads are waiting in
5110Sstevel@tonic-gate  * the kernel.  Once they complete in the kernel, they will process
5120Sstevel@tonic-gate  * the stop signal and stop.
5130Sstevel@tonic-gate  */
5140Sstevel@tonic-gate #define	DR_VSTOPPED(t)			\
5150Sstevel@tonic-gate 	((t)->t_state == TS_SLEEP &&	\
5160Sstevel@tonic-gate 	(t)->t_wchan != NULL &&		\
5170Sstevel@tonic-gate 	(t)->t_astflag &&		\
5180Sstevel@tonic-gate 	((t)->t_proc_flag & TP_CHKPT))
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate /* ARGSUSED */
5210Sstevel@tonic-gate static int
5220Sstevel@tonic-gate dr_stop_user_threads(dr_sr_handle_t *srh)
5230Sstevel@tonic-gate {
5240Sstevel@tonic-gate 	int		count;
5250Sstevel@tonic-gate 	int		bailout;
5260Sstevel@tonic-gate 	dr_handle_t	*handle = srh->sr_dr_handlep;
5270Sstevel@tonic-gate 	static fn_t	f = "dr_stop_user_threads";
5280Sstevel@tonic-gate 	kthread_id_t 	tp;
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 	extern void add_one_utstop();
5310Sstevel@tonic-gate 	extern void utstop_timedwait(clock_t);
5320Sstevel@tonic-gate 	extern void utstop_init(void);
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate #define	DR_UTSTOP_RETRY	4
5350Sstevel@tonic-gate #define	DR_UTSTOP_WAIT	hz
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate 	if (dr_skip_user_threads)
5380Sstevel@tonic-gate 		return (DDI_SUCCESS);
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 	utstop_init();
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate 	/* we need to try a few times to get past fork, etc. */
5430Sstevel@tonic-gate 	srh->sr_err_idx = 0;
5440Sstevel@tonic-gate 	for (count = 0; count < DR_UTSTOP_RETRY; count++) {
5450Sstevel@tonic-gate 		/* walk the entire threadlist */
5460Sstevel@tonic-gate 		mutex_enter(&pidlock);
5470Sstevel@tonic-gate 		for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) {
5480Sstevel@tonic-gate 			proc_t *p = ttoproc(tp);
5490Sstevel@tonic-gate 
5500Sstevel@tonic-gate 			/* handle kernel threads separately */
5510Sstevel@tonic-gate 			if (p->p_as == &kas || p->p_stat == SZOMB)
5520Sstevel@tonic-gate 				continue;
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 			mutex_enter(&p->p_lock);
5550Sstevel@tonic-gate 			thread_lock(tp);
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 			if (tp->t_state == TS_STOPPED) {
5580Sstevel@tonic-gate 				/* add another reason to stop this thread */
5590Sstevel@tonic-gate 				tp->t_schedflag &= ~TS_RESUME;
5600Sstevel@tonic-gate 			} else {
5610Sstevel@tonic-gate 				tp->t_proc_flag |= TP_CHKPT;
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 				thread_unlock(tp);
5640Sstevel@tonic-gate 				mutex_exit(&p->p_lock);
5650Sstevel@tonic-gate 				add_one_utstop();
5660Sstevel@tonic-gate 				mutex_enter(&p->p_lock);
5670Sstevel@tonic-gate 				thread_lock(tp);
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 				aston(tp);
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 				if (tp->t_state == TS_SLEEP &&
5720Sstevel@tonic-gate 				    (tp->t_flag & T_WAKEABLE)) {
5730Sstevel@tonic-gate 					setrun_locked(tp);
5740Sstevel@tonic-gate 				}
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate 			}
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate 			/* grab thread if needed */
5790Sstevel@tonic-gate 			if (tp->t_state == TS_ONPROC && tp->t_cpu != CPU)
5800Sstevel@tonic-gate 				poke_cpu(tp->t_cpu->cpu_id);
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 			thread_unlock(tp);
5840Sstevel@tonic-gate 			mutex_exit(&p->p_lock);
5850Sstevel@tonic-gate 		}
5860Sstevel@tonic-gate 		mutex_exit(&pidlock);
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 		/* let everything catch up */
5900Sstevel@tonic-gate 		utstop_timedwait(count * count * DR_UTSTOP_WAIT);
5910Sstevel@tonic-gate 
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 		/* now, walk the threadlist again to see if we are done */
5940Sstevel@tonic-gate 		mutex_enter(&pidlock);
5950Sstevel@tonic-gate 		for (tp = curthread->t_next, bailout = 0;
5960Sstevel@tonic-gate 		    tp != curthread; tp = tp->t_next) {
5970Sstevel@tonic-gate 			proc_t *p = ttoproc(tp);
5980Sstevel@tonic-gate 
5990Sstevel@tonic-gate 			/* handle kernel threads separately */
6000Sstevel@tonic-gate 			if (p->p_as == &kas || p->p_stat == SZOMB)
6010Sstevel@tonic-gate 				continue;
6020Sstevel@tonic-gate 
6030Sstevel@tonic-gate 			/*
6040Sstevel@tonic-gate 			 * If this thread didn't stop, and we don't allow
6050Sstevel@tonic-gate 			 * unstopped blocked threads, bail.
6060Sstevel@tonic-gate 			 */
6070Sstevel@tonic-gate 			thread_lock(tp);
6080Sstevel@tonic-gate 			if (!CPR_ISTOPPED(tp) &&
6090Sstevel@tonic-gate 			    !(dr_allow_blocked_threads &&
6100Sstevel@tonic-gate 			    DR_VSTOPPED(tp))) {
6110Sstevel@tonic-gate 				bailout = 1;
6120Sstevel@tonic-gate 				if (count == DR_UTSTOP_RETRY - 1) {
6130Sstevel@tonic-gate 					/*
6140Sstevel@tonic-gate 					 * save the pid for later reporting
6150Sstevel@tonic-gate 					 */
6160Sstevel@tonic-gate 					srh->sr_err_idx =
6170Sstevel@tonic-gate 					    dr_add_int(srh->sr_err_ints,
6180Sstevel@tonic-gate 					    srh->sr_err_idx, DR_MAX_ERR_INT,
6190Sstevel@tonic-gate 					    (uint64_t)p->p_pid);
6200Sstevel@tonic-gate 
6210Sstevel@tonic-gate 					cmn_err(CE_WARN, "%s: "
6220Sstevel@tonic-gate 					    "failed to stop thread: "
6230Sstevel@tonic-gate 					    "process=%s, pid=%d",
6240Sstevel@tonic-gate 					    f, p->p_user.u_psargs, p->p_pid);
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 					PR_QR("%s: failed to stop thread: "
627*930Smathue 					    "process=%s, pid=%d, t_id=0x%p, "
6280Sstevel@tonic-gate 					    "t_state=0x%x, t_proc_flag=0x%x, "
6290Sstevel@tonic-gate 					    "t_schedflag=0x%x\n",
6300Sstevel@tonic-gate 					    f, p->p_user.u_psargs, p->p_pid,
6310Sstevel@tonic-gate 					    tp, tp->t_state, tp->t_proc_flag,
6320Sstevel@tonic-gate 					    tp->t_schedflag);
6330Sstevel@tonic-gate 				}
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 			}
6360Sstevel@tonic-gate 			thread_unlock(tp);
6370Sstevel@tonic-gate 		}
6380Sstevel@tonic-gate 		mutex_exit(&pidlock);
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 		/* were all the threads stopped? */
6410Sstevel@tonic-gate 		if (!bailout)
6420Sstevel@tonic-gate 			break;
6430Sstevel@tonic-gate 	}
6440Sstevel@tonic-gate 
6450Sstevel@tonic-gate 	/* were we unable to stop all threads after a few tries? */
6460Sstevel@tonic-gate 	if (bailout) {
6470Sstevel@tonic-gate 		handle->h_err = drerr_int(ESBD_UTHREAD, srh->sr_err_ints,
6480Sstevel@tonic-gate 			srh->sr_err_idx, 0);
6490Sstevel@tonic-gate 		return (ESRCH);
6500Sstevel@tonic-gate 	}
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate 	return (DDI_SUCCESS);
6530Sstevel@tonic-gate }
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate static void
6560Sstevel@tonic-gate dr_start_user_threads(void)
6570Sstevel@tonic-gate {
6580Sstevel@tonic-gate 	kthread_id_t tp;
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate 	mutex_enter(&pidlock);
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 	/* walk all threads and release them */
6630Sstevel@tonic-gate 	for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) {
6640Sstevel@tonic-gate 		proc_t *p = ttoproc(tp);
6650Sstevel@tonic-gate 
6660Sstevel@tonic-gate 		/* skip kernel threads */
6670Sstevel@tonic-gate 		if (ttoproc(tp)->p_as == &kas)
6680Sstevel@tonic-gate 			continue;
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 		mutex_enter(&p->p_lock);
6710Sstevel@tonic-gate 		tp->t_proc_flag &= ~TP_CHKPT;
6720Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate 		thread_lock(tp);
6750Sstevel@tonic-gate 		if (CPR_ISTOPPED(tp)) {
6760Sstevel@tonic-gate 			/* back on the runq */
6770Sstevel@tonic-gate 			tp->t_schedflag |= TS_RESUME;
6780Sstevel@tonic-gate 			setrun_locked(tp);
6790Sstevel@tonic-gate 		}
6800Sstevel@tonic-gate 		thread_unlock(tp);
6810Sstevel@tonic-gate 	}
6820Sstevel@tonic-gate 
6830Sstevel@tonic-gate 	mutex_exit(&pidlock);
6840Sstevel@tonic-gate }
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate static void
6870Sstevel@tonic-gate dr_signal_user(int sig)
6880Sstevel@tonic-gate {
6890Sstevel@tonic-gate 	struct proc *p;
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate 	mutex_enter(&pidlock);
6920Sstevel@tonic-gate 
6930Sstevel@tonic-gate 	for (p = practive; p != NULL; p = p->p_next) {
6940Sstevel@tonic-gate 		/* only user threads */
6950Sstevel@tonic-gate 		if (p->p_exec == NULL || p->p_stat == SZOMB ||
6960Sstevel@tonic-gate 		    p == proc_init || p == ttoproc(curthread))
6970Sstevel@tonic-gate 			continue;
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate 		mutex_enter(&p->p_lock);
7000Sstevel@tonic-gate 		sigtoproc(p, NULL, sig);
7010Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
7020Sstevel@tonic-gate 	}
7030Sstevel@tonic-gate 
7040Sstevel@tonic-gate 	mutex_exit(&pidlock);
7050Sstevel@tonic-gate 
7060Sstevel@tonic-gate 	/* add a bit of delay */
7070Sstevel@tonic-gate 	delay(hz);
7080Sstevel@tonic-gate }
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate void
7110Sstevel@tonic-gate dr_resume(dr_sr_handle_t *srh)
7120Sstevel@tonic-gate {
7130Sstevel@tonic-gate 	dr_handle_t	*handle;
7140Sstevel@tonic-gate 
7150Sstevel@tonic-gate 	handle = srh->sr_dr_handlep;
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate 	if (srh->sr_suspend_state < DR_SRSTATE_FULL) {
7180Sstevel@tonic-gate 		/*
7190Sstevel@tonic-gate 		 * Update the signature block.
7200Sstevel@tonic-gate 		 * If cpus are not paused, this can be done now.
7210Sstevel@tonic-gate 		 * See comments below.
7220Sstevel@tonic-gate 		 */
7230Sstevel@tonic-gate 		CPU_SIGNATURE(OS_SIG, SIGST_RESUME_INPROGRESS, SIGSUBST_NULL,
7240Sstevel@tonic-gate 		    CPU->cpu_id);
7250Sstevel@tonic-gate 	}
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 	switch (srh->sr_suspend_state) {
7280Sstevel@tonic-gate 	case DR_SRSTATE_FULL:
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate 		ASSERT(MUTEX_HELD(&cpu_lock));
7310Sstevel@tonic-gate 
7320Sstevel@tonic-gate 		dr_enable_intr(); 	/* enable intr & clock */
7330Sstevel@tonic-gate 
7340Sstevel@tonic-gate 		start_cpus();
7350Sstevel@tonic-gate 		mutex_exit(&cpu_lock);
7360Sstevel@tonic-gate 
7370Sstevel@tonic-gate 		/*
7380Sstevel@tonic-gate 		 * Update the signature block.
7390Sstevel@tonic-gate 		 * This must not be done while cpus are paused, since on
7400Sstevel@tonic-gate 		 * Starcat the cpu signature update aquires an adaptive
7410Sstevel@tonic-gate 		 * mutex in the iosram driver. Blocking with cpus paused
7420Sstevel@tonic-gate 		 * can lead to deadlock.
7430Sstevel@tonic-gate 		 */
7440Sstevel@tonic-gate 		CPU_SIGNATURE(OS_SIG, SIGST_RESUME_INPROGRESS, SIGSUBST_NULL,
7450Sstevel@tonic-gate 		    CPU->cpu_id);
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate 		/*
7480Sstevel@tonic-gate 		 * If we suspended hw watchdog at suspend,
7490Sstevel@tonic-gate 		 * re-enable it now.
7500Sstevel@tonic-gate 		 */
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate 		if (srh->sr_flags & (SR_FLAG_WATCHDOG)) {
7530Sstevel@tonic-gate 			mutex_enter(&tod_lock);
7540Sstevel@tonic-gate 			tod_ops.tod_set_watchdog_timer(
7550Sstevel@tonic-gate 				watchdog_timeout_seconds);
7560Sstevel@tonic-gate 			mutex_exit(&tod_lock);
7570Sstevel@tonic-gate 		}
7580Sstevel@tonic-gate 
7590Sstevel@tonic-gate 		/*
7600Sstevel@tonic-gate 		 * This should only be called if drmach_suspend_last()
7610Sstevel@tonic-gate 		 * was called and state transitioned to DR_SRSTATE_FULL
7620Sstevel@tonic-gate 		 * to prevent resume attempts on device instances that
7630Sstevel@tonic-gate 		 * were not previously suspended.
7640Sstevel@tonic-gate 		 */
7650Sstevel@tonic-gate 		drmach_resume_first();
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate 		/* FALLTHROUGH */
7680Sstevel@tonic-gate 
7690Sstevel@tonic-gate 	case DR_SRSTATE_DRIVER:
7700Sstevel@tonic-gate 		/*
7710Sstevel@tonic-gate 		 * resume drivers
7720Sstevel@tonic-gate 		 */
7730Sstevel@tonic-gate 		srh->sr_err_idx = 0;
7740Sstevel@tonic-gate 
7750Sstevel@tonic-gate 		/* no parent dip to hold busy */
7760Sstevel@tonic-gate 		dr_resume_devices(ddi_root_node(), srh);
7770Sstevel@tonic-gate 
7780Sstevel@tonic-gate 		if (srh->sr_err_idx && srh->sr_dr_handlep) {
7790Sstevel@tonic-gate 			(srh->sr_dr_handlep)->h_err = drerr_int(ESBD_RESUME,
7800Sstevel@tonic-gate 				srh->sr_err_ints, srh->sr_err_idx, 1);
7810Sstevel@tonic-gate 		}
7820Sstevel@tonic-gate 
7830Sstevel@tonic-gate 		/*
7840Sstevel@tonic-gate 		 * resume the lock manager
7850Sstevel@tonic-gate 		 */
7860Sstevel@tonic-gate 		lm_cprresume();
7870Sstevel@tonic-gate 
7880Sstevel@tonic-gate 		/* FALLTHROUGH */
7890Sstevel@tonic-gate 
7900Sstevel@tonic-gate 	case DR_SRSTATE_USER:
7910Sstevel@tonic-gate 		/*
7920Sstevel@tonic-gate 		 * finally, resume user threads
7930Sstevel@tonic-gate 		 */
7940Sstevel@tonic-gate 		if (!dr_skip_user_threads) {
7950Sstevel@tonic-gate 			prom_printf("DR: resuming user threads...\n");
7960Sstevel@tonic-gate 			dr_start_user_threads();
7970Sstevel@tonic-gate 		}
7980Sstevel@tonic-gate 		/* FALLTHROUGH */
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate 	case DR_SRSTATE_BEGIN:
8010Sstevel@tonic-gate 	default:
8020Sstevel@tonic-gate 		/*
8030Sstevel@tonic-gate 		 * let those who care know that we've just resumed
8040Sstevel@tonic-gate 		 */
8050Sstevel@tonic-gate 		PR_QR("sending SIGTHAW...\n");
8060Sstevel@tonic-gate 		dr_signal_user(SIGTHAW);
8070Sstevel@tonic-gate 		break;
8080Sstevel@tonic-gate 	}
8090Sstevel@tonic-gate 
8100Sstevel@tonic-gate 	i_ndi_allow_device_tree_changes(handle->h_ndi);
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate 	/*
8130Sstevel@tonic-gate 	 * update the signature block
8140Sstevel@tonic-gate 	 */
8150Sstevel@tonic-gate 	CPU_SIGNATURE(OS_SIG, SIGST_RUN, SIGSUBST_NULL, CPU->cpu_id);
8160Sstevel@tonic-gate 
8170Sstevel@tonic-gate 	prom_printf("DR: resume COMPLETED\n");
8180Sstevel@tonic-gate }
8190Sstevel@tonic-gate 
8200Sstevel@tonic-gate int
8210Sstevel@tonic-gate dr_suspend(dr_sr_handle_t *srh)
8220Sstevel@tonic-gate {
8230Sstevel@tonic-gate 	dr_handle_t	*handle;
8240Sstevel@tonic-gate 	int		force;
8250Sstevel@tonic-gate 	int		dev_errs_idx;
8260Sstevel@tonic-gate 	uint64_t	dev_errs[DR_MAX_ERR_INT];
8270Sstevel@tonic-gate 	int		rc = DDI_SUCCESS;
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate 	handle = srh->sr_dr_handlep;
8300Sstevel@tonic-gate 
8310Sstevel@tonic-gate 	force = dr_cmd_flags(handle) & SBD_FLAG_FORCE;
8320Sstevel@tonic-gate 
8330Sstevel@tonic-gate 	/*
8340Sstevel@tonic-gate 	 * update the signature block
8350Sstevel@tonic-gate 	 */
8360Sstevel@tonic-gate 	CPU_SIGNATURE(OS_SIG, SIGST_QUIESCE_INPROGRESS, SIGSUBST_NULL,
8370Sstevel@tonic-gate 	    CPU->cpu_id);
8380Sstevel@tonic-gate 
8390Sstevel@tonic-gate 	i_ndi_block_device_tree_changes(&handle->h_ndi);
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate 	prom_printf("\nDR: suspending user threads...\n");
8420Sstevel@tonic-gate 	srh->sr_suspend_state = DR_SRSTATE_USER;
8430Sstevel@tonic-gate 	if (((rc = dr_stop_user_threads(srh)) != DDI_SUCCESS) &&
8440Sstevel@tonic-gate 	    dr_check_user_stop_result) {
8450Sstevel@tonic-gate 		dr_resume(srh);
8460Sstevel@tonic-gate 		return (rc);
8470Sstevel@tonic-gate 	}
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 	if (!force) {
8500Sstevel@tonic-gate 		struct dr_ref drc = {0};
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate 		prom_printf("\nDR: checking devices...\n");
8530Sstevel@tonic-gate 		dev_errs_idx = 0;
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 		drc.arr = dev_errs;
8560Sstevel@tonic-gate 		drc.idx = &dev_errs_idx;
8570Sstevel@tonic-gate 		drc.len = DR_MAX_ERR_INT;
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate 		/*
8600Sstevel@tonic-gate 		 * Since the root node can never go away, it
8610Sstevel@tonic-gate 		 * doesn't have to be held.
8620Sstevel@tonic-gate 		 */
8630Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), dr_check_unsafe_major, &drc);
8640Sstevel@tonic-gate 		if (dev_errs_idx) {
8650Sstevel@tonic-gate 			handle->h_err = drerr_int(ESBD_UNSAFE, dev_errs,
8660Sstevel@tonic-gate 				dev_errs_idx, 1);
8670Sstevel@tonic-gate 			dr_resume(srh);
8680Sstevel@tonic-gate 			return (DDI_FAILURE);
8690Sstevel@tonic-gate 		}
8700Sstevel@tonic-gate 		PR_QR("done\n");
8710Sstevel@tonic-gate 	} else {
8720Sstevel@tonic-gate 		prom_printf("\nDR: dr_suspend invoked with force flag\n");
8730Sstevel@tonic-gate 	}
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate #ifndef	SKIP_SYNC
8760Sstevel@tonic-gate 	/*
8770Sstevel@tonic-gate 	 * This sync swap out all user pages
8780Sstevel@tonic-gate 	 */
8790Sstevel@tonic-gate 	vfs_sync(SYNC_ALL);
8800Sstevel@tonic-gate #endif
8810Sstevel@tonic-gate 
8820Sstevel@tonic-gate 	/*
8830Sstevel@tonic-gate 	 * special treatment for lock manager
8840Sstevel@tonic-gate 	 */
8850Sstevel@tonic-gate 	lm_cprsuspend();
8860Sstevel@tonic-gate 
8870Sstevel@tonic-gate #ifndef	SKIP_SYNC
8880Sstevel@tonic-gate 	/*
8890Sstevel@tonic-gate 	 * sync the file system in case we never make it back
8900Sstevel@tonic-gate 	 */
8910Sstevel@tonic-gate 	sync();
8920Sstevel@tonic-gate #endif
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate 	/*
8950Sstevel@tonic-gate 	 * now suspend drivers
8960Sstevel@tonic-gate 	 */
8970Sstevel@tonic-gate 	prom_printf("DR: suspending drivers...\n");
8980Sstevel@tonic-gate 	srh->sr_suspend_state = DR_SRSTATE_DRIVER;
8990Sstevel@tonic-gate 	srh->sr_err_idx = 0;
9000Sstevel@tonic-gate 	/* No parent to hold busy */
9010Sstevel@tonic-gate 	if ((rc = dr_suspend_devices(ddi_root_node(), srh)) != DDI_SUCCESS) {
9020Sstevel@tonic-gate 		if (srh->sr_err_idx && srh->sr_dr_handlep) {
9030Sstevel@tonic-gate 			(srh->sr_dr_handlep)->h_err = drerr_int(ESBD_SUSPEND,
9040Sstevel@tonic-gate 				srh->sr_err_ints, srh->sr_err_idx, 1);
9050Sstevel@tonic-gate 		}
9060Sstevel@tonic-gate 		dr_resume(srh);
9070Sstevel@tonic-gate 		return (rc);
9080Sstevel@tonic-gate 	}
9090Sstevel@tonic-gate 
9100Sstevel@tonic-gate 	drmach_suspend_last();
9110Sstevel@tonic-gate 
9120Sstevel@tonic-gate 	/*
9130Sstevel@tonic-gate 	 * finally, grab all cpus
9140Sstevel@tonic-gate 	 */
9150Sstevel@tonic-gate 	srh->sr_suspend_state = DR_SRSTATE_FULL;
9160Sstevel@tonic-gate 
9170Sstevel@tonic-gate 	/*
9180Sstevel@tonic-gate 	 * if watchdog was activated, disable it
9190Sstevel@tonic-gate 	 */
9200Sstevel@tonic-gate 	if (watchdog_activated) {
9210Sstevel@tonic-gate 		mutex_enter(&tod_lock);
9220Sstevel@tonic-gate 		tod_ops.tod_clear_watchdog_timer();
9230Sstevel@tonic-gate 		mutex_exit(&tod_lock);
9240Sstevel@tonic-gate 		srh->sr_flags |= SR_FLAG_WATCHDOG;
9250Sstevel@tonic-gate 	} else {
9260Sstevel@tonic-gate 		srh->sr_flags &= ~(SR_FLAG_WATCHDOG);
9270Sstevel@tonic-gate 	}
9280Sstevel@tonic-gate 
9290Sstevel@tonic-gate 	/*
9300Sstevel@tonic-gate 	 * Update the signature block.
9310Sstevel@tonic-gate 	 * This must be done before cpus are paused, since on Starcat the
9320Sstevel@tonic-gate 	 * cpu signature update aquires an adaptive mutex in the iosram driver.
9330Sstevel@tonic-gate 	 * Blocking with cpus paused can lead to deadlock.
9340Sstevel@tonic-gate 	 */
9350Sstevel@tonic-gate 	CPU_SIGNATURE(OS_SIG, SIGST_QUIESCED, SIGSUBST_NULL, CPU->cpu_id);
9360Sstevel@tonic-gate 
9370Sstevel@tonic-gate 	mutex_enter(&cpu_lock);
9380Sstevel@tonic-gate 	pause_cpus(NULL);
9390Sstevel@tonic-gate 	dr_stop_intr();
9400Sstevel@tonic-gate 
9410Sstevel@tonic-gate 	return (rc);
9420Sstevel@tonic-gate }
9430Sstevel@tonic-gate 
9440Sstevel@tonic-gate int
9450Sstevel@tonic-gate dr_pt_test_suspend(dr_handle_t *hp)
9460Sstevel@tonic-gate {
9470Sstevel@tonic-gate 	dr_sr_handle_t *srh;
9480Sstevel@tonic-gate 	int		err;
9490Sstevel@tonic-gate 	uint_t		psmerr;
9500Sstevel@tonic-gate 	static fn_t	f = "dr_pt_test_suspend";
9510Sstevel@tonic-gate 
9520Sstevel@tonic-gate 	PR_QR("%s...\n", f);
9530Sstevel@tonic-gate 
9540Sstevel@tonic-gate 	srh = dr_get_sr_handle(hp);
9550Sstevel@tonic-gate 	if ((err = dr_suspend(srh)) == DDI_SUCCESS) {
9560Sstevel@tonic-gate 		dr_resume(srh);
9570Sstevel@tonic-gate 		if ((hp->h_err) && ((psmerr = hp->h_err->e_code) != 0)) {
9580Sstevel@tonic-gate 			PR_QR("%s: error on dr_resume()", f);
9590Sstevel@tonic-gate 			switch (psmerr) {
9600Sstevel@tonic-gate 			case ESBD_RESUME:
9610Sstevel@tonic-gate 				PR_QR("Couldn't resume devices: %s\n",
9620Sstevel@tonic-gate 					DR_GET_E_RSC(hp->h_err));
9630Sstevel@tonic-gate 				break;
9640Sstevel@tonic-gate 
9650Sstevel@tonic-gate 			case ESBD_KTHREAD:
9660Sstevel@tonic-gate 				PR_ALL("psmerr is ESBD_KTHREAD\n");
9670Sstevel@tonic-gate 				break;
9680Sstevel@tonic-gate 			default:
9690Sstevel@tonic-gate 				PR_ALL("Resume error unknown = %d\n",
9700Sstevel@tonic-gate 					psmerr);
9710Sstevel@tonic-gate 				break;
9720Sstevel@tonic-gate 			}
9730Sstevel@tonic-gate 		}
9740Sstevel@tonic-gate 	} else {
9750Sstevel@tonic-gate 		PR_ALL("%s: dr_suspend() failed, err = 0x%x\n",
9760Sstevel@tonic-gate 			f, err);
9770Sstevel@tonic-gate 		psmerr = hp->h_err ? hp->h_err->e_code : ESBD_NOERROR;
9780Sstevel@tonic-gate 		switch (psmerr) {
9790Sstevel@tonic-gate 		case ESBD_UNSAFE:
9800Sstevel@tonic-gate 			PR_ALL("Unsafe devices (major #): %s\n",
9810Sstevel@tonic-gate 				DR_GET_E_RSC(hp->h_err));
9820Sstevel@tonic-gate 			break;
9830Sstevel@tonic-gate 
9840Sstevel@tonic-gate 		case ESBD_RTTHREAD:
9850Sstevel@tonic-gate 			PR_ALL("RT threads (PIDs): %s\n",
9860Sstevel@tonic-gate 				DR_GET_E_RSC(hp->h_err));
9870Sstevel@tonic-gate 			break;
9880Sstevel@tonic-gate 
9890Sstevel@tonic-gate 		case ESBD_UTHREAD:
9900Sstevel@tonic-gate 			PR_ALL("User threads (PIDs): %s\n",
9910Sstevel@tonic-gate 				DR_GET_E_RSC(hp->h_err));
9920Sstevel@tonic-gate 			break;
9930Sstevel@tonic-gate 
9940Sstevel@tonic-gate 		case ESBD_SUSPEND:
9950Sstevel@tonic-gate 			PR_ALL("Non-suspendable devices (major #): %s\n",
9960Sstevel@tonic-gate 				DR_GET_E_RSC(hp->h_err));
9970Sstevel@tonic-gate 			break;
9980Sstevel@tonic-gate 
9990Sstevel@tonic-gate 		case ESBD_RESUME:
10000Sstevel@tonic-gate 			PR_ALL("Could not resume devices (major #): %s\n",
10010Sstevel@tonic-gate 				DR_GET_E_RSC(hp->h_err));
10020Sstevel@tonic-gate 			break;
10030Sstevel@tonic-gate 
10040Sstevel@tonic-gate 		case ESBD_KTHREAD:
10050Sstevel@tonic-gate 			PR_ALL("psmerr is ESBD_KTHREAD\n");
10060Sstevel@tonic-gate 			break;
10070Sstevel@tonic-gate 
10080Sstevel@tonic-gate 		case ESBD_NOERROR:
10090Sstevel@tonic-gate 			PR_ALL("sbd_error_t error code not set\n");
10100Sstevel@tonic-gate 			break;
10110Sstevel@tonic-gate 
10120Sstevel@tonic-gate 		default:
10130Sstevel@tonic-gate 			PR_ALL("Unknown error psmerr = %d\n", psmerr);
10140Sstevel@tonic-gate 			break;
10150Sstevel@tonic-gate 		}
10160Sstevel@tonic-gate 	}
10170Sstevel@tonic-gate 	dr_release_sr_handle(srh);
10180Sstevel@tonic-gate 
10190Sstevel@tonic-gate 	return (0);
10200Sstevel@tonic-gate }
10210Sstevel@tonic-gate 
10220Sstevel@tonic-gate /*
10230Sstevel@tonic-gate  * Add a new integer value to the end of an array.  Don't allow duplicates to
10240Sstevel@tonic-gate  * appear in the array, and don't allow the array to overflow.  Return the new
10250Sstevel@tonic-gate  * total number of entries in the array.
10260Sstevel@tonic-gate  */
10270Sstevel@tonic-gate static int
10280Sstevel@tonic-gate dr_add_int(uint64_t *arr, int idx, int len, uint64_t val)
10290Sstevel@tonic-gate {
10300Sstevel@tonic-gate 	int i;
10310Sstevel@tonic-gate 
10320Sstevel@tonic-gate 	if (arr == NULL)
10330Sstevel@tonic-gate 		return (0);
10340Sstevel@tonic-gate 
10350Sstevel@tonic-gate 	if (idx >= len)
10360Sstevel@tonic-gate 		return (idx);
10370Sstevel@tonic-gate 
10380Sstevel@tonic-gate 	for (i = 0; i < idx; i++) {
10390Sstevel@tonic-gate 		if (arr[i] == val)
10400Sstevel@tonic-gate 			return (idx);
10410Sstevel@tonic-gate 	}
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate 	arr[idx++] = val;
10440Sstevel@tonic-gate 
10450Sstevel@tonic-gate 	return (idx);
10460Sstevel@tonic-gate }
10470Sstevel@tonic-gate 
10480Sstevel@tonic-gate /*
10490Sstevel@tonic-gate  * Construct an sbd_error_t featuring a string representation of an array of
10500Sstevel@tonic-gate  * integers as its e_rsc.
10510Sstevel@tonic-gate  */
10520Sstevel@tonic-gate static sbd_error_t *
10530Sstevel@tonic-gate drerr_int(int e_code, uint64_t *arr, int idx, int majors)
10540Sstevel@tonic-gate {
10550Sstevel@tonic-gate 	int		i, n, buf_len, buf_idx, buf_avail;
10560Sstevel@tonic-gate 	char		*dname;
10570Sstevel@tonic-gate 	char		*buf;
10580Sstevel@tonic-gate 	sbd_error_t	*new_sbd_err;
10590Sstevel@tonic-gate 	static char	s_ellipsis[] = "...";
10600Sstevel@tonic-gate 
10610Sstevel@tonic-gate 	if (arr == NULL || idx <= 0)
10620Sstevel@tonic-gate 		return (NULL);
10630Sstevel@tonic-gate 
10640Sstevel@tonic-gate 	/* MAXPATHLEN is the size of the e_rsc field in sbd_error_t. */
10650Sstevel@tonic-gate 	buf = (char *)kmem_zalloc(MAXPATHLEN, KM_SLEEP);
10660Sstevel@tonic-gate 
10670Sstevel@tonic-gate 	/*
10680Sstevel@tonic-gate 	 * This is the total working area of the buffer.  It must be computed
10690Sstevel@tonic-gate 	 * as the size of 'buf', minus reserved space for the null terminator
10700Sstevel@tonic-gate 	 * and the ellipsis string.
10710Sstevel@tonic-gate 	 */
10720Sstevel@tonic-gate 	buf_len = MAXPATHLEN - (strlen(s_ellipsis) + 1);
10730Sstevel@tonic-gate 
10740Sstevel@tonic-gate 	/* Construct a string representation of the array values */
10750Sstevel@tonic-gate 	for (buf_idx = 0, i = 0; i < idx; i++) {
10760Sstevel@tonic-gate 		buf_avail = buf_len - buf_idx;
10770Sstevel@tonic-gate 		if (majors) {
10780Sstevel@tonic-gate 			dname = ddi_major_to_name(arr[i]);
10790Sstevel@tonic-gate 			if (dname) {
10800Sstevel@tonic-gate 				n = snprintf(&buf[buf_idx], buf_avail,
10810Sstevel@tonic-gate 					"%s, ", dname);
10820Sstevel@tonic-gate 			} else {
10830Sstevel@tonic-gate 				n = snprintf(&buf[buf_idx], buf_avail,
1084*930Smathue 					"major %lu, ", arr[i]);
10850Sstevel@tonic-gate 			}
10860Sstevel@tonic-gate 		} else {
1087*930Smathue 			n = snprintf(&buf[buf_idx], buf_avail, "%lu, ",
10880Sstevel@tonic-gate 				arr[i]);
10890Sstevel@tonic-gate 		}
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 		/* An ellipsis gets appended when no more values fit */
10920Sstevel@tonic-gate 		if (n >= buf_avail) {
10930Sstevel@tonic-gate 			(void) strcpy(&buf[buf_idx], s_ellipsis);
10940Sstevel@tonic-gate 			break;
10950Sstevel@tonic-gate 		}
10960Sstevel@tonic-gate 
10970Sstevel@tonic-gate 		buf_idx += n;
10980Sstevel@tonic-gate 	}
10990Sstevel@tonic-gate 
11000Sstevel@tonic-gate 	/* If all the contents fit, remove the trailing comma */
11010Sstevel@tonic-gate 	if (n < buf_avail) {
11020Sstevel@tonic-gate 		buf[--buf_idx] = '\0';
11030Sstevel@tonic-gate 		buf[--buf_idx] = '\0';
11040Sstevel@tonic-gate 	}
11050Sstevel@tonic-gate 
11060Sstevel@tonic-gate 	/* Return an sbd_error_t with the buffer and e_code */
11070Sstevel@tonic-gate 	new_sbd_err = drerr_new(1, e_code, buf);
11080Sstevel@tonic-gate 	kmem_free(buf, MAXPATHLEN);
11090Sstevel@tonic-gate 	return (new_sbd_err);
11100Sstevel@tonic-gate }
1111