xref: /titanic_44/usr/src/uts/sun4u/ngdr/io/dr_quiesce.c (revision 0ed5c46e82c989cfa9726d9dae452e3d24ef83be)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5646e55b6Scth  * Common Development and Distribution License (the "License").
6646e55b6Scth  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
2107d06da5SSurya Prakki 
227c478bd9Sstevel@tonic-gate /*
23bec42f4bSMary Beale  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * A CPR derivative specifically for starfire/starcat
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #include <sys/types.h>
317c478bd9Sstevel@tonic-gate #include <sys/systm.h>
327c478bd9Sstevel@tonic-gate #include <sys/machparam.h>
337c478bd9Sstevel@tonic-gate #include <sys/machsystm.h>
347c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
357c478bd9Sstevel@tonic-gate #define	SUNDDI_IMPL
367c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
377c478bd9Sstevel@tonic-gate #include <sys/sunndi.h>
387c478bd9Sstevel@tonic-gate #include <sys/devctl.h>
397c478bd9Sstevel@tonic-gate #include <sys/time.h>
407c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
417c478bd9Sstevel@tonic-gate #include <nfs/lm.h>
427c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
437c478bd9Sstevel@tonic-gate #include <sys/ndi_impldefs.h>
447c478bd9Sstevel@tonic-gate #include <sys/obpdefs.h>
457c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
467c478bd9Sstevel@tonic-gate #include <sys/debug.h>
477c478bd9Sstevel@tonic-gate #include <sys/errno.h>
487c478bd9Sstevel@tonic-gate #include <sys/callb.h>
497c478bd9Sstevel@tonic-gate #include <sys/clock.h>
507c478bd9Sstevel@tonic-gate #include <sys/x_call.h>
517c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
527c478bd9Sstevel@tonic-gate #include <sys/epm.h>
537c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate #include <sys/cpu_sgnblk_defs.h>
567c478bd9Sstevel@tonic-gate #include <sys/dr.h>
577c478bd9Sstevel@tonic-gate #include <sys/dr_util.h>
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate #include <sys/promif.h>
607c478bd9Sstevel@tonic-gate #include <sys/conf.h>
617c478bd9Sstevel@tonic-gate #include <sys/cyclic.h>
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate extern void	e_ddi_enter_driver_list(struct devnames *dnp, int *listcnt);
647c478bd9Sstevel@tonic-gate extern void	e_ddi_exit_driver_list(struct devnames *dnp, int listcnt);
657c478bd9Sstevel@tonic-gate extern int	is_pseudo_device(dev_info_t *dip);
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate extern kmutex_t	cpu_lock;
687c478bd9Sstevel@tonic-gate extern dr_unsafe_devs_t dr_unsafe_devs;
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate static int		dr_is_real_device(dev_info_t *dip);
717c478bd9Sstevel@tonic-gate static int		dr_is_unsafe_major(major_t major);
727c478bd9Sstevel@tonic-gate static int		dr_bypass_device(char *dname);
737c478bd9Sstevel@tonic-gate static int		dr_check_dip(dev_info_t *dip, void *arg, uint_t ref);
747c478bd9Sstevel@tonic-gate static int		dr_resolve_devname(dev_info_t *dip, char *buffer,
757c478bd9Sstevel@tonic-gate 				char *alias);
767c478bd9Sstevel@tonic-gate static sbd_error_t	*drerr_int(int e_code, uint64_t *arr, int idx,
777c478bd9Sstevel@tonic-gate 				int majors);
787c478bd9Sstevel@tonic-gate static int		dr_add_int(uint64_t *arr, int idx, int len,
797c478bd9Sstevel@tonic-gate 				uint64_t val);
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate int dr_pt_test_suspend(dr_handle_t *hp);
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate /*
847c478bd9Sstevel@tonic-gate  * dr_quiesce.c interface
857c478bd9Sstevel@tonic-gate  * NOTE: states used internally by dr_suspend and dr_resume
867c478bd9Sstevel@tonic-gate  */
877c478bd9Sstevel@tonic-gate typedef enum dr_suspend_state {
887c478bd9Sstevel@tonic-gate 	DR_SRSTATE_BEGIN = 0,
897c478bd9Sstevel@tonic-gate 	DR_SRSTATE_USER,
907c478bd9Sstevel@tonic-gate 	DR_SRSTATE_DRIVER,
917c478bd9Sstevel@tonic-gate 	DR_SRSTATE_FULL
927c478bd9Sstevel@tonic-gate } suspend_state_t;
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate struct dr_sr_handle {
957c478bd9Sstevel@tonic-gate 	dr_handle_t		*sr_dr_handlep;
967c478bd9Sstevel@tonic-gate 	dev_info_t		*sr_failed_dip;
977c478bd9Sstevel@tonic-gate 	suspend_state_t		sr_suspend_state;
987c478bd9Sstevel@tonic-gate 	uint_t			sr_flags;
997c478bd9Sstevel@tonic-gate 	uint64_t		sr_err_ints[DR_MAX_ERR_INT];
1007c478bd9Sstevel@tonic-gate 	int			sr_err_idx;
1017c478bd9Sstevel@tonic-gate };
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate #define	SR_FLAG_WATCHDOG	0x1
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate /*
1067c478bd9Sstevel@tonic-gate  * XXX
1077c478bd9Sstevel@tonic-gate  * This hack will go away before RTI.  Just for testing.
1087c478bd9Sstevel@tonic-gate  * List of drivers to bypass when performing a suspend.
1097c478bd9Sstevel@tonic-gate  */
1107c478bd9Sstevel@tonic-gate static char *dr_bypass_list[] = {
1117c478bd9Sstevel@tonic-gate 	""
1127c478bd9Sstevel@tonic-gate };
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate #define		SKIP_SYNC	/* bypass sync ops in dr_suspend */
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate /*
1187c478bd9Sstevel@tonic-gate  * dr_skip_user_threads is used to control if user threads should
1197c478bd9Sstevel@tonic-gate  * be suspended.  If dr_skip_user_threads is true, the rest of the
1207c478bd9Sstevel@tonic-gate  * flags are not used; if it is false, dr_check_user_stop_result
1217c478bd9Sstevel@tonic-gate  * will be used to control whether or not we need to check suspend
1227c478bd9Sstevel@tonic-gate  * result, and dr_allow_blocked_threads will be used to control
1237c478bd9Sstevel@tonic-gate  * whether or not we allow suspend to continue if there are blocked
1247c478bd9Sstevel@tonic-gate  * threads.  We allow all combinations of dr_check_user_stop_result
1257c478bd9Sstevel@tonic-gate  * and dr_allow_block_threads, even though it might not make much
1267c478bd9Sstevel@tonic-gate  * sense to not allow block threads when we don't even check stop
1277c478bd9Sstevel@tonic-gate  * result.
1287c478bd9Sstevel@tonic-gate  */
1297c478bd9Sstevel@tonic-gate static int	dr_skip_user_threads = 0;	/* default to FALSE */
1307c478bd9Sstevel@tonic-gate static int	dr_check_user_stop_result = 1;	/* default to TRUE */
1317c478bd9Sstevel@tonic-gate static int	dr_allow_blocked_threads = 1;	/* default to TRUE */
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate #define	DR_CPU_LOOP_MSEC	1000
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate static void
dr_stop_intr(void)1367c478bd9Sstevel@tonic-gate dr_stop_intr(void)
1377c478bd9Sstevel@tonic-gate {
1387c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&cpu_lock));
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 	kpreempt_disable();
1417c478bd9Sstevel@tonic-gate 	cyclic_suspend();
1427c478bd9Sstevel@tonic-gate }
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate static void
dr_enable_intr(void)1457c478bd9Sstevel@tonic-gate dr_enable_intr(void)
1467c478bd9Sstevel@tonic-gate {
1477c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&cpu_lock));
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate 	cyclic_resume();
1507c478bd9Sstevel@tonic-gate 	kpreempt_enable();
1517c478bd9Sstevel@tonic-gate }
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate dr_sr_handle_t *
dr_get_sr_handle(dr_handle_t * hp)1547c478bd9Sstevel@tonic-gate dr_get_sr_handle(dr_handle_t *hp)
1557c478bd9Sstevel@tonic-gate {
1567c478bd9Sstevel@tonic-gate 	dr_sr_handle_t *srh;
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate 	srh = GETSTRUCT(dr_sr_handle_t, 1);
1597c478bd9Sstevel@tonic-gate 	srh->sr_dr_handlep = hp;
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 	return (srh);
1627c478bd9Sstevel@tonic-gate }
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate void
dr_release_sr_handle(dr_sr_handle_t * srh)1657c478bd9Sstevel@tonic-gate dr_release_sr_handle(dr_sr_handle_t *srh)
1667c478bd9Sstevel@tonic-gate {
1677c478bd9Sstevel@tonic-gate 	ASSERT(srh->sr_failed_dip == NULL);
1687c478bd9Sstevel@tonic-gate 	FREESTRUCT(srh, dr_sr_handle_t, 1);
1697c478bd9Sstevel@tonic-gate }
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate static int
dr_is_real_device(dev_info_t * dip)1727c478bd9Sstevel@tonic-gate dr_is_real_device(dev_info_t *dip)
1737c478bd9Sstevel@tonic-gate {
1747c478bd9Sstevel@tonic-gate 	struct regspec *regbuf = NULL;
1757c478bd9Sstevel@tonic-gate 	int length = 0;
1767c478bd9Sstevel@tonic-gate 	int rc;
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate 	if (ddi_get_driver(dip) == NULL)
1797c478bd9Sstevel@tonic-gate 		return (0);
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_flags & (PMC_NEEDS_SR|PMC_PARENTAL_SR))
1827c478bd9Sstevel@tonic-gate 		return (1);
1837c478bd9Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_flags & PMC_NO_SR)
1847c478bd9Sstevel@tonic-gate 		return (0);
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 	/*
1877c478bd9Sstevel@tonic-gate 	 * now the general case
1887c478bd9Sstevel@tonic-gate 	 */
189a3282898Scth 	rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
1907c478bd9Sstevel@tonic-gate 	    (caddr_t)&regbuf, &length);
1917c478bd9Sstevel@tonic-gate 	ASSERT(rc != DDI_PROP_NO_MEMORY);
1927c478bd9Sstevel@tonic-gate 	if (rc != DDI_PROP_SUCCESS) {
1937c478bd9Sstevel@tonic-gate 		return (0);
1947c478bd9Sstevel@tonic-gate 	} else {
1957c478bd9Sstevel@tonic-gate 		if ((length > 0) && (regbuf != NULL))
1967c478bd9Sstevel@tonic-gate 			kmem_free(regbuf, length);
1977c478bd9Sstevel@tonic-gate 		return (1);
1987c478bd9Sstevel@tonic-gate 	}
1997c478bd9Sstevel@tonic-gate }
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate static int
dr_is_unsafe_major(major_t major)2027c478bd9Sstevel@tonic-gate dr_is_unsafe_major(major_t major)
2037c478bd9Sstevel@tonic-gate {
2047c478bd9Sstevel@tonic-gate 	char    *dname, **cpp;
2057c478bd9Sstevel@tonic-gate 	int	i, ndevs;
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	if ((dname = ddi_major_to_name(major)) == NULL) {
2087c478bd9Sstevel@tonic-gate 		PR_QR("dr_is_unsafe_major: invalid major # %d\n", major);
2097c478bd9Sstevel@tonic-gate 		return (0);
2107c478bd9Sstevel@tonic-gate 	}
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate 	ndevs = dr_unsafe_devs.ndevs;
2137c478bd9Sstevel@tonic-gate 	for (i = 0, cpp = dr_unsafe_devs.devnames; i < ndevs; i++) {
2147c478bd9Sstevel@tonic-gate 		if (strcmp(dname, *cpp++) == 0)
2157c478bd9Sstevel@tonic-gate 			return (1);
2167c478bd9Sstevel@tonic-gate 	}
2177c478bd9Sstevel@tonic-gate 	return (0);
2187c478bd9Sstevel@tonic-gate }
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate static int
dr_bypass_device(char * dname)2217c478bd9Sstevel@tonic-gate dr_bypass_device(char *dname)
2227c478bd9Sstevel@tonic-gate {
2237c478bd9Sstevel@tonic-gate 	int i;
2247c478bd9Sstevel@tonic-gate 	char **lname;
225bec42f4bSMary Beale 
226bec42f4bSMary Beale 	if (dname == NULL)
227bec42f4bSMary Beale 		return (0);
228bec42f4bSMary Beale 
2297c478bd9Sstevel@tonic-gate 	/* check the bypass list */
2307c478bd9Sstevel@tonic-gate 	for (i = 0, lname = &dr_bypass_list[i]; **lname != '\0'; lname++) {
2317c478bd9Sstevel@tonic-gate 		if (strcmp(dname, dr_bypass_list[i++]) == 0)
2327c478bd9Sstevel@tonic-gate 			return (1);
2337c478bd9Sstevel@tonic-gate 	}
2347c478bd9Sstevel@tonic-gate 	return (0);
2357c478bd9Sstevel@tonic-gate }
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate static int
dr_resolve_devname(dev_info_t * dip,char * buffer,char * alias)2387c478bd9Sstevel@tonic-gate dr_resolve_devname(dev_info_t *dip, char *buffer, char *alias)
2397c478bd9Sstevel@tonic-gate {
2407c478bd9Sstevel@tonic-gate 	major_t	devmajor;
2417c478bd9Sstevel@tonic-gate 	char	*aka, *name;
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 	*buffer = *alias = 0;
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 	if (dip == NULL)
2467c478bd9Sstevel@tonic-gate 		return (-1);
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 	if ((name = ddi_get_name(dip)) == NULL)
2497c478bd9Sstevel@tonic-gate 		name = "<null name>";
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 	aka = name;
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate 	if ((devmajor = ddi_name_to_major(aka)) != -1)
2547c478bd9Sstevel@tonic-gate 		aka = ddi_major_to_name(devmajor);
2557c478bd9Sstevel@tonic-gate 
25607d06da5SSurya Prakki 	(void) strcpy(buffer, name);
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	if (strcmp(name, aka))
25907d06da5SSurya Prakki 		(void) strcpy(alias, aka);
2607c478bd9Sstevel@tonic-gate 	else
2617c478bd9Sstevel@tonic-gate 		*alias = 0;
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate 	return (0);
2647c478bd9Sstevel@tonic-gate }
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate struct dr_ref {
2677c478bd9Sstevel@tonic-gate 	int		*refcount;
268df3cd224SVijay S Balakrishna 	int		*refcount_non_gldv3;
2697c478bd9Sstevel@tonic-gate 	uint64_t	*arr;
2707c478bd9Sstevel@tonic-gate 	int		*idx;
2717c478bd9Sstevel@tonic-gate 	int		len;
2727c478bd9Sstevel@tonic-gate };
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate /* ARGSUSED */
2757c478bd9Sstevel@tonic-gate static int
dr_check_dip(dev_info_t * dip,void * arg,uint_t ref)2767c478bd9Sstevel@tonic-gate dr_check_dip(dev_info_t *dip, void *arg, uint_t ref)
2777c478bd9Sstevel@tonic-gate {
2787c478bd9Sstevel@tonic-gate 	major_t		major;
2797c478bd9Sstevel@tonic-gate 	char		*dname;
2807c478bd9Sstevel@tonic-gate 	struct dr_ref	*rp = (struct dr_ref *)arg;
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 	if (dip == NULL)
2837c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 	if (!dr_is_real_device(dip))
2867c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate 	dname = ddi_binding_name(dip);
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 	if (dr_bypass_device(dname))
2917c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate 	if (dname && ((major = ddi_name_to_major(dname)) != (major_t)-1)) {
2947c478bd9Sstevel@tonic-gate 		if (ref && rp->refcount) {
2957c478bd9Sstevel@tonic-gate 			*rp->refcount += ref;
2968fc99e42STrevor Thompson 			PR_QR("\n  %s (major# %d) is referenced(%u)\n", dname,
2978fc99e42STrevor Thompson 			    major, ref);
2987c478bd9Sstevel@tonic-gate 		}
299df3cd224SVijay S Balakrishna 		if (ref && rp->refcount_non_gldv3) {
300df3cd224SVijay S Balakrishna 			if (NETWORK_PHYSDRV(major) && !GLDV3_DRV(major))
301df3cd224SVijay S Balakrishna 				*rp->refcount_non_gldv3 += ref;
302df3cd224SVijay S Balakrishna 		}
303737d277aScth 		if (dr_is_unsafe_major(major) && i_ddi_devi_attached(dip)) {
3048fc99e42STrevor Thompson 			PR_QR("\n  %s (major# %d) not hotpluggable\n", dname,
3058fc99e42STrevor Thompson 			    major);
3067c478bd9Sstevel@tonic-gate 			if (rp->arr != NULL && rp->idx != NULL)
3077c478bd9Sstevel@tonic-gate 				*rp->idx = dr_add_int(rp->arr, *rp->idx,
3087c478bd9Sstevel@tonic-gate 				    rp->len, (uint64_t)major);
3097c478bd9Sstevel@tonic-gate 		}
3107c478bd9Sstevel@tonic-gate 	}
3117c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
3127c478bd9Sstevel@tonic-gate }
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate static int
dr_check_unsafe_major(dev_info_t * dip,void * arg)3157c478bd9Sstevel@tonic-gate dr_check_unsafe_major(dev_info_t *dip, void *arg)
3167c478bd9Sstevel@tonic-gate {
3177c478bd9Sstevel@tonic-gate 	return (dr_check_dip(dip, arg, 0));
3187c478bd9Sstevel@tonic-gate }
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3227c478bd9Sstevel@tonic-gate void
dr_check_devices(dev_info_t * dip,int * refcount,dr_handle_t * handle,uint64_t * arr,int * idx,int len,int * refcount_non_gldv3)3237c478bd9Sstevel@tonic-gate dr_check_devices(dev_info_t *dip, int *refcount, dr_handle_t *handle,
324df3cd224SVijay S Balakrishna     uint64_t *arr, int *idx, int len, int *refcount_non_gldv3)
3257c478bd9Sstevel@tonic-gate {
3267c478bd9Sstevel@tonic-gate 	struct dr_ref bref = {0};
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	if (dip == NULL)
3297c478bd9Sstevel@tonic-gate 		return;
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 	bref.refcount = refcount;
332df3cd224SVijay S Balakrishna 	bref.refcount_non_gldv3 = refcount_non_gldv3;
3337c478bd9Sstevel@tonic-gate 	bref.arr = arr;
3347c478bd9Sstevel@tonic-gate 	bref.idx = idx;
3357c478bd9Sstevel@tonic-gate 	bref.len = len;
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate 	ASSERT(e_ddi_branch_held(dip));
3387c478bd9Sstevel@tonic-gate 	(void) e_ddi_branch_referenced(dip, dr_check_dip, &bref);
3397c478bd9Sstevel@tonic-gate }
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate /*
3427c478bd9Sstevel@tonic-gate  * The "dip" argument's parent (if it exists) must be held busy.
3437c478bd9Sstevel@tonic-gate  */
3447c478bd9Sstevel@tonic-gate static int
dr_suspend_devices(dev_info_t * dip,dr_sr_handle_t * srh)3457c478bd9Sstevel@tonic-gate dr_suspend_devices(dev_info_t *dip, dr_sr_handle_t *srh)
3467c478bd9Sstevel@tonic-gate {
3477c478bd9Sstevel@tonic-gate 	dr_handle_t	*handle;
3487c478bd9Sstevel@tonic-gate 	major_t		major;
3497c478bd9Sstevel@tonic-gate 	char		*dname;
3507c478bd9Sstevel@tonic-gate 	int		circ;
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate 	/*
3537c478bd9Sstevel@tonic-gate 	 * If dip is the root node, it has no siblings and it is
3547c478bd9Sstevel@tonic-gate 	 * always held. If dip is not the root node, dr_suspend_devices()
3557c478bd9Sstevel@tonic-gate 	 * will be invoked with the parent held busy.
3567c478bd9Sstevel@tonic-gate 	 */
3577c478bd9Sstevel@tonic-gate 	for (; dip != NULL; dip = ddi_get_next_sibling(dip)) {
3587c478bd9Sstevel@tonic-gate 		char	d_name[40], d_alias[40], *d_info;
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate 		ndi_devi_enter(dip, &circ);
3617c478bd9Sstevel@tonic-gate 		if (dr_suspend_devices(ddi_get_child(dip), srh)) {
3627c478bd9Sstevel@tonic-gate 			ndi_devi_exit(dip, circ);
3637c478bd9Sstevel@tonic-gate 			return (ENXIO);
3647c478bd9Sstevel@tonic-gate 		}
3657c478bd9Sstevel@tonic-gate 		ndi_devi_exit(dip, circ);
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 		if (!dr_is_real_device(dip))
3687c478bd9Sstevel@tonic-gate 			continue;
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate 		major = (major_t)-1;
3717c478bd9Sstevel@tonic-gate 		if ((dname = ddi_binding_name(dip)) != NULL)
3727c478bd9Sstevel@tonic-gate 			major = ddi_name_to_major(dname);
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate 		if (dr_bypass_device(dname)) {
3757c478bd9Sstevel@tonic-gate 			PR_QR(" bypassed suspend of %s (major# %d)\n", dname,
3767c478bd9Sstevel@tonic-gate 			    major);
3777c478bd9Sstevel@tonic-gate 			continue;
3787c478bd9Sstevel@tonic-gate 		}
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 		if (drmach_verify_sr(dip, 1)) {
3817c478bd9Sstevel@tonic-gate 			PR_QR(" bypassed suspend of %s (major# %d)\n", dname,
3827c478bd9Sstevel@tonic-gate 			    major);
3837c478bd9Sstevel@tonic-gate 			continue;
3847c478bd9Sstevel@tonic-gate 		}
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 		if ((d_info = ddi_get_name_addr(dip)) == NULL)
3877c478bd9Sstevel@tonic-gate 			d_info = "<null>";
3887c478bd9Sstevel@tonic-gate 
3897c478bd9Sstevel@tonic-gate 		d_name[0] = 0;
3907c478bd9Sstevel@tonic-gate 		if (dr_resolve_devname(dip, d_name, d_alias) == 0) {
3917c478bd9Sstevel@tonic-gate 			if (d_alias[0] != 0) {
3927c478bd9Sstevel@tonic-gate 				prom_printf("\tsuspending %s@%s (aka %s)\n",
3937c478bd9Sstevel@tonic-gate 				    d_name, d_info, d_alias);
3947c478bd9Sstevel@tonic-gate 			} else {
3958fc99e42STrevor Thompson 				prom_printf("\tsuspending %s@%s\n", d_name,
3968fc99e42STrevor Thompson 				    d_info);
3977c478bd9Sstevel@tonic-gate 			}
3987c478bd9Sstevel@tonic-gate 		} else {
3997c478bd9Sstevel@tonic-gate 			prom_printf("\tsuspending %s@%s\n", dname, d_info);
4007c478bd9Sstevel@tonic-gate 		}
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 		if (devi_detach(dip, DDI_SUSPEND) != DDI_SUCCESS) {
4037c478bd9Sstevel@tonic-gate 			prom_printf("\tFAILED to suspend %s@%s\n",
4047c478bd9Sstevel@tonic-gate 			    d_name[0] ? d_name : dname, d_info);
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 			srh->sr_err_idx = dr_add_int(srh->sr_err_ints,
407df3cd224SVijay S Balakrishna 			    srh->sr_err_idx, DR_MAX_ERR_INT, (uint64_t)major);
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 			ndi_hold_devi(dip);
4107c478bd9Sstevel@tonic-gate 			srh->sr_failed_dip = dip;
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 			handle = srh->sr_dr_handlep;
4137c478bd9Sstevel@tonic-gate 			dr_op_err(CE_IGNORE, handle, ESBD_SUSPEND, "%s@%s",
4147c478bd9Sstevel@tonic-gate 			    d_name[0] ? d_name : dname, d_info);
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
4177c478bd9Sstevel@tonic-gate 		}
4187c478bd9Sstevel@tonic-gate 	}
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
4217c478bd9Sstevel@tonic-gate }
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate static void
dr_resume_devices(dev_info_t * start,dr_sr_handle_t * srh)4247c478bd9Sstevel@tonic-gate dr_resume_devices(dev_info_t *start, dr_sr_handle_t *srh)
4257c478bd9Sstevel@tonic-gate {
4267c478bd9Sstevel@tonic-gate 	dr_handle_t	*handle;
4277c478bd9Sstevel@tonic-gate 	dev_info_t	*dip, *next, *last = NULL;
4287c478bd9Sstevel@tonic-gate 	major_t		major;
4297c478bd9Sstevel@tonic-gate 	char		*bn;
4307c478bd9Sstevel@tonic-gate 	int		circ;
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 	major = (major_t)-1;
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 	/* attach in reverse device tree order */
4357c478bd9Sstevel@tonic-gate 	while (last != start) {
4367c478bd9Sstevel@tonic-gate 		dip = start;
4377c478bd9Sstevel@tonic-gate 		next = ddi_get_next_sibling(dip);
4387c478bd9Sstevel@tonic-gate 		while (next != last && dip != srh->sr_failed_dip) {
4397c478bd9Sstevel@tonic-gate 			dip = next;
4407c478bd9Sstevel@tonic-gate 			next = ddi_get_next_sibling(dip);
4417c478bd9Sstevel@tonic-gate 		}
4427c478bd9Sstevel@tonic-gate 		if (dip == srh->sr_failed_dip) {
4437c478bd9Sstevel@tonic-gate 			/* release hold acquired in dr_suspend_devices() */
4447c478bd9Sstevel@tonic-gate 			srh->sr_failed_dip = NULL;
4457c478bd9Sstevel@tonic-gate 			ndi_rele_devi(dip);
4468fc99e42STrevor Thompson 		} else if (dr_is_real_device(dip) &&
4478fc99e42STrevor Thompson 		    srh->sr_failed_dip == NULL) {
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 			if ((bn = ddi_binding_name(dip)) != NULL) {
4507c478bd9Sstevel@tonic-gate 				major = ddi_name_to_major(bn);
4517c478bd9Sstevel@tonic-gate 			} else {
4527c478bd9Sstevel@tonic-gate 				bn = "<null>";
4537c478bd9Sstevel@tonic-gate 			}
4547c478bd9Sstevel@tonic-gate 			if (!dr_bypass_device(bn) &&
4557c478bd9Sstevel@tonic-gate 			    !drmach_verify_sr(dip, 0)) {
4567c478bd9Sstevel@tonic-gate 				char	d_name[40], d_alias[40], *d_info;
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 				d_name[0] = 0;
4597c478bd9Sstevel@tonic-gate 				d_info = ddi_get_name_addr(dip);
4607c478bd9Sstevel@tonic-gate 				if (d_info == NULL)
4617c478bd9Sstevel@tonic-gate 					d_info = "<null>";
4627c478bd9Sstevel@tonic-gate 
4638fc99e42STrevor Thompson 				if (!dr_resolve_devname(dip, d_name, d_alias)) {
4647c478bd9Sstevel@tonic-gate 					if (d_alias[0] != 0) {
4657c478bd9Sstevel@tonic-gate 						prom_printf("\tresuming "
4668fc99e42STrevor Thompson 						    "%s@%s (aka %s)\n", d_name,
4678fc99e42STrevor Thompson 						    d_info, d_alias);
4687c478bd9Sstevel@tonic-gate 					} else {
4697c478bd9Sstevel@tonic-gate 						prom_printf("\tresuming "
470df3cd224SVijay S Balakrishna 						    "%s@%s\n", d_name, d_info);
4717c478bd9Sstevel@tonic-gate 					}
4727c478bd9Sstevel@tonic-gate 				} else {
4738fc99e42STrevor Thompson 					prom_printf("\tresuming %s@%s\n", bn,
4748fc99e42STrevor Thompson 					    d_info);
4757c478bd9Sstevel@tonic-gate 				}
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate 				if (devi_attach(dip, DDI_RESUME) !=
4787c478bd9Sstevel@tonic-gate 				    DDI_SUCCESS) {
4797c478bd9Sstevel@tonic-gate 					/*
4807c478bd9Sstevel@tonic-gate 					 * Print a console warning,
4817c478bd9Sstevel@tonic-gate 					 * set an e_code of ESBD_RESUME,
4827c478bd9Sstevel@tonic-gate 					 * and save the driver major
4837c478bd9Sstevel@tonic-gate 					 * number in the e_rsc.
4847c478bd9Sstevel@tonic-gate 					 */
4857c478bd9Sstevel@tonic-gate 					prom_printf("\tFAILED to resume %s@%s",
4867c478bd9Sstevel@tonic-gate 					    d_name[0] ? d_name : bn, d_info);
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate 					srh->sr_err_idx =
4897c478bd9Sstevel@tonic-gate 					    dr_add_int(srh->sr_err_ints,
4907c478bd9Sstevel@tonic-gate 					    srh->sr_err_idx, DR_MAX_ERR_INT,
4917c478bd9Sstevel@tonic-gate 					    (uint64_t)major);
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate 					handle = srh->sr_dr_handlep;
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate 					dr_op_err(CE_IGNORE, handle,
4967c478bd9Sstevel@tonic-gate 					    ESBD_RESUME, "%s@%s",
4977c478bd9Sstevel@tonic-gate 					    d_name[0] ? d_name : bn, d_info);
4987c478bd9Sstevel@tonic-gate 				}
4997c478bd9Sstevel@tonic-gate 			}
5007c478bd9Sstevel@tonic-gate 		}
5017c478bd9Sstevel@tonic-gate 
5027c478bd9Sstevel@tonic-gate 		/* Hold parent busy while walking its children */
5037c478bd9Sstevel@tonic-gate 		ndi_devi_enter(dip, &circ);
5047c478bd9Sstevel@tonic-gate 		dr_resume_devices(ddi_get_child(dip), srh);
5057c478bd9Sstevel@tonic-gate 		ndi_devi_exit(dip, circ);
5067c478bd9Sstevel@tonic-gate 		last = dip;
5077c478bd9Sstevel@tonic-gate 	}
5087c478bd9Sstevel@tonic-gate }
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate /*
5117c478bd9Sstevel@tonic-gate  * True if thread is virtually stopped.  Similar to CPR_VSTOPPED
5127c478bd9Sstevel@tonic-gate  * but from DR point of view.  These user threads are waiting in
5137c478bd9Sstevel@tonic-gate  * the kernel.  Once they complete in the kernel, they will process
5147c478bd9Sstevel@tonic-gate  * the stop signal and stop.
5157c478bd9Sstevel@tonic-gate  */
5167c478bd9Sstevel@tonic-gate #define	DR_VSTOPPED(t)			\
5177c478bd9Sstevel@tonic-gate 	((t)->t_state == TS_SLEEP &&	\
5187c478bd9Sstevel@tonic-gate 	(t)->t_wchan != NULL &&		\
5197c478bd9Sstevel@tonic-gate 	(t)->t_astflag &&		\
5207c478bd9Sstevel@tonic-gate 	((t)->t_proc_flag & TP_CHKPT))
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate /* ARGSUSED */
5237c478bd9Sstevel@tonic-gate static int
dr_stop_user_threads(dr_sr_handle_t * srh)5247c478bd9Sstevel@tonic-gate dr_stop_user_threads(dr_sr_handle_t *srh)
5257c478bd9Sstevel@tonic-gate {
5267c478bd9Sstevel@tonic-gate 	int		count;
5277c478bd9Sstevel@tonic-gate 	int		bailout;
5287c478bd9Sstevel@tonic-gate 	dr_handle_t	*handle = srh->sr_dr_handlep;
5297c478bd9Sstevel@tonic-gate 	static fn_t	f = "dr_stop_user_threads";
5307c478bd9Sstevel@tonic-gate 	kthread_id_t 	tp;
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate 	extern void add_one_utstop();
5337c478bd9Sstevel@tonic-gate 	extern void utstop_timedwait(clock_t);
5347c478bd9Sstevel@tonic-gate 	extern void utstop_init(void);
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate #define	DR_UTSTOP_RETRY	4
5377c478bd9Sstevel@tonic-gate #define	DR_UTSTOP_WAIT	hz
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate 	if (dr_skip_user_threads)
5407c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 	utstop_init();
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	/* we need to try a few times to get past fork, etc. */
5457c478bd9Sstevel@tonic-gate 	srh->sr_err_idx = 0;
5467c478bd9Sstevel@tonic-gate 	for (count = 0; count < DR_UTSTOP_RETRY; count++) {
5477c478bd9Sstevel@tonic-gate 		/* walk the entire threadlist */
5487c478bd9Sstevel@tonic-gate 		mutex_enter(&pidlock);
5497c478bd9Sstevel@tonic-gate 		for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) {
5507c478bd9Sstevel@tonic-gate 			proc_t *p = ttoproc(tp);
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate 			/* handle kernel threads separately */
5537c478bd9Sstevel@tonic-gate 			if (p->p_as == &kas || p->p_stat == SZOMB)
5547c478bd9Sstevel@tonic-gate 				continue;
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate 			mutex_enter(&p->p_lock);
5577c478bd9Sstevel@tonic-gate 			thread_lock(tp);
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate 			if (tp->t_state == TS_STOPPED) {
5607c478bd9Sstevel@tonic-gate 				/* add another reason to stop this thread */
5617c478bd9Sstevel@tonic-gate 				tp->t_schedflag &= ~TS_RESUME;
5627c478bd9Sstevel@tonic-gate 			} else {
5637c478bd9Sstevel@tonic-gate 				tp->t_proc_flag |= TP_CHKPT;
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 				thread_unlock(tp);
5667c478bd9Sstevel@tonic-gate 				mutex_exit(&p->p_lock);
5677c478bd9Sstevel@tonic-gate 				add_one_utstop();
5687c478bd9Sstevel@tonic-gate 				mutex_enter(&p->p_lock);
5697c478bd9Sstevel@tonic-gate 				thread_lock(tp);
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate 				aston(tp);
5727c478bd9Sstevel@tonic-gate 
573c97ad5cdSakolb 				if (ISWAKEABLE(tp) || ISWAITING(tp)) {
5747c478bd9Sstevel@tonic-gate 					setrun_locked(tp);
5757c478bd9Sstevel@tonic-gate 				}
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 			}
5787c478bd9Sstevel@tonic-gate 
5797c478bd9Sstevel@tonic-gate 			/* grab thread if needed */
5807c478bd9Sstevel@tonic-gate 			if (tp->t_state == TS_ONPROC && tp->t_cpu != CPU)
5817c478bd9Sstevel@tonic-gate 				poke_cpu(tp->t_cpu->cpu_id);
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate 			thread_unlock(tp);
5857c478bd9Sstevel@tonic-gate 			mutex_exit(&p->p_lock);
5867c478bd9Sstevel@tonic-gate 		}
5877c478bd9Sstevel@tonic-gate 		mutex_exit(&pidlock);
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 
5907c478bd9Sstevel@tonic-gate 		/* let everything catch up */
5917c478bd9Sstevel@tonic-gate 		utstop_timedwait(count * count * DR_UTSTOP_WAIT);
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate 		/* now, walk the threadlist again to see if we are done */
5957c478bd9Sstevel@tonic-gate 		mutex_enter(&pidlock);
5967c478bd9Sstevel@tonic-gate 		for (tp = curthread->t_next, bailout = 0;
5977c478bd9Sstevel@tonic-gate 		    tp != curthread; tp = tp->t_next) {
5987c478bd9Sstevel@tonic-gate 			proc_t *p = ttoproc(tp);
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 			/* handle kernel threads separately */
6017c478bd9Sstevel@tonic-gate 			if (p->p_as == &kas || p->p_stat == SZOMB)
6027c478bd9Sstevel@tonic-gate 				continue;
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 			/*
6057c478bd9Sstevel@tonic-gate 			 * If this thread didn't stop, and we don't allow
6067c478bd9Sstevel@tonic-gate 			 * unstopped blocked threads, bail.
6077c478bd9Sstevel@tonic-gate 			 */
6087c478bd9Sstevel@tonic-gate 			thread_lock(tp);
6097c478bd9Sstevel@tonic-gate 			if (!CPR_ISTOPPED(tp) &&
6107c478bd9Sstevel@tonic-gate 			    !(dr_allow_blocked_threads &&
6117c478bd9Sstevel@tonic-gate 			    DR_VSTOPPED(tp))) {
6127c478bd9Sstevel@tonic-gate 				bailout = 1;
6137c478bd9Sstevel@tonic-gate 				if (count == DR_UTSTOP_RETRY - 1) {
6147c478bd9Sstevel@tonic-gate 					/*
6157c478bd9Sstevel@tonic-gate 					 * save the pid for later reporting
6167c478bd9Sstevel@tonic-gate 					 */
6177c478bd9Sstevel@tonic-gate 					srh->sr_err_idx =
6187c478bd9Sstevel@tonic-gate 					    dr_add_int(srh->sr_err_ints,
6197c478bd9Sstevel@tonic-gate 					    srh->sr_err_idx, DR_MAX_ERR_INT,
6207c478bd9Sstevel@tonic-gate 					    (uint64_t)p->p_pid);
6217c478bd9Sstevel@tonic-gate 
6227c478bd9Sstevel@tonic-gate 					cmn_err(CE_WARN, "%s: "
6237c478bd9Sstevel@tonic-gate 					    "failed to stop thread: "
6247c478bd9Sstevel@tonic-gate 					    "process=%s, pid=%d",
6257c478bd9Sstevel@tonic-gate 					    f, p->p_user.u_psargs, p->p_pid);
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate 					PR_QR("%s: failed to stop thread: "
62804580fdfSmathue 					    "process=%s, pid=%d, t_id=0x%p, "
6297c478bd9Sstevel@tonic-gate 					    "t_state=0x%x, t_proc_flag=0x%x, "
6307c478bd9Sstevel@tonic-gate 					    "t_schedflag=0x%x\n",
6317c478bd9Sstevel@tonic-gate 					    f, p->p_user.u_psargs, p->p_pid,
63207d06da5SSurya Prakki 					    (void *)tp, tp->t_state,
63307d06da5SSurya Prakki 					    tp->t_proc_flag, tp->t_schedflag);
6347c478bd9Sstevel@tonic-gate 				}
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate 			}
6377c478bd9Sstevel@tonic-gate 			thread_unlock(tp);
6387c478bd9Sstevel@tonic-gate 		}
6397c478bd9Sstevel@tonic-gate 		mutex_exit(&pidlock);
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 		/* were all the threads stopped? */
6427c478bd9Sstevel@tonic-gate 		if (!bailout)
6437c478bd9Sstevel@tonic-gate 			break;
6447c478bd9Sstevel@tonic-gate 	}
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 	/* were we unable to stop all threads after a few tries? */
6477c478bd9Sstevel@tonic-gate 	if (bailout) {
6487c478bd9Sstevel@tonic-gate 		handle->h_err = drerr_int(ESBD_UTHREAD, srh->sr_err_ints,
6497c478bd9Sstevel@tonic-gate 		    srh->sr_err_idx, 0);
6507c478bd9Sstevel@tonic-gate 		return (ESRCH);
6517c478bd9Sstevel@tonic-gate 	}
6527c478bd9Sstevel@tonic-gate 
6537c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
6547c478bd9Sstevel@tonic-gate }
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate static void
dr_start_user_threads(void)6577c478bd9Sstevel@tonic-gate dr_start_user_threads(void)
6587c478bd9Sstevel@tonic-gate {
6597c478bd9Sstevel@tonic-gate 	kthread_id_t tp;
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate 	mutex_enter(&pidlock);
6627c478bd9Sstevel@tonic-gate 
6637c478bd9Sstevel@tonic-gate 	/* walk all threads and release them */
6647c478bd9Sstevel@tonic-gate 	for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) {
6657c478bd9Sstevel@tonic-gate 		proc_t *p = ttoproc(tp);
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 		/* skip kernel threads */
6687c478bd9Sstevel@tonic-gate 		if (ttoproc(tp)->p_as == &kas)
6697c478bd9Sstevel@tonic-gate 			continue;
6707c478bd9Sstevel@tonic-gate 
6717c478bd9Sstevel@tonic-gate 		mutex_enter(&p->p_lock);
6727c478bd9Sstevel@tonic-gate 		tp->t_proc_flag &= ~TP_CHKPT;
6737c478bd9Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
6747c478bd9Sstevel@tonic-gate 
6757c478bd9Sstevel@tonic-gate 		thread_lock(tp);
6767c478bd9Sstevel@tonic-gate 		if (CPR_ISTOPPED(tp)) {
6777c478bd9Sstevel@tonic-gate 			/* back on the runq */
6787c478bd9Sstevel@tonic-gate 			tp->t_schedflag |= TS_RESUME;
6797c478bd9Sstevel@tonic-gate 			setrun_locked(tp);
6807c478bd9Sstevel@tonic-gate 		}
6817c478bd9Sstevel@tonic-gate 		thread_unlock(tp);
6827c478bd9Sstevel@tonic-gate 	}
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 	mutex_exit(&pidlock);
6857c478bd9Sstevel@tonic-gate }
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate static void
dr_signal_user(int sig)6887c478bd9Sstevel@tonic-gate dr_signal_user(int sig)
6897c478bd9Sstevel@tonic-gate {
6907c478bd9Sstevel@tonic-gate 	struct proc *p;
6917c478bd9Sstevel@tonic-gate 
6927c478bd9Sstevel@tonic-gate 	mutex_enter(&pidlock);
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 	for (p = practive; p != NULL; p = p->p_next) {
6957c478bd9Sstevel@tonic-gate 		/* only user threads */
6967c478bd9Sstevel@tonic-gate 		if (p->p_exec == NULL || p->p_stat == SZOMB ||
6977c478bd9Sstevel@tonic-gate 		    p == proc_init || p == ttoproc(curthread))
6987c478bd9Sstevel@tonic-gate 			continue;
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate 		mutex_enter(&p->p_lock);
7017c478bd9Sstevel@tonic-gate 		sigtoproc(p, NULL, sig);
7027c478bd9Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
7037c478bd9Sstevel@tonic-gate 	}
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 	mutex_exit(&pidlock);
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate 	/* add a bit of delay */
7087c478bd9Sstevel@tonic-gate 	delay(hz);
7097c478bd9Sstevel@tonic-gate }
7107c478bd9Sstevel@tonic-gate 
7117c478bd9Sstevel@tonic-gate void
dr_resume(dr_sr_handle_t * srh)7127c478bd9Sstevel@tonic-gate dr_resume(dr_sr_handle_t *srh)
7137c478bd9Sstevel@tonic-gate {
7147c478bd9Sstevel@tonic-gate 	if (srh->sr_suspend_state < DR_SRSTATE_FULL) {
7157c478bd9Sstevel@tonic-gate 		/*
7167c478bd9Sstevel@tonic-gate 		 * Update the signature block.
7177c478bd9Sstevel@tonic-gate 		 * If cpus are not paused, this can be done now.
7187c478bd9Sstevel@tonic-gate 		 * See comments below.
7197c478bd9Sstevel@tonic-gate 		 */
7207c478bd9Sstevel@tonic-gate 		CPU_SIGNATURE(OS_SIG, SIGST_RESUME_INPROGRESS, SIGSUBST_NULL,
7217c478bd9Sstevel@tonic-gate 		    CPU->cpu_id);
7227c478bd9Sstevel@tonic-gate 	}
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 	switch (srh->sr_suspend_state) {
7257c478bd9Sstevel@tonic-gate 	case DR_SRSTATE_FULL:
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 		ASSERT(MUTEX_HELD(&cpu_lock));
7287c478bd9Sstevel@tonic-gate 
729646e55b6Scth 		/*
730646e55b6Scth 		 * Prevent false alarm in tod_validate() due to tod
731646e55b6Scth 		 * value change between suspend and resume
732646e55b6Scth 		 */
733646e55b6Scth 		mutex_enter(&tod_lock);
7348fc99e42STrevor Thompson 		tod_status_set(TOD_DR_RESUME_DONE);
735646e55b6Scth 		mutex_exit(&tod_lock);
736646e55b6Scth 
7377c478bd9Sstevel@tonic-gate 		dr_enable_intr(); 	/* enable intr & clock */
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate 		start_cpus();
7407c478bd9Sstevel@tonic-gate 		mutex_exit(&cpu_lock);
7417c478bd9Sstevel@tonic-gate 
7427c478bd9Sstevel@tonic-gate 		/*
7437c478bd9Sstevel@tonic-gate 		 * Update the signature block.
7447c478bd9Sstevel@tonic-gate 		 * This must not be done while cpus are paused, since on
7457c478bd9Sstevel@tonic-gate 		 * Starcat the cpu signature update aquires an adaptive
7467c478bd9Sstevel@tonic-gate 		 * mutex in the iosram driver. Blocking with cpus paused
7477c478bd9Sstevel@tonic-gate 		 * can lead to deadlock.
7487c478bd9Sstevel@tonic-gate 		 */
7497c478bd9Sstevel@tonic-gate 		CPU_SIGNATURE(OS_SIG, SIGST_RESUME_INPROGRESS, SIGSUBST_NULL,
7507c478bd9Sstevel@tonic-gate 		    CPU->cpu_id);
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 		/*
7537c478bd9Sstevel@tonic-gate 		 * If we suspended hw watchdog at suspend,
7547c478bd9Sstevel@tonic-gate 		 * re-enable it now.
7557c478bd9Sstevel@tonic-gate 		 */
7567c478bd9Sstevel@tonic-gate 		if (srh->sr_flags & (SR_FLAG_WATCHDOG)) {
7577c478bd9Sstevel@tonic-gate 			mutex_enter(&tod_lock);
7587c478bd9Sstevel@tonic-gate 			tod_ops.tod_set_watchdog_timer(
7597c478bd9Sstevel@tonic-gate 			    watchdog_timeout_seconds);
7607c478bd9Sstevel@tonic-gate 			mutex_exit(&tod_lock);
7617c478bd9Sstevel@tonic-gate 		}
7627c478bd9Sstevel@tonic-gate 
7637c478bd9Sstevel@tonic-gate 		/*
7647c478bd9Sstevel@tonic-gate 		 * This should only be called if drmach_suspend_last()
7657c478bd9Sstevel@tonic-gate 		 * was called and state transitioned to DR_SRSTATE_FULL
7667c478bd9Sstevel@tonic-gate 		 * to prevent resume attempts on device instances that
7677c478bd9Sstevel@tonic-gate 		 * were not previously suspended.
7687c478bd9Sstevel@tonic-gate 		 */
7697c478bd9Sstevel@tonic-gate 		drmach_resume_first();
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate 	case DR_SRSTATE_DRIVER:
7747c478bd9Sstevel@tonic-gate 		/*
7757c478bd9Sstevel@tonic-gate 		 * resume drivers
7767c478bd9Sstevel@tonic-gate 		 */
7777c478bd9Sstevel@tonic-gate 		srh->sr_err_idx = 0;
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate 		/* no parent dip to hold busy */
7807c478bd9Sstevel@tonic-gate 		dr_resume_devices(ddi_root_node(), srh);
7817c478bd9Sstevel@tonic-gate 
7827c478bd9Sstevel@tonic-gate 		if (srh->sr_err_idx && srh->sr_dr_handlep) {
7837c478bd9Sstevel@tonic-gate 			(srh->sr_dr_handlep)->h_err = drerr_int(ESBD_RESUME,
7847c478bd9Sstevel@tonic-gate 			    srh->sr_err_ints, srh->sr_err_idx, 1);
7857c478bd9Sstevel@tonic-gate 		}
7867c478bd9Sstevel@tonic-gate 
7877c478bd9Sstevel@tonic-gate 		/*
7887c478bd9Sstevel@tonic-gate 		 * resume the lock manager
7897c478bd9Sstevel@tonic-gate 		 */
7907c478bd9Sstevel@tonic-gate 		lm_cprresume();
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 	case DR_SRSTATE_USER:
7957c478bd9Sstevel@tonic-gate 		/*
7967c478bd9Sstevel@tonic-gate 		 * finally, resume user threads
7977c478bd9Sstevel@tonic-gate 		 */
7987c478bd9Sstevel@tonic-gate 		if (!dr_skip_user_threads) {
7997c478bd9Sstevel@tonic-gate 			prom_printf("DR: resuming user threads...\n");
8007c478bd9Sstevel@tonic-gate 			dr_start_user_threads();
8017c478bd9Sstevel@tonic-gate 		}
8027c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate 	case DR_SRSTATE_BEGIN:
8057c478bd9Sstevel@tonic-gate 	default:
8067c478bd9Sstevel@tonic-gate 		/*
8077c478bd9Sstevel@tonic-gate 		 * let those who care know that we've just resumed
8087c478bd9Sstevel@tonic-gate 		 */
8097c478bd9Sstevel@tonic-gate 		PR_QR("sending SIGTHAW...\n");
8107c478bd9Sstevel@tonic-gate 		dr_signal_user(SIGTHAW);
8117c478bd9Sstevel@tonic-gate 		break;
8127c478bd9Sstevel@tonic-gate 	}
8137c478bd9Sstevel@tonic-gate 
8147c478bd9Sstevel@tonic-gate 	/*
8157c478bd9Sstevel@tonic-gate 	 * update the signature block
8167c478bd9Sstevel@tonic-gate 	 */
8177c478bd9Sstevel@tonic-gate 	CPU_SIGNATURE(OS_SIG, SIGST_RUN, SIGSUBST_NULL, CPU->cpu_id);
8187c478bd9Sstevel@tonic-gate 
8197c478bd9Sstevel@tonic-gate 	prom_printf("DR: resume COMPLETED\n");
8207c478bd9Sstevel@tonic-gate }
8217c478bd9Sstevel@tonic-gate 
8227c478bd9Sstevel@tonic-gate int
dr_suspend(dr_sr_handle_t * srh)8237c478bd9Sstevel@tonic-gate dr_suspend(dr_sr_handle_t *srh)
8247c478bd9Sstevel@tonic-gate {
8257c478bd9Sstevel@tonic-gate 	dr_handle_t	*handle;
8267c478bd9Sstevel@tonic-gate 	int		force;
8277c478bd9Sstevel@tonic-gate 	int		dev_errs_idx;
8287c478bd9Sstevel@tonic-gate 	uint64_t	dev_errs[DR_MAX_ERR_INT];
8297c478bd9Sstevel@tonic-gate 	int		rc = DDI_SUCCESS;
8307c478bd9Sstevel@tonic-gate 
8317c478bd9Sstevel@tonic-gate 	handle = srh->sr_dr_handlep;
8327c478bd9Sstevel@tonic-gate 
8337c478bd9Sstevel@tonic-gate 	force = dr_cmd_flags(handle) & SBD_FLAG_FORCE;
8347c478bd9Sstevel@tonic-gate 
8357c478bd9Sstevel@tonic-gate 	/*
8367c478bd9Sstevel@tonic-gate 	 * update the signature block
8377c478bd9Sstevel@tonic-gate 	 */
8387c478bd9Sstevel@tonic-gate 	CPU_SIGNATURE(OS_SIG, SIGST_QUIESCE_INPROGRESS, SIGSUBST_NULL,
8397c478bd9Sstevel@tonic-gate 	    CPU->cpu_id);
8407c478bd9Sstevel@tonic-gate 
8417c478bd9Sstevel@tonic-gate 	prom_printf("\nDR: suspending user threads...\n");
8427c478bd9Sstevel@tonic-gate 	srh->sr_suspend_state = DR_SRSTATE_USER;
8437c478bd9Sstevel@tonic-gate 	if (((rc = dr_stop_user_threads(srh)) != DDI_SUCCESS) &&
8447c478bd9Sstevel@tonic-gate 	    dr_check_user_stop_result) {
8457c478bd9Sstevel@tonic-gate 		dr_resume(srh);
8467c478bd9Sstevel@tonic-gate 		return (rc);
8477c478bd9Sstevel@tonic-gate 	}
8487c478bd9Sstevel@tonic-gate 
8497c478bd9Sstevel@tonic-gate 	if (!force) {
8507c478bd9Sstevel@tonic-gate 		struct dr_ref drc = {0};
8517c478bd9Sstevel@tonic-gate 
8527c478bd9Sstevel@tonic-gate 		prom_printf("\nDR: checking devices...\n");
8537c478bd9Sstevel@tonic-gate 		dev_errs_idx = 0;
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 		drc.arr = dev_errs;
8567c478bd9Sstevel@tonic-gate 		drc.idx = &dev_errs_idx;
8577c478bd9Sstevel@tonic-gate 		drc.len = DR_MAX_ERR_INT;
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate 		/*
8607c478bd9Sstevel@tonic-gate 		 * Since the root node can never go away, it
8617c478bd9Sstevel@tonic-gate 		 * doesn't have to be held.
8627c478bd9Sstevel@tonic-gate 		 */
8637c478bd9Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), dr_check_unsafe_major, &drc);
8647c478bd9Sstevel@tonic-gate 		if (dev_errs_idx) {
8657c478bd9Sstevel@tonic-gate 			handle->h_err = drerr_int(ESBD_UNSAFE, dev_errs,
8667c478bd9Sstevel@tonic-gate 			    dev_errs_idx, 1);
8677c478bd9Sstevel@tonic-gate 			dr_resume(srh);
8687c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
8697c478bd9Sstevel@tonic-gate 		}
8707c478bd9Sstevel@tonic-gate 		PR_QR("done\n");
8717c478bd9Sstevel@tonic-gate 	} else {
8727c478bd9Sstevel@tonic-gate 		prom_printf("\nDR: dr_suspend invoked with force flag\n");
8737c478bd9Sstevel@tonic-gate 	}
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate #ifndef	SKIP_SYNC
8767c478bd9Sstevel@tonic-gate 	/*
8777c478bd9Sstevel@tonic-gate 	 * This sync swap out all user pages
8787c478bd9Sstevel@tonic-gate 	 */
8797c478bd9Sstevel@tonic-gate 	vfs_sync(SYNC_ALL);
8807c478bd9Sstevel@tonic-gate #endif
8817c478bd9Sstevel@tonic-gate 
8827c478bd9Sstevel@tonic-gate 	/*
8837c478bd9Sstevel@tonic-gate 	 * special treatment for lock manager
8847c478bd9Sstevel@tonic-gate 	 */
8857c478bd9Sstevel@tonic-gate 	lm_cprsuspend();
8867c478bd9Sstevel@tonic-gate 
8877c478bd9Sstevel@tonic-gate #ifndef	SKIP_SYNC
8887c478bd9Sstevel@tonic-gate 	/*
8897c478bd9Sstevel@tonic-gate 	 * sync the file system in case we never make it back
8907c478bd9Sstevel@tonic-gate 	 */
8917c478bd9Sstevel@tonic-gate 	sync();
8927c478bd9Sstevel@tonic-gate #endif
8937c478bd9Sstevel@tonic-gate 
8947c478bd9Sstevel@tonic-gate 	/*
8957c478bd9Sstevel@tonic-gate 	 * now suspend drivers
8967c478bd9Sstevel@tonic-gate 	 */
8977c478bd9Sstevel@tonic-gate 	prom_printf("DR: suspending drivers...\n");
8987c478bd9Sstevel@tonic-gate 	srh->sr_suspend_state = DR_SRSTATE_DRIVER;
8997c478bd9Sstevel@tonic-gate 	srh->sr_err_idx = 0;
9007c478bd9Sstevel@tonic-gate 	/* No parent to hold busy */
9017c478bd9Sstevel@tonic-gate 	if ((rc = dr_suspend_devices(ddi_root_node(), srh)) != DDI_SUCCESS) {
9027c478bd9Sstevel@tonic-gate 		if (srh->sr_err_idx && srh->sr_dr_handlep) {
9037c478bd9Sstevel@tonic-gate 			(srh->sr_dr_handlep)->h_err = drerr_int(ESBD_SUSPEND,
9047c478bd9Sstevel@tonic-gate 			    srh->sr_err_ints, srh->sr_err_idx, 1);
9057c478bd9Sstevel@tonic-gate 		}
9067c478bd9Sstevel@tonic-gate 		dr_resume(srh);
9077c478bd9Sstevel@tonic-gate 		return (rc);
9087c478bd9Sstevel@tonic-gate 	}
9097c478bd9Sstevel@tonic-gate 
9107c478bd9Sstevel@tonic-gate 	drmach_suspend_last();
9117c478bd9Sstevel@tonic-gate 
9127c478bd9Sstevel@tonic-gate 	/*
9137c478bd9Sstevel@tonic-gate 	 * finally, grab all cpus
9147c478bd9Sstevel@tonic-gate 	 */
9157c478bd9Sstevel@tonic-gate 	srh->sr_suspend_state = DR_SRSTATE_FULL;
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate 	/*
9187c478bd9Sstevel@tonic-gate 	 * if watchdog was activated, disable it
9197c478bd9Sstevel@tonic-gate 	 */
9207c478bd9Sstevel@tonic-gate 	if (watchdog_activated) {
9217c478bd9Sstevel@tonic-gate 		mutex_enter(&tod_lock);
9227c478bd9Sstevel@tonic-gate 		tod_ops.tod_clear_watchdog_timer();
9237c478bd9Sstevel@tonic-gate 		mutex_exit(&tod_lock);
9247c478bd9Sstevel@tonic-gate 		srh->sr_flags |= SR_FLAG_WATCHDOG;
9257c478bd9Sstevel@tonic-gate 	} else {
9267c478bd9Sstevel@tonic-gate 		srh->sr_flags &= ~(SR_FLAG_WATCHDOG);
9277c478bd9Sstevel@tonic-gate 	}
9287c478bd9Sstevel@tonic-gate 
9297c478bd9Sstevel@tonic-gate 	/*
9307c478bd9Sstevel@tonic-gate 	 * Update the signature block.
9317c478bd9Sstevel@tonic-gate 	 * This must be done before cpus are paused, since on Starcat the
9327c478bd9Sstevel@tonic-gate 	 * cpu signature update aquires an adaptive mutex in the iosram driver.
9337c478bd9Sstevel@tonic-gate 	 * Blocking with cpus paused can lead to deadlock.
9347c478bd9Sstevel@tonic-gate 	 */
9357c478bd9Sstevel@tonic-gate 	CPU_SIGNATURE(OS_SIG, SIGST_QUIESCED, SIGSUBST_NULL, CPU->cpu_id);
9367c478bd9Sstevel@tonic-gate 
9377c478bd9Sstevel@tonic-gate 	mutex_enter(&cpu_lock);
938*0ed5c46eSJosef 'Jeff' Sipek 	pause_cpus(NULL, NULL);
9397c478bd9Sstevel@tonic-gate 	dr_stop_intr();
9407c478bd9Sstevel@tonic-gate 
9417c478bd9Sstevel@tonic-gate 	return (rc);
9427c478bd9Sstevel@tonic-gate }
9437c478bd9Sstevel@tonic-gate 
9447c478bd9Sstevel@tonic-gate int
dr_pt_test_suspend(dr_handle_t * hp)9457c478bd9Sstevel@tonic-gate dr_pt_test_suspend(dr_handle_t *hp)
9467c478bd9Sstevel@tonic-gate {
9477c478bd9Sstevel@tonic-gate 	dr_sr_handle_t *srh;
9487c478bd9Sstevel@tonic-gate 	int		err;
9497c478bd9Sstevel@tonic-gate 	uint_t		psmerr;
9507c478bd9Sstevel@tonic-gate 	static fn_t	f = "dr_pt_test_suspend";
9517c478bd9Sstevel@tonic-gate 
9527c478bd9Sstevel@tonic-gate 	PR_QR("%s...\n", f);
9537c478bd9Sstevel@tonic-gate 
9547c478bd9Sstevel@tonic-gate 	srh = dr_get_sr_handle(hp);
9557c478bd9Sstevel@tonic-gate 	if ((err = dr_suspend(srh)) == DDI_SUCCESS) {
9567c478bd9Sstevel@tonic-gate 		dr_resume(srh);
9577c478bd9Sstevel@tonic-gate 		if ((hp->h_err) && ((psmerr = hp->h_err->e_code) != 0)) {
9587c478bd9Sstevel@tonic-gate 			PR_QR("%s: error on dr_resume()", f);
9597c478bd9Sstevel@tonic-gate 			switch (psmerr) {
9607c478bd9Sstevel@tonic-gate 			case ESBD_RESUME:
9617c478bd9Sstevel@tonic-gate 				PR_QR("Couldn't resume devices: %s\n",
9627c478bd9Sstevel@tonic-gate 				    DR_GET_E_RSC(hp->h_err));
9637c478bd9Sstevel@tonic-gate 				break;
9647c478bd9Sstevel@tonic-gate 
9657c478bd9Sstevel@tonic-gate 			case ESBD_KTHREAD:
9667c478bd9Sstevel@tonic-gate 				PR_ALL("psmerr is ESBD_KTHREAD\n");
9677c478bd9Sstevel@tonic-gate 				break;
9687c478bd9Sstevel@tonic-gate 			default:
9698fc99e42STrevor Thompson 				PR_ALL("Resume error unknown = %d\n", psmerr);
9707c478bd9Sstevel@tonic-gate 				break;
9717c478bd9Sstevel@tonic-gate 			}
9727c478bd9Sstevel@tonic-gate 		}
9737c478bd9Sstevel@tonic-gate 	} else {
9748fc99e42STrevor Thompson 		PR_ALL("%s: dr_suspend() failed, err = 0x%x\n", f, err);
9757c478bd9Sstevel@tonic-gate 		psmerr = hp->h_err ? hp->h_err->e_code : ESBD_NOERROR;
9767c478bd9Sstevel@tonic-gate 		switch (psmerr) {
9777c478bd9Sstevel@tonic-gate 		case ESBD_UNSAFE:
9787c478bd9Sstevel@tonic-gate 			PR_ALL("Unsafe devices (major #): %s\n",
9797c478bd9Sstevel@tonic-gate 			    DR_GET_E_RSC(hp->h_err));
9807c478bd9Sstevel@tonic-gate 			break;
9817c478bd9Sstevel@tonic-gate 
9827c478bd9Sstevel@tonic-gate 		case ESBD_RTTHREAD:
9837c478bd9Sstevel@tonic-gate 			PR_ALL("RT threads (PIDs): %s\n",
9847c478bd9Sstevel@tonic-gate 			    DR_GET_E_RSC(hp->h_err));
9857c478bd9Sstevel@tonic-gate 			break;
9867c478bd9Sstevel@tonic-gate 
9877c478bd9Sstevel@tonic-gate 		case ESBD_UTHREAD:
9887c478bd9Sstevel@tonic-gate 			PR_ALL("User threads (PIDs): %s\n",
9897c478bd9Sstevel@tonic-gate 			    DR_GET_E_RSC(hp->h_err));
9907c478bd9Sstevel@tonic-gate 			break;
9917c478bd9Sstevel@tonic-gate 
9927c478bd9Sstevel@tonic-gate 		case ESBD_SUSPEND:
9937c478bd9Sstevel@tonic-gate 			PR_ALL("Non-suspendable devices (major #): %s\n",
9947c478bd9Sstevel@tonic-gate 			    DR_GET_E_RSC(hp->h_err));
9957c478bd9Sstevel@tonic-gate 			break;
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 		case ESBD_RESUME:
9987c478bd9Sstevel@tonic-gate 			PR_ALL("Could not resume devices (major #): %s\n",
9997c478bd9Sstevel@tonic-gate 			    DR_GET_E_RSC(hp->h_err));
10007c478bd9Sstevel@tonic-gate 			break;
10017c478bd9Sstevel@tonic-gate 
10027c478bd9Sstevel@tonic-gate 		case ESBD_KTHREAD:
10037c478bd9Sstevel@tonic-gate 			PR_ALL("psmerr is ESBD_KTHREAD\n");
10047c478bd9Sstevel@tonic-gate 			break;
10057c478bd9Sstevel@tonic-gate 
10067c478bd9Sstevel@tonic-gate 		case ESBD_NOERROR:
10077c478bd9Sstevel@tonic-gate 			PR_ALL("sbd_error_t error code not set\n");
10087c478bd9Sstevel@tonic-gate 			break;
10097c478bd9Sstevel@tonic-gate 
10107c478bd9Sstevel@tonic-gate 		default:
10117c478bd9Sstevel@tonic-gate 			PR_ALL("Unknown error psmerr = %d\n", psmerr);
10127c478bd9Sstevel@tonic-gate 			break;
10137c478bd9Sstevel@tonic-gate 		}
10147c478bd9Sstevel@tonic-gate 	}
10157c478bd9Sstevel@tonic-gate 	dr_release_sr_handle(srh);
10167c478bd9Sstevel@tonic-gate 
10177c478bd9Sstevel@tonic-gate 	return (0);
10187c478bd9Sstevel@tonic-gate }
10197c478bd9Sstevel@tonic-gate 
10207c478bd9Sstevel@tonic-gate /*
10217c478bd9Sstevel@tonic-gate  * Add a new integer value to the end of an array.  Don't allow duplicates to
10227c478bd9Sstevel@tonic-gate  * appear in the array, and don't allow the array to overflow.  Return the new
10237c478bd9Sstevel@tonic-gate  * total number of entries in the array.
10247c478bd9Sstevel@tonic-gate  */
10257c478bd9Sstevel@tonic-gate static int
dr_add_int(uint64_t * arr,int idx,int len,uint64_t val)10267c478bd9Sstevel@tonic-gate dr_add_int(uint64_t *arr, int idx, int len, uint64_t val)
10277c478bd9Sstevel@tonic-gate {
10287c478bd9Sstevel@tonic-gate 	int i;
10297c478bd9Sstevel@tonic-gate 
10307c478bd9Sstevel@tonic-gate 	if (arr == NULL)
10317c478bd9Sstevel@tonic-gate 		return (0);
10327c478bd9Sstevel@tonic-gate 
10337c478bd9Sstevel@tonic-gate 	if (idx >= len)
10347c478bd9Sstevel@tonic-gate 		return (idx);
10357c478bd9Sstevel@tonic-gate 
10367c478bd9Sstevel@tonic-gate 	for (i = 0; i < idx; i++) {
10377c478bd9Sstevel@tonic-gate 		if (arr[i] == val)
10387c478bd9Sstevel@tonic-gate 			return (idx);
10397c478bd9Sstevel@tonic-gate 	}
10407c478bd9Sstevel@tonic-gate 
10417c478bd9Sstevel@tonic-gate 	arr[idx++] = val;
10427c478bd9Sstevel@tonic-gate 
10437c478bd9Sstevel@tonic-gate 	return (idx);
10447c478bd9Sstevel@tonic-gate }
10457c478bd9Sstevel@tonic-gate 
10467c478bd9Sstevel@tonic-gate /*
10477c478bd9Sstevel@tonic-gate  * Construct an sbd_error_t featuring a string representation of an array of
10487c478bd9Sstevel@tonic-gate  * integers as its e_rsc.
10497c478bd9Sstevel@tonic-gate  */
10507c478bd9Sstevel@tonic-gate static sbd_error_t *
drerr_int(int e_code,uint64_t * arr,int idx,int majors)10517c478bd9Sstevel@tonic-gate drerr_int(int e_code, uint64_t *arr, int idx, int majors)
10527c478bd9Sstevel@tonic-gate {
10537c478bd9Sstevel@tonic-gate 	int		i, n, buf_len, buf_idx, buf_avail;
10547c478bd9Sstevel@tonic-gate 	char		*dname;
10557c478bd9Sstevel@tonic-gate 	char		*buf;
10567c478bd9Sstevel@tonic-gate 	sbd_error_t	*new_sbd_err;
10577c478bd9Sstevel@tonic-gate 	static char	s_ellipsis[] = "...";
10587c478bd9Sstevel@tonic-gate 
10597c478bd9Sstevel@tonic-gate 	if (arr == NULL || idx <= 0)
10607c478bd9Sstevel@tonic-gate 		return (NULL);
10617c478bd9Sstevel@tonic-gate 
10627c478bd9Sstevel@tonic-gate 	/* MAXPATHLEN is the size of the e_rsc field in sbd_error_t. */
10637c478bd9Sstevel@tonic-gate 	buf = (char *)kmem_zalloc(MAXPATHLEN, KM_SLEEP);
10647c478bd9Sstevel@tonic-gate 
10657c478bd9Sstevel@tonic-gate 	/*
10667c478bd9Sstevel@tonic-gate 	 * This is the total working area of the buffer.  It must be computed
10677c478bd9Sstevel@tonic-gate 	 * as the size of 'buf', minus reserved space for the null terminator
10687c478bd9Sstevel@tonic-gate 	 * and the ellipsis string.
10697c478bd9Sstevel@tonic-gate 	 */
10707c478bd9Sstevel@tonic-gate 	buf_len = MAXPATHLEN - (strlen(s_ellipsis) + 1);
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate 	/* Construct a string representation of the array values */
10737c478bd9Sstevel@tonic-gate 	for (buf_idx = 0, i = 0; i < idx; i++) {
10747c478bd9Sstevel@tonic-gate 		buf_avail = buf_len - buf_idx;
10757c478bd9Sstevel@tonic-gate 		if (majors) {
10767c478bd9Sstevel@tonic-gate 			dname = ddi_major_to_name(arr[i]);
10777c478bd9Sstevel@tonic-gate 			if (dname) {
10788fc99e42STrevor Thompson 				n = snprintf(&buf[buf_idx], buf_avail, "%s, ",
10798fc99e42STrevor Thompson 				    dname);
10807c478bd9Sstevel@tonic-gate 			} else {
10817c478bd9Sstevel@tonic-gate 				n = snprintf(&buf[buf_idx], buf_avail,
108204580fdfSmathue 				    "major %lu, ", arr[i]);
10837c478bd9Sstevel@tonic-gate 			}
10847c478bd9Sstevel@tonic-gate 		} else {
1085df3cd224SVijay S Balakrishna 			n = snprintf(&buf[buf_idx], buf_avail, "%lu, ", arr[i]);
10867c478bd9Sstevel@tonic-gate 		}
10877c478bd9Sstevel@tonic-gate 
10887c478bd9Sstevel@tonic-gate 		/* An ellipsis gets appended when no more values fit */
10897c478bd9Sstevel@tonic-gate 		if (n >= buf_avail) {
10907c478bd9Sstevel@tonic-gate 			(void) strcpy(&buf[buf_idx], s_ellipsis);
10917c478bd9Sstevel@tonic-gate 			break;
10927c478bd9Sstevel@tonic-gate 		}
10937c478bd9Sstevel@tonic-gate 
10947c478bd9Sstevel@tonic-gate 		buf_idx += n;
10957c478bd9Sstevel@tonic-gate 	}
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate 	/* If all the contents fit, remove the trailing comma */
10987c478bd9Sstevel@tonic-gate 	if (n < buf_avail) {
10997c478bd9Sstevel@tonic-gate 		buf[--buf_idx] = '\0';
11007c478bd9Sstevel@tonic-gate 		buf[--buf_idx] = '\0';
11017c478bd9Sstevel@tonic-gate 	}
11027c478bd9Sstevel@tonic-gate 
11037c478bd9Sstevel@tonic-gate 	/* Return an sbd_error_t with the buffer and e_code */
11047c478bd9Sstevel@tonic-gate 	new_sbd_err = drerr_new(1, e_code, buf);
11057c478bd9Sstevel@tonic-gate 	kmem_free(buf, MAXPATHLEN);
11067c478bd9Sstevel@tonic-gate 	return (new_sbd_err);
11077c478bd9Sstevel@tonic-gate }
1108