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