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
dr_stop_intr(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
dr_enable_intr(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 *
dr_get_sr_handle(dr_handle_t * hp)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
dr_release_sr_handle(dr_sr_handle_t * srh)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
dr_is_real_device(dev_info_t * dip)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)®buf, &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
dr_is_unsafe_major(major_t major)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
dr_bypass_device(char * dname)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
dr_resolve_devname(dev_info_t * dip,char * buffer,char * alias)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
dr_check_dip(dev_info_t * dip,void * arg,uint_t ref)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
dr_check_unsafe_major(dev_info_t * dip,void * arg)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
dr_check_devices(dev_info_t * dip,int * refcount,dr_handle_t * handle,uint64_t * arr,int * idx,int len,int * refcount_non_gldv3)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
dr_suspend_devices(dev_info_t * dip,dr_sr_handle_t * srh)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
dr_resume_devices(dev_info_t * start,dr_sr_handle_t * srh)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
dr_stop_user_threads(dr_sr_handle_t * srh)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
dr_start_user_threads(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
dr_signal_user(int sig)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
dr_resume(dr_sr_handle_t * srh)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
dr_suspend(dr_sr_handle_t * srh)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
dr_pt_test_suspend(dr_handle_t * hp)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
dr_add_int(uint64_t * arr,int idx,int len,uint64_t val)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 *
drerr_int(int e_code,uint64_t * arr,int idx,int majors)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