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)®buf, &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