xref: /onnv-gate/usr/src/uts/sun4u/ngdr/io/dr_quiesce.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * A CPR derivative specifically for starfire/starcat
31*0Sstevel@tonic-gate  */
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate #include <sys/types.h>
34*0Sstevel@tonic-gate #include <sys/systm.h>
35*0Sstevel@tonic-gate #include <sys/machparam.h>
36*0Sstevel@tonic-gate #include <sys/machsystm.h>
37*0Sstevel@tonic-gate #include <sys/ddi.h>
38*0Sstevel@tonic-gate #define	SUNDDI_IMPL
39*0Sstevel@tonic-gate #include <sys/sunddi.h>
40*0Sstevel@tonic-gate #include <sys/sunndi.h>
41*0Sstevel@tonic-gate #include <sys/devctl.h>
42*0Sstevel@tonic-gate #include <sys/time.h>
43*0Sstevel@tonic-gate #include <sys/kmem.h>
44*0Sstevel@tonic-gate #include <nfs/lm.h>
45*0Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
46*0Sstevel@tonic-gate #include <sys/ndi_impldefs.h>
47*0Sstevel@tonic-gate #include <sys/obpdefs.h>
48*0Sstevel@tonic-gate #include <sys/cmn_err.h>
49*0Sstevel@tonic-gate #include <sys/debug.h>
50*0Sstevel@tonic-gate #include <sys/errno.h>
51*0Sstevel@tonic-gate #include <sys/callb.h>
52*0Sstevel@tonic-gate #include <sys/clock.h>
53*0Sstevel@tonic-gate #include <sys/x_call.h>
54*0Sstevel@tonic-gate #include <sys/cpuvar.h>
55*0Sstevel@tonic-gate #include <sys/epm.h>
56*0Sstevel@tonic-gate #include <sys/vfs.h>
57*0Sstevel@tonic-gate 
58*0Sstevel@tonic-gate #include <sys/cpu_sgnblk_defs.h>
59*0Sstevel@tonic-gate #include <sys/dr.h>
60*0Sstevel@tonic-gate #include <sys/dr_util.h>
61*0Sstevel@tonic-gate 
62*0Sstevel@tonic-gate #include <sys/promif.h>
63*0Sstevel@tonic-gate #include <sys/conf.h>
64*0Sstevel@tonic-gate #include <sys/cyclic.h>
65*0Sstevel@tonic-gate 
66*0Sstevel@tonic-gate extern void	e_ddi_enter_driver_list(struct devnames *dnp, int *listcnt);
67*0Sstevel@tonic-gate extern void	e_ddi_exit_driver_list(struct devnames *dnp, int listcnt);
68*0Sstevel@tonic-gate extern int	is_pseudo_device(dev_info_t *dip);
69*0Sstevel@tonic-gate 
70*0Sstevel@tonic-gate extern kmutex_t	cpu_lock;
71*0Sstevel@tonic-gate extern dr_unsafe_devs_t dr_unsafe_devs;
72*0Sstevel@tonic-gate 
73*0Sstevel@tonic-gate static int		dr_is_real_device(dev_info_t *dip);
74*0Sstevel@tonic-gate static int		dr_is_unsafe_major(major_t major);
75*0Sstevel@tonic-gate static int		dr_bypass_device(char *dname);
76*0Sstevel@tonic-gate static int		dr_check_dip(dev_info_t *dip, void *arg, uint_t ref);
77*0Sstevel@tonic-gate static int		dr_resolve_devname(dev_info_t *dip, char *buffer,
78*0Sstevel@tonic-gate 				char *alias);
79*0Sstevel@tonic-gate static sbd_error_t	*drerr_int(int e_code, uint64_t *arr, int idx,
80*0Sstevel@tonic-gate 				int majors);
81*0Sstevel@tonic-gate static int		dr_add_int(uint64_t *arr, int idx, int len,
82*0Sstevel@tonic-gate 				uint64_t val);
83*0Sstevel@tonic-gate 
84*0Sstevel@tonic-gate int dr_pt_test_suspend(dr_handle_t *hp);
85*0Sstevel@tonic-gate 
86*0Sstevel@tonic-gate /*
87*0Sstevel@tonic-gate  * dr_quiesce.c interface
88*0Sstevel@tonic-gate  * NOTE: states used internally by dr_suspend and dr_resume
89*0Sstevel@tonic-gate  */
90*0Sstevel@tonic-gate typedef enum dr_suspend_state {
91*0Sstevel@tonic-gate 	DR_SRSTATE_BEGIN = 0,
92*0Sstevel@tonic-gate 	DR_SRSTATE_USER,
93*0Sstevel@tonic-gate 	DR_SRSTATE_DAEMON,
94*0Sstevel@tonic-gate 	DR_SRSTATE_DRIVER,
95*0Sstevel@tonic-gate 	DR_SRSTATE_FULL
96*0Sstevel@tonic-gate } suspend_state_t;
97*0Sstevel@tonic-gate 
98*0Sstevel@tonic-gate struct dr_sr_handle {
99*0Sstevel@tonic-gate 	dr_handle_t		*sr_dr_handlep;
100*0Sstevel@tonic-gate 	dev_info_t		*sr_failed_dip;
101*0Sstevel@tonic-gate 	suspend_state_t		sr_suspend_state;
102*0Sstevel@tonic-gate 	uint_t			sr_flags;
103*0Sstevel@tonic-gate 	uint64_t		sr_err_ints[DR_MAX_ERR_INT];
104*0Sstevel@tonic-gate 	int			sr_err_idx;
105*0Sstevel@tonic-gate };
106*0Sstevel@tonic-gate 
107*0Sstevel@tonic-gate #define	SR_FLAG_WATCHDOG	0x1
108*0Sstevel@tonic-gate 
109*0Sstevel@tonic-gate /*
110*0Sstevel@tonic-gate  * XXX
111*0Sstevel@tonic-gate  * This hack will go away before RTI.  Just for testing.
112*0Sstevel@tonic-gate  * List of drivers to bypass when performing a suspend.
113*0Sstevel@tonic-gate  */
114*0Sstevel@tonic-gate static char *dr_bypass_list[] = {
115*0Sstevel@tonic-gate 	""
116*0Sstevel@tonic-gate };
117*0Sstevel@tonic-gate 
118*0Sstevel@tonic-gate 
119*0Sstevel@tonic-gate static int	dr_skip_kernel_threads = 1;	/* "TRUE" */
120*0Sstevel@tonic-gate #define		SKIP_SYNC	/* bypass sync ops in dr_suspend */
121*0Sstevel@tonic-gate 
122*0Sstevel@tonic-gate /*
123*0Sstevel@tonic-gate  * dr_skip_user_threads is used to control if user threads should
124*0Sstevel@tonic-gate  * be suspended.  If dr_skip_user_threads is true, the rest of the
125*0Sstevel@tonic-gate  * flags are not used; if it is false, dr_check_user_stop_result
126*0Sstevel@tonic-gate  * will be used to control whether or not we need to check suspend
127*0Sstevel@tonic-gate  * result, and dr_allow_blocked_threads will be used to control
128*0Sstevel@tonic-gate  * whether or not we allow suspend to continue if there are blocked
129*0Sstevel@tonic-gate  * threads.  We allow all combinations of dr_check_user_stop_result
130*0Sstevel@tonic-gate  * and dr_allow_block_threads, even though it might not make much
131*0Sstevel@tonic-gate  * sense to not allow block threads when we don't even check stop
132*0Sstevel@tonic-gate  * result.
133*0Sstevel@tonic-gate  */
134*0Sstevel@tonic-gate static int	dr_skip_user_threads = 0;	/* default to FALSE */
135*0Sstevel@tonic-gate static int	dr_check_user_stop_result = 1;	/* default to TRUE */
136*0Sstevel@tonic-gate static int	dr_allow_blocked_threads = 1;	/* default to TRUE */
137*0Sstevel@tonic-gate 
138*0Sstevel@tonic-gate #define	DR_CPU_LOOP_MSEC	1000
139*0Sstevel@tonic-gate 
140*0Sstevel@tonic-gate static void
141*0Sstevel@tonic-gate dr_stop_intr(void)
142*0Sstevel@tonic-gate {
143*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&cpu_lock));
144*0Sstevel@tonic-gate 
145*0Sstevel@tonic-gate 	kpreempt_disable();
146*0Sstevel@tonic-gate 	cyclic_suspend();
147*0Sstevel@tonic-gate }
148*0Sstevel@tonic-gate 
149*0Sstevel@tonic-gate static void
150*0Sstevel@tonic-gate dr_enable_intr(void)
151*0Sstevel@tonic-gate {
152*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&cpu_lock));
153*0Sstevel@tonic-gate 
154*0Sstevel@tonic-gate 	cyclic_resume();
155*0Sstevel@tonic-gate 	kpreempt_enable();
156*0Sstevel@tonic-gate }
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate dr_sr_handle_t *
159*0Sstevel@tonic-gate dr_get_sr_handle(dr_handle_t *hp)
160*0Sstevel@tonic-gate {
161*0Sstevel@tonic-gate 	dr_sr_handle_t *srh;
162*0Sstevel@tonic-gate 
163*0Sstevel@tonic-gate 	srh = GETSTRUCT(dr_sr_handle_t, 1);
164*0Sstevel@tonic-gate 	srh->sr_dr_handlep = hp;
165*0Sstevel@tonic-gate 
166*0Sstevel@tonic-gate 	return (srh);
167*0Sstevel@tonic-gate }
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate void
170*0Sstevel@tonic-gate dr_release_sr_handle(dr_sr_handle_t *srh)
171*0Sstevel@tonic-gate {
172*0Sstevel@tonic-gate 	ASSERT(srh->sr_failed_dip == NULL);
173*0Sstevel@tonic-gate 	FREESTRUCT(srh, dr_sr_handle_t, 1);
174*0Sstevel@tonic-gate }
175*0Sstevel@tonic-gate 
176*0Sstevel@tonic-gate static int
177*0Sstevel@tonic-gate dr_is_real_device(dev_info_t *dip)
178*0Sstevel@tonic-gate {
179*0Sstevel@tonic-gate 	struct regspec *regbuf = NULL;
180*0Sstevel@tonic-gate 	int length = 0;
181*0Sstevel@tonic-gate 	int rc;
182*0Sstevel@tonic-gate 
183*0Sstevel@tonic-gate 	if (ddi_get_driver(dip) == NULL)
184*0Sstevel@tonic-gate 		return (0);
185*0Sstevel@tonic-gate 
186*0Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_flags & (PMC_NEEDS_SR|PMC_PARENTAL_SR))
187*0Sstevel@tonic-gate 		return (1);
188*0Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_flags & PMC_NO_SR)
189*0Sstevel@tonic-gate 		return (0);
190*0Sstevel@tonic-gate 
191*0Sstevel@tonic-gate 	/*
192*0Sstevel@tonic-gate 	 * now the general case
193*0Sstevel@tonic-gate 	 */
194*0Sstevel@tonic-gate 	rc = ddi_getlongprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS, "reg",
195*0Sstevel@tonic-gate 		(caddr_t)&regbuf, &length);
196*0Sstevel@tonic-gate 	ASSERT(rc != DDI_PROP_NO_MEMORY);
197*0Sstevel@tonic-gate 	if (rc != DDI_PROP_SUCCESS) {
198*0Sstevel@tonic-gate 		return (0);
199*0Sstevel@tonic-gate 	} else {
200*0Sstevel@tonic-gate 		if ((length > 0) && (regbuf != NULL))
201*0Sstevel@tonic-gate 			kmem_free(regbuf, length);
202*0Sstevel@tonic-gate 		return (1);
203*0Sstevel@tonic-gate 	}
204*0Sstevel@tonic-gate }
205*0Sstevel@tonic-gate 
206*0Sstevel@tonic-gate static int
207*0Sstevel@tonic-gate dr_is_unsafe_major(major_t major)
208*0Sstevel@tonic-gate {
209*0Sstevel@tonic-gate 	char    *dname, **cpp;
210*0Sstevel@tonic-gate 	int	i, ndevs;
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate 	if ((dname = ddi_major_to_name(major)) == NULL) {
213*0Sstevel@tonic-gate 		PR_QR("dr_is_unsafe_major: invalid major # %d\n", major);
214*0Sstevel@tonic-gate 		return (0);
215*0Sstevel@tonic-gate 	}
216*0Sstevel@tonic-gate 
217*0Sstevel@tonic-gate 	ndevs = dr_unsafe_devs.ndevs;
218*0Sstevel@tonic-gate 	for (i = 0, cpp = dr_unsafe_devs.devnames; i < ndevs; i++) {
219*0Sstevel@tonic-gate 		if (strcmp(dname, *cpp++) == 0)
220*0Sstevel@tonic-gate 			return (1);
221*0Sstevel@tonic-gate 	}
222*0Sstevel@tonic-gate 	return (0);
223*0Sstevel@tonic-gate }
224*0Sstevel@tonic-gate 
225*0Sstevel@tonic-gate static int
226*0Sstevel@tonic-gate dr_bypass_device(char *dname)
227*0Sstevel@tonic-gate {
228*0Sstevel@tonic-gate 	int i;
229*0Sstevel@tonic-gate 	char **lname;
230*0Sstevel@tonic-gate 	/* check the bypass list */
231*0Sstevel@tonic-gate 	for (i = 0, lname = &dr_bypass_list[i]; **lname != '\0'; lname++) {
232*0Sstevel@tonic-gate 		if (strcmp(dname, dr_bypass_list[i++]) == 0)
233*0Sstevel@tonic-gate 			return (1);
234*0Sstevel@tonic-gate 	}
235*0Sstevel@tonic-gate 	return (0);
236*0Sstevel@tonic-gate }
237*0Sstevel@tonic-gate 
238*0Sstevel@tonic-gate static int
239*0Sstevel@tonic-gate dr_resolve_devname(dev_info_t *dip, char *buffer, char *alias)
240*0Sstevel@tonic-gate {
241*0Sstevel@tonic-gate 	major_t	devmajor;
242*0Sstevel@tonic-gate 	char	*aka, *name;
243*0Sstevel@tonic-gate 
244*0Sstevel@tonic-gate 	*buffer = *alias = 0;
245*0Sstevel@tonic-gate 
246*0Sstevel@tonic-gate 	if (dip == NULL)
247*0Sstevel@tonic-gate 		return (-1);
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate 	if ((name = ddi_get_name(dip)) == NULL)
250*0Sstevel@tonic-gate 		name = "<null name>";
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate 	aka = name;
253*0Sstevel@tonic-gate 
254*0Sstevel@tonic-gate 	if ((devmajor = ddi_name_to_major(aka)) != -1)
255*0Sstevel@tonic-gate 		aka = ddi_major_to_name(devmajor);
256*0Sstevel@tonic-gate 
257*0Sstevel@tonic-gate 	strcpy(buffer, name);
258*0Sstevel@tonic-gate 
259*0Sstevel@tonic-gate 	if (strcmp(name, aka))
260*0Sstevel@tonic-gate 		strcpy(alias, aka);
261*0Sstevel@tonic-gate 	else
262*0Sstevel@tonic-gate 		*alias = 0;
263*0Sstevel@tonic-gate 
264*0Sstevel@tonic-gate 	return (0);
265*0Sstevel@tonic-gate }
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate struct dr_ref {
268*0Sstevel@tonic-gate 	int		*refcount;
269*0Sstevel@tonic-gate 	uint64_t	*arr;
270*0Sstevel@tonic-gate 	int		*idx;
271*0Sstevel@tonic-gate 	int		len;
272*0Sstevel@tonic-gate };
273*0Sstevel@tonic-gate 
274*0Sstevel@tonic-gate /* ARGSUSED */
275*0Sstevel@tonic-gate static int
276*0Sstevel@tonic-gate dr_check_dip(dev_info_t *dip, void *arg, uint_t ref)
277*0Sstevel@tonic-gate {
278*0Sstevel@tonic-gate 	major_t		major;
279*0Sstevel@tonic-gate 	char		*dname;
280*0Sstevel@tonic-gate 	struct dr_ref	*rp = (struct dr_ref *)arg;
281*0Sstevel@tonic-gate 
282*0Sstevel@tonic-gate 	if (dip == NULL)
283*0Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
284*0Sstevel@tonic-gate 
285*0Sstevel@tonic-gate 	if (!dr_is_real_device(dip))
286*0Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
287*0Sstevel@tonic-gate 
288*0Sstevel@tonic-gate 	dname = ddi_binding_name(dip);
289*0Sstevel@tonic-gate 
290*0Sstevel@tonic-gate 	if (dr_bypass_device(dname))
291*0Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
292*0Sstevel@tonic-gate 
293*0Sstevel@tonic-gate 	if (dname && ((major = ddi_name_to_major(dname)) != (major_t)-1)) {
294*0Sstevel@tonic-gate 		if (ref && rp->refcount) {
295*0Sstevel@tonic-gate 			*rp->refcount += ref;
296*0Sstevel@tonic-gate 			PR_QR("\n  %s (major# %d) is referenced(%u)\n",
297*0Sstevel@tonic-gate 				dname, major, ref);
298*0Sstevel@tonic-gate 		}
299*0Sstevel@tonic-gate 		if (dr_is_unsafe_major(major) &&
300*0Sstevel@tonic-gate 		    i_ddi_node_state(dip) >= DS_ATTACHED) {
301*0Sstevel@tonic-gate 			PR_QR("\n  %s (major# %d) not hotpluggable\n",
302*0Sstevel@tonic-gate 				dname, major);
303*0Sstevel@tonic-gate 			if (rp->arr != NULL && rp->idx != NULL)
304*0Sstevel@tonic-gate 				*rp->idx = dr_add_int(rp->arr, *rp->idx,
305*0Sstevel@tonic-gate 					rp->len, (uint64_t)major);
306*0Sstevel@tonic-gate 		}
307*0Sstevel@tonic-gate 	}
308*0Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
309*0Sstevel@tonic-gate }
310*0Sstevel@tonic-gate 
311*0Sstevel@tonic-gate static int
312*0Sstevel@tonic-gate dr_check_unsafe_major(dev_info_t *dip, void *arg)
313*0Sstevel@tonic-gate {
314*0Sstevel@tonic-gate 	return (dr_check_dip(dip, arg, 0));
315*0Sstevel@tonic-gate }
316*0Sstevel@tonic-gate 
317*0Sstevel@tonic-gate 
318*0Sstevel@tonic-gate /*ARGSUSED*/
319*0Sstevel@tonic-gate void
320*0Sstevel@tonic-gate dr_check_devices(dev_info_t *dip, int *refcount, dr_handle_t *handle,
321*0Sstevel@tonic-gate     uint64_t *arr, int *idx, int len)
322*0Sstevel@tonic-gate {
323*0Sstevel@tonic-gate 	struct dr_ref bref = {0};
324*0Sstevel@tonic-gate 
325*0Sstevel@tonic-gate 	if (dip == NULL)
326*0Sstevel@tonic-gate 		return;
327*0Sstevel@tonic-gate 
328*0Sstevel@tonic-gate 	bref.refcount = refcount;
329*0Sstevel@tonic-gate 	bref.arr = arr;
330*0Sstevel@tonic-gate 	bref.idx = idx;
331*0Sstevel@tonic-gate 	bref.len = len;
332*0Sstevel@tonic-gate 
333*0Sstevel@tonic-gate 	ASSERT(e_ddi_branch_held(dip));
334*0Sstevel@tonic-gate 	(void) e_ddi_branch_referenced(dip, dr_check_dip, &bref);
335*0Sstevel@tonic-gate }
336*0Sstevel@tonic-gate 
337*0Sstevel@tonic-gate /*
338*0Sstevel@tonic-gate  * The "dip" argument's parent (if it exists) must be held busy.
339*0Sstevel@tonic-gate  */
340*0Sstevel@tonic-gate static int
341*0Sstevel@tonic-gate dr_suspend_devices(dev_info_t *dip, dr_sr_handle_t *srh)
342*0Sstevel@tonic-gate {
343*0Sstevel@tonic-gate 	dr_handle_t	*handle;
344*0Sstevel@tonic-gate 	major_t		major;
345*0Sstevel@tonic-gate 	char		*dname;
346*0Sstevel@tonic-gate 	int		circ;
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate 	/*
349*0Sstevel@tonic-gate 	 * If dip is the root node, it has no siblings and it is
350*0Sstevel@tonic-gate 	 * always held. If dip is not the root node, dr_suspend_devices()
351*0Sstevel@tonic-gate 	 * will be invoked with the parent held busy.
352*0Sstevel@tonic-gate 	 */
353*0Sstevel@tonic-gate 	for (; dip != NULL; dip = ddi_get_next_sibling(dip)) {
354*0Sstevel@tonic-gate 		char	d_name[40], d_alias[40], *d_info;
355*0Sstevel@tonic-gate 
356*0Sstevel@tonic-gate 		ndi_devi_enter(dip, &circ);
357*0Sstevel@tonic-gate 		if (dr_suspend_devices(ddi_get_child(dip), srh)) {
358*0Sstevel@tonic-gate 			ndi_devi_exit(dip, circ);
359*0Sstevel@tonic-gate 			return (ENXIO);
360*0Sstevel@tonic-gate 		}
361*0Sstevel@tonic-gate 		ndi_devi_exit(dip, circ);
362*0Sstevel@tonic-gate 
363*0Sstevel@tonic-gate 		if (!dr_is_real_device(dip))
364*0Sstevel@tonic-gate 			continue;
365*0Sstevel@tonic-gate 
366*0Sstevel@tonic-gate 		major = (major_t)-1;
367*0Sstevel@tonic-gate 		if ((dname = ddi_binding_name(dip)) != NULL)
368*0Sstevel@tonic-gate 			major = ddi_name_to_major(dname);
369*0Sstevel@tonic-gate 
370*0Sstevel@tonic-gate 		if (dr_bypass_device(dname)) {
371*0Sstevel@tonic-gate 			PR_QR(" bypassed suspend of %s (major# %d)\n", dname,
372*0Sstevel@tonic-gate 				major);
373*0Sstevel@tonic-gate 			continue;
374*0Sstevel@tonic-gate 		}
375*0Sstevel@tonic-gate 
376*0Sstevel@tonic-gate 		if (drmach_verify_sr(dip, 1)) {
377*0Sstevel@tonic-gate 			PR_QR(" bypassed suspend of %s (major# %d)\n", dname,
378*0Sstevel@tonic-gate 				major);
379*0Sstevel@tonic-gate 			continue;
380*0Sstevel@tonic-gate 		}
381*0Sstevel@tonic-gate 
382*0Sstevel@tonic-gate 		if ((d_info = ddi_get_name_addr(dip)) == NULL)
383*0Sstevel@tonic-gate 			d_info = "<null>";
384*0Sstevel@tonic-gate 
385*0Sstevel@tonic-gate 		d_name[0] = 0;
386*0Sstevel@tonic-gate 		if (dr_resolve_devname(dip, d_name, d_alias) == 0) {
387*0Sstevel@tonic-gate 			if (d_alias[0] != 0) {
388*0Sstevel@tonic-gate 				prom_printf("\tsuspending %s@%s (aka %s)\n",
389*0Sstevel@tonic-gate 					d_name, d_info, d_alias);
390*0Sstevel@tonic-gate 			} else {
391*0Sstevel@tonic-gate 				prom_printf("\tsuspending %s@%s\n",
392*0Sstevel@tonic-gate 					d_name, d_info);
393*0Sstevel@tonic-gate 			}
394*0Sstevel@tonic-gate 		} else {
395*0Sstevel@tonic-gate 			prom_printf("\tsuspending %s@%s\n", dname, d_info);
396*0Sstevel@tonic-gate 		}
397*0Sstevel@tonic-gate 
398*0Sstevel@tonic-gate 		if (devi_detach(dip, DDI_SUSPEND) != DDI_SUCCESS) {
399*0Sstevel@tonic-gate 			prom_printf("\tFAILED to suspend %s@%s\n",
400*0Sstevel@tonic-gate 				d_name[0] ? d_name : dname, d_info);
401*0Sstevel@tonic-gate 
402*0Sstevel@tonic-gate 			srh->sr_err_idx = dr_add_int(srh->sr_err_ints,
403*0Sstevel@tonic-gate 				srh->sr_err_idx, DR_MAX_ERR_INT,
404*0Sstevel@tonic-gate 				(uint64_t)major);
405*0Sstevel@tonic-gate 
406*0Sstevel@tonic-gate 			ndi_hold_devi(dip);
407*0Sstevel@tonic-gate 			srh->sr_failed_dip = dip;
408*0Sstevel@tonic-gate 
409*0Sstevel@tonic-gate 			handle = srh->sr_dr_handlep;
410*0Sstevel@tonic-gate 			dr_op_err(CE_IGNORE, handle, ESBD_SUSPEND, "%s@%s",
411*0Sstevel@tonic-gate 				d_name[0] ? d_name : dname, d_info);
412*0Sstevel@tonic-gate 
413*0Sstevel@tonic-gate 			return (DDI_FAILURE);
414*0Sstevel@tonic-gate 		}
415*0Sstevel@tonic-gate 	}
416*0Sstevel@tonic-gate 
417*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
418*0Sstevel@tonic-gate }
419*0Sstevel@tonic-gate 
420*0Sstevel@tonic-gate static void
421*0Sstevel@tonic-gate dr_resume_devices(dev_info_t *start, dr_sr_handle_t *srh)
422*0Sstevel@tonic-gate {
423*0Sstevel@tonic-gate 	dr_handle_t	*handle;
424*0Sstevel@tonic-gate 	dev_info_t	*dip, *next, *last = NULL;
425*0Sstevel@tonic-gate 	major_t		major;
426*0Sstevel@tonic-gate 	char		*bn;
427*0Sstevel@tonic-gate 	int		circ;
428*0Sstevel@tonic-gate 
429*0Sstevel@tonic-gate 	major = (major_t)-1;
430*0Sstevel@tonic-gate 
431*0Sstevel@tonic-gate 	/* attach in reverse device tree order */
432*0Sstevel@tonic-gate 	while (last != start) {
433*0Sstevel@tonic-gate 		dip = start;
434*0Sstevel@tonic-gate 		next = ddi_get_next_sibling(dip);
435*0Sstevel@tonic-gate 		while (next != last && dip != srh->sr_failed_dip) {
436*0Sstevel@tonic-gate 			dip = next;
437*0Sstevel@tonic-gate 			next = ddi_get_next_sibling(dip);
438*0Sstevel@tonic-gate 		}
439*0Sstevel@tonic-gate 		if (dip == srh->sr_failed_dip) {
440*0Sstevel@tonic-gate 			/* release hold acquired in dr_suspend_devices() */
441*0Sstevel@tonic-gate 			srh->sr_failed_dip = NULL;
442*0Sstevel@tonic-gate 			ndi_rele_devi(dip);
443*0Sstevel@tonic-gate 		} else if (dr_is_real_device(dip) &&
444*0Sstevel@tonic-gate 				srh->sr_failed_dip == NULL) {
445*0Sstevel@tonic-gate 
446*0Sstevel@tonic-gate 			if ((bn = ddi_binding_name(dip)) != NULL) {
447*0Sstevel@tonic-gate 				major = ddi_name_to_major(bn);
448*0Sstevel@tonic-gate 			} else {
449*0Sstevel@tonic-gate 				bn = "<null>";
450*0Sstevel@tonic-gate 			}
451*0Sstevel@tonic-gate 			if (!dr_bypass_device(bn) &&
452*0Sstevel@tonic-gate 				!drmach_verify_sr(dip, 0)) {
453*0Sstevel@tonic-gate 				char	d_name[40], d_alias[40], *d_info;
454*0Sstevel@tonic-gate 
455*0Sstevel@tonic-gate 				d_name[0] = 0;
456*0Sstevel@tonic-gate 				d_info = ddi_get_name_addr(dip);
457*0Sstevel@tonic-gate 				if (d_info == NULL)
458*0Sstevel@tonic-gate 					d_info = "<null>";
459*0Sstevel@tonic-gate 
460*0Sstevel@tonic-gate 				if (!dr_resolve_devname(dip, d_name,
461*0Sstevel@tonic-gate 								d_alias)) {
462*0Sstevel@tonic-gate 					if (d_alias[0] != 0) {
463*0Sstevel@tonic-gate 						prom_printf("\tresuming "
464*0Sstevel@tonic-gate 							"%s@%s (aka %s)\n",
465*0Sstevel@tonic-gate 							d_name, d_info,
466*0Sstevel@tonic-gate 							d_alias);
467*0Sstevel@tonic-gate 					} else {
468*0Sstevel@tonic-gate 						prom_printf("\tresuming "
469*0Sstevel@tonic-gate 							"%s@%s\n",
470*0Sstevel@tonic-gate 							d_name, d_info);
471*0Sstevel@tonic-gate 					}
472*0Sstevel@tonic-gate 				} else {
473*0Sstevel@tonic-gate 					prom_printf("\tresuming %s@%s\n",
474*0Sstevel@tonic-gate 						bn, d_info);
475*0Sstevel@tonic-gate 				}
476*0Sstevel@tonic-gate 
477*0Sstevel@tonic-gate 				if (devi_attach(dip, DDI_RESUME) !=
478*0Sstevel@tonic-gate 							DDI_SUCCESS) {
479*0Sstevel@tonic-gate 					/*
480*0Sstevel@tonic-gate 					 * Print a console warning,
481*0Sstevel@tonic-gate 					 * set an e_code of ESBD_RESUME,
482*0Sstevel@tonic-gate 					 * and save the driver major
483*0Sstevel@tonic-gate 					 * number in the e_rsc.
484*0Sstevel@tonic-gate 					 */
485*0Sstevel@tonic-gate 					prom_printf("\tFAILED to resume %s@%s",
486*0Sstevel@tonic-gate 					    d_name[0] ? d_name : bn, d_info);
487*0Sstevel@tonic-gate 
488*0Sstevel@tonic-gate 					srh->sr_err_idx =
489*0Sstevel@tonic-gate 						dr_add_int(srh->sr_err_ints,
490*0Sstevel@tonic-gate 						srh->sr_err_idx, DR_MAX_ERR_INT,
491*0Sstevel@tonic-gate 						(uint64_t)major);
492*0Sstevel@tonic-gate 
493*0Sstevel@tonic-gate 					handle = srh->sr_dr_handlep;
494*0Sstevel@tonic-gate 
495*0Sstevel@tonic-gate 					dr_op_err(CE_IGNORE, handle,
496*0Sstevel@tonic-gate 					    ESBD_RESUME, "%s@%s",
497*0Sstevel@tonic-gate 					    d_name[0] ? d_name : bn, d_info);
498*0Sstevel@tonic-gate 				}
499*0Sstevel@tonic-gate 			}
500*0Sstevel@tonic-gate 		}
501*0Sstevel@tonic-gate 
502*0Sstevel@tonic-gate 		/* Hold parent busy while walking its children */
503*0Sstevel@tonic-gate 		ndi_devi_enter(dip, &circ);
504*0Sstevel@tonic-gate 		dr_resume_devices(ddi_get_child(dip), srh);
505*0Sstevel@tonic-gate 		ndi_devi_exit(dip, circ);
506*0Sstevel@tonic-gate 		last = dip;
507*0Sstevel@tonic-gate 	}
508*0Sstevel@tonic-gate }
509*0Sstevel@tonic-gate 
510*0Sstevel@tonic-gate /*
511*0Sstevel@tonic-gate  * True if thread is virtually stopped.  Similar to CPR_VSTOPPED
512*0Sstevel@tonic-gate  * but from DR point of view.  These user threads are waiting in
513*0Sstevel@tonic-gate  * the kernel.  Once they complete in the kernel, they will process
514*0Sstevel@tonic-gate  * the stop signal and stop.
515*0Sstevel@tonic-gate  */
516*0Sstevel@tonic-gate #define	DR_VSTOPPED(t)			\
517*0Sstevel@tonic-gate 	((t)->t_state == TS_SLEEP &&	\
518*0Sstevel@tonic-gate 	(t)->t_wchan != NULL &&		\
519*0Sstevel@tonic-gate 	(t)->t_astflag &&		\
520*0Sstevel@tonic-gate 	((t)->t_proc_flag & TP_CHKPT))
521*0Sstevel@tonic-gate 
522*0Sstevel@tonic-gate /* ARGSUSED */
523*0Sstevel@tonic-gate static int
524*0Sstevel@tonic-gate dr_stop_user_threads(dr_sr_handle_t *srh)
525*0Sstevel@tonic-gate {
526*0Sstevel@tonic-gate 	int		count;
527*0Sstevel@tonic-gate 	int		bailout;
528*0Sstevel@tonic-gate 	dr_handle_t	*handle = srh->sr_dr_handlep;
529*0Sstevel@tonic-gate 	static fn_t	f = "dr_stop_user_threads";
530*0Sstevel@tonic-gate 	kthread_id_t 	tp;
531*0Sstevel@tonic-gate 
532*0Sstevel@tonic-gate 	extern void add_one_utstop();
533*0Sstevel@tonic-gate 	extern void utstop_timedwait(clock_t);
534*0Sstevel@tonic-gate 	extern void utstop_init(void);
535*0Sstevel@tonic-gate 
536*0Sstevel@tonic-gate #define	DR_UTSTOP_RETRY	4
537*0Sstevel@tonic-gate #define	DR_UTSTOP_WAIT	hz
538*0Sstevel@tonic-gate 
539*0Sstevel@tonic-gate 	if (dr_skip_user_threads)
540*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
541*0Sstevel@tonic-gate 
542*0Sstevel@tonic-gate 	utstop_init();
543*0Sstevel@tonic-gate 
544*0Sstevel@tonic-gate 	/* we need to try a few times to get past fork, etc. */
545*0Sstevel@tonic-gate 	srh->sr_err_idx = 0;
546*0Sstevel@tonic-gate 	for (count = 0; count < DR_UTSTOP_RETRY; count++) {
547*0Sstevel@tonic-gate 		/* walk the entire threadlist */
548*0Sstevel@tonic-gate 		mutex_enter(&pidlock);
549*0Sstevel@tonic-gate 		for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) {
550*0Sstevel@tonic-gate 			proc_t *p = ttoproc(tp);
551*0Sstevel@tonic-gate 
552*0Sstevel@tonic-gate 			/* handle kernel threads separately */
553*0Sstevel@tonic-gate 			if (p->p_as == &kas || p->p_stat == SZOMB)
554*0Sstevel@tonic-gate 				continue;
555*0Sstevel@tonic-gate 
556*0Sstevel@tonic-gate 			mutex_enter(&p->p_lock);
557*0Sstevel@tonic-gate 			thread_lock(tp);
558*0Sstevel@tonic-gate 
559*0Sstevel@tonic-gate 			if (tp->t_state == TS_STOPPED) {
560*0Sstevel@tonic-gate 				/* add another reason to stop this thread */
561*0Sstevel@tonic-gate 				tp->t_schedflag &= ~TS_RESUME;
562*0Sstevel@tonic-gate 			} else {
563*0Sstevel@tonic-gate 				tp->t_proc_flag |= TP_CHKPT;
564*0Sstevel@tonic-gate 
565*0Sstevel@tonic-gate 				thread_unlock(tp);
566*0Sstevel@tonic-gate 				mutex_exit(&p->p_lock);
567*0Sstevel@tonic-gate 				add_one_utstop();
568*0Sstevel@tonic-gate 				mutex_enter(&p->p_lock);
569*0Sstevel@tonic-gate 				thread_lock(tp);
570*0Sstevel@tonic-gate 
571*0Sstevel@tonic-gate 				aston(tp);
572*0Sstevel@tonic-gate 
573*0Sstevel@tonic-gate 				if (tp->t_state == TS_SLEEP &&
574*0Sstevel@tonic-gate 				    (tp->t_flag & T_WAKEABLE)) {
575*0Sstevel@tonic-gate 					setrun_locked(tp);
576*0Sstevel@tonic-gate 				}
577*0Sstevel@tonic-gate 
578*0Sstevel@tonic-gate 			}
579*0Sstevel@tonic-gate 
580*0Sstevel@tonic-gate 			/* grab thread if needed */
581*0Sstevel@tonic-gate 			if (tp->t_state == TS_ONPROC && tp->t_cpu != CPU)
582*0Sstevel@tonic-gate 				poke_cpu(tp->t_cpu->cpu_id);
583*0Sstevel@tonic-gate 
584*0Sstevel@tonic-gate 
585*0Sstevel@tonic-gate 			thread_unlock(tp);
586*0Sstevel@tonic-gate 			mutex_exit(&p->p_lock);
587*0Sstevel@tonic-gate 		}
588*0Sstevel@tonic-gate 		mutex_exit(&pidlock);
589*0Sstevel@tonic-gate 
590*0Sstevel@tonic-gate 
591*0Sstevel@tonic-gate 		/* let everything catch up */
592*0Sstevel@tonic-gate 		utstop_timedwait(count * count * DR_UTSTOP_WAIT);
593*0Sstevel@tonic-gate 
594*0Sstevel@tonic-gate 
595*0Sstevel@tonic-gate 		/* now, walk the threadlist again to see if we are done */
596*0Sstevel@tonic-gate 		mutex_enter(&pidlock);
597*0Sstevel@tonic-gate 		for (tp = curthread->t_next, bailout = 0;
598*0Sstevel@tonic-gate 		    tp != curthread; tp = tp->t_next) {
599*0Sstevel@tonic-gate 			proc_t *p = ttoproc(tp);
600*0Sstevel@tonic-gate 
601*0Sstevel@tonic-gate 			/* handle kernel threads separately */
602*0Sstevel@tonic-gate 			if (p->p_as == &kas || p->p_stat == SZOMB)
603*0Sstevel@tonic-gate 				continue;
604*0Sstevel@tonic-gate 
605*0Sstevel@tonic-gate 			/*
606*0Sstevel@tonic-gate 			 * If this thread didn't stop, and we don't allow
607*0Sstevel@tonic-gate 			 * unstopped blocked threads, bail.
608*0Sstevel@tonic-gate 			 */
609*0Sstevel@tonic-gate 			thread_lock(tp);
610*0Sstevel@tonic-gate 			if (!CPR_ISTOPPED(tp) &&
611*0Sstevel@tonic-gate 			    !(dr_allow_blocked_threads &&
612*0Sstevel@tonic-gate 			    DR_VSTOPPED(tp))) {
613*0Sstevel@tonic-gate 				bailout = 1;
614*0Sstevel@tonic-gate 				if (count == DR_UTSTOP_RETRY - 1) {
615*0Sstevel@tonic-gate 					/*
616*0Sstevel@tonic-gate 					 * save the pid for later reporting
617*0Sstevel@tonic-gate 					 */
618*0Sstevel@tonic-gate 					srh->sr_err_idx =
619*0Sstevel@tonic-gate 					    dr_add_int(srh->sr_err_ints,
620*0Sstevel@tonic-gate 					    srh->sr_err_idx, DR_MAX_ERR_INT,
621*0Sstevel@tonic-gate 					    (uint64_t)p->p_pid);
622*0Sstevel@tonic-gate 
623*0Sstevel@tonic-gate 					cmn_err(CE_WARN, "%s: "
624*0Sstevel@tonic-gate 					    "failed to stop thread: "
625*0Sstevel@tonic-gate 					    "process=%s, pid=%d",
626*0Sstevel@tonic-gate 					    f, p->p_user.u_psargs, p->p_pid);
627*0Sstevel@tonic-gate 
628*0Sstevel@tonic-gate 					PR_QR("%s: failed to stop thread: "
629*0Sstevel@tonic-gate 					    "process=%s, pid=%d, t_id=0x%lx, "
630*0Sstevel@tonic-gate 					    "t_state=0x%x, t_proc_flag=0x%x, "
631*0Sstevel@tonic-gate 					    "t_schedflag=0x%x\n",
632*0Sstevel@tonic-gate 					    f, p->p_user.u_psargs, p->p_pid,
633*0Sstevel@tonic-gate 					    tp, tp->t_state, tp->t_proc_flag,
634*0Sstevel@tonic-gate 					    tp->t_schedflag);
635*0Sstevel@tonic-gate 				}
636*0Sstevel@tonic-gate 
637*0Sstevel@tonic-gate 			}
638*0Sstevel@tonic-gate 			thread_unlock(tp);
639*0Sstevel@tonic-gate 		}
640*0Sstevel@tonic-gate 		mutex_exit(&pidlock);
641*0Sstevel@tonic-gate 
642*0Sstevel@tonic-gate 		/* were all the threads stopped? */
643*0Sstevel@tonic-gate 		if (!bailout)
644*0Sstevel@tonic-gate 			break;
645*0Sstevel@tonic-gate 	}
646*0Sstevel@tonic-gate 
647*0Sstevel@tonic-gate 	/* were we unable to stop all threads after a few tries? */
648*0Sstevel@tonic-gate 	if (bailout) {
649*0Sstevel@tonic-gate 		handle->h_err = drerr_int(ESBD_UTHREAD, srh->sr_err_ints,
650*0Sstevel@tonic-gate 			srh->sr_err_idx, 0);
651*0Sstevel@tonic-gate 		return (ESRCH);
652*0Sstevel@tonic-gate 	}
653*0Sstevel@tonic-gate 
654*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
655*0Sstevel@tonic-gate }
656*0Sstevel@tonic-gate 
657*0Sstevel@tonic-gate static int
658*0Sstevel@tonic-gate dr_stop_kernel_threads(dr_handle_t *handle)
659*0Sstevel@tonic-gate {
660*0Sstevel@tonic-gate 	caddr_t		name;
661*0Sstevel@tonic-gate 	kthread_id_t	tp;
662*0Sstevel@tonic-gate 
663*0Sstevel@tonic-gate 	if (dr_skip_kernel_threads) {
664*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
665*0Sstevel@tonic-gate 	}
666*0Sstevel@tonic-gate 
667*0Sstevel@tonic-gate 	/*
668*0Sstevel@tonic-gate 	 * Note: we unlock the table in resume.
669*0Sstevel@tonic-gate 	 * We need to lock the callback table only if we are actually
670*0Sstevel@tonic-gate 	 * suspending kernel threads.
671*0Sstevel@tonic-gate 	 */
672*0Sstevel@tonic-gate 	callb_lock_table();
673*0Sstevel@tonic-gate 	name = callb_execute_class(CB_CL_CPR_DAEMON, CB_CODE_CPR_CHKPT);
674*0Sstevel@tonic-gate 	if (name != NULL) {
675*0Sstevel@tonic-gate 		dr_op_err(CE_IGNORE, handle, ESBD_KTHREAD, name);
676*0Sstevel@tonic-gate 		return (EBUSY);
677*0Sstevel@tonic-gate 	}
678*0Sstevel@tonic-gate 
679*0Sstevel@tonic-gate 	/*
680*0Sstevel@tonic-gate 	 * Verify that all threads are accounted for
681*0Sstevel@tonic-gate 	 */
682*0Sstevel@tonic-gate 	mutex_enter(&pidlock);
683*0Sstevel@tonic-gate 	for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) {
684*0Sstevel@tonic-gate 		proc_t	*p = ttoproc(tp);
685*0Sstevel@tonic-gate 
686*0Sstevel@tonic-gate 		if (p->p_as != &kas)
687*0Sstevel@tonic-gate 			continue;
688*0Sstevel@tonic-gate 
689*0Sstevel@tonic-gate 		if (tp->t_flag & T_INTR_THREAD)
690*0Sstevel@tonic-gate 			continue;
691*0Sstevel@tonic-gate 
692*0Sstevel@tonic-gate 		if (!callb_is_stopped(tp, &name)) {
693*0Sstevel@tonic-gate 			mutex_exit(&pidlock);
694*0Sstevel@tonic-gate 			dr_op_err(CE_IGNORE, handle, ESBD_KTHREAD, name);
695*0Sstevel@tonic-gate 			return (EBUSY);
696*0Sstevel@tonic-gate 		}
697*0Sstevel@tonic-gate 	}
698*0Sstevel@tonic-gate 
699*0Sstevel@tonic-gate 	mutex_exit(&pidlock);
700*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
701*0Sstevel@tonic-gate }
702*0Sstevel@tonic-gate 
703*0Sstevel@tonic-gate static void
704*0Sstevel@tonic-gate dr_start_user_threads(void)
705*0Sstevel@tonic-gate {
706*0Sstevel@tonic-gate 	kthread_id_t tp;
707*0Sstevel@tonic-gate 
708*0Sstevel@tonic-gate 	mutex_enter(&pidlock);
709*0Sstevel@tonic-gate 
710*0Sstevel@tonic-gate 	/* walk all threads and release them */
711*0Sstevel@tonic-gate 	for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) {
712*0Sstevel@tonic-gate 		proc_t *p = ttoproc(tp);
713*0Sstevel@tonic-gate 
714*0Sstevel@tonic-gate 		/* skip kernel threads */
715*0Sstevel@tonic-gate 		if (ttoproc(tp)->p_as == &kas)
716*0Sstevel@tonic-gate 			continue;
717*0Sstevel@tonic-gate 
718*0Sstevel@tonic-gate 		mutex_enter(&p->p_lock);
719*0Sstevel@tonic-gate 		tp->t_proc_flag &= ~TP_CHKPT;
720*0Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
721*0Sstevel@tonic-gate 
722*0Sstevel@tonic-gate 		thread_lock(tp);
723*0Sstevel@tonic-gate 		if (CPR_ISTOPPED(tp)) {
724*0Sstevel@tonic-gate 			/* back on the runq */
725*0Sstevel@tonic-gate 			tp->t_schedflag |= TS_RESUME;
726*0Sstevel@tonic-gate 			setrun_locked(tp);
727*0Sstevel@tonic-gate 		}
728*0Sstevel@tonic-gate 		thread_unlock(tp);
729*0Sstevel@tonic-gate 	}
730*0Sstevel@tonic-gate 
731*0Sstevel@tonic-gate 	mutex_exit(&pidlock);
732*0Sstevel@tonic-gate }
733*0Sstevel@tonic-gate 
734*0Sstevel@tonic-gate static void
735*0Sstevel@tonic-gate dr_signal_user(int sig)
736*0Sstevel@tonic-gate {
737*0Sstevel@tonic-gate 	struct proc *p;
738*0Sstevel@tonic-gate 
739*0Sstevel@tonic-gate 	mutex_enter(&pidlock);
740*0Sstevel@tonic-gate 
741*0Sstevel@tonic-gate 	for (p = practive; p != NULL; p = p->p_next) {
742*0Sstevel@tonic-gate 		/* only user threads */
743*0Sstevel@tonic-gate 		if (p->p_exec == NULL || p->p_stat == SZOMB ||
744*0Sstevel@tonic-gate 		    p == proc_init || p == ttoproc(curthread))
745*0Sstevel@tonic-gate 			continue;
746*0Sstevel@tonic-gate 
747*0Sstevel@tonic-gate 		mutex_enter(&p->p_lock);
748*0Sstevel@tonic-gate 		sigtoproc(p, NULL, sig);
749*0Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
750*0Sstevel@tonic-gate 	}
751*0Sstevel@tonic-gate 
752*0Sstevel@tonic-gate 	mutex_exit(&pidlock);
753*0Sstevel@tonic-gate 
754*0Sstevel@tonic-gate 	/* add a bit of delay */
755*0Sstevel@tonic-gate 	delay(hz);
756*0Sstevel@tonic-gate }
757*0Sstevel@tonic-gate 
758*0Sstevel@tonic-gate void
759*0Sstevel@tonic-gate dr_resume(dr_sr_handle_t *srh)
760*0Sstevel@tonic-gate {
761*0Sstevel@tonic-gate 	dr_handle_t	*handle;
762*0Sstevel@tonic-gate 
763*0Sstevel@tonic-gate 	handle = srh->sr_dr_handlep;
764*0Sstevel@tonic-gate 
765*0Sstevel@tonic-gate 	if (srh->sr_suspend_state < DR_SRSTATE_FULL) {
766*0Sstevel@tonic-gate 		/*
767*0Sstevel@tonic-gate 		 * Update the signature block.
768*0Sstevel@tonic-gate 		 * If cpus are not paused, this can be done now.
769*0Sstevel@tonic-gate 		 * See comments below.
770*0Sstevel@tonic-gate 		 */
771*0Sstevel@tonic-gate 		CPU_SIGNATURE(OS_SIG, SIGST_RESUME_INPROGRESS, SIGSUBST_NULL,
772*0Sstevel@tonic-gate 		    CPU->cpu_id);
773*0Sstevel@tonic-gate 	}
774*0Sstevel@tonic-gate 
775*0Sstevel@tonic-gate 	switch (srh->sr_suspend_state) {
776*0Sstevel@tonic-gate 	case DR_SRSTATE_FULL:
777*0Sstevel@tonic-gate 
778*0Sstevel@tonic-gate 		ASSERT(MUTEX_HELD(&cpu_lock));
779*0Sstevel@tonic-gate 
780*0Sstevel@tonic-gate 		dr_enable_intr(); 	/* enable intr & clock */
781*0Sstevel@tonic-gate 
782*0Sstevel@tonic-gate 		start_cpus();
783*0Sstevel@tonic-gate 		mutex_exit(&cpu_lock);
784*0Sstevel@tonic-gate 
785*0Sstevel@tonic-gate 		/*
786*0Sstevel@tonic-gate 		 * Update the signature block.
787*0Sstevel@tonic-gate 		 * This must not be done while cpus are paused, since on
788*0Sstevel@tonic-gate 		 * Starcat the cpu signature update aquires an adaptive
789*0Sstevel@tonic-gate 		 * mutex in the iosram driver. Blocking with cpus paused
790*0Sstevel@tonic-gate 		 * can lead to deadlock.
791*0Sstevel@tonic-gate 		 */
792*0Sstevel@tonic-gate 		CPU_SIGNATURE(OS_SIG, SIGST_RESUME_INPROGRESS, SIGSUBST_NULL,
793*0Sstevel@tonic-gate 		    CPU->cpu_id);
794*0Sstevel@tonic-gate 
795*0Sstevel@tonic-gate 		/*
796*0Sstevel@tonic-gate 		 * If we suspended hw watchdog at suspend,
797*0Sstevel@tonic-gate 		 * re-enable it now.
798*0Sstevel@tonic-gate 		 */
799*0Sstevel@tonic-gate 
800*0Sstevel@tonic-gate 		if (srh->sr_flags & (SR_FLAG_WATCHDOG)) {
801*0Sstevel@tonic-gate 			mutex_enter(&tod_lock);
802*0Sstevel@tonic-gate 			tod_ops.tod_set_watchdog_timer(
803*0Sstevel@tonic-gate 				watchdog_timeout_seconds);
804*0Sstevel@tonic-gate 			mutex_exit(&tod_lock);
805*0Sstevel@tonic-gate 		}
806*0Sstevel@tonic-gate 
807*0Sstevel@tonic-gate 		/*
808*0Sstevel@tonic-gate 		 * This should only be called if drmach_suspend_last()
809*0Sstevel@tonic-gate 		 * was called and state transitioned to DR_SRSTATE_FULL
810*0Sstevel@tonic-gate 		 * to prevent resume attempts on device instances that
811*0Sstevel@tonic-gate 		 * were not previously suspended.
812*0Sstevel@tonic-gate 		 */
813*0Sstevel@tonic-gate 		drmach_resume_first();
814*0Sstevel@tonic-gate 
815*0Sstevel@tonic-gate 		/* FALLTHROUGH */
816*0Sstevel@tonic-gate 
817*0Sstevel@tonic-gate 	case DR_SRSTATE_DRIVER:
818*0Sstevel@tonic-gate 		/*
819*0Sstevel@tonic-gate 		 * resume drivers
820*0Sstevel@tonic-gate 		 */
821*0Sstevel@tonic-gate 		srh->sr_err_idx = 0;
822*0Sstevel@tonic-gate 
823*0Sstevel@tonic-gate 		/* no parent dip to hold busy */
824*0Sstevel@tonic-gate 		dr_resume_devices(ddi_root_node(), srh);
825*0Sstevel@tonic-gate 
826*0Sstevel@tonic-gate 		if (srh->sr_err_idx && srh->sr_dr_handlep) {
827*0Sstevel@tonic-gate 			(srh->sr_dr_handlep)->h_err = drerr_int(ESBD_RESUME,
828*0Sstevel@tonic-gate 				srh->sr_err_ints, srh->sr_err_idx, 1);
829*0Sstevel@tonic-gate 		}
830*0Sstevel@tonic-gate 
831*0Sstevel@tonic-gate 		/*
832*0Sstevel@tonic-gate 		 * resume the lock manager
833*0Sstevel@tonic-gate 		 */
834*0Sstevel@tonic-gate 		lm_cprresume();
835*0Sstevel@tonic-gate 
836*0Sstevel@tonic-gate 		/* FALLTHROUGH */
837*0Sstevel@tonic-gate 
838*0Sstevel@tonic-gate 	case DR_SRSTATE_DAEMON:
839*0Sstevel@tonic-gate 		/*
840*0Sstevel@tonic-gate 		 * resume kernel daemons
841*0Sstevel@tonic-gate 		 */
842*0Sstevel@tonic-gate 		if (!dr_skip_kernel_threads) {
843*0Sstevel@tonic-gate 			prom_printf("DR: resuming kernel daemons...\n");
844*0Sstevel@tonic-gate 			(void) callb_execute_class(CB_CL_CPR_DAEMON,
845*0Sstevel@tonic-gate 				CB_CODE_CPR_RESUME);
846*0Sstevel@tonic-gate 			callb_unlock_table();
847*0Sstevel@tonic-gate 		}
848*0Sstevel@tonic-gate 
849*0Sstevel@tonic-gate 		/* FALLTHROUGH */
850*0Sstevel@tonic-gate 
851*0Sstevel@tonic-gate 	case DR_SRSTATE_USER:
852*0Sstevel@tonic-gate 		/*
853*0Sstevel@tonic-gate 		 * finally, resume user threads
854*0Sstevel@tonic-gate 		 */
855*0Sstevel@tonic-gate 		if (!dr_skip_user_threads) {
856*0Sstevel@tonic-gate 			prom_printf("DR: resuming user threads...\n");
857*0Sstevel@tonic-gate 			dr_start_user_threads();
858*0Sstevel@tonic-gate 		}
859*0Sstevel@tonic-gate 		/* FALLTHROUGH */
860*0Sstevel@tonic-gate 
861*0Sstevel@tonic-gate 	case DR_SRSTATE_BEGIN:
862*0Sstevel@tonic-gate 	default:
863*0Sstevel@tonic-gate 		/*
864*0Sstevel@tonic-gate 		 * let those who care know that we've just resumed
865*0Sstevel@tonic-gate 		 */
866*0Sstevel@tonic-gate 		PR_QR("sending SIGTHAW...\n");
867*0Sstevel@tonic-gate 		dr_signal_user(SIGTHAW);
868*0Sstevel@tonic-gate 		break;
869*0Sstevel@tonic-gate 	}
870*0Sstevel@tonic-gate 
871*0Sstevel@tonic-gate 	i_ndi_allow_device_tree_changes(handle->h_ndi);
872*0Sstevel@tonic-gate 
873*0Sstevel@tonic-gate 	/*
874*0Sstevel@tonic-gate 	 * update the signature block
875*0Sstevel@tonic-gate 	 */
876*0Sstevel@tonic-gate 	CPU_SIGNATURE(OS_SIG, SIGST_RUN, SIGSUBST_NULL, CPU->cpu_id);
877*0Sstevel@tonic-gate 
878*0Sstevel@tonic-gate 	prom_printf("DR: resume COMPLETED\n");
879*0Sstevel@tonic-gate }
880*0Sstevel@tonic-gate 
881*0Sstevel@tonic-gate int
882*0Sstevel@tonic-gate dr_suspend(dr_sr_handle_t *srh)
883*0Sstevel@tonic-gate {
884*0Sstevel@tonic-gate 	dr_handle_t	*handle;
885*0Sstevel@tonic-gate 	int		force;
886*0Sstevel@tonic-gate 	int		dev_errs_idx;
887*0Sstevel@tonic-gate 	uint64_t	dev_errs[DR_MAX_ERR_INT];
888*0Sstevel@tonic-gate 	int		rc = DDI_SUCCESS;
889*0Sstevel@tonic-gate 
890*0Sstevel@tonic-gate 	handle = srh->sr_dr_handlep;
891*0Sstevel@tonic-gate 
892*0Sstevel@tonic-gate 	force = dr_cmd_flags(handle) & SBD_FLAG_FORCE;
893*0Sstevel@tonic-gate 
894*0Sstevel@tonic-gate 	/*
895*0Sstevel@tonic-gate 	 * update the signature block
896*0Sstevel@tonic-gate 	 */
897*0Sstevel@tonic-gate 	CPU_SIGNATURE(OS_SIG, SIGST_QUIESCE_INPROGRESS, SIGSUBST_NULL,
898*0Sstevel@tonic-gate 	    CPU->cpu_id);
899*0Sstevel@tonic-gate 
900*0Sstevel@tonic-gate 	i_ndi_block_device_tree_changes(&handle->h_ndi);
901*0Sstevel@tonic-gate 
902*0Sstevel@tonic-gate 	prom_printf("\nDR: suspending user threads...\n");
903*0Sstevel@tonic-gate 	srh->sr_suspend_state = DR_SRSTATE_USER;
904*0Sstevel@tonic-gate 	if (((rc = dr_stop_user_threads(srh)) != DDI_SUCCESS) &&
905*0Sstevel@tonic-gate 	    dr_check_user_stop_result) {
906*0Sstevel@tonic-gate 		dr_resume(srh);
907*0Sstevel@tonic-gate 		return (rc);
908*0Sstevel@tonic-gate 	}
909*0Sstevel@tonic-gate 
910*0Sstevel@tonic-gate 	if (!force) {
911*0Sstevel@tonic-gate 		struct dr_ref drc = {0};
912*0Sstevel@tonic-gate 
913*0Sstevel@tonic-gate 		prom_printf("\nDR: checking devices...\n");
914*0Sstevel@tonic-gate 		dev_errs_idx = 0;
915*0Sstevel@tonic-gate 
916*0Sstevel@tonic-gate 		drc.arr = dev_errs;
917*0Sstevel@tonic-gate 		drc.idx = &dev_errs_idx;
918*0Sstevel@tonic-gate 		drc.len = DR_MAX_ERR_INT;
919*0Sstevel@tonic-gate 
920*0Sstevel@tonic-gate 		/*
921*0Sstevel@tonic-gate 		 * Since the root node can never go away, it
922*0Sstevel@tonic-gate 		 * doesn't have to be held.
923*0Sstevel@tonic-gate 		 */
924*0Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), dr_check_unsafe_major, &drc);
925*0Sstevel@tonic-gate 		if (dev_errs_idx) {
926*0Sstevel@tonic-gate 			handle->h_err = drerr_int(ESBD_UNSAFE, dev_errs,
927*0Sstevel@tonic-gate 				dev_errs_idx, 1);
928*0Sstevel@tonic-gate 			dr_resume(srh);
929*0Sstevel@tonic-gate 			return (DDI_FAILURE);
930*0Sstevel@tonic-gate 		}
931*0Sstevel@tonic-gate 		PR_QR("done\n");
932*0Sstevel@tonic-gate 	} else {
933*0Sstevel@tonic-gate 		prom_printf("\nDR: dr_suspend invoked with force flag\n");
934*0Sstevel@tonic-gate 	}
935*0Sstevel@tonic-gate 
936*0Sstevel@tonic-gate 	/*
937*0Sstevel@tonic-gate 	 * now stop daemon activities
938*0Sstevel@tonic-gate 	 */
939*0Sstevel@tonic-gate 	prom_printf("DR: suspending kernel daemons...\n");
940*0Sstevel@tonic-gate 	srh->sr_suspend_state = DR_SRSTATE_DAEMON;
941*0Sstevel@tonic-gate 	if ((rc = dr_stop_kernel_threads(handle)) != DDI_SUCCESS) {
942*0Sstevel@tonic-gate 		dr_resume(srh);
943*0Sstevel@tonic-gate 		return (rc);
944*0Sstevel@tonic-gate 	}
945*0Sstevel@tonic-gate 
946*0Sstevel@tonic-gate #ifndef	SKIP_SYNC
947*0Sstevel@tonic-gate 	/*
948*0Sstevel@tonic-gate 	 * This sync swap out all user pages
949*0Sstevel@tonic-gate 	 */
950*0Sstevel@tonic-gate 	vfs_sync(SYNC_ALL);
951*0Sstevel@tonic-gate #endif
952*0Sstevel@tonic-gate 
953*0Sstevel@tonic-gate 	/*
954*0Sstevel@tonic-gate 	 * special treatment for lock manager
955*0Sstevel@tonic-gate 	 */
956*0Sstevel@tonic-gate 	lm_cprsuspend();
957*0Sstevel@tonic-gate 
958*0Sstevel@tonic-gate #ifndef	SKIP_SYNC
959*0Sstevel@tonic-gate 	/*
960*0Sstevel@tonic-gate 	 * sync the file system in case we never make it back
961*0Sstevel@tonic-gate 	 */
962*0Sstevel@tonic-gate 	sync();
963*0Sstevel@tonic-gate #endif
964*0Sstevel@tonic-gate 
965*0Sstevel@tonic-gate 	/*
966*0Sstevel@tonic-gate 	 * now suspend drivers
967*0Sstevel@tonic-gate 	 */
968*0Sstevel@tonic-gate 	prom_printf("DR: suspending drivers...\n");
969*0Sstevel@tonic-gate 	srh->sr_suspend_state = DR_SRSTATE_DRIVER;
970*0Sstevel@tonic-gate 	srh->sr_err_idx = 0;
971*0Sstevel@tonic-gate 	/* No parent to hold busy */
972*0Sstevel@tonic-gate 	if ((rc = dr_suspend_devices(ddi_root_node(), srh)) != DDI_SUCCESS) {
973*0Sstevel@tonic-gate 		if (srh->sr_err_idx && srh->sr_dr_handlep) {
974*0Sstevel@tonic-gate 			(srh->sr_dr_handlep)->h_err = drerr_int(ESBD_SUSPEND,
975*0Sstevel@tonic-gate 				srh->sr_err_ints, srh->sr_err_idx, 1);
976*0Sstevel@tonic-gate 		}
977*0Sstevel@tonic-gate 		dr_resume(srh);
978*0Sstevel@tonic-gate 		return (rc);
979*0Sstevel@tonic-gate 	}
980*0Sstevel@tonic-gate 
981*0Sstevel@tonic-gate 	drmach_suspend_last();
982*0Sstevel@tonic-gate 
983*0Sstevel@tonic-gate 	/*
984*0Sstevel@tonic-gate 	 * finally, grab all cpus
985*0Sstevel@tonic-gate 	 */
986*0Sstevel@tonic-gate 	srh->sr_suspend_state = DR_SRSTATE_FULL;
987*0Sstevel@tonic-gate 
988*0Sstevel@tonic-gate 	/*
989*0Sstevel@tonic-gate 	 * if watchdog was activated, disable it
990*0Sstevel@tonic-gate 	 */
991*0Sstevel@tonic-gate 	if (watchdog_activated) {
992*0Sstevel@tonic-gate 		mutex_enter(&tod_lock);
993*0Sstevel@tonic-gate 		tod_ops.tod_clear_watchdog_timer();
994*0Sstevel@tonic-gate 		mutex_exit(&tod_lock);
995*0Sstevel@tonic-gate 		srh->sr_flags |= SR_FLAG_WATCHDOG;
996*0Sstevel@tonic-gate 	} else {
997*0Sstevel@tonic-gate 		srh->sr_flags &= ~(SR_FLAG_WATCHDOG);
998*0Sstevel@tonic-gate 	}
999*0Sstevel@tonic-gate 
1000*0Sstevel@tonic-gate 	/*
1001*0Sstevel@tonic-gate 	 * Update the signature block.
1002*0Sstevel@tonic-gate 	 * This must be done before cpus are paused, since on Starcat the
1003*0Sstevel@tonic-gate 	 * cpu signature update aquires an adaptive mutex in the iosram driver.
1004*0Sstevel@tonic-gate 	 * Blocking with cpus paused can lead to deadlock.
1005*0Sstevel@tonic-gate 	 */
1006*0Sstevel@tonic-gate 	CPU_SIGNATURE(OS_SIG, SIGST_QUIESCED, SIGSUBST_NULL, CPU->cpu_id);
1007*0Sstevel@tonic-gate 
1008*0Sstevel@tonic-gate 	mutex_enter(&cpu_lock);
1009*0Sstevel@tonic-gate 	pause_cpus(NULL);
1010*0Sstevel@tonic-gate 	dr_stop_intr();
1011*0Sstevel@tonic-gate 
1012*0Sstevel@tonic-gate 	return (rc);
1013*0Sstevel@tonic-gate }
1014*0Sstevel@tonic-gate 
1015*0Sstevel@tonic-gate int
1016*0Sstevel@tonic-gate dr_pt_test_suspend(dr_handle_t *hp)
1017*0Sstevel@tonic-gate {
1018*0Sstevel@tonic-gate 	dr_sr_handle_t *srh;
1019*0Sstevel@tonic-gate 	int		err;
1020*0Sstevel@tonic-gate 	uint_t		psmerr;
1021*0Sstevel@tonic-gate 	static fn_t	f = "dr_pt_test_suspend";
1022*0Sstevel@tonic-gate 
1023*0Sstevel@tonic-gate 	PR_QR("%s...\n", f);
1024*0Sstevel@tonic-gate 
1025*0Sstevel@tonic-gate 	srh = dr_get_sr_handle(hp);
1026*0Sstevel@tonic-gate 	if ((err = dr_suspend(srh)) == DDI_SUCCESS) {
1027*0Sstevel@tonic-gate 		dr_resume(srh);
1028*0Sstevel@tonic-gate 		if ((hp->h_err) && ((psmerr = hp->h_err->e_code) != 0)) {
1029*0Sstevel@tonic-gate 			PR_QR("%s: error on dr_resume()", f);
1030*0Sstevel@tonic-gate 			switch (psmerr) {
1031*0Sstevel@tonic-gate 			case ESBD_RESUME:
1032*0Sstevel@tonic-gate 				PR_QR("Couldn't resume devices: %s\n",
1033*0Sstevel@tonic-gate 					DR_GET_E_RSC(hp->h_err));
1034*0Sstevel@tonic-gate 				break;
1035*0Sstevel@tonic-gate 
1036*0Sstevel@tonic-gate 			case ESBD_KTHREAD:
1037*0Sstevel@tonic-gate 				PR_ALL("psmerr is ESBD_KTHREAD\n");
1038*0Sstevel@tonic-gate 				break;
1039*0Sstevel@tonic-gate 			default:
1040*0Sstevel@tonic-gate 				PR_ALL("Resume error unknown = %d\n",
1041*0Sstevel@tonic-gate 					psmerr);
1042*0Sstevel@tonic-gate 				break;
1043*0Sstevel@tonic-gate 			}
1044*0Sstevel@tonic-gate 		}
1045*0Sstevel@tonic-gate 	} else {
1046*0Sstevel@tonic-gate 		PR_ALL("%s: dr_suspend() failed, err = 0x%x\n",
1047*0Sstevel@tonic-gate 			f, err);
1048*0Sstevel@tonic-gate 		psmerr = hp->h_err ? hp->h_err->e_code : ESBD_NOERROR;
1049*0Sstevel@tonic-gate 		switch (psmerr) {
1050*0Sstevel@tonic-gate 		case ESBD_UNSAFE:
1051*0Sstevel@tonic-gate 			PR_ALL("Unsafe devices (major #): %s\n",
1052*0Sstevel@tonic-gate 				DR_GET_E_RSC(hp->h_err));
1053*0Sstevel@tonic-gate 			break;
1054*0Sstevel@tonic-gate 
1055*0Sstevel@tonic-gate 		case ESBD_RTTHREAD:
1056*0Sstevel@tonic-gate 			PR_ALL("RT threads (PIDs): %s\n",
1057*0Sstevel@tonic-gate 				DR_GET_E_RSC(hp->h_err));
1058*0Sstevel@tonic-gate 			break;
1059*0Sstevel@tonic-gate 
1060*0Sstevel@tonic-gate 		case ESBD_UTHREAD:
1061*0Sstevel@tonic-gate 			PR_ALL("User threads (PIDs): %s\n",
1062*0Sstevel@tonic-gate 				DR_GET_E_RSC(hp->h_err));
1063*0Sstevel@tonic-gate 			break;
1064*0Sstevel@tonic-gate 
1065*0Sstevel@tonic-gate 		case ESBD_SUSPEND:
1066*0Sstevel@tonic-gate 			PR_ALL("Non-suspendable devices (major #): %s\n",
1067*0Sstevel@tonic-gate 				DR_GET_E_RSC(hp->h_err));
1068*0Sstevel@tonic-gate 			break;
1069*0Sstevel@tonic-gate 
1070*0Sstevel@tonic-gate 		case ESBD_RESUME:
1071*0Sstevel@tonic-gate 			PR_ALL("Could not resume devices (major #): %s\n",
1072*0Sstevel@tonic-gate 				DR_GET_E_RSC(hp->h_err));
1073*0Sstevel@tonic-gate 			break;
1074*0Sstevel@tonic-gate 
1075*0Sstevel@tonic-gate 		case ESBD_KTHREAD:
1076*0Sstevel@tonic-gate 			PR_ALL("psmerr is ESBD_KTHREAD\n");
1077*0Sstevel@tonic-gate 			break;
1078*0Sstevel@tonic-gate 
1079*0Sstevel@tonic-gate 		case ESBD_NOERROR:
1080*0Sstevel@tonic-gate 			PR_ALL("sbd_error_t error code not set\n");
1081*0Sstevel@tonic-gate 			break;
1082*0Sstevel@tonic-gate 
1083*0Sstevel@tonic-gate 		default:
1084*0Sstevel@tonic-gate 			PR_ALL("Unknown error psmerr = %d\n", psmerr);
1085*0Sstevel@tonic-gate 			break;
1086*0Sstevel@tonic-gate 		}
1087*0Sstevel@tonic-gate 	}
1088*0Sstevel@tonic-gate 	dr_release_sr_handle(srh);
1089*0Sstevel@tonic-gate 
1090*0Sstevel@tonic-gate 	return (0);
1091*0Sstevel@tonic-gate }
1092*0Sstevel@tonic-gate 
1093*0Sstevel@tonic-gate /*
1094*0Sstevel@tonic-gate  * Add a new integer value to the end of an array.  Don't allow duplicates to
1095*0Sstevel@tonic-gate  * appear in the array, and don't allow the array to overflow.  Return the new
1096*0Sstevel@tonic-gate  * total number of entries in the array.
1097*0Sstevel@tonic-gate  */
1098*0Sstevel@tonic-gate static int
1099*0Sstevel@tonic-gate dr_add_int(uint64_t *arr, int idx, int len, uint64_t val)
1100*0Sstevel@tonic-gate {
1101*0Sstevel@tonic-gate 	int i;
1102*0Sstevel@tonic-gate 
1103*0Sstevel@tonic-gate 	if (arr == NULL)
1104*0Sstevel@tonic-gate 		return (0);
1105*0Sstevel@tonic-gate 
1106*0Sstevel@tonic-gate 	if (idx >= len)
1107*0Sstevel@tonic-gate 		return (idx);
1108*0Sstevel@tonic-gate 
1109*0Sstevel@tonic-gate 	for (i = 0; i < idx; i++) {
1110*0Sstevel@tonic-gate 		if (arr[i] == val)
1111*0Sstevel@tonic-gate 			return (idx);
1112*0Sstevel@tonic-gate 	}
1113*0Sstevel@tonic-gate 
1114*0Sstevel@tonic-gate 	arr[idx++] = val;
1115*0Sstevel@tonic-gate 
1116*0Sstevel@tonic-gate 	return (idx);
1117*0Sstevel@tonic-gate }
1118*0Sstevel@tonic-gate 
1119*0Sstevel@tonic-gate /*
1120*0Sstevel@tonic-gate  * Construct an sbd_error_t featuring a string representation of an array of
1121*0Sstevel@tonic-gate  * integers as its e_rsc.
1122*0Sstevel@tonic-gate  */
1123*0Sstevel@tonic-gate static sbd_error_t *
1124*0Sstevel@tonic-gate drerr_int(int e_code, uint64_t *arr, int idx, int majors)
1125*0Sstevel@tonic-gate {
1126*0Sstevel@tonic-gate 	int		i, n, buf_len, buf_idx, buf_avail;
1127*0Sstevel@tonic-gate 	char		*dname;
1128*0Sstevel@tonic-gate 	char		*buf;
1129*0Sstevel@tonic-gate 	sbd_error_t	*new_sbd_err;
1130*0Sstevel@tonic-gate 	static char	s_ellipsis[] = "...";
1131*0Sstevel@tonic-gate 
1132*0Sstevel@tonic-gate 	if (arr == NULL || idx <= 0)
1133*0Sstevel@tonic-gate 		return (NULL);
1134*0Sstevel@tonic-gate 
1135*0Sstevel@tonic-gate 	/* MAXPATHLEN is the size of the e_rsc field in sbd_error_t. */
1136*0Sstevel@tonic-gate 	buf = (char *)kmem_zalloc(MAXPATHLEN, KM_SLEEP);
1137*0Sstevel@tonic-gate 
1138*0Sstevel@tonic-gate 	/*
1139*0Sstevel@tonic-gate 	 * This is the total working area of the buffer.  It must be computed
1140*0Sstevel@tonic-gate 	 * as the size of 'buf', minus reserved space for the null terminator
1141*0Sstevel@tonic-gate 	 * and the ellipsis string.
1142*0Sstevel@tonic-gate 	 */
1143*0Sstevel@tonic-gate 	buf_len = MAXPATHLEN - (strlen(s_ellipsis) + 1);
1144*0Sstevel@tonic-gate 
1145*0Sstevel@tonic-gate 	/* Construct a string representation of the array values */
1146*0Sstevel@tonic-gate 	for (buf_idx = 0, i = 0; i < idx; i++) {
1147*0Sstevel@tonic-gate 		buf_avail = buf_len - buf_idx;
1148*0Sstevel@tonic-gate 		if (majors) {
1149*0Sstevel@tonic-gate 			dname = ddi_major_to_name(arr[i]);
1150*0Sstevel@tonic-gate 			if (dname) {
1151*0Sstevel@tonic-gate 				n = snprintf(&buf[buf_idx], buf_avail,
1152*0Sstevel@tonic-gate 					"%s, ", dname);
1153*0Sstevel@tonic-gate 			} else {
1154*0Sstevel@tonic-gate 				n = snprintf(&buf[buf_idx], buf_avail,
1155*0Sstevel@tonic-gate 					"major %llu, ", arr[i]);
1156*0Sstevel@tonic-gate 			}
1157*0Sstevel@tonic-gate 		} else {
1158*0Sstevel@tonic-gate 			n = snprintf(&buf[buf_idx], buf_avail, "%llu, ",
1159*0Sstevel@tonic-gate 				arr[i]);
1160*0Sstevel@tonic-gate 		}
1161*0Sstevel@tonic-gate 
1162*0Sstevel@tonic-gate 		/* An ellipsis gets appended when no more values fit */
1163*0Sstevel@tonic-gate 		if (n >= buf_avail) {
1164*0Sstevel@tonic-gate 			(void) strcpy(&buf[buf_idx], s_ellipsis);
1165*0Sstevel@tonic-gate 			break;
1166*0Sstevel@tonic-gate 		}
1167*0Sstevel@tonic-gate 
1168*0Sstevel@tonic-gate 		buf_idx += n;
1169*0Sstevel@tonic-gate 	}
1170*0Sstevel@tonic-gate 
1171*0Sstevel@tonic-gate 	/* If all the contents fit, remove the trailing comma */
1172*0Sstevel@tonic-gate 	if (n < buf_avail) {
1173*0Sstevel@tonic-gate 		buf[--buf_idx] = '\0';
1174*0Sstevel@tonic-gate 		buf[--buf_idx] = '\0';
1175*0Sstevel@tonic-gate 	}
1176*0Sstevel@tonic-gate 
1177*0Sstevel@tonic-gate 	/* Return an sbd_error_t with the buffer and e_code */
1178*0Sstevel@tonic-gate 	new_sbd_err = drerr_new(1, e_code, buf);
1179*0Sstevel@tonic-gate 	kmem_free(buf, MAXPATHLEN);
1180*0Sstevel@tonic-gate 	return (new_sbd_err);
1181*0Sstevel@tonic-gate }
1182