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