xref: /titanic_52/usr/src/uts/sun4u/serengeti/io/sbdp_quiesce.c (revision 0ed5c46e82c989cfa9726d9dae452e3d24ef83be)
103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
503831d35Sstevel  * Common Development and Distribution License (the "License").
603831d35Sstevel  * You may not use this file except in compliance with the License.
703831d35Sstevel  *
803831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel  * See the License for the specific language governing permissions
1103831d35Sstevel  * and limitations under the License.
1203831d35Sstevel  *
1303831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel  *
1903831d35Sstevel  * CDDL HEADER END
2003831d35Sstevel  */
2103831d35Sstevel 
2203831d35Sstevel /*
23df3cd224SVijay S Balakrishna  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2403831d35Sstevel  * Use is subject to license terms.
2503831d35Sstevel  */
2603831d35Sstevel 
2703831d35Sstevel /*
2803831d35Sstevel  * A CPR derivative specifically for sbd
2903831d35Sstevel  */
3003831d35Sstevel 
3103831d35Sstevel #include <sys/types.h>
3203831d35Sstevel #include <sys/systm.h>
3303831d35Sstevel #include <sys/machparam.h>
3403831d35Sstevel #include <sys/machsystm.h>
3503831d35Sstevel #include <sys/ddi.h>
3603831d35Sstevel #define	SUNDDI_IMPL
3703831d35Sstevel #include <sys/sunddi.h>
3803831d35Sstevel #include <sys/sunndi.h>
3903831d35Sstevel #include <sys/devctl.h>
4003831d35Sstevel #include <sys/time.h>
4103831d35Sstevel #include <sys/kmem.h>
4203831d35Sstevel #include <nfs/lm.h>
4303831d35Sstevel #include <sys/ddi_impldefs.h>
4403831d35Sstevel #include <sys/ndi_impldefs.h>
4503831d35Sstevel #include <sys/obpdefs.h>
4603831d35Sstevel #include <sys/cmn_err.h>
4703831d35Sstevel #include <sys/debug.h>
4803831d35Sstevel #include <sys/errno.h>
4903831d35Sstevel #include <sys/callb.h>
5003831d35Sstevel #include <sys/clock.h>
5103831d35Sstevel #include <sys/x_call.h>
5203831d35Sstevel #include <sys/cpuvar.h>
5303831d35Sstevel #include <sys/epm.h>
5403831d35Sstevel #include <sys/vfs.h>
5503831d35Sstevel 
5603831d35Sstevel #ifdef DEBUG
5703831d35Sstevel #include <sys/note.h>
5803831d35Sstevel #endif
5903831d35Sstevel 
6003831d35Sstevel #include <sys/promif.h>
6103831d35Sstevel #include <sys/conf.h>
6203831d35Sstevel #include <sys/cyclic.h>
6303831d35Sstevel 
6403831d35Sstevel #include <sys/sbd_ioctl.h>
6503831d35Sstevel #include <sys/sbd.h>
6603831d35Sstevel #include <sys/sbdp_priv.h>
6703831d35Sstevel #include <sys/cpu_sgnblk_defs.h>
6803831d35Sstevel 
6903831d35Sstevel static char *
7003831d35Sstevel sbdp_get_err_buf(sbd_error_t *ep)
7103831d35Sstevel {
7203831d35Sstevel 	return (ep->e_rsc);
7303831d35Sstevel }
7403831d35Sstevel 
7503831d35Sstevel extern void	e_ddi_enter_driver_list(struct devnames *dnp, int *listcnt);
7603831d35Sstevel extern void	e_ddi_exit_driver_list(struct devnames *dnp, int listcnt);
7703831d35Sstevel extern int	is_pseudo_device(dev_info_t *dip);
7803831d35Sstevel 
7903831d35Sstevel extern kmutex_t	cpu_lock;
8003831d35Sstevel 
8103831d35Sstevel static int	sbdp_is_real_device(dev_info_t *dip);
8203831d35Sstevel #ifdef DEBUG
8303831d35Sstevel static int	sbdp_bypass_device(char *dname);
8403831d35Sstevel #endif
8503831d35Sstevel static int	sbdp_check_dip(dev_info_t *dip, void *arg, uint_t ref);
8603831d35Sstevel 
8703831d35Sstevel static int	sbdp_resolve_devname(dev_info_t *dip, char *buffer,
8803831d35Sstevel 				char *alias);
8903831d35Sstevel 
9003831d35Sstevel int sbdp_test_suspend(sbdp_handle_t *hp);
9103831d35Sstevel 
9203831d35Sstevel #define	SR_STATE(srh)			((srh)->sr_suspend_state)
9303831d35Sstevel #define	SR_SET_STATE(srh, state)	(SR_STATE((srh)) = (state))
9403831d35Sstevel #define	SR_FAILED_DIP(srh)		((srh)->sr_failed_dip)
9503831d35Sstevel 
9603831d35Sstevel #define	SR_FLAG_WATCHDOG	0x1
9703831d35Sstevel #define	SR_CHECK_FLAG(srh, flag)	((srh)->sr_flags & (flag))
9803831d35Sstevel #define	SR_SET_FLAG(srh, flag)		((srh)->sr_flags |= (flag))
9903831d35Sstevel #define	SR_CLEAR_FLAG(srh, flag)	((srh)->sr_flags &= ~(flag))
10003831d35Sstevel 
10103831d35Sstevel #ifdef DEBUG
10203831d35Sstevel /*
10303831d35Sstevel  * Just for testing. List of drivers to bypass when performing a suspend.
10403831d35Sstevel  */
10503831d35Sstevel static char *sbdp_bypass_list[] = {
10603831d35Sstevel 	/* "sgsbbc", this is an example when needed */
10703831d35Sstevel 	""
10803831d35Sstevel };
10903831d35Sstevel #endif
11003831d35Sstevel 
11103831d35Sstevel #define		SKIP_SYNC	/* bypass sync ops in sbdp_suspend */
11203831d35Sstevel 
11303831d35Sstevel /*
11403831d35Sstevel  * sbdp_skip_user_threads is used to control if user threads should
11503831d35Sstevel  * be suspended.  If sbdp_skip_user_threads is true, the rest of the
11603831d35Sstevel  * flags are not used; if it is false, sbdp_check_user_stop_result
11703831d35Sstevel  * will be used to control whether or not we need to check suspend
11803831d35Sstevel  * result, and sbdp_allow_blocked_threads will be used to control
11903831d35Sstevel  * whether or not we allow suspend to continue if there are blocked
12003831d35Sstevel  * threads.  We allow all combinations of sbdp_check_user_stop_result
12103831d35Sstevel  * and sbdp_allow_block_threads, even though it might not make much
12203831d35Sstevel  * sense to not allow block threads when we don't even check stop
12303831d35Sstevel  * result.
12403831d35Sstevel  */
12503831d35Sstevel static int	sbdp_skip_user_threads = 0;		/* default to FALSE */
12603831d35Sstevel static int	sbdp_check_user_stop_result = 1;	/* default to TRUE */
12703831d35Sstevel static int	sbdp_allow_blocked_threads = 1;		/* default to TRUE */
12803831d35Sstevel 
12903831d35Sstevel 
13003831d35Sstevel static void
13103831d35Sstevel sbdp_stop_intr(void)
13203831d35Sstevel {
13303831d35Sstevel 	kpreempt_disable();
13403831d35Sstevel 	cyclic_suspend();
13503831d35Sstevel }
13603831d35Sstevel 
13703831d35Sstevel static void
13803831d35Sstevel sbdp_enable_intr(void)
13903831d35Sstevel {
14003831d35Sstevel 	cyclic_resume();
14103831d35Sstevel 	kpreempt_enable();
14203831d35Sstevel }
14303831d35Sstevel 
14403831d35Sstevel sbdp_sr_handle_t *
14503831d35Sstevel sbdp_get_sr_handle(void)
14603831d35Sstevel {
14703831d35Sstevel 	sbdp_sr_handle_t *srh;
14803831d35Sstevel 	srh = kmem_zalloc(sizeof (sbdp_sr_handle_t), KM_SLEEP);
14903831d35Sstevel 
15003831d35Sstevel 	return (srh);
15103831d35Sstevel }
15203831d35Sstevel 
15303831d35Sstevel void
15403831d35Sstevel sbdp_release_sr_handle(sbdp_sr_handle_t *srh)
15503831d35Sstevel {
15603831d35Sstevel 	ASSERT(SR_FAILED_DIP(srh) == NULL);
15703831d35Sstevel 	kmem_free((caddr_t)srh, sizeof (sbdp_sr_handle_t));
15803831d35Sstevel }
15903831d35Sstevel 
16003831d35Sstevel static int
16103831d35Sstevel sbdp_is_real_device(dev_info_t *dip)
16203831d35Sstevel {
16303831d35Sstevel 	struct regspec *regbuf = NULL;
16403831d35Sstevel 	int length = 0;
16503831d35Sstevel 	int rc;
16603831d35Sstevel 
16703831d35Sstevel 	if (ddi_get_driver(dip) == NULL)
16803831d35Sstevel 		return (0);
16903831d35Sstevel 
17003831d35Sstevel 	if (DEVI(dip)->devi_pm_flags & (PMC_NEEDS_SR|PMC_PARENTAL_SR))
17103831d35Sstevel 		return (1);
17203831d35Sstevel 	if (DEVI(dip)->devi_pm_flags & PMC_NO_SR)
17303831d35Sstevel 		return (0);
17403831d35Sstevel 
17503831d35Sstevel 	/*
17603831d35Sstevel 	 * now the general case
17703831d35Sstevel 	 */
17803831d35Sstevel 	rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
17903831d35Sstevel 	    (caddr_t)&regbuf, &length);
18003831d35Sstevel 	ASSERT(rc != DDI_PROP_NO_MEMORY);
18103831d35Sstevel 	if (rc != DDI_PROP_SUCCESS) {
18203831d35Sstevel 		return (0);
18303831d35Sstevel 	} else {
18403831d35Sstevel 		if ((length > 0) && (regbuf != NULL))
18503831d35Sstevel 			kmem_free(regbuf, length);
18603831d35Sstevel 		return (1);
18703831d35Sstevel 	}
18803831d35Sstevel }
18903831d35Sstevel 
19003831d35Sstevel #ifdef DEBUG
19103831d35Sstevel static int
19203831d35Sstevel sbdp_bypass_device(char *dname)
19303831d35Sstevel {
19403831d35Sstevel 	int i;
19503831d35Sstevel 	char **lname;
19603831d35Sstevel 	/* check the bypass list */
19703831d35Sstevel 	for (i = 0, lname = &sbdp_bypass_list[i]; **lname != '\0'; lname++) {
19803831d35Sstevel 		SBDP_DBG_QR("Checking %s\n", *lname);
19903831d35Sstevel 		if (strcmp(dname, sbdp_bypass_list[i++]) == 0)
20003831d35Sstevel 			return (1);
20103831d35Sstevel 	}
20203831d35Sstevel 	return (0);
20303831d35Sstevel }
20403831d35Sstevel #endif
20503831d35Sstevel 
20603831d35Sstevel static int
20703831d35Sstevel sbdp_resolve_devname(dev_info_t *dip, char *buffer, char *alias)
20803831d35Sstevel {
20903831d35Sstevel 	major_t	devmajor;
21003831d35Sstevel 	char	*aka, *name;
21103831d35Sstevel 
21203831d35Sstevel 	*buffer = *alias = 0;
21303831d35Sstevel 
21403831d35Sstevel 	if (dip == NULL)
21503831d35Sstevel 		return (-1);
21603831d35Sstevel 
21703831d35Sstevel 	if ((name = ddi_get_name(dip)) == NULL)
21803831d35Sstevel 		name = "<null name>";
21903831d35Sstevel 
22003831d35Sstevel 	aka = name;
22103831d35Sstevel 
22203831d35Sstevel 	if ((devmajor = ddi_name_to_major(aka)) != -1)
22303831d35Sstevel 		aka = ddi_major_to_name(devmajor);
22403831d35Sstevel 
22503831d35Sstevel 	(void) strcpy(buffer, name);
22603831d35Sstevel 
22703831d35Sstevel 	if (strcmp(name, aka))
22803831d35Sstevel 		(void) strcpy(alias, aka);
22903831d35Sstevel 	else
23003831d35Sstevel 		*alias = 0;
23103831d35Sstevel 
23203831d35Sstevel 	return (0);
23303831d35Sstevel }
23403831d35Sstevel 
23503831d35Sstevel typedef struct sbdp_ref {
23603831d35Sstevel 	int *refcount;
237df3cd224SVijay S Balakrishna 	int *refcount_non_gldv3;
23803831d35Sstevel 	sbd_error_t *sep;
23903831d35Sstevel } sbdp_ref_t;
24003831d35Sstevel 
24103831d35Sstevel static int
24203831d35Sstevel sbdp_check_dip(dev_info_t *dip, void *arg, uint_t ref)
24303831d35Sstevel {
24403831d35Sstevel 	char		*dname;
24503831d35Sstevel 	sbdp_ref_t	*sbrp = (sbdp_ref_t *)arg;
24603831d35Sstevel 
24703831d35Sstevel 	if (dip == NULL)
24803831d35Sstevel 		return (DDI_WALK_CONTINUE);
24903831d35Sstevel 
25003831d35Sstevel 	ASSERT(sbrp->sep != NULL);
25103831d35Sstevel 	ASSERT(sbrp->refcount != NULL);
25203831d35Sstevel 
25303831d35Sstevel 	if (!sbdp_is_real_device(dip))
25403831d35Sstevel 		return (DDI_WALK_CONTINUE);
25503831d35Sstevel 
25603831d35Sstevel 	dname = ddi_binding_name(dip);
25703831d35Sstevel 
25803831d35Sstevel 	if ((strcmp(dname, "pciclass,060940") == 0) || (strcmp(dname,
25903831d35Sstevel 	    "pciclass,060980") == 0)) {
26003831d35Sstevel 		(void) ddi_pathname(dip, sbdp_get_err_buf(sbrp->sep));
26103831d35Sstevel 		sbdp_set_err(sbrp->sep, ESBD_BUSY, NULL);
26203831d35Sstevel 		(*sbrp->refcount)++;
26303831d35Sstevel 		return (DDI_WALK_TERMINATE);
26403831d35Sstevel 	}
26503831d35Sstevel 
26603831d35Sstevel #ifdef DEBUG
26703831d35Sstevel 	if (sbdp_bypass_device(dname))
26803831d35Sstevel 		return (DDI_WALK_CONTINUE);
26903831d35Sstevel #endif
27003831d35Sstevel 
27103831d35Sstevel 	if (ref) {
272df3cd224SVijay S Balakrishna 		major_t	major;
273df3cd224SVijay S Balakrishna 
27403831d35Sstevel 		(*sbrp->refcount)++;
27503831d35Sstevel 		SBDP_DBG_QR("\n%s (major# %d) is referenced\n",
27603831d35Sstevel 		    dname, ddi_name_to_major(dname));
27703831d35Sstevel 		(void) ddi_pathname(dip, sbdp_get_err_buf(sbrp->sep));
278df3cd224SVijay S Balakrishna 		major = ddi_driver_major(dip);
279df3cd224SVijay S Balakrishna 		if (sbrp->refcount_non_gldv3 && NETWORK_PHYSDRV(major) &&
280df3cd224SVijay S Balakrishna 		    !GLDV3_DRV(major)) {
281df3cd224SVijay S Balakrishna 			(*sbrp->refcount_non_gldv3)++;
282df3cd224SVijay S Balakrishna 			return (DDI_WALK_CONTINUE);
283df3cd224SVijay S Balakrishna 		}
28403831d35Sstevel 		sbdp_set_err(sbrp->sep, ESBD_BUSY, NULL);
28503831d35Sstevel 		return (DDI_WALK_TERMINATE);
28603831d35Sstevel 	}
28703831d35Sstevel 	return (DDI_WALK_CONTINUE);
28803831d35Sstevel }
28903831d35Sstevel 
29003831d35Sstevel void
291df3cd224SVijay S Balakrishna sbdp_check_devices(dev_info_t *dip, int *refcount, sbd_error_t *sep,
292df3cd224SVijay S Balakrishna     int *refcount_non_gldv3)
29303831d35Sstevel {
29403831d35Sstevel 	sbdp_ref_t sbr;
29503831d35Sstevel 
29603831d35Sstevel 	sbr.refcount = refcount;
297df3cd224SVijay S Balakrishna 	sbr.refcount_non_gldv3 = refcount_non_gldv3;
29803831d35Sstevel 	sbr.sep = sep;
29903831d35Sstevel 
30003831d35Sstevel 	ASSERT(e_ddi_branch_held(dip));
30103831d35Sstevel 
30203831d35Sstevel 	(void) e_ddi_branch_referenced(dip, sbdp_check_dip, &sbr);
30303831d35Sstevel }
30403831d35Sstevel 
305b8073120Smb158278 /*
306b8073120Smb158278  * Starting from the root node suspend all devices in the device tree.
307b8073120Smb158278  * Assumes that all devices have already been marked busy.
308b8073120Smb158278  */
30903831d35Sstevel static int
310b8073120Smb158278 sbdp_suspend_devices_(dev_info_t *dip, sbdp_sr_handle_t *srh)
31103831d35Sstevel {
31203831d35Sstevel 	major_t	major;
31303831d35Sstevel 	char	*dname;
31403831d35Sstevel 
31503831d35Sstevel 	for (; dip != NULL; dip = ddi_get_next_sibling(dip)) {
31603831d35Sstevel 		char	d_name[40], d_alias[40], *d_info;
31703831d35Sstevel 
318b8073120Smb158278 		if (sbdp_suspend_devices_(ddi_get_child(dip), srh)) {
31903831d35Sstevel 			return (ENXIO);
32003831d35Sstevel 		}
32103831d35Sstevel 
32203831d35Sstevel 		if (!sbdp_is_real_device(dip))
32303831d35Sstevel 			continue;
32403831d35Sstevel 
32503831d35Sstevel 		major = (major_t)-1;
32603831d35Sstevel 		if ((dname = DEVI(dip)->devi_binding_name) != NULL)
32703831d35Sstevel 			major = ddi_name_to_major(dname);
32803831d35Sstevel 
32903831d35Sstevel #ifdef DEBUG
33003831d35Sstevel 		if (sbdp_bypass_device(dname)) {
33103831d35Sstevel 			SBDP_DBG_QR("bypassed suspend of %s (major# %d)\n",
33203831d35Sstevel 			    dname, major);
33303831d35Sstevel 			continue;
33403831d35Sstevel 		}
33503831d35Sstevel #endif
33603831d35Sstevel 
33703831d35Sstevel 		if ((d_info = ddi_get_name_addr(dip)) == NULL)
33803831d35Sstevel 			d_info = "<null>";
33903831d35Sstevel 
34003831d35Sstevel 		d_name[0] = 0;
34103831d35Sstevel 		if (sbdp_resolve_devname(dip, d_name, d_alias) == 0) {
34203831d35Sstevel 			if (d_alias[0] != 0) {
34303831d35Sstevel 				SBDP_DBG_QR("\tsuspending %s@%s (aka %s)\n",
34403831d35Sstevel 				    d_name, d_info, d_alias);
34503831d35Sstevel 			} else {
34603831d35Sstevel 				SBDP_DBG_QR("\tsuspending %s@%s\n",
34703831d35Sstevel 				    d_name, d_info);
34803831d35Sstevel 			}
34903831d35Sstevel 		} else {
35003831d35Sstevel 			SBDP_DBG_QR("\tsuspending %s@%s\n", dname, d_info);
35103831d35Sstevel 		}
35203831d35Sstevel 
35303831d35Sstevel 		if (devi_detach(dip, DDI_SUSPEND) != DDI_SUCCESS) {
35403831d35Sstevel 			(void) sprintf(sbdp_get_err_buf(&srh->sep),
35503831d35Sstevel 			    "%d", major);
35603831d35Sstevel 
35703831d35Sstevel 			sbdp_set_err(&srh->sep, ESGT_SUSPEND, NULL);
35803831d35Sstevel 			ndi_hold_devi(dip);
35903831d35Sstevel 			SR_FAILED_DIP(srh) = dip;
36003831d35Sstevel 			return (DDI_FAILURE);
36103831d35Sstevel 		}
36203831d35Sstevel 	}
36303831d35Sstevel 
36403831d35Sstevel 	return (DDI_SUCCESS);
36503831d35Sstevel }
36603831d35Sstevel 
367b8073120Smb158278 /*ARGSUSED*/
368b8073120Smb158278 static int
369b8073120Smb158278 sbdp_suspend_devices_enter(dev_info_t *dip, void *arg)
370b8073120Smb158278 {
371b8073120Smb158278 	struct dev_info *devi = DEVI(dip);
372b8073120Smb158278 	ndi_devi_enter(dip, &devi->devi_circular);
373b8073120Smb158278 	return (DDI_WALK_CONTINUE);
374b8073120Smb158278 }
375b8073120Smb158278 
376b8073120Smb158278 /*ARGSUSED*/
377b8073120Smb158278 static int
378b8073120Smb158278 sbdp_suspend_devices_exit(dev_info_t *dip, void *arg)
379b8073120Smb158278 {
380b8073120Smb158278 	struct dev_info *devi = DEVI(dip);
381b8073120Smb158278 	ndi_devi_exit(dip, devi->devi_circular);
382b8073120Smb158278 	return (DDI_WALK_CONTINUE);
383b8073120Smb158278 }
384b8073120Smb158278 
385b8073120Smb158278 /*
386b8073120Smb158278  * Before suspending devices first mark all device nodes busy. This
387b8073120Smb158278  * avoids a deadlock situation when another thread holds a device busy
388b8073120Smb158278  * and accesses an already suspended device.
389b8073120Smb158278  */
390b8073120Smb158278 static int
391b8073120Smb158278 sbdp_suspend_devices(dev_info_t *dip, sbdp_sr_handle_t *srh)
392b8073120Smb158278 {
393b8073120Smb158278 	int	rv;
394b8073120Smb158278 
395b8073120Smb158278 	/* assumes dip is ddi_root_node so no ndi_devi_enter required */
396b8073120Smb158278 	ASSERT(dip == ddi_root_node());
397b8073120Smb158278 	ddi_walk_devs(dip, sbdp_suspend_devices_enter, NULL);
398b8073120Smb158278 	rv = sbdp_suspend_devices_(dip, srh);
399b8073120Smb158278 	ddi_walk_devs(dip, sbdp_suspend_devices_exit, NULL);
400b8073120Smb158278 	return (rv);
401b8073120Smb158278 }
402b8073120Smb158278 
40303831d35Sstevel static void
40403831d35Sstevel sbdp_resume_devices(dev_info_t *start, sbdp_sr_handle_t *srh)
40503831d35Sstevel {
40603831d35Sstevel 	int circ;
40703831d35Sstevel 	dev_info_t	*dip, *next, *last = NULL;
40803831d35Sstevel 	char		*bn;
40903831d35Sstevel 	sbd_error_t	*sep;
41003831d35Sstevel 
41103831d35Sstevel 	sep = &srh->sep;
41203831d35Sstevel 
41303831d35Sstevel 	/* attach in reverse device tree order */
41403831d35Sstevel 	while (last != start) {
41503831d35Sstevel 		dip = start;
41603831d35Sstevel 		next = ddi_get_next_sibling(dip);
41703831d35Sstevel 		while (next != last && dip != SR_FAILED_DIP(srh)) {
41803831d35Sstevel 			dip = next;
41903831d35Sstevel 			next = ddi_get_next_sibling(dip);
42003831d35Sstevel 		}
42103831d35Sstevel 		if (dip == SR_FAILED_DIP(srh)) {
42203831d35Sstevel 			/* Release hold acquired in sbdp_suspend_devices() */
42303831d35Sstevel 			ndi_rele_devi(dip);
42403831d35Sstevel 			SR_FAILED_DIP(srh) = NULL;
42503831d35Sstevel 		} else if (sbdp_is_real_device(dip) &&
42603831d35Sstevel 		    SR_FAILED_DIP(srh) == NULL) {
42703831d35Sstevel 
42803831d35Sstevel 			if (DEVI(dip)->devi_binding_name != NULL) {
42903831d35Sstevel 				bn = ddi_binding_name(dip);
43003831d35Sstevel 			}
43103831d35Sstevel #ifdef DEBUG
43203831d35Sstevel 			if (!sbdp_bypass_device(bn)) {
43303831d35Sstevel #else
43403831d35Sstevel 			{
43503831d35Sstevel #endif
43603831d35Sstevel 				char	d_name[40], d_alias[40], *d_info;
43703831d35Sstevel 
43803831d35Sstevel 				d_name[0] = 0;
43903831d35Sstevel 				d_info = ddi_get_name_addr(dip);
44003831d35Sstevel 				if (d_info == NULL)
44103831d35Sstevel 					d_info = "<null>";
44203831d35Sstevel 
44303831d35Sstevel 				if (!sbdp_resolve_devname(dip, d_name,
44403831d35Sstevel 				    d_alias)) {
44503831d35Sstevel 					if (d_alias[0] != 0) {
44603831d35Sstevel 						SBDP_DBG_QR("\tresuming "
44703831d35Sstevel 						    "%s@%s (aka %s)\n",
44803831d35Sstevel 						    d_name, d_info,
44903831d35Sstevel 						    d_alias);
45003831d35Sstevel 					} else {
45103831d35Sstevel 						SBDP_DBG_QR("\tresuming "
45203831d35Sstevel 						    "%s@%s\n",
45303831d35Sstevel 						    d_name, d_info);
45403831d35Sstevel 					}
45503831d35Sstevel 				} else {
45603831d35Sstevel 					SBDP_DBG_QR("\tresuming %s@%s\n",
45703831d35Sstevel 					    bn, d_info);
45803831d35Sstevel 				}
45903831d35Sstevel 
46003831d35Sstevel 				if (devi_attach(dip, DDI_RESUME) !=
46103831d35Sstevel 				    DDI_SUCCESS) {
46203831d35Sstevel 					/*
46303831d35Sstevel 					 * Print a console warning,
46403831d35Sstevel 					 * set an errno of ESGT_RESUME,
46503831d35Sstevel 					 * and save the driver major
46603831d35Sstevel 					 * number in the e_str.
46703831d35Sstevel 					 */
46803831d35Sstevel 
46903831d35Sstevel 					(void) sprintf(sbdp_get_err_buf(sep),
47003831d35Sstevel 					    "%s@%s",
47103831d35Sstevel 					    d_name[0] ? d_name : bn, d_info);
47203831d35Sstevel 					SBDP_DBG_QR("\tFAILED to resume "
47303831d35Sstevel 					    "%s\n", sbdp_get_err_buf(sep));
47403831d35Sstevel 					sbdp_set_err(sep,
47503831d35Sstevel 					    ESGT_RESUME, NULL);
47603831d35Sstevel 				}
47703831d35Sstevel 			}
47803831d35Sstevel 		}
47903831d35Sstevel 		ndi_devi_enter(dip, &circ);
48003831d35Sstevel 		sbdp_resume_devices(ddi_get_child(dip), srh);
48103831d35Sstevel 		ndi_devi_exit(dip, circ);
48203831d35Sstevel 		last = dip;
48303831d35Sstevel 	}
48403831d35Sstevel }
48503831d35Sstevel 
48603831d35Sstevel /*
48703831d35Sstevel  * True if thread is virtually stopped.  Similar to CPR_VSTOPPED
48803831d35Sstevel  * but from DR point of view.  These user threads are waiting in
48903831d35Sstevel  * the kernel.  Once they return from kernel, they will process
49003831d35Sstevel  * the stop signal and stop.
49103831d35Sstevel  */
49203831d35Sstevel #define	SBDP_VSTOPPED(t)			\
49303831d35Sstevel 	((t)->t_state == TS_SLEEP &&		\
49403831d35Sstevel 	(t)->t_wchan != NULL &&			\
49503831d35Sstevel 	(t)->t_astflag &&		\
49603831d35Sstevel 	((t)->t_proc_flag & TP_CHKPT))
49703831d35Sstevel 
49803831d35Sstevel 
49903831d35Sstevel static int
50003831d35Sstevel sbdp_stop_user_threads(sbdp_sr_handle_t *srh)
50103831d35Sstevel {
50203831d35Sstevel 	int		count;
50303831d35Sstevel 	char		cache_psargs[PSARGSZ];
50403831d35Sstevel 	kthread_id_t	cache_tp;
50503831d35Sstevel 	uint_t		cache_t_state;
50603831d35Sstevel 	int		bailout;
50703831d35Sstevel 	sbd_error_t	*sep;
50803831d35Sstevel 	kthread_id_t 	tp;
50903831d35Sstevel 
51003831d35Sstevel 	extern void add_one_utstop();
51103831d35Sstevel 	extern void utstop_timedwait(clock_t);
51203831d35Sstevel 	extern void utstop_init(void);
51303831d35Sstevel 
51403831d35Sstevel #define	SBDP_UTSTOP_RETRY	4
51503831d35Sstevel #define	SBDP_UTSTOP_WAIT	hz
51603831d35Sstevel 
51703831d35Sstevel 	if (sbdp_skip_user_threads)
51803831d35Sstevel 		return (DDI_SUCCESS);
51903831d35Sstevel 
52003831d35Sstevel 	sep = &srh->sep;
52103831d35Sstevel 	ASSERT(sep);
52203831d35Sstevel 
52303831d35Sstevel 	utstop_init();
52403831d35Sstevel 
52503831d35Sstevel 	/* we need to try a few times to get past fork, etc. */
52603831d35Sstevel 	for (count = 0; count < SBDP_UTSTOP_RETRY; count++) {
52703831d35Sstevel 		/* walk the entire threadlist */
52803831d35Sstevel 		mutex_enter(&pidlock);
52903831d35Sstevel 		for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) {
53003831d35Sstevel 			proc_t *p = ttoproc(tp);
53103831d35Sstevel 
53203831d35Sstevel 			/* handle kernel threads separately */
53303831d35Sstevel 			if (p->p_as == &kas || p->p_stat == SZOMB)
53403831d35Sstevel 				continue;
53503831d35Sstevel 
53603831d35Sstevel 			mutex_enter(&p->p_lock);
53703831d35Sstevel 			thread_lock(tp);
53803831d35Sstevel 
53903831d35Sstevel 			if (tp->t_state == TS_STOPPED) {
54003831d35Sstevel 				/* add another reason to stop this thread */
54103831d35Sstevel 				tp->t_schedflag &= ~TS_RESUME;
54203831d35Sstevel 			} else {
54303831d35Sstevel 				tp->t_proc_flag |= TP_CHKPT;
54403831d35Sstevel 
54503831d35Sstevel 				thread_unlock(tp);
54603831d35Sstevel 				mutex_exit(&p->p_lock);
54703831d35Sstevel 				add_one_utstop();
54803831d35Sstevel 				mutex_enter(&p->p_lock);
54903831d35Sstevel 				thread_lock(tp);
55003831d35Sstevel 
55103831d35Sstevel 				aston(tp);
55203831d35Sstevel 
553c97ad5cdSakolb 				if (ISWAKEABLE(tp) || ISWAITING(tp)) {
55403831d35Sstevel 					setrun_locked(tp);
55503831d35Sstevel 				}
55603831d35Sstevel 			}
55703831d35Sstevel 
55803831d35Sstevel 			/* grab thread if needed */
55903831d35Sstevel 			if (tp->t_state == TS_ONPROC && tp->t_cpu != CPU)
56003831d35Sstevel 				poke_cpu(tp->t_cpu->cpu_id);
56103831d35Sstevel 
56203831d35Sstevel 
56303831d35Sstevel 			thread_unlock(tp);
56403831d35Sstevel 			mutex_exit(&p->p_lock);
56503831d35Sstevel 		}
56603831d35Sstevel 		mutex_exit(&pidlock);
56703831d35Sstevel 
56803831d35Sstevel 
56903831d35Sstevel 		/* let everything catch up */
57003831d35Sstevel 		utstop_timedwait(count * count * SBDP_UTSTOP_WAIT);
57103831d35Sstevel 
57203831d35Sstevel 
57303831d35Sstevel 		/* now, walk the threadlist again to see if we are done */
57403831d35Sstevel 		mutex_enter(&pidlock);
57503831d35Sstevel 		for (tp = curthread->t_next, bailout = 0;
57603831d35Sstevel 		    tp != curthread; tp = tp->t_next) {
57703831d35Sstevel 			proc_t *p = ttoproc(tp);
57803831d35Sstevel 
57903831d35Sstevel 			/* handle kernel threads separately */
58003831d35Sstevel 			if (p->p_as == &kas || p->p_stat == SZOMB)
58103831d35Sstevel 				continue;
58203831d35Sstevel 
58303831d35Sstevel 			/*
58403831d35Sstevel 			 * If this thread didn't stop, and we don't allow
58503831d35Sstevel 			 * unstopped blocked threads, bail.
58603831d35Sstevel 			 */
58703831d35Sstevel 			thread_lock(tp);
58803831d35Sstevel 			if (!CPR_ISTOPPED(tp) &&
58903831d35Sstevel 			    !(sbdp_allow_blocked_threads &&
59003831d35Sstevel 			    SBDP_VSTOPPED(tp))) {
59103831d35Sstevel 
59203831d35Sstevel 				/* nope, cache the details for later */
59303831d35Sstevel 				bcopy(p->p_user.u_psargs, cache_psargs,
59403831d35Sstevel 				    sizeof (cache_psargs));
59503831d35Sstevel 				cache_tp = tp;
59603831d35Sstevel 				cache_t_state = tp->t_state;
59703831d35Sstevel 				bailout = 1;
59803831d35Sstevel 			}
59903831d35Sstevel 			thread_unlock(tp);
60003831d35Sstevel 		}
60103831d35Sstevel 		mutex_exit(&pidlock);
60203831d35Sstevel 
60303831d35Sstevel 		/* were all the threads stopped? */
60403831d35Sstevel 		if (!bailout)
60503831d35Sstevel 			break;
60603831d35Sstevel 	}
60703831d35Sstevel 
60803831d35Sstevel 	/* were we unable to stop all threads after a few tries? */
60903831d35Sstevel 	if (bailout) {
61003831d35Sstevel 		cmn_err(CE_NOTE, "process: %s id: %p state: %x\n",
61107d06da5SSurya Prakki 		    cache_psargs, (void *)cache_tp, cache_t_state);
61203831d35Sstevel 
61303831d35Sstevel 		(void) sprintf(sbdp_get_err_buf(sep), "%s", cache_psargs);
61403831d35Sstevel 		sbdp_set_err(sep, ESGT_UTHREAD, NULL);
61503831d35Sstevel 		return (ESRCH);
61603831d35Sstevel 	}
61703831d35Sstevel 
61803831d35Sstevel 	return (DDI_SUCCESS);
61903831d35Sstevel }
62003831d35Sstevel 
62103831d35Sstevel static void
62203831d35Sstevel sbdp_start_user_threads(void)
62303831d35Sstevel {
62403831d35Sstevel 	kthread_id_t tp;
62503831d35Sstevel 
62603831d35Sstevel 	mutex_enter(&pidlock);
62703831d35Sstevel 
62803831d35Sstevel 	/* walk all threads and release them */
62903831d35Sstevel 	for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) {
63003831d35Sstevel 		proc_t *p = ttoproc(tp);
63103831d35Sstevel 
63203831d35Sstevel 		/* skip kernel threads */
63303831d35Sstevel 		if (ttoproc(tp)->p_as == &kas)
63403831d35Sstevel 			continue;
63503831d35Sstevel 
63603831d35Sstevel 		mutex_enter(&p->p_lock);
63703831d35Sstevel 		tp->t_proc_flag &= ~TP_CHKPT;
63803831d35Sstevel 		mutex_exit(&p->p_lock);
63903831d35Sstevel 
64003831d35Sstevel 		thread_lock(tp);
64103831d35Sstevel 		if (CPR_ISTOPPED(tp)) {
64203831d35Sstevel 			/* back on the runq */
64303831d35Sstevel 			tp->t_schedflag |= TS_RESUME;
64403831d35Sstevel 			setrun_locked(tp);
64503831d35Sstevel 		}
64603831d35Sstevel 		thread_unlock(tp);
64703831d35Sstevel 	}
64803831d35Sstevel 
64903831d35Sstevel 	mutex_exit(&pidlock);
65003831d35Sstevel }
65103831d35Sstevel 
65203831d35Sstevel static void
65303831d35Sstevel sbdp_signal_user(int sig)
65403831d35Sstevel {
65503831d35Sstevel 	struct proc *p;
65603831d35Sstevel 
65703831d35Sstevel 	mutex_enter(&pidlock);
65803831d35Sstevel 
65903831d35Sstevel 	for (p = practive; p != NULL; p = p->p_next) {
66003831d35Sstevel 		/* only user threads */
66103831d35Sstevel 		if (p->p_exec == NULL || p->p_stat == SZOMB ||
66203831d35Sstevel 		    p == proc_init || p == ttoproc(curthread))
66303831d35Sstevel 			continue;
66403831d35Sstevel 
66503831d35Sstevel 		mutex_enter(&p->p_lock);
66603831d35Sstevel 		sigtoproc(p, NULL, sig);
66703831d35Sstevel 		mutex_exit(&p->p_lock);
66803831d35Sstevel 	}
66903831d35Sstevel 
67003831d35Sstevel 	mutex_exit(&pidlock);
67103831d35Sstevel 
67203831d35Sstevel 	/* add a bit of delay */
67303831d35Sstevel 	delay(hz);
67403831d35Sstevel }
67503831d35Sstevel 
67603831d35Sstevel static uint_t saved_watchdog_seconds;
67703831d35Sstevel 
67803831d35Sstevel void
67903831d35Sstevel sbdp_resume(sbdp_sr_handle_t *srh)
68003831d35Sstevel {
68103831d35Sstevel 	/*
68203831d35Sstevel 	 * update the signature block
68303831d35Sstevel 	 */
68403831d35Sstevel 	CPU_SIGNATURE(OS_SIG, SIGST_RESUME_INPROGRESS, SIGSUBST_NULL,
68503831d35Sstevel 	    CPU->cpu_id);
68603831d35Sstevel 
68703831d35Sstevel 	switch (SR_STATE(srh)) {
68803831d35Sstevel 	case SBDP_SRSTATE_FULL:
68903831d35Sstevel 
69003831d35Sstevel 		ASSERT(MUTEX_HELD(&cpu_lock));
69103831d35Sstevel 
692646e55b6Scth 		/*
693646e55b6Scth 		 * Prevent false alarm in tod_validate() due to tod
694646e55b6Scth 		 * value change between suspend and resume
695646e55b6Scth 		 */
696646e55b6Scth 		mutex_enter(&tod_lock);
6978fc99e42STrevor Thompson 		tod_status_set(TOD_DR_RESUME_DONE);
698646e55b6Scth 		mutex_exit(&tod_lock);
699646e55b6Scth 
70003831d35Sstevel 		sbdp_enable_intr(); 	/* enable intr & clock */
70103831d35Sstevel 
70203831d35Sstevel 		/*
70303831d35Sstevel 		 * release all the other cpus
70403831d35Sstevel 		 * using start_cpus() vice sbdp_release_cpus()
70503831d35Sstevel 		 */
70603831d35Sstevel 		start_cpus();
70703831d35Sstevel 		mutex_exit(&cpu_lock);
70803831d35Sstevel 
70903831d35Sstevel 		/*
71003831d35Sstevel 		 * If we suspended hw watchdog at suspend,
71103831d35Sstevel 		 * re-enable it now.
71203831d35Sstevel 		 */
71303831d35Sstevel 		if (SR_CHECK_FLAG(srh, SR_FLAG_WATCHDOG)) {
71403831d35Sstevel 			mutex_enter(&tod_lock);
71503831d35Sstevel 			tod_ops.tod_set_watchdog_timer(
71603831d35Sstevel 			    saved_watchdog_seconds);
71703831d35Sstevel 			mutex_exit(&tod_lock);
71803831d35Sstevel 		}
71903831d35Sstevel 
72003831d35Sstevel 		/* FALLTHROUGH */
72103831d35Sstevel 
72203831d35Sstevel 	case SBDP_SRSTATE_DRIVER:
72303831d35Sstevel 		/*
72403831d35Sstevel 		 * resume devices: root node doesn't have to
72503831d35Sstevel 		 * be held in any way.
72603831d35Sstevel 		 */
72703831d35Sstevel 		sbdp_resume_devices(ddi_root_node(), srh);
72803831d35Sstevel 
72903831d35Sstevel 		/*
73003831d35Sstevel 		 * resume the lock manager
73103831d35Sstevel 		 */
73203831d35Sstevel 		lm_cprresume();
73303831d35Sstevel 
73403831d35Sstevel 		/* FALLTHROUGH */
73503831d35Sstevel 
73603831d35Sstevel 	case SBDP_SRSTATE_USER:
73703831d35Sstevel 		/*
73803831d35Sstevel 		 * finally, resume user threads
73903831d35Sstevel 		 */
74003831d35Sstevel 		if (!sbdp_skip_user_threads) {
74103831d35Sstevel 			SBDP_DBG_QR("DR: resuming user threads...\n");
74203831d35Sstevel 			sbdp_start_user_threads();
74303831d35Sstevel 		}
74403831d35Sstevel 		/* FALLTHROUGH */
74503831d35Sstevel 
74603831d35Sstevel 	case SBDP_SRSTATE_BEGIN:
74703831d35Sstevel 	default:
74803831d35Sstevel 		/*
74903831d35Sstevel 		 * let those who care know that we've just resumed
75003831d35Sstevel 		 */
75103831d35Sstevel 		SBDP_DBG_QR("sending SIGTHAW...\n");
75203831d35Sstevel 		sbdp_signal_user(SIGTHAW);
75303831d35Sstevel 		break;
75403831d35Sstevel 	}
75503831d35Sstevel 
75603831d35Sstevel 	/*
75703831d35Sstevel 	 * update the signature block
75803831d35Sstevel 	 */
75903831d35Sstevel 	CPU_SIGNATURE(OS_SIG, SIGST_RUN, SIGSUBST_NULL, CPU->cpu_id);
76003831d35Sstevel 
76103831d35Sstevel 	SBDP_DBG_QR("DR: resume COMPLETED\n");
76203831d35Sstevel }
76303831d35Sstevel 
76403831d35Sstevel int
76503831d35Sstevel sbdp_suspend(sbdp_sr_handle_t *srh)
76603831d35Sstevel {
76703831d35Sstevel 	int force;
76803831d35Sstevel 	int rc = DDI_SUCCESS;
76903831d35Sstevel 
77003831d35Sstevel 	force = (srh && (srh->sr_flags & SBDP_IOCTL_FLAG_FORCE));
77103831d35Sstevel 
77203831d35Sstevel 	/*
77303831d35Sstevel 	 * if no force flag, check for unsafe drivers
77403831d35Sstevel 	 */
77503831d35Sstevel 	if (force) {
77603831d35Sstevel 		SBDP_DBG_QR("\nsbdp_suspend invoked with force flag");
77703831d35Sstevel 	}
77803831d35Sstevel 
77903831d35Sstevel 	/*
78003831d35Sstevel 	 * update the signature block
78103831d35Sstevel 	 */
78203831d35Sstevel 	CPU_SIGNATURE(OS_SIG, SIGST_QUIESCE_INPROGRESS, SIGSUBST_NULL,
78303831d35Sstevel 	    CPU->cpu_id);
78403831d35Sstevel 
78503831d35Sstevel 	/*
78603831d35Sstevel 	 * first, stop all user threads
78703831d35Sstevel 	 */
78803831d35Sstevel 	SBDP_DBG_QR("SBDP: suspending user threads...\n");
78903831d35Sstevel 	SR_SET_STATE(srh, SBDP_SRSTATE_USER);
79003831d35Sstevel 	if (((rc = sbdp_stop_user_threads(srh)) != DDI_SUCCESS) &&
79103831d35Sstevel 	    sbdp_check_user_stop_result) {
79203831d35Sstevel 		sbdp_resume(srh);
79303831d35Sstevel 		return (rc);
79403831d35Sstevel 	}
79503831d35Sstevel 
79603831d35Sstevel #ifndef	SKIP_SYNC
79703831d35Sstevel 	/*
79803831d35Sstevel 	 * This sync swap out all user pages
79903831d35Sstevel 	 */
80003831d35Sstevel 	vfs_sync(SYNC_ALL);
80103831d35Sstevel #endif
80203831d35Sstevel 
80303831d35Sstevel 	/*
80403831d35Sstevel 	 * special treatment for lock manager
80503831d35Sstevel 	 */
80603831d35Sstevel 	lm_cprsuspend();
80703831d35Sstevel 
80803831d35Sstevel #ifndef	SKIP_SYNC
80903831d35Sstevel 	/*
81003831d35Sstevel 	 * sync the file system in case we never make it back
81103831d35Sstevel 	 */
81203831d35Sstevel 	sync();
81303831d35Sstevel 
81403831d35Sstevel #endif
81503831d35Sstevel 	/*
81603831d35Sstevel 	 * now suspend drivers
81703831d35Sstevel 	 */
81803831d35Sstevel 	SBDP_DBG_QR("SBDP: suspending drivers...\n");
81903831d35Sstevel 	SR_SET_STATE(srh, SBDP_SRSTATE_DRIVER);
82003831d35Sstevel 
82103831d35Sstevel 	/*
82203831d35Sstevel 	 * Root node doesn't have to be held in any way.
82303831d35Sstevel 	 */
82403831d35Sstevel 	if ((rc = sbdp_suspend_devices(ddi_root_node(), srh)) != DDI_SUCCESS) {
82503831d35Sstevel 		sbdp_resume(srh);
82603831d35Sstevel 		return (rc);
82703831d35Sstevel 	}
82803831d35Sstevel 
82903831d35Sstevel 	/*
83003831d35Sstevel 	 * finally, grab all cpus
83103831d35Sstevel 	 */
83203831d35Sstevel 	SR_SET_STATE(srh, SBDP_SRSTATE_FULL);
83303831d35Sstevel 
83403831d35Sstevel 	/*
83503831d35Sstevel 	 * if watchdog was activated, disable it
83603831d35Sstevel 	 */
83703831d35Sstevel 	if (watchdog_activated) {
83803831d35Sstevel 		mutex_enter(&tod_lock);
83903831d35Sstevel 		saved_watchdog_seconds = tod_ops.tod_clear_watchdog_timer();
84003831d35Sstevel 		mutex_exit(&tod_lock);
84103831d35Sstevel 		SR_SET_FLAG(srh, SR_FLAG_WATCHDOG);
84203831d35Sstevel 	} else {
84303831d35Sstevel 		SR_CLEAR_FLAG(srh, SR_FLAG_WATCHDOG);
84403831d35Sstevel 	}
84503831d35Sstevel 
84603831d35Sstevel 	mutex_enter(&cpu_lock);
847*0ed5c46eSJosef 'Jeff' Sipek 	pause_cpus(NULL, NULL);
84803831d35Sstevel 	sbdp_stop_intr();
84903831d35Sstevel 
85003831d35Sstevel 	/*
85103831d35Sstevel 	 * update the signature block
85203831d35Sstevel 	 */
85303831d35Sstevel 	CPU_SIGNATURE(OS_SIG, SIGST_QUIESCED, SIGSUBST_NULL, CPU->cpu_id);
85403831d35Sstevel 
85503831d35Sstevel 	return (rc);
85603831d35Sstevel }
85703831d35Sstevel 
85803831d35Sstevel /*ARGSUSED*/
85903831d35Sstevel int
86003831d35Sstevel sbdp_test_suspend(sbdp_handle_t *hp)
86103831d35Sstevel {
86203831d35Sstevel 	sbdp_sr_handle_t	*srh;
86303831d35Sstevel 	int			err;
86403831d35Sstevel 
86503831d35Sstevel 	SBDP_DBG_QR("%s...\n", "sbdp_test_suspend");
86603831d35Sstevel 
86703831d35Sstevel 	srh = sbdp_get_sr_handle();
86803831d35Sstevel 
86903831d35Sstevel 	srh->sr_flags = hp->h_flags;
87003831d35Sstevel 
87103831d35Sstevel 	if ((err = sbdp_suspend(srh)) == DDI_SUCCESS) {
87203831d35Sstevel 		sbdp_resume(srh);
87303831d35Sstevel 	} else {
87403831d35Sstevel 		SBDP_DBG_MISC("sbdp_suspend() failed, err = 0x%x\n", err);
87503831d35Sstevel 	}
87603831d35Sstevel 	sbdp_release_sr_handle(srh);
87703831d35Sstevel 
87803831d35Sstevel 	return (0);
87903831d35Sstevel }
88003831d35Sstevel 
88103831d35Sstevel #ifdef	DEBUG
88203831d35Sstevel int
88303831d35Sstevel sbdp_passthru_test_quiesce(sbdp_handle_t *hp, void *arg)
88403831d35Sstevel {
88503831d35Sstevel 	_NOTE(ARGUNUSED(arg))
88603831d35Sstevel 
88703831d35Sstevel 	return (sbdp_test_suspend(hp));
88803831d35Sstevel }
88903831d35Sstevel #endif
890