xref: /onnv-gate/usr/src/uts/i86pc/io/dr/dr_quiesce.c (revision 12885:dc96b005136b)
112004Sjiang.liu@intel.com /*
212004Sjiang.liu@intel.com  * CDDL HEADER START
312004Sjiang.liu@intel.com  *
412004Sjiang.liu@intel.com  * The contents of this file are subject to the terms of the
512004Sjiang.liu@intel.com  * Common Development and Distribution License (the "License").
612004Sjiang.liu@intel.com  * You may not use this file except in compliance with the License.
712004Sjiang.liu@intel.com  *
812004Sjiang.liu@intel.com  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
912004Sjiang.liu@intel.com  * or http://www.opensolaris.org/os/licensing.
1012004Sjiang.liu@intel.com  * See the License for the specific language governing permissions
1112004Sjiang.liu@intel.com  * and limitations under the License.
1212004Sjiang.liu@intel.com  *
1312004Sjiang.liu@intel.com  * When distributing Covered Code, include this CDDL HEADER in each
1412004Sjiang.liu@intel.com  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1512004Sjiang.liu@intel.com  * If applicable, add the following below this CDDL HEADER, with the
1612004Sjiang.liu@intel.com  * fields enclosed by brackets "[]" replaced with your own identifying
1712004Sjiang.liu@intel.com  * information: Portions Copyright [yyyy] [name of copyright owner]
1812004Sjiang.liu@intel.com  *
1912004Sjiang.liu@intel.com  * CDDL HEADER END
2012004Sjiang.liu@intel.com  */
2112004Sjiang.liu@intel.com 
2212004Sjiang.liu@intel.com /*
23*12885SMary.Beale@Sun.COM  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2412004Sjiang.liu@intel.com  */
2512004Sjiang.liu@intel.com 
2612004Sjiang.liu@intel.com /*
2712004Sjiang.liu@intel.com  * A CPR derivative specifically for starfire/starcat
2812004Sjiang.liu@intel.com  * X86 doesn't make use of the quiesce interfaces, it's kept for simplicity.
2912004Sjiang.liu@intel.com  */
3012004Sjiang.liu@intel.com 
3112004Sjiang.liu@intel.com #include <sys/types.h>
3212004Sjiang.liu@intel.com #include <sys/systm.h>
3312004Sjiang.liu@intel.com #include <sys/machparam.h>
3412004Sjiang.liu@intel.com #include <sys/machsystm.h>
3512004Sjiang.liu@intel.com #include <sys/ddi.h>
3612004Sjiang.liu@intel.com #define	SUNDDI_IMPL
3712004Sjiang.liu@intel.com #include <sys/sunddi.h>
3812004Sjiang.liu@intel.com #include <sys/sunndi.h>
3912004Sjiang.liu@intel.com #include <sys/devctl.h>
4012004Sjiang.liu@intel.com #include <sys/time.h>
4112004Sjiang.liu@intel.com #include <sys/kmem.h>
4212004Sjiang.liu@intel.com #include <nfs/lm.h>
4312004Sjiang.liu@intel.com #include <sys/ddi_impldefs.h>
4412004Sjiang.liu@intel.com #include <sys/ndi_impldefs.h>
4512004Sjiang.liu@intel.com #include <sys/obpdefs.h>
4612004Sjiang.liu@intel.com #include <sys/cmn_err.h>
4712004Sjiang.liu@intel.com #include <sys/debug.h>
4812004Sjiang.liu@intel.com #include <sys/errno.h>
4912004Sjiang.liu@intel.com #include <sys/callb.h>
5012004Sjiang.liu@intel.com #include <sys/clock.h>
5112004Sjiang.liu@intel.com #include <sys/x_call.h>
5212004Sjiang.liu@intel.com #include <sys/cpuvar.h>
5312004Sjiang.liu@intel.com #include <sys/epm.h>
5412004Sjiang.liu@intel.com #include <sys/vfs.h>
5512004Sjiang.liu@intel.com #include <sys/promif.h>
5612004Sjiang.liu@intel.com #include <sys/conf.h>
5712004Sjiang.liu@intel.com #include <sys/cyclic.h>
5812004Sjiang.liu@intel.com 
5912004Sjiang.liu@intel.com #include <sys/dr.h>
6012004Sjiang.liu@intel.com #include <sys/dr_util.h>
6112004Sjiang.liu@intel.com 
6212004Sjiang.liu@intel.com extern void	e_ddi_enter_driver_list(struct devnames *dnp, int *listcnt);
6312004Sjiang.liu@intel.com extern void	e_ddi_exit_driver_list(struct devnames *dnp, int listcnt);
6412004Sjiang.liu@intel.com extern int	is_pseudo_device(dev_info_t *dip);
6512004Sjiang.liu@intel.com 
6612004Sjiang.liu@intel.com extern kmutex_t	cpu_lock;
6712004Sjiang.liu@intel.com extern dr_unsafe_devs_t dr_unsafe_devs;
6812004Sjiang.liu@intel.com 
6912004Sjiang.liu@intel.com static int		dr_is_real_device(dev_info_t *dip);
7012004Sjiang.liu@intel.com static int		dr_is_unsafe_major(major_t major);
7112004Sjiang.liu@intel.com static int		dr_bypass_device(char *dname);
7212004Sjiang.liu@intel.com static int		dr_check_dip(dev_info_t *dip, void *arg, uint_t ref);
7312004Sjiang.liu@intel.com static int		dr_resolve_devname(dev_info_t *dip, char *buffer,
7412004Sjiang.liu@intel.com 				char *alias);
7512004Sjiang.liu@intel.com static sbd_error_t	*drerr_int(int e_code, uint64_t *arr, int idx,
7612004Sjiang.liu@intel.com 				int majors);
7712004Sjiang.liu@intel.com static int		dr_add_int(uint64_t *arr, int idx, int len,
7812004Sjiang.liu@intel.com 				uint64_t val);
7912004Sjiang.liu@intel.com 
8012004Sjiang.liu@intel.com int dr_pt_test_suspend(dr_handle_t *hp);
8112004Sjiang.liu@intel.com 
8212004Sjiang.liu@intel.com /*
8312004Sjiang.liu@intel.com  * dr_quiesce.c interface
8412004Sjiang.liu@intel.com  * NOTE: states used internally by dr_suspend and dr_resume
8512004Sjiang.liu@intel.com  */
8612004Sjiang.liu@intel.com typedef enum dr_suspend_state {
8712004Sjiang.liu@intel.com 	DR_SRSTATE_BEGIN = 0,
8812004Sjiang.liu@intel.com 	DR_SRSTATE_USER,
8912004Sjiang.liu@intel.com 	DR_SRSTATE_DRIVER,
9012004Sjiang.liu@intel.com 	DR_SRSTATE_FULL
9112004Sjiang.liu@intel.com } suspend_state_t;
9212004Sjiang.liu@intel.com 
9312004Sjiang.liu@intel.com struct dr_sr_handle {
9412004Sjiang.liu@intel.com 	dr_handle_t		*sr_dr_handlep;
9512004Sjiang.liu@intel.com 	dev_info_t		*sr_failed_dip;
9612004Sjiang.liu@intel.com 	suspend_state_t		sr_suspend_state;
9712004Sjiang.liu@intel.com 	uint_t			sr_flags;
9812004Sjiang.liu@intel.com 	uint64_t		sr_err_ints[DR_MAX_ERR_INT];
9912004Sjiang.liu@intel.com 	int			sr_err_idx;
10012004Sjiang.liu@intel.com };
10112004Sjiang.liu@intel.com 
10212004Sjiang.liu@intel.com #define	SR_FLAG_WATCHDOG	0x1
10312004Sjiang.liu@intel.com 
10412004Sjiang.liu@intel.com /*
10512004Sjiang.liu@intel.com  * XXX
10612004Sjiang.liu@intel.com  * This hack will go away before RTI.  Just for testing.
10712004Sjiang.liu@intel.com  * List of drivers to bypass when performing a suspend.
10812004Sjiang.liu@intel.com  */
10912004Sjiang.liu@intel.com static char *dr_bypass_list[] = {
11012004Sjiang.liu@intel.com 	""
11112004Sjiang.liu@intel.com };
11212004Sjiang.liu@intel.com 
11312004Sjiang.liu@intel.com 
11412004Sjiang.liu@intel.com #define		SKIP_SYNC	/* bypass sync ops in dr_suspend */
11512004Sjiang.liu@intel.com 
11612004Sjiang.liu@intel.com /*
11712004Sjiang.liu@intel.com  * dr_skip_user_threads is used to control if user threads should
11812004Sjiang.liu@intel.com  * be suspended.  If dr_skip_user_threads is true, the rest of the
11912004Sjiang.liu@intel.com  * flags are not used; if it is false, dr_check_user_stop_result
12012004Sjiang.liu@intel.com  * will be used to control whether or not we need to check suspend
12112004Sjiang.liu@intel.com  * result, and dr_allow_blocked_threads will be used to control
12212004Sjiang.liu@intel.com  * whether or not we allow suspend to continue if there are blocked
12312004Sjiang.liu@intel.com  * threads.  We allow all combinations of dr_check_user_stop_result
12412004Sjiang.liu@intel.com  * and dr_allow_block_threads, even though it might not make much
12512004Sjiang.liu@intel.com  * sense to not allow block threads when we don't even check stop
12612004Sjiang.liu@intel.com  * result.
12712004Sjiang.liu@intel.com  */
12812004Sjiang.liu@intel.com static int	dr_skip_user_threads = 0;	/* default to FALSE */
12912004Sjiang.liu@intel.com static int	dr_check_user_stop_result = 1;	/* default to TRUE */
13012004Sjiang.liu@intel.com static int	dr_allow_blocked_threads = 1;	/* default to TRUE */
13112004Sjiang.liu@intel.com 
13212004Sjiang.liu@intel.com #define	DR_CPU_LOOP_MSEC	1000
13312004Sjiang.liu@intel.com 
13412004Sjiang.liu@intel.com static void
dr_stop_intr(void)13512004Sjiang.liu@intel.com dr_stop_intr(void)
13612004Sjiang.liu@intel.com {
13712004Sjiang.liu@intel.com 	ASSERT(MUTEX_HELD(&cpu_lock));
13812004Sjiang.liu@intel.com 
13912004Sjiang.liu@intel.com 	kpreempt_disable();
14012004Sjiang.liu@intel.com 	cyclic_suspend();
14112004Sjiang.liu@intel.com }
14212004Sjiang.liu@intel.com 
14312004Sjiang.liu@intel.com static void
dr_enable_intr(void)14412004Sjiang.liu@intel.com dr_enable_intr(void)
14512004Sjiang.liu@intel.com {
14612004Sjiang.liu@intel.com 	ASSERT(MUTEX_HELD(&cpu_lock));
14712004Sjiang.liu@intel.com 
14812004Sjiang.liu@intel.com 	cyclic_resume();
14912004Sjiang.liu@intel.com 	kpreempt_enable();
15012004Sjiang.liu@intel.com }
15112004Sjiang.liu@intel.com 
15212004Sjiang.liu@intel.com dr_sr_handle_t *
dr_get_sr_handle(dr_handle_t * hp)15312004Sjiang.liu@intel.com dr_get_sr_handle(dr_handle_t *hp)
15412004Sjiang.liu@intel.com {
15512004Sjiang.liu@intel.com 	dr_sr_handle_t *srh;
15612004Sjiang.liu@intel.com 
15712004Sjiang.liu@intel.com 	srh = GETSTRUCT(dr_sr_handle_t, 1);
15812004Sjiang.liu@intel.com 	srh->sr_dr_handlep = hp;
15912004Sjiang.liu@intel.com 
16012004Sjiang.liu@intel.com 	return (srh);
16112004Sjiang.liu@intel.com }
16212004Sjiang.liu@intel.com 
16312004Sjiang.liu@intel.com void
dr_release_sr_handle(dr_sr_handle_t * srh)16412004Sjiang.liu@intel.com dr_release_sr_handle(dr_sr_handle_t *srh)
16512004Sjiang.liu@intel.com {
16612004Sjiang.liu@intel.com 	ASSERT(srh->sr_failed_dip == NULL);
16712004Sjiang.liu@intel.com 	FREESTRUCT(srh, dr_sr_handle_t, 1);
16812004Sjiang.liu@intel.com }
16912004Sjiang.liu@intel.com 
17012004Sjiang.liu@intel.com static int
dr_is_real_device(dev_info_t * dip)17112004Sjiang.liu@intel.com dr_is_real_device(dev_info_t *dip)
17212004Sjiang.liu@intel.com {
17312004Sjiang.liu@intel.com 	struct regspec *regbuf = NULL;
17412004Sjiang.liu@intel.com 	int length = 0;
17512004Sjiang.liu@intel.com 	int rc;
17612004Sjiang.liu@intel.com 
17712004Sjiang.liu@intel.com 	if (ddi_get_driver(dip) == NULL)
17812004Sjiang.liu@intel.com 		return (0);
17912004Sjiang.liu@intel.com 
18012004Sjiang.liu@intel.com 	if (DEVI(dip)->devi_pm_flags & (PMC_NEEDS_SR|PMC_PARENTAL_SR))
18112004Sjiang.liu@intel.com 		return (1);
18212004Sjiang.liu@intel.com 	if (DEVI(dip)->devi_pm_flags & PMC_NO_SR)
18312004Sjiang.liu@intel.com 		return (0);
18412004Sjiang.liu@intel.com 
18512004Sjiang.liu@intel.com 	/*
18612004Sjiang.liu@intel.com 	 * now the general case
18712004Sjiang.liu@intel.com 	 */
18812004Sjiang.liu@intel.com 	rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
18912004Sjiang.liu@intel.com 	    (caddr_t)&regbuf, &length);
19012004Sjiang.liu@intel.com 	ASSERT(rc != DDI_PROP_NO_MEMORY);
19112004Sjiang.liu@intel.com 	if (rc != DDI_PROP_SUCCESS) {
19212004Sjiang.liu@intel.com 		return (0);
19312004Sjiang.liu@intel.com 	} else {
19412004Sjiang.liu@intel.com 		if ((length > 0) && (regbuf != NULL))
19512004Sjiang.liu@intel.com 			kmem_free(regbuf, length);
19612004Sjiang.liu@intel.com 		return (1);
19712004Sjiang.liu@intel.com 	}
19812004Sjiang.liu@intel.com }
19912004Sjiang.liu@intel.com 
20012004Sjiang.liu@intel.com static int
dr_is_unsafe_major(major_t major)20112004Sjiang.liu@intel.com dr_is_unsafe_major(major_t major)
20212004Sjiang.liu@intel.com {
20312004Sjiang.liu@intel.com 	char    *dname, **cpp;
20412004Sjiang.liu@intel.com 	int	i, ndevs;
20512004Sjiang.liu@intel.com 
20612004Sjiang.liu@intel.com 	if ((dname = ddi_major_to_name(major)) == NULL) {
20712004Sjiang.liu@intel.com 		PR_QR("dr_is_unsafe_major: invalid major # %d\n", major);
20812004Sjiang.liu@intel.com 		return (0);
20912004Sjiang.liu@intel.com 	}
21012004Sjiang.liu@intel.com 
21112004Sjiang.liu@intel.com 	ndevs = dr_unsafe_devs.ndevs;
21212004Sjiang.liu@intel.com 	for (i = 0, cpp = dr_unsafe_devs.devnames; i < ndevs; i++) {
21312004Sjiang.liu@intel.com 		if (strcmp(dname, *cpp++) == 0)
21412004Sjiang.liu@intel.com 			return (1);
21512004Sjiang.liu@intel.com 	}
21612004Sjiang.liu@intel.com 	return (0);
21712004Sjiang.liu@intel.com }
21812004Sjiang.liu@intel.com 
21912004Sjiang.liu@intel.com static int
dr_bypass_device(char * dname)22012004Sjiang.liu@intel.com dr_bypass_device(char *dname)
22112004Sjiang.liu@intel.com {
22212004Sjiang.liu@intel.com 	int i;
22312004Sjiang.liu@intel.com 	char **lname;
224*12885SMary.Beale@Sun.COM 
225*12885SMary.Beale@Sun.COM 	if (dname == NULL)
226*12885SMary.Beale@Sun.COM 		return (0);
227*12885SMary.Beale@Sun.COM 
22812004Sjiang.liu@intel.com 	/* check the bypass list */
22912004Sjiang.liu@intel.com 	for (i = 0, lname = &dr_bypass_list[i]; **lname != '\0'; lname++) {
23012004Sjiang.liu@intel.com 		if (strcmp(dname, dr_bypass_list[i++]) == 0)
23112004Sjiang.liu@intel.com 			return (1);
23212004Sjiang.liu@intel.com 	}
23312004Sjiang.liu@intel.com 	return (0);
23412004Sjiang.liu@intel.com }
23512004Sjiang.liu@intel.com 
23612004Sjiang.liu@intel.com static int
dr_resolve_devname(dev_info_t * dip,char * buffer,char * alias)23712004Sjiang.liu@intel.com dr_resolve_devname(dev_info_t *dip, char *buffer, char *alias)
23812004Sjiang.liu@intel.com {
23912004Sjiang.liu@intel.com 	major_t	devmajor;
24012004Sjiang.liu@intel.com 	char	*aka, *name;
24112004Sjiang.liu@intel.com 
24212004Sjiang.liu@intel.com 	*buffer = *alias = 0;
24312004Sjiang.liu@intel.com 
24412004Sjiang.liu@intel.com 	if (dip == NULL)
24512004Sjiang.liu@intel.com 		return (-1);
24612004Sjiang.liu@intel.com 
24712004Sjiang.liu@intel.com 	if ((name = ddi_get_name(dip)) == NULL)
24812004Sjiang.liu@intel.com 		name = "<null name>";
24912004Sjiang.liu@intel.com 
25012004Sjiang.liu@intel.com 	aka = name;
25112004Sjiang.liu@intel.com 
25212004Sjiang.liu@intel.com 	if ((devmajor = ddi_name_to_major(aka)) != DDI_MAJOR_T_NONE)
25312004Sjiang.liu@intel.com 		aka = ddi_major_to_name(devmajor);
25412004Sjiang.liu@intel.com 
25512004Sjiang.liu@intel.com 	(void) strcpy(buffer, name);
25612004Sjiang.liu@intel.com 
25712004Sjiang.liu@intel.com 	if (strcmp(name, aka))
25812004Sjiang.liu@intel.com 		(void) strcpy(alias, aka);
25912004Sjiang.liu@intel.com 	else
26012004Sjiang.liu@intel.com 		*alias = 0;
26112004Sjiang.liu@intel.com 
26212004Sjiang.liu@intel.com 	return (0);
26312004Sjiang.liu@intel.com }
26412004Sjiang.liu@intel.com 
26512004Sjiang.liu@intel.com struct dr_ref {
26612004Sjiang.liu@intel.com 	int		*refcount;
26712004Sjiang.liu@intel.com 	int		*refcount_non_gldv3;
26812004Sjiang.liu@intel.com 	uint64_t	*arr;
26912004Sjiang.liu@intel.com 	int		*idx;
27012004Sjiang.liu@intel.com 	int		len;
27112004Sjiang.liu@intel.com };
27212004Sjiang.liu@intel.com 
27312004Sjiang.liu@intel.com /* ARGSUSED */
27412004Sjiang.liu@intel.com static int
dr_check_dip(dev_info_t * dip,void * arg,uint_t ref)27512004Sjiang.liu@intel.com dr_check_dip(dev_info_t *dip, void *arg, uint_t ref)
27612004Sjiang.liu@intel.com {
27712004Sjiang.liu@intel.com 	major_t		major;
27812004Sjiang.liu@intel.com 	char		*dname;
27912004Sjiang.liu@intel.com 	struct dr_ref	*rp = (struct dr_ref *)arg;
28012004Sjiang.liu@intel.com 
28112004Sjiang.liu@intel.com 	if (dip == NULL)
28212004Sjiang.liu@intel.com 		return (DDI_WALK_CONTINUE);
28312004Sjiang.liu@intel.com 
28412004Sjiang.liu@intel.com 	if (!dr_is_real_device(dip))
28512004Sjiang.liu@intel.com 		return (DDI_WALK_CONTINUE);
28612004Sjiang.liu@intel.com 
28712004Sjiang.liu@intel.com 	dname = ddi_binding_name(dip);
28812004Sjiang.liu@intel.com 
28912004Sjiang.liu@intel.com 	if (dr_bypass_device(dname))
29012004Sjiang.liu@intel.com 		return (DDI_WALK_CONTINUE);
29112004Sjiang.liu@intel.com 
29212004Sjiang.liu@intel.com 	if (dname && ((major = ddi_name_to_major(dname)) != (major_t)-1)) {
29312004Sjiang.liu@intel.com 		if (ref && rp->refcount) {
29412004Sjiang.liu@intel.com 			*rp->refcount += ref;
29512004Sjiang.liu@intel.com 			PR_QR("\n  %s (major# %d) is referenced(%u)\n", dname,
29612004Sjiang.liu@intel.com 			    major, ref);
29712004Sjiang.liu@intel.com 		}
29812004Sjiang.liu@intel.com 		if (ref && rp->refcount_non_gldv3) {
29912004Sjiang.liu@intel.com 			if (NETWORK_PHYSDRV(major) && !GLDV3_DRV(major))
30012004Sjiang.liu@intel.com 				*rp->refcount_non_gldv3 += ref;
30112004Sjiang.liu@intel.com 		}
30212004Sjiang.liu@intel.com 		if (dr_is_unsafe_major(major) && i_ddi_devi_attached(dip)) {
30312004Sjiang.liu@intel.com 			PR_QR("\n  %s (major# %d) not hotpluggable\n", dname,
30412004Sjiang.liu@intel.com 			    major);
30512004Sjiang.liu@intel.com 			if (rp->arr != NULL && rp->idx != NULL)
30612004Sjiang.liu@intel.com 				*rp->idx = dr_add_int(rp->arr, *rp->idx,
30712004Sjiang.liu@intel.com 				    rp->len, (uint64_t)major);
30812004Sjiang.liu@intel.com 		}
30912004Sjiang.liu@intel.com 	}
31012004Sjiang.liu@intel.com 	return (DDI_WALK_CONTINUE);
31112004Sjiang.liu@intel.com }
31212004Sjiang.liu@intel.com 
31312004Sjiang.liu@intel.com static int
dr_check_unsafe_major(dev_info_t * dip,void * arg)31412004Sjiang.liu@intel.com dr_check_unsafe_major(dev_info_t *dip, void *arg)
31512004Sjiang.liu@intel.com {
31612004Sjiang.liu@intel.com 	return (dr_check_dip(dip, arg, 0));
31712004Sjiang.liu@intel.com }
31812004Sjiang.liu@intel.com 
31912004Sjiang.liu@intel.com 
32012004Sjiang.liu@intel.com /*ARGSUSED*/
32112004Sjiang.liu@intel.com 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)32212004Sjiang.liu@intel.com dr_check_devices(dev_info_t *dip, int *refcount, dr_handle_t *handle,
32312004Sjiang.liu@intel.com     uint64_t *arr, int *idx, int len, int *refcount_non_gldv3)
32412004Sjiang.liu@intel.com {
32512004Sjiang.liu@intel.com 	struct dr_ref bref = {0};
32612004Sjiang.liu@intel.com 
32712004Sjiang.liu@intel.com 	if (dip == NULL)
32812004Sjiang.liu@intel.com 		return;
32912004Sjiang.liu@intel.com 
33012004Sjiang.liu@intel.com 	bref.refcount = refcount;
33112004Sjiang.liu@intel.com 	bref.refcount_non_gldv3 = refcount_non_gldv3;
33212004Sjiang.liu@intel.com 	bref.arr = arr;
33312004Sjiang.liu@intel.com 	bref.idx = idx;
33412004Sjiang.liu@intel.com 	bref.len = len;
33512004Sjiang.liu@intel.com 
33612004Sjiang.liu@intel.com 	ASSERT(e_ddi_branch_held(dip));
33712004Sjiang.liu@intel.com 	(void) e_ddi_branch_referenced(dip, dr_check_dip, &bref);
33812004Sjiang.liu@intel.com }
33912004Sjiang.liu@intel.com 
34012004Sjiang.liu@intel.com /*
34112004Sjiang.liu@intel.com  * The "dip" argument's parent (if it exists) must be held busy.
34212004Sjiang.liu@intel.com  */
34312004Sjiang.liu@intel.com static int
dr_suspend_devices(dev_info_t * dip,dr_sr_handle_t * srh)34412004Sjiang.liu@intel.com dr_suspend_devices(dev_info_t *dip, dr_sr_handle_t *srh)
34512004Sjiang.liu@intel.com {
34612004Sjiang.liu@intel.com 	dr_handle_t	*handle;
34712004Sjiang.liu@intel.com 	major_t		major;
34812004Sjiang.liu@intel.com 	char		*dname;
34912004Sjiang.liu@intel.com 	int		circ;
35012004Sjiang.liu@intel.com 
35112004Sjiang.liu@intel.com 	/*
35212004Sjiang.liu@intel.com 	 * If dip is the root node, it has no siblings and it is
35312004Sjiang.liu@intel.com 	 * always held. If dip is not the root node, dr_suspend_devices()
35412004Sjiang.liu@intel.com 	 * will be invoked with the parent held busy.
35512004Sjiang.liu@intel.com 	 */
35612004Sjiang.liu@intel.com 	for (; dip != NULL; dip = ddi_get_next_sibling(dip)) {
35712004Sjiang.liu@intel.com 		char	d_name[40], d_alias[40], *d_info;
35812004Sjiang.liu@intel.com 
35912004Sjiang.liu@intel.com 		ndi_devi_enter(dip, &circ);
36012004Sjiang.liu@intel.com 		if (dr_suspend_devices(ddi_get_child(dip), srh)) {
36112004Sjiang.liu@intel.com 			ndi_devi_exit(dip, circ);
36212004Sjiang.liu@intel.com 			return (ENXIO);
36312004Sjiang.liu@intel.com 		}
36412004Sjiang.liu@intel.com 		ndi_devi_exit(dip, circ);
36512004Sjiang.liu@intel.com 
36612004Sjiang.liu@intel.com 		if (!dr_is_real_device(dip))
36712004Sjiang.liu@intel.com 			continue;
36812004Sjiang.liu@intel.com 
36912004Sjiang.liu@intel.com 		major = (major_t)-1;
37012004Sjiang.liu@intel.com 		if ((dname = ddi_binding_name(dip)) != NULL)
37112004Sjiang.liu@intel.com 			major = ddi_name_to_major(dname);
37212004Sjiang.liu@intel.com 
37312004Sjiang.liu@intel.com 		if (dr_bypass_device(dname)) {
37412004Sjiang.liu@intel.com 			PR_QR(" bypassed suspend of %s (major# %d)\n", dname,
37512004Sjiang.liu@intel.com 			    major);
37612004Sjiang.liu@intel.com 			continue;
37712004Sjiang.liu@intel.com 		}
37812004Sjiang.liu@intel.com 
37912004Sjiang.liu@intel.com 		if (drmach_verify_sr(dip, 1)) {
38012004Sjiang.liu@intel.com 			PR_QR(" bypassed suspend of %s (major# %d)\n", dname,
38112004Sjiang.liu@intel.com 			    major);
38212004Sjiang.liu@intel.com 			continue;
38312004Sjiang.liu@intel.com 		}
38412004Sjiang.liu@intel.com 
38512004Sjiang.liu@intel.com 		if ((d_info = ddi_get_name_addr(dip)) == NULL)
38612004Sjiang.liu@intel.com 			d_info = "<null>";
38712004Sjiang.liu@intel.com 
38812004Sjiang.liu@intel.com 		d_name[0] = 0;
38912004Sjiang.liu@intel.com 		if (dr_resolve_devname(dip, d_name, d_alias) == 0) {
39012004Sjiang.liu@intel.com 			if (d_alias[0] != 0) {
39112004Sjiang.liu@intel.com 				prom_printf("\tsuspending %s@%s (aka %s)\n",
39212004Sjiang.liu@intel.com 				    d_name, d_info, d_alias);
39312004Sjiang.liu@intel.com 			} else {
39412004Sjiang.liu@intel.com 				prom_printf("\tsuspending %s@%s\n", d_name,
39512004Sjiang.liu@intel.com 				    d_info);
39612004Sjiang.liu@intel.com 			}
39712004Sjiang.liu@intel.com 		} else {
39812004Sjiang.liu@intel.com 			prom_printf("\tsuspending %s@%s\n", dname, d_info);
39912004Sjiang.liu@intel.com 		}
40012004Sjiang.liu@intel.com 
40112004Sjiang.liu@intel.com 		if (devi_detach(dip, DDI_SUSPEND) != DDI_SUCCESS) {
40212004Sjiang.liu@intel.com 			prom_printf("\tFAILED to suspend %s@%s\n",
40312004Sjiang.liu@intel.com 			    d_name[0] ? d_name : dname, d_info);
40412004Sjiang.liu@intel.com 
40512004Sjiang.liu@intel.com 			srh->sr_err_idx = dr_add_int(srh->sr_err_ints,
40612004Sjiang.liu@intel.com 			    srh->sr_err_idx, DR_MAX_ERR_INT, (uint64_t)major);
40712004Sjiang.liu@intel.com 
40812004Sjiang.liu@intel.com 			ndi_hold_devi(dip);
40912004Sjiang.liu@intel.com 			srh->sr_failed_dip = dip;
41012004Sjiang.liu@intel.com 
41112004Sjiang.liu@intel.com 			handle = srh->sr_dr_handlep;
41212004Sjiang.liu@intel.com 			dr_op_err(CE_IGNORE, handle, ESBD_SUSPEND, "%s@%s",
41312004Sjiang.liu@intel.com 			    d_name[0] ? d_name : dname, d_info);
41412004Sjiang.liu@intel.com 
41512004Sjiang.liu@intel.com 			return (DDI_FAILURE);
41612004Sjiang.liu@intel.com 		}
41712004Sjiang.liu@intel.com 	}
41812004Sjiang.liu@intel.com 
41912004Sjiang.liu@intel.com 	return (DDI_SUCCESS);
42012004Sjiang.liu@intel.com }
42112004Sjiang.liu@intel.com 
42212004Sjiang.liu@intel.com static void
dr_resume_devices(dev_info_t * start,dr_sr_handle_t * srh)42312004Sjiang.liu@intel.com dr_resume_devices(dev_info_t *start, dr_sr_handle_t *srh)
42412004Sjiang.liu@intel.com {
42512004Sjiang.liu@intel.com 	dr_handle_t	*handle;
42612004Sjiang.liu@intel.com 	dev_info_t	*dip, *next, *last = NULL;
42712004Sjiang.liu@intel.com 	major_t		major;
42812004Sjiang.liu@intel.com 	char		*bn;
42912004Sjiang.liu@intel.com 	int		circ;
43012004Sjiang.liu@intel.com 
43112004Sjiang.liu@intel.com 	major = (major_t)-1;
43212004Sjiang.liu@intel.com 
43312004Sjiang.liu@intel.com 	/* attach in reverse device tree order */
43412004Sjiang.liu@intel.com 	while (last != start) {
43512004Sjiang.liu@intel.com 		dip = start;
43612004Sjiang.liu@intel.com 		next = ddi_get_next_sibling(dip);
43712004Sjiang.liu@intel.com 		while (next != last && dip != srh->sr_failed_dip) {
43812004Sjiang.liu@intel.com 			dip = next;
43912004Sjiang.liu@intel.com 			next = ddi_get_next_sibling(dip);
44012004Sjiang.liu@intel.com 		}
44112004Sjiang.liu@intel.com 		if (dip == srh->sr_failed_dip) {
44212004Sjiang.liu@intel.com 			/* release hold acquired in dr_suspend_devices() */
44312004Sjiang.liu@intel.com 			srh->sr_failed_dip = NULL;
44412004Sjiang.liu@intel.com 			ndi_rele_devi(dip);
44512004Sjiang.liu@intel.com 		} else if (dr_is_real_device(dip) &&
44612004Sjiang.liu@intel.com 		    srh->sr_failed_dip == NULL) {
44712004Sjiang.liu@intel.com 
44812004Sjiang.liu@intel.com 			if ((bn = ddi_binding_name(dip)) != NULL) {
44912004Sjiang.liu@intel.com 				major = ddi_name_to_major(bn);
45012004Sjiang.liu@intel.com 			} else {
45112004Sjiang.liu@intel.com 				bn = "<null>";
45212004Sjiang.liu@intel.com 			}
45312004Sjiang.liu@intel.com 			if (!dr_bypass_device(bn) &&
45412004Sjiang.liu@intel.com 			    !drmach_verify_sr(dip, 0)) {
45512004Sjiang.liu@intel.com 				char	d_name[40], d_alias[40], *d_info;
45612004Sjiang.liu@intel.com 
45712004Sjiang.liu@intel.com 				d_name[0] = 0;
45812004Sjiang.liu@intel.com 				d_info = ddi_get_name_addr(dip);
45912004Sjiang.liu@intel.com 				if (d_info == NULL)
46012004Sjiang.liu@intel.com 					d_info = "<null>";
46112004Sjiang.liu@intel.com 
46212004Sjiang.liu@intel.com 				if (!dr_resolve_devname(dip, d_name, d_alias)) {
46312004Sjiang.liu@intel.com 					if (d_alias[0] != 0) {
46412004Sjiang.liu@intel.com 						prom_printf("\tresuming "
46512004Sjiang.liu@intel.com 						    "%s@%s (aka %s)\n", d_name,
46612004Sjiang.liu@intel.com 						    d_info, d_alias);
46712004Sjiang.liu@intel.com 					} else {
46812004Sjiang.liu@intel.com 						prom_printf("\tresuming "
46912004Sjiang.liu@intel.com 						    "%s@%s\n", d_name, d_info);
47012004Sjiang.liu@intel.com 					}
47112004Sjiang.liu@intel.com 				} else {
47212004Sjiang.liu@intel.com 					prom_printf("\tresuming %s@%s\n", bn,
47312004Sjiang.liu@intel.com 					    d_info);
47412004Sjiang.liu@intel.com 				}
47512004Sjiang.liu@intel.com 
47612004Sjiang.liu@intel.com 				if (devi_attach(dip, DDI_RESUME) !=
47712004Sjiang.liu@intel.com 				    DDI_SUCCESS) {
47812004Sjiang.liu@intel.com 					/*
47912004Sjiang.liu@intel.com 					 * Print a console warning,
48012004Sjiang.liu@intel.com 					 * set an e_code of ESBD_RESUME,
48112004Sjiang.liu@intel.com 					 * and save the driver major
48212004Sjiang.liu@intel.com 					 * number in the e_rsc.
48312004Sjiang.liu@intel.com 					 */
48412004Sjiang.liu@intel.com 					prom_printf("\tFAILED to resume %s@%s",
48512004Sjiang.liu@intel.com 					    d_name[0] ? d_name : bn, d_info);
48612004Sjiang.liu@intel.com 
48712004Sjiang.liu@intel.com 					srh->sr_err_idx =
48812004Sjiang.liu@intel.com 					    dr_add_int(srh->sr_err_ints,
48912004Sjiang.liu@intel.com 					    srh->sr_err_idx, DR_MAX_ERR_INT,
49012004Sjiang.liu@intel.com 					    (uint64_t)major);
49112004Sjiang.liu@intel.com 
49212004Sjiang.liu@intel.com 					handle = srh->sr_dr_handlep;
49312004Sjiang.liu@intel.com 
49412004Sjiang.liu@intel.com 					dr_op_err(CE_IGNORE, handle,
49512004Sjiang.liu@intel.com 					    ESBD_RESUME, "%s@%s",
49612004Sjiang.liu@intel.com 					    d_name[0] ? d_name : bn, d_info);
49712004Sjiang.liu@intel.com 				}
49812004Sjiang.liu@intel.com 			}
49912004Sjiang.liu@intel.com 		}
50012004Sjiang.liu@intel.com 
50112004Sjiang.liu@intel.com 		/* Hold parent busy while walking its children */
50212004Sjiang.liu@intel.com 		ndi_devi_enter(dip, &circ);
50312004Sjiang.liu@intel.com 		dr_resume_devices(ddi_get_child(dip), srh);
50412004Sjiang.liu@intel.com 		ndi_devi_exit(dip, circ);
50512004Sjiang.liu@intel.com 		last = dip;
50612004Sjiang.liu@intel.com 	}
50712004Sjiang.liu@intel.com }
50812004Sjiang.liu@intel.com 
50912004Sjiang.liu@intel.com /*
51012004Sjiang.liu@intel.com  * True if thread is virtually stopped.  Similar to CPR_VSTOPPED
51112004Sjiang.liu@intel.com  * but from DR point of view.  These user threads are waiting in
51212004Sjiang.liu@intel.com  * the kernel.  Once they complete in the kernel, they will process
51312004Sjiang.liu@intel.com  * the stop signal and stop.
51412004Sjiang.liu@intel.com  */
51512004Sjiang.liu@intel.com #define	DR_VSTOPPED(t)			\
51612004Sjiang.liu@intel.com 	((t)->t_state == TS_SLEEP &&	\
51712004Sjiang.liu@intel.com 	(t)->t_wchan != NULL &&		\
51812004Sjiang.liu@intel.com 	(t)->t_astflag &&		\
51912004Sjiang.liu@intel.com 	((t)->t_proc_flag & TP_CHKPT))
52012004Sjiang.liu@intel.com 
52112004Sjiang.liu@intel.com /* ARGSUSED */
52212004Sjiang.liu@intel.com static int
dr_stop_user_threads(dr_sr_handle_t * srh)52312004Sjiang.liu@intel.com dr_stop_user_threads(dr_sr_handle_t *srh)
52412004Sjiang.liu@intel.com {
52512004Sjiang.liu@intel.com 	int		count;
52612004Sjiang.liu@intel.com 	int		bailout;
52712004Sjiang.liu@intel.com 	dr_handle_t	*handle = srh->sr_dr_handlep;
52812004Sjiang.liu@intel.com 	static fn_t	f = "dr_stop_user_threads";
52912004Sjiang.liu@intel.com 	kthread_id_t 	tp;
53012004Sjiang.liu@intel.com 
53112004Sjiang.liu@intel.com 	extern void add_one_utstop();
53212004Sjiang.liu@intel.com 	extern void utstop_timedwait(clock_t);
53312004Sjiang.liu@intel.com 	extern void utstop_init(void);
53412004Sjiang.liu@intel.com 
53512004Sjiang.liu@intel.com #define	DR_UTSTOP_RETRY	4
53612004Sjiang.liu@intel.com #define	DR_UTSTOP_WAIT	hz
53712004Sjiang.liu@intel.com 
53812004Sjiang.liu@intel.com 	if (dr_skip_user_threads)
53912004Sjiang.liu@intel.com 		return (DDI_SUCCESS);
54012004Sjiang.liu@intel.com 
54112004Sjiang.liu@intel.com 	utstop_init();
54212004Sjiang.liu@intel.com 
54312004Sjiang.liu@intel.com 	/* we need to try a few times to get past fork, etc. */
54412004Sjiang.liu@intel.com 	srh->sr_err_idx = 0;
54512004Sjiang.liu@intel.com 	for (count = 0; count < DR_UTSTOP_RETRY; count++) {
54612004Sjiang.liu@intel.com 		/* walk the entire threadlist */
54712004Sjiang.liu@intel.com 		mutex_enter(&pidlock);
54812004Sjiang.liu@intel.com 		for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) {
54912004Sjiang.liu@intel.com 			proc_t *p = ttoproc(tp);
55012004Sjiang.liu@intel.com 
55112004Sjiang.liu@intel.com 			/* handle kernel threads separately */
55212004Sjiang.liu@intel.com 			if (p->p_as == &kas || p->p_stat == SZOMB)
55312004Sjiang.liu@intel.com 				continue;
55412004Sjiang.liu@intel.com 
55512004Sjiang.liu@intel.com 			mutex_enter(&p->p_lock);
55612004Sjiang.liu@intel.com 			thread_lock(tp);
55712004Sjiang.liu@intel.com 
55812004Sjiang.liu@intel.com 			if (tp->t_state == TS_STOPPED) {
55912004Sjiang.liu@intel.com 				/* add another reason to stop this thread */
56012004Sjiang.liu@intel.com 				tp->t_schedflag &= ~TS_RESUME;
56112004Sjiang.liu@intel.com 			} else {
56212004Sjiang.liu@intel.com 				tp->t_proc_flag |= TP_CHKPT;
56312004Sjiang.liu@intel.com 
56412004Sjiang.liu@intel.com 				thread_unlock(tp);
56512004Sjiang.liu@intel.com 				mutex_exit(&p->p_lock);
56612004Sjiang.liu@intel.com 				add_one_utstop();
56712004Sjiang.liu@intel.com 				mutex_enter(&p->p_lock);
56812004Sjiang.liu@intel.com 				thread_lock(tp);
56912004Sjiang.liu@intel.com 
57012004Sjiang.liu@intel.com 				aston(tp);
57112004Sjiang.liu@intel.com 
57212004Sjiang.liu@intel.com 				if (ISWAKEABLE(tp) || ISWAITING(tp)) {
57312004Sjiang.liu@intel.com 					setrun_locked(tp);
57412004Sjiang.liu@intel.com 				}
57512004Sjiang.liu@intel.com 
57612004Sjiang.liu@intel.com 			}
57712004Sjiang.liu@intel.com 
57812004Sjiang.liu@intel.com 			/* grab thread if needed */
57912004Sjiang.liu@intel.com 			if (tp->t_state == TS_ONPROC && tp->t_cpu != CPU)
58012004Sjiang.liu@intel.com 				poke_cpu(tp->t_cpu->cpu_id);
58112004Sjiang.liu@intel.com 
58212004Sjiang.liu@intel.com 
58312004Sjiang.liu@intel.com 			thread_unlock(tp);
58412004Sjiang.liu@intel.com 			mutex_exit(&p->p_lock);
58512004Sjiang.liu@intel.com 		}
58612004Sjiang.liu@intel.com 		mutex_exit(&pidlock);
58712004Sjiang.liu@intel.com 
58812004Sjiang.liu@intel.com 
58912004Sjiang.liu@intel.com 		/* let everything catch up */
59012004Sjiang.liu@intel.com 		utstop_timedwait(count * count * DR_UTSTOP_WAIT);
59112004Sjiang.liu@intel.com 
59212004Sjiang.liu@intel.com 
59312004Sjiang.liu@intel.com 		/* now, walk the threadlist again to see if we are done */
59412004Sjiang.liu@intel.com 		mutex_enter(&pidlock);
59512004Sjiang.liu@intel.com 		for (tp = curthread->t_next, bailout = 0;
59612004Sjiang.liu@intel.com 		    tp != curthread; tp = tp->t_next) {
59712004Sjiang.liu@intel.com 			proc_t *p = ttoproc(tp);
59812004Sjiang.liu@intel.com 
59912004Sjiang.liu@intel.com 			/* handle kernel threads separately */
60012004Sjiang.liu@intel.com 			if (p->p_as == &kas || p->p_stat == SZOMB)
60112004Sjiang.liu@intel.com 				continue;
60212004Sjiang.liu@intel.com 
60312004Sjiang.liu@intel.com 			/*
60412004Sjiang.liu@intel.com 			 * If this thread didn't stop, and we don't allow
60512004Sjiang.liu@intel.com 			 * unstopped blocked threads, bail.
60612004Sjiang.liu@intel.com 			 */
60712004Sjiang.liu@intel.com 			thread_lock(tp);
60812004Sjiang.liu@intel.com 			if (!CPR_ISTOPPED(tp) &&
60912004Sjiang.liu@intel.com 			    !(dr_allow_blocked_threads &&
61012004Sjiang.liu@intel.com 			    DR_VSTOPPED(tp))) {
61112004Sjiang.liu@intel.com 				bailout = 1;
61212004Sjiang.liu@intel.com 				if (count == DR_UTSTOP_RETRY - 1) {
61312004Sjiang.liu@intel.com 					/*
61412004Sjiang.liu@intel.com 					 * save the pid for later reporting
61512004Sjiang.liu@intel.com 					 */
61612004Sjiang.liu@intel.com 					srh->sr_err_idx =
61712004Sjiang.liu@intel.com 					    dr_add_int(srh->sr_err_ints,
61812004Sjiang.liu@intel.com 					    srh->sr_err_idx, DR_MAX_ERR_INT,
61912004Sjiang.liu@intel.com 					    (uint64_t)p->p_pid);
62012004Sjiang.liu@intel.com 
62112004Sjiang.liu@intel.com 					cmn_err(CE_WARN, "%s: "
62212004Sjiang.liu@intel.com 					    "failed to stop thread: "
62312004Sjiang.liu@intel.com 					    "process=%s, pid=%d",
62412004Sjiang.liu@intel.com 					    f, p->p_user.u_psargs, p->p_pid);
62512004Sjiang.liu@intel.com 
62612004Sjiang.liu@intel.com 					PR_QR("%s: failed to stop thread: "
62712004Sjiang.liu@intel.com 					    "process=%s, pid=%d, t_id=0x%p, "
62812004Sjiang.liu@intel.com 					    "t_state=0x%x, t_proc_flag=0x%x, "
62912004Sjiang.liu@intel.com 					    "t_schedflag=0x%x\n",
63012004Sjiang.liu@intel.com 					    f, p->p_user.u_psargs, p->p_pid,
63112004Sjiang.liu@intel.com 					    (void *)tp, tp->t_state,
63212004Sjiang.liu@intel.com 					    tp->t_proc_flag, tp->t_schedflag);
63312004Sjiang.liu@intel.com 				}
63412004Sjiang.liu@intel.com 
63512004Sjiang.liu@intel.com 			}
63612004Sjiang.liu@intel.com 			thread_unlock(tp);
63712004Sjiang.liu@intel.com 		}
63812004Sjiang.liu@intel.com 		mutex_exit(&pidlock);
63912004Sjiang.liu@intel.com 
64012004Sjiang.liu@intel.com 		/* were all the threads stopped? */
64112004Sjiang.liu@intel.com 		if (!bailout)
64212004Sjiang.liu@intel.com 			break;
64312004Sjiang.liu@intel.com 	}
64412004Sjiang.liu@intel.com 
64512004Sjiang.liu@intel.com 	/* were we unable to stop all threads after a few tries? */
64612004Sjiang.liu@intel.com 	if (bailout) {
64712004Sjiang.liu@intel.com 		handle->h_err = drerr_int(ESBD_UTHREAD, srh->sr_err_ints,
64812004Sjiang.liu@intel.com 		    srh->sr_err_idx, 0);
64912004Sjiang.liu@intel.com 		return (ESRCH);
65012004Sjiang.liu@intel.com 	}
65112004Sjiang.liu@intel.com 
65212004Sjiang.liu@intel.com 	return (DDI_SUCCESS);
65312004Sjiang.liu@intel.com }
65412004Sjiang.liu@intel.com 
65512004Sjiang.liu@intel.com static void
dr_start_user_threads(void)65612004Sjiang.liu@intel.com dr_start_user_threads(void)
65712004Sjiang.liu@intel.com {
65812004Sjiang.liu@intel.com 	kthread_id_t tp;
65912004Sjiang.liu@intel.com 
66012004Sjiang.liu@intel.com 	mutex_enter(&pidlock);
66112004Sjiang.liu@intel.com 
66212004Sjiang.liu@intel.com 	/* walk all threads and release them */
66312004Sjiang.liu@intel.com 	for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) {
66412004Sjiang.liu@intel.com 		proc_t *p = ttoproc(tp);
66512004Sjiang.liu@intel.com 
66612004Sjiang.liu@intel.com 		/* skip kernel threads */
66712004Sjiang.liu@intel.com 		if (ttoproc(tp)->p_as == &kas)
66812004Sjiang.liu@intel.com 			continue;
66912004Sjiang.liu@intel.com 
67012004Sjiang.liu@intel.com 		mutex_enter(&p->p_lock);
67112004Sjiang.liu@intel.com 		tp->t_proc_flag &= ~TP_CHKPT;
67212004Sjiang.liu@intel.com 		mutex_exit(&p->p_lock);
67312004Sjiang.liu@intel.com 
67412004Sjiang.liu@intel.com 		thread_lock(tp);
67512004Sjiang.liu@intel.com 		if (CPR_ISTOPPED(tp)) {
67612004Sjiang.liu@intel.com 			/* back on the runq */
67712004Sjiang.liu@intel.com 			tp->t_schedflag |= TS_RESUME;
67812004Sjiang.liu@intel.com 			setrun_locked(tp);
67912004Sjiang.liu@intel.com 		}
68012004Sjiang.liu@intel.com 		thread_unlock(tp);
68112004Sjiang.liu@intel.com 	}
68212004Sjiang.liu@intel.com 
68312004Sjiang.liu@intel.com 	mutex_exit(&pidlock);
68412004Sjiang.liu@intel.com }
68512004Sjiang.liu@intel.com 
68612004Sjiang.liu@intel.com static void
dr_signal_user(int sig)68712004Sjiang.liu@intel.com dr_signal_user(int sig)
68812004Sjiang.liu@intel.com {
68912004Sjiang.liu@intel.com 	struct proc *p;
69012004Sjiang.liu@intel.com 
69112004Sjiang.liu@intel.com 	mutex_enter(&pidlock);
69212004Sjiang.liu@intel.com 
69312004Sjiang.liu@intel.com 	for (p = practive; p != NULL; p = p->p_next) {
69412004Sjiang.liu@intel.com 		/* only user threads */
69512004Sjiang.liu@intel.com 		if (p->p_exec == NULL || p->p_stat == SZOMB ||
69612004Sjiang.liu@intel.com 		    p == proc_init || p == ttoproc(curthread))
69712004Sjiang.liu@intel.com 			continue;
69812004Sjiang.liu@intel.com 
69912004Sjiang.liu@intel.com 		mutex_enter(&p->p_lock);
70012004Sjiang.liu@intel.com 		sigtoproc(p, NULL, sig);
70112004Sjiang.liu@intel.com 		mutex_exit(&p->p_lock);
70212004Sjiang.liu@intel.com 	}
70312004Sjiang.liu@intel.com 
70412004Sjiang.liu@intel.com 	mutex_exit(&pidlock);
70512004Sjiang.liu@intel.com 
70612004Sjiang.liu@intel.com 	/* add a bit of delay */
70712004Sjiang.liu@intel.com 	delay(hz);
70812004Sjiang.liu@intel.com }
70912004Sjiang.liu@intel.com 
71012004Sjiang.liu@intel.com void
dr_resume(dr_sr_handle_t * srh)71112004Sjiang.liu@intel.com dr_resume(dr_sr_handle_t *srh)
71212004Sjiang.liu@intel.com {
71312004Sjiang.liu@intel.com 	switch (srh->sr_suspend_state) {
71412004Sjiang.liu@intel.com 	case DR_SRSTATE_FULL:
71512004Sjiang.liu@intel.com 
71612004Sjiang.liu@intel.com 		ASSERT(MUTEX_HELD(&cpu_lock));
71712004Sjiang.liu@intel.com 
71812004Sjiang.liu@intel.com 		/*
71912004Sjiang.liu@intel.com 		 * Prevent false alarm in tod_validate() due to tod
72012004Sjiang.liu@intel.com 		 * value change between suspend and resume
72112004Sjiang.liu@intel.com 		 */
72212004Sjiang.liu@intel.com 		mutex_enter(&tod_lock);
72312004Sjiang.liu@intel.com 		tod_status_set(TOD_DR_RESUME_DONE);
72412004Sjiang.liu@intel.com 		mutex_exit(&tod_lock);
72512004Sjiang.liu@intel.com 
72612004Sjiang.liu@intel.com 		dr_enable_intr(); 	/* enable intr & clock */
72712004Sjiang.liu@intel.com 
72812004Sjiang.liu@intel.com 		start_cpus();
72912004Sjiang.liu@intel.com 		mutex_exit(&cpu_lock);
73012004Sjiang.liu@intel.com 
73112004Sjiang.liu@intel.com 		/*
73212004Sjiang.liu@intel.com 		 * This should only be called if drmach_suspend_last()
73312004Sjiang.liu@intel.com 		 * was called and state transitioned to DR_SRSTATE_FULL
73412004Sjiang.liu@intel.com 		 * to prevent resume attempts on device instances that
73512004Sjiang.liu@intel.com 		 * were not previously suspended.
73612004Sjiang.liu@intel.com 		 */
73712004Sjiang.liu@intel.com 		drmach_resume_first();
73812004Sjiang.liu@intel.com 
73912004Sjiang.liu@intel.com 		/* FALLTHROUGH */
74012004Sjiang.liu@intel.com 
74112004Sjiang.liu@intel.com 	case DR_SRSTATE_DRIVER:
74212004Sjiang.liu@intel.com 		/*
74312004Sjiang.liu@intel.com 		 * resume drivers
74412004Sjiang.liu@intel.com 		 */
74512004Sjiang.liu@intel.com 		srh->sr_err_idx = 0;
74612004Sjiang.liu@intel.com 
74712004Sjiang.liu@intel.com 		/* no parent dip to hold busy */
74812004Sjiang.liu@intel.com 		dr_resume_devices(ddi_root_node(), srh);
74912004Sjiang.liu@intel.com 
75012004Sjiang.liu@intel.com 		if (srh->sr_err_idx && srh->sr_dr_handlep) {
75112004Sjiang.liu@intel.com 			(srh->sr_dr_handlep)->h_err = drerr_int(ESBD_RESUME,
75212004Sjiang.liu@intel.com 			    srh->sr_err_ints, srh->sr_err_idx, 1);
75312004Sjiang.liu@intel.com 		}
75412004Sjiang.liu@intel.com 
75512004Sjiang.liu@intel.com 		/*
75612004Sjiang.liu@intel.com 		 * resume the lock manager
75712004Sjiang.liu@intel.com 		 */
75812004Sjiang.liu@intel.com 		lm_cprresume();
75912004Sjiang.liu@intel.com 
76012004Sjiang.liu@intel.com 		/* FALLTHROUGH */
76112004Sjiang.liu@intel.com 
76212004Sjiang.liu@intel.com 	case DR_SRSTATE_USER:
76312004Sjiang.liu@intel.com 		/*
76412004Sjiang.liu@intel.com 		 * finally, resume user threads
76512004Sjiang.liu@intel.com 		 */
76612004Sjiang.liu@intel.com 		if (!dr_skip_user_threads) {
76712004Sjiang.liu@intel.com 			prom_printf("DR: resuming user threads...\n");
76812004Sjiang.liu@intel.com 			dr_start_user_threads();
76912004Sjiang.liu@intel.com 		}
77012004Sjiang.liu@intel.com 		/* FALLTHROUGH */
77112004Sjiang.liu@intel.com 
77212004Sjiang.liu@intel.com 	case DR_SRSTATE_BEGIN:
77312004Sjiang.liu@intel.com 	default:
77412004Sjiang.liu@intel.com 		/*
77512004Sjiang.liu@intel.com 		 * let those who care know that we've just resumed
77612004Sjiang.liu@intel.com 		 */
77712004Sjiang.liu@intel.com 		PR_QR("sending SIGTHAW...\n");
77812004Sjiang.liu@intel.com 		dr_signal_user(SIGTHAW);
77912004Sjiang.liu@intel.com 		break;
78012004Sjiang.liu@intel.com 	}
78112004Sjiang.liu@intel.com 
78212004Sjiang.liu@intel.com 	prom_printf("DR: resume COMPLETED\n");
78312004Sjiang.liu@intel.com }
78412004Sjiang.liu@intel.com 
78512004Sjiang.liu@intel.com int
dr_suspend(dr_sr_handle_t * srh)78612004Sjiang.liu@intel.com dr_suspend(dr_sr_handle_t *srh)
78712004Sjiang.liu@intel.com {
78812004Sjiang.liu@intel.com 	dr_handle_t	*handle;
78912004Sjiang.liu@intel.com 	int		force;
79012004Sjiang.liu@intel.com 	int		dev_errs_idx;
79112004Sjiang.liu@intel.com 	uint64_t	dev_errs[DR_MAX_ERR_INT];
79212004Sjiang.liu@intel.com 	int		rc = DDI_SUCCESS;
79312004Sjiang.liu@intel.com 
79412004Sjiang.liu@intel.com 	handle = srh->sr_dr_handlep;
79512004Sjiang.liu@intel.com 
79612004Sjiang.liu@intel.com 	force = dr_cmd_flags(handle) & SBD_FLAG_FORCE;
79712004Sjiang.liu@intel.com 
79812004Sjiang.liu@intel.com 	prom_printf("\nDR: suspending user threads...\n");
79912004Sjiang.liu@intel.com 	srh->sr_suspend_state = DR_SRSTATE_USER;
80012004Sjiang.liu@intel.com 	if (((rc = dr_stop_user_threads(srh)) != DDI_SUCCESS) &&
80112004Sjiang.liu@intel.com 	    dr_check_user_stop_result) {
80212004Sjiang.liu@intel.com 		dr_resume(srh);
80312004Sjiang.liu@intel.com 		return (rc);
80412004Sjiang.liu@intel.com 	}
80512004Sjiang.liu@intel.com 
80612004Sjiang.liu@intel.com 	if (!force) {
80712004Sjiang.liu@intel.com 		struct dr_ref drc = {0};
80812004Sjiang.liu@intel.com 
80912004Sjiang.liu@intel.com 		prom_printf("\nDR: checking devices...\n");
81012004Sjiang.liu@intel.com 		dev_errs_idx = 0;
81112004Sjiang.liu@intel.com 
81212004Sjiang.liu@intel.com 		drc.arr = dev_errs;
81312004Sjiang.liu@intel.com 		drc.idx = &dev_errs_idx;
81412004Sjiang.liu@intel.com 		drc.len = DR_MAX_ERR_INT;
81512004Sjiang.liu@intel.com 
81612004Sjiang.liu@intel.com 		/*
81712004Sjiang.liu@intel.com 		 * Since the root node can never go away, it
81812004Sjiang.liu@intel.com 		 * doesn't have to be held.
81912004Sjiang.liu@intel.com 		 */
82012004Sjiang.liu@intel.com 		ddi_walk_devs(ddi_root_node(), dr_check_unsafe_major, &drc);
82112004Sjiang.liu@intel.com 		if (dev_errs_idx) {
82212004Sjiang.liu@intel.com 			handle->h_err = drerr_int(ESBD_UNSAFE, dev_errs,
82312004Sjiang.liu@intel.com 			    dev_errs_idx, 1);
82412004Sjiang.liu@intel.com 			dr_resume(srh);
82512004Sjiang.liu@intel.com 			return (DDI_FAILURE);
82612004Sjiang.liu@intel.com 		}
82712004Sjiang.liu@intel.com 		PR_QR("done\n");
82812004Sjiang.liu@intel.com 	} else {
82912004Sjiang.liu@intel.com 		prom_printf("\nDR: dr_suspend invoked with force flag\n");
83012004Sjiang.liu@intel.com 	}
83112004Sjiang.liu@intel.com 
83212004Sjiang.liu@intel.com #ifndef	SKIP_SYNC
83312004Sjiang.liu@intel.com 	/*
83412004Sjiang.liu@intel.com 	 * This sync swap out all user pages
83512004Sjiang.liu@intel.com 	 */
83612004Sjiang.liu@intel.com 	vfs_sync(SYNC_ALL);
83712004Sjiang.liu@intel.com #endif
83812004Sjiang.liu@intel.com 
83912004Sjiang.liu@intel.com 	/*
84012004Sjiang.liu@intel.com 	 * special treatment for lock manager
84112004Sjiang.liu@intel.com 	 */
84212004Sjiang.liu@intel.com 	lm_cprsuspend();
84312004Sjiang.liu@intel.com 
84412004Sjiang.liu@intel.com #ifndef	SKIP_SYNC
84512004Sjiang.liu@intel.com 	/*
84612004Sjiang.liu@intel.com 	 * sync the file system in case we never make it back
84712004Sjiang.liu@intel.com 	 */
84812004Sjiang.liu@intel.com 	sync();
84912004Sjiang.liu@intel.com #endif
85012004Sjiang.liu@intel.com 
85112004Sjiang.liu@intel.com 	/*
85212004Sjiang.liu@intel.com 	 * now suspend drivers
85312004Sjiang.liu@intel.com 	 */
85412004Sjiang.liu@intel.com 	prom_printf("DR: suspending drivers...\n");
85512004Sjiang.liu@intel.com 	srh->sr_suspend_state = DR_SRSTATE_DRIVER;
85612004Sjiang.liu@intel.com 	srh->sr_err_idx = 0;
85712004Sjiang.liu@intel.com 	/* No parent to hold busy */
85812004Sjiang.liu@intel.com 	if ((rc = dr_suspend_devices(ddi_root_node(), srh)) != DDI_SUCCESS) {
85912004Sjiang.liu@intel.com 		if (srh->sr_err_idx && srh->sr_dr_handlep) {
86012004Sjiang.liu@intel.com 			(srh->sr_dr_handlep)->h_err = drerr_int(ESBD_SUSPEND,
86112004Sjiang.liu@intel.com 			    srh->sr_err_ints, srh->sr_err_idx, 1);
86212004Sjiang.liu@intel.com 		}
86312004Sjiang.liu@intel.com 		dr_resume(srh);
86412004Sjiang.liu@intel.com 		return (rc);
86512004Sjiang.liu@intel.com 	}
86612004Sjiang.liu@intel.com 
86712004Sjiang.liu@intel.com 	drmach_suspend_last();
86812004Sjiang.liu@intel.com 
86912004Sjiang.liu@intel.com 	/*
87012004Sjiang.liu@intel.com 	 * finally, grab all cpus
87112004Sjiang.liu@intel.com 	 */
87212004Sjiang.liu@intel.com 	srh->sr_suspend_state = DR_SRSTATE_FULL;
87312004Sjiang.liu@intel.com 
87412004Sjiang.liu@intel.com 	mutex_enter(&cpu_lock);
87512004Sjiang.liu@intel.com 	pause_cpus(NULL);
87612004Sjiang.liu@intel.com 	dr_stop_intr();
87712004Sjiang.liu@intel.com 
87812004Sjiang.liu@intel.com 	return (rc);
87912004Sjiang.liu@intel.com }
88012004Sjiang.liu@intel.com 
88112004Sjiang.liu@intel.com int
dr_pt_test_suspend(dr_handle_t * hp)88212004Sjiang.liu@intel.com dr_pt_test_suspend(dr_handle_t *hp)
88312004Sjiang.liu@intel.com {
88412004Sjiang.liu@intel.com 	dr_sr_handle_t *srh;
88512004Sjiang.liu@intel.com 	int		err;
88612004Sjiang.liu@intel.com 	uint_t		psmerr;
88712004Sjiang.liu@intel.com 	static fn_t	f = "dr_pt_test_suspend";
88812004Sjiang.liu@intel.com 
88912004Sjiang.liu@intel.com 	PR_QR("%s...\n", f);
89012004Sjiang.liu@intel.com 
89112004Sjiang.liu@intel.com 	srh = dr_get_sr_handle(hp);
89212004Sjiang.liu@intel.com 	if ((err = dr_suspend(srh)) == DDI_SUCCESS) {
89312004Sjiang.liu@intel.com 		dr_resume(srh);
89412004Sjiang.liu@intel.com 		if ((hp->h_err) && ((psmerr = hp->h_err->e_code) != 0)) {
89512004Sjiang.liu@intel.com 			PR_QR("%s: error on dr_resume()", f);
89612004Sjiang.liu@intel.com 			switch (psmerr) {
89712004Sjiang.liu@intel.com 			case ESBD_RESUME:
89812004Sjiang.liu@intel.com 				PR_QR("Couldn't resume devices: %s\n",
89912004Sjiang.liu@intel.com 				    DR_GET_E_RSC(hp->h_err));
90012004Sjiang.liu@intel.com 				break;
90112004Sjiang.liu@intel.com 
90212004Sjiang.liu@intel.com 			case ESBD_KTHREAD:
90312004Sjiang.liu@intel.com 				PR_ALL("psmerr is ESBD_KTHREAD\n");
90412004Sjiang.liu@intel.com 				break;
90512004Sjiang.liu@intel.com 			default:
90612004Sjiang.liu@intel.com 				PR_ALL("Resume error unknown = %d\n", psmerr);
90712004Sjiang.liu@intel.com 				break;
90812004Sjiang.liu@intel.com 			}
90912004Sjiang.liu@intel.com 		}
91012004Sjiang.liu@intel.com 	} else {
91112004Sjiang.liu@intel.com 		PR_ALL("%s: dr_suspend() failed, err = 0x%x\n", f, err);
91212004Sjiang.liu@intel.com 		psmerr = hp->h_err ? hp->h_err->e_code : ESBD_NOERROR;
91312004Sjiang.liu@intel.com 		switch (psmerr) {
91412004Sjiang.liu@intel.com 		case ESBD_UNSAFE:
91512004Sjiang.liu@intel.com 			PR_ALL("Unsafe devices (major #): %s\n",
91612004Sjiang.liu@intel.com 			    DR_GET_E_RSC(hp->h_err));
91712004Sjiang.liu@intel.com 			break;
91812004Sjiang.liu@intel.com 
91912004Sjiang.liu@intel.com 		case ESBD_RTTHREAD:
92012004Sjiang.liu@intel.com 			PR_ALL("RT threads (PIDs): %s\n",
92112004Sjiang.liu@intel.com 			    DR_GET_E_RSC(hp->h_err));
92212004Sjiang.liu@intel.com 			break;
92312004Sjiang.liu@intel.com 
92412004Sjiang.liu@intel.com 		case ESBD_UTHREAD:
92512004Sjiang.liu@intel.com 			PR_ALL("User threads (PIDs): %s\n",
92612004Sjiang.liu@intel.com 			    DR_GET_E_RSC(hp->h_err));
92712004Sjiang.liu@intel.com 			break;
92812004Sjiang.liu@intel.com 
92912004Sjiang.liu@intel.com 		case ESBD_SUSPEND:
93012004Sjiang.liu@intel.com 			PR_ALL("Non-suspendable devices (major #): %s\n",
93112004Sjiang.liu@intel.com 			    DR_GET_E_RSC(hp->h_err));
93212004Sjiang.liu@intel.com 			break;
93312004Sjiang.liu@intel.com 
93412004Sjiang.liu@intel.com 		case ESBD_RESUME:
93512004Sjiang.liu@intel.com 			PR_ALL("Could not resume devices (major #): %s\n",
93612004Sjiang.liu@intel.com 			    DR_GET_E_RSC(hp->h_err));
93712004Sjiang.liu@intel.com 			break;
93812004Sjiang.liu@intel.com 
93912004Sjiang.liu@intel.com 		case ESBD_KTHREAD:
94012004Sjiang.liu@intel.com 			PR_ALL("psmerr is ESBD_KTHREAD\n");
94112004Sjiang.liu@intel.com 			break;
94212004Sjiang.liu@intel.com 
94312004Sjiang.liu@intel.com 		case ESBD_NOERROR:
94412004Sjiang.liu@intel.com 			PR_ALL("sbd_error_t error code not set\n");
94512004Sjiang.liu@intel.com 			break;
94612004Sjiang.liu@intel.com 
94712004Sjiang.liu@intel.com 		default:
94812004Sjiang.liu@intel.com 			PR_ALL("Unknown error psmerr = %d\n", psmerr);
94912004Sjiang.liu@intel.com 			break;
95012004Sjiang.liu@intel.com 		}
95112004Sjiang.liu@intel.com 	}
95212004Sjiang.liu@intel.com 	dr_release_sr_handle(srh);
95312004Sjiang.liu@intel.com 
95412004Sjiang.liu@intel.com 	return (0);
95512004Sjiang.liu@intel.com }
95612004Sjiang.liu@intel.com 
95712004Sjiang.liu@intel.com /*
95812004Sjiang.liu@intel.com  * Add a new integer value to the end of an array.  Don't allow duplicates to
95912004Sjiang.liu@intel.com  * appear in the array, and don't allow the array to overflow.  Return the new
96012004Sjiang.liu@intel.com  * total number of entries in the array.
96112004Sjiang.liu@intel.com  */
96212004Sjiang.liu@intel.com static int
dr_add_int(uint64_t * arr,int idx,int len,uint64_t val)96312004Sjiang.liu@intel.com dr_add_int(uint64_t *arr, int idx, int len, uint64_t val)
96412004Sjiang.liu@intel.com {
96512004Sjiang.liu@intel.com 	int i;
96612004Sjiang.liu@intel.com 
96712004Sjiang.liu@intel.com 	if (arr == NULL)
96812004Sjiang.liu@intel.com 		return (0);
96912004Sjiang.liu@intel.com 
97012004Sjiang.liu@intel.com 	if (idx >= len)
97112004Sjiang.liu@intel.com 		return (idx);
97212004Sjiang.liu@intel.com 
97312004Sjiang.liu@intel.com 	for (i = 0; i < idx; i++) {
97412004Sjiang.liu@intel.com 		if (arr[i] == val)
97512004Sjiang.liu@intel.com 			return (idx);
97612004Sjiang.liu@intel.com 	}
97712004Sjiang.liu@intel.com 
97812004Sjiang.liu@intel.com 	arr[idx++] = val;
97912004Sjiang.liu@intel.com 
98012004Sjiang.liu@intel.com 	return (idx);
98112004Sjiang.liu@intel.com }
98212004Sjiang.liu@intel.com 
98312004Sjiang.liu@intel.com /*
98412004Sjiang.liu@intel.com  * Construct an sbd_error_t featuring a string representation of an array of
98512004Sjiang.liu@intel.com  * integers as its e_rsc.
98612004Sjiang.liu@intel.com  */
98712004Sjiang.liu@intel.com static sbd_error_t *
drerr_int(int e_code,uint64_t * arr,int idx,int majors)98812004Sjiang.liu@intel.com drerr_int(int e_code, uint64_t *arr, int idx, int majors)
98912004Sjiang.liu@intel.com {
99012004Sjiang.liu@intel.com 	int		i, n, buf_len, buf_idx, buf_avail;
99112004Sjiang.liu@intel.com 	char		*dname;
99212004Sjiang.liu@intel.com 	char		*buf;
99312004Sjiang.liu@intel.com 	sbd_error_t	*new_sbd_err;
99412004Sjiang.liu@intel.com 	static char	s_ellipsis[] = "...";
99512004Sjiang.liu@intel.com 
99612004Sjiang.liu@intel.com 	if (arr == NULL || idx <= 0)
99712004Sjiang.liu@intel.com 		return (NULL);
99812004Sjiang.liu@intel.com 
99912004Sjiang.liu@intel.com 	/* MAXPATHLEN is the size of the e_rsc field in sbd_error_t. */
100012004Sjiang.liu@intel.com 	buf = (char *)kmem_zalloc(MAXPATHLEN, KM_SLEEP);
100112004Sjiang.liu@intel.com 
100212004Sjiang.liu@intel.com 	/*
100312004Sjiang.liu@intel.com 	 * This is the total working area of the buffer.  It must be computed
100412004Sjiang.liu@intel.com 	 * as the size of 'buf', minus reserved space for the null terminator
100512004Sjiang.liu@intel.com 	 * and the ellipsis string.
100612004Sjiang.liu@intel.com 	 */
100712004Sjiang.liu@intel.com 	buf_len = MAXPATHLEN - (strlen(s_ellipsis) + 1);
100812004Sjiang.liu@intel.com 
100912004Sjiang.liu@intel.com 	/* Construct a string representation of the array values */
101012004Sjiang.liu@intel.com 	for (buf_idx = 0, i = 0; i < idx; i++) {
101112004Sjiang.liu@intel.com 		buf_avail = buf_len - buf_idx;
101212004Sjiang.liu@intel.com 		if (majors) {
101312004Sjiang.liu@intel.com 			dname = ddi_major_to_name(arr[i]);
101412004Sjiang.liu@intel.com 			if (dname) {
101512004Sjiang.liu@intel.com 				n = snprintf(&buf[buf_idx], buf_avail, "%s, ",
101612004Sjiang.liu@intel.com 				    dname);
101712004Sjiang.liu@intel.com 			} else {
101812004Sjiang.liu@intel.com 				n = snprintf(&buf[buf_idx], buf_avail,
101912004Sjiang.liu@intel.com 				    "major %" PRIu64 ", ", arr[i]);
102012004Sjiang.liu@intel.com 			}
102112004Sjiang.liu@intel.com 		} else {
102212004Sjiang.liu@intel.com 			n = snprintf(&buf[buf_idx], buf_avail, "%" PRIu64 ", ",
102312004Sjiang.liu@intel.com 			    arr[i]);
102412004Sjiang.liu@intel.com 		}
102512004Sjiang.liu@intel.com 
102612004Sjiang.liu@intel.com 		/* An ellipsis gets appended when no more values fit */
102712004Sjiang.liu@intel.com 		if (n >= buf_avail) {
102812004Sjiang.liu@intel.com 			(void) strcpy(&buf[buf_idx], s_ellipsis);
102912004Sjiang.liu@intel.com 			break;
103012004Sjiang.liu@intel.com 		}
103112004Sjiang.liu@intel.com 
103212004Sjiang.liu@intel.com 		buf_idx += n;
103312004Sjiang.liu@intel.com 	}
103412004Sjiang.liu@intel.com 
103512004Sjiang.liu@intel.com 	/* If all the contents fit, remove the trailing comma */
103612004Sjiang.liu@intel.com 	if (n < buf_avail) {
103712004Sjiang.liu@intel.com 		buf[--buf_idx] = '\0';
103812004Sjiang.liu@intel.com 		buf[--buf_idx] = '\0';
103912004Sjiang.liu@intel.com 	}
104012004Sjiang.liu@intel.com 
104112004Sjiang.liu@intel.com 	/* Return an sbd_error_t with the buffer and e_code */
104212004Sjiang.liu@intel.com 	new_sbd_err = drerr_new(1, e_code, buf);
104312004Sjiang.liu@intel.com 	kmem_free(buf, MAXPATHLEN);
104412004Sjiang.liu@intel.com 	return (new_sbd_err);
104512004Sjiang.liu@intel.com }
1046