10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
52399Scth * Common Development and Distribution License (the "License").
62399Scth * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
2111311SSurya.Prakki@Sun.COM
220Sstevel@tonic-gate /*
23*12885SMary.Beale@Sun.COM * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate * A CPR derivative specifically for starfire/starcat
280Sstevel@tonic-gate */
290Sstevel@tonic-gate
300Sstevel@tonic-gate #include <sys/types.h>
310Sstevel@tonic-gate #include <sys/systm.h>
320Sstevel@tonic-gate #include <sys/machparam.h>
330Sstevel@tonic-gate #include <sys/machsystm.h>
340Sstevel@tonic-gate #include <sys/ddi.h>
350Sstevel@tonic-gate #define SUNDDI_IMPL
360Sstevel@tonic-gate #include <sys/sunddi.h>
370Sstevel@tonic-gate #include <sys/sunndi.h>
380Sstevel@tonic-gate #include <sys/devctl.h>
390Sstevel@tonic-gate #include <sys/time.h>
400Sstevel@tonic-gate #include <sys/kmem.h>
410Sstevel@tonic-gate #include <nfs/lm.h>
420Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
430Sstevel@tonic-gate #include <sys/ndi_impldefs.h>
440Sstevel@tonic-gate #include <sys/obpdefs.h>
450Sstevel@tonic-gate #include <sys/cmn_err.h>
460Sstevel@tonic-gate #include <sys/debug.h>
470Sstevel@tonic-gate #include <sys/errno.h>
480Sstevel@tonic-gate #include <sys/callb.h>
490Sstevel@tonic-gate #include <sys/clock.h>
500Sstevel@tonic-gate #include <sys/x_call.h>
510Sstevel@tonic-gate #include <sys/cpuvar.h>
520Sstevel@tonic-gate #include <sys/epm.h>
530Sstevel@tonic-gate #include <sys/vfs.h>
540Sstevel@tonic-gate
550Sstevel@tonic-gate #include <sys/cpu_sgnblk_defs.h>
560Sstevel@tonic-gate #include <sys/dr.h>
570Sstevel@tonic-gate #include <sys/dr_util.h>
580Sstevel@tonic-gate
590Sstevel@tonic-gate #include <sys/promif.h>
600Sstevel@tonic-gate #include <sys/conf.h>
610Sstevel@tonic-gate #include <sys/cyclic.h>
620Sstevel@tonic-gate
630Sstevel@tonic-gate extern void e_ddi_enter_driver_list(struct devnames *dnp, int *listcnt);
640Sstevel@tonic-gate extern void e_ddi_exit_driver_list(struct devnames *dnp, int listcnt);
650Sstevel@tonic-gate extern int is_pseudo_device(dev_info_t *dip);
660Sstevel@tonic-gate
670Sstevel@tonic-gate extern kmutex_t cpu_lock;
680Sstevel@tonic-gate extern dr_unsafe_devs_t dr_unsafe_devs;
690Sstevel@tonic-gate
700Sstevel@tonic-gate static int dr_is_real_device(dev_info_t *dip);
710Sstevel@tonic-gate static int dr_is_unsafe_major(major_t major);
720Sstevel@tonic-gate static int dr_bypass_device(char *dname);
730Sstevel@tonic-gate static int dr_check_dip(dev_info_t *dip, void *arg, uint_t ref);
740Sstevel@tonic-gate static int dr_resolve_devname(dev_info_t *dip, char *buffer,
750Sstevel@tonic-gate char *alias);
760Sstevel@tonic-gate static sbd_error_t *drerr_int(int e_code, uint64_t *arr, int idx,
770Sstevel@tonic-gate int majors);
780Sstevel@tonic-gate static int dr_add_int(uint64_t *arr, int idx, int len,
790Sstevel@tonic-gate uint64_t val);
800Sstevel@tonic-gate
810Sstevel@tonic-gate int dr_pt_test_suspend(dr_handle_t *hp);
820Sstevel@tonic-gate
830Sstevel@tonic-gate /*
840Sstevel@tonic-gate * dr_quiesce.c interface
850Sstevel@tonic-gate * NOTE: states used internally by dr_suspend and dr_resume
860Sstevel@tonic-gate */
870Sstevel@tonic-gate typedef enum dr_suspend_state {
880Sstevel@tonic-gate DR_SRSTATE_BEGIN = 0,
890Sstevel@tonic-gate DR_SRSTATE_USER,
900Sstevel@tonic-gate DR_SRSTATE_DRIVER,
910Sstevel@tonic-gate DR_SRSTATE_FULL
920Sstevel@tonic-gate } suspend_state_t;
930Sstevel@tonic-gate
940Sstevel@tonic-gate struct dr_sr_handle {
950Sstevel@tonic-gate dr_handle_t *sr_dr_handlep;
960Sstevel@tonic-gate dev_info_t *sr_failed_dip;
970Sstevel@tonic-gate suspend_state_t sr_suspend_state;
980Sstevel@tonic-gate uint_t sr_flags;
990Sstevel@tonic-gate uint64_t sr_err_ints[DR_MAX_ERR_INT];
1000Sstevel@tonic-gate int sr_err_idx;
1010Sstevel@tonic-gate };
1020Sstevel@tonic-gate
1030Sstevel@tonic-gate #define SR_FLAG_WATCHDOG 0x1
1040Sstevel@tonic-gate
1050Sstevel@tonic-gate /*
1060Sstevel@tonic-gate * XXX
1070Sstevel@tonic-gate * This hack will go away before RTI. Just for testing.
1080Sstevel@tonic-gate * List of drivers to bypass when performing a suspend.
1090Sstevel@tonic-gate */
1100Sstevel@tonic-gate static char *dr_bypass_list[] = {
1110Sstevel@tonic-gate ""
1120Sstevel@tonic-gate };
1130Sstevel@tonic-gate
1140Sstevel@tonic-gate
1150Sstevel@tonic-gate #define SKIP_SYNC /* bypass sync ops in dr_suspend */
1160Sstevel@tonic-gate
1170Sstevel@tonic-gate /*
1180Sstevel@tonic-gate * dr_skip_user_threads is used to control if user threads should
1190Sstevel@tonic-gate * be suspended. If dr_skip_user_threads is true, the rest of the
1200Sstevel@tonic-gate * flags are not used; if it is false, dr_check_user_stop_result
1210Sstevel@tonic-gate * will be used to control whether or not we need to check suspend
1220Sstevel@tonic-gate * result, and dr_allow_blocked_threads will be used to control
1230Sstevel@tonic-gate * whether or not we allow suspend to continue if there are blocked
1240Sstevel@tonic-gate * threads. We allow all combinations of dr_check_user_stop_result
1250Sstevel@tonic-gate * and dr_allow_block_threads, even though it might not make much
1260Sstevel@tonic-gate * sense to not allow block threads when we don't even check stop
1270Sstevel@tonic-gate * result.
1280Sstevel@tonic-gate */
1290Sstevel@tonic-gate static int dr_skip_user_threads = 0; /* default to FALSE */
1300Sstevel@tonic-gate static int dr_check_user_stop_result = 1; /* default to TRUE */
1310Sstevel@tonic-gate static int dr_allow_blocked_threads = 1; /* default to TRUE */
1320Sstevel@tonic-gate
1330Sstevel@tonic-gate #define DR_CPU_LOOP_MSEC 1000
1340Sstevel@tonic-gate
1350Sstevel@tonic-gate static void
dr_stop_intr(void)1360Sstevel@tonic-gate dr_stop_intr(void)
1370Sstevel@tonic-gate {
1380Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock));
1390Sstevel@tonic-gate
1400Sstevel@tonic-gate kpreempt_disable();
1410Sstevel@tonic-gate cyclic_suspend();
1420Sstevel@tonic-gate }
1430Sstevel@tonic-gate
1440Sstevel@tonic-gate static void
dr_enable_intr(void)1450Sstevel@tonic-gate dr_enable_intr(void)
1460Sstevel@tonic-gate {
1470Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock));
1480Sstevel@tonic-gate
1490Sstevel@tonic-gate cyclic_resume();
1500Sstevel@tonic-gate kpreempt_enable();
1510Sstevel@tonic-gate }
1520Sstevel@tonic-gate
1530Sstevel@tonic-gate dr_sr_handle_t *
dr_get_sr_handle(dr_handle_t * hp)1540Sstevel@tonic-gate dr_get_sr_handle(dr_handle_t *hp)
1550Sstevel@tonic-gate {
1560Sstevel@tonic-gate dr_sr_handle_t *srh;
1570Sstevel@tonic-gate
1580Sstevel@tonic-gate srh = GETSTRUCT(dr_sr_handle_t, 1);
1590Sstevel@tonic-gate srh->sr_dr_handlep = hp;
1600Sstevel@tonic-gate
1610Sstevel@tonic-gate return (srh);
1620Sstevel@tonic-gate }
1630Sstevel@tonic-gate
1640Sstevel@tonic-gate void
dr_release_sr_handle(dr_sr_handle_t * srh)1650Sstevel@tonic-gate dr_release_sr_handle(dr_sr_handle_t *srh)
1660Sstevel@tonic-gate {
1670Sstevel@tonic-gate ASSERT(srh->sr_failed_dip == NULL);
1680Sstevel@tonic-gate FREESTRUCT(srh, dr_sr_handle_t, 1);
1690Sstevel@tonic-gate }
1700Sstevel@tonic-gate
1710Sstevel@tonic-gate static int
dr_is_real_device(dev_info_t * dip)1720Sstevel@tonic-gate dr_is_real_device(dev_info_t *dip)
1730Sstevel@tonic-gate {
1740Sstevel@tonic-gate struct regspec *regbuf = NULL;
1750Sstevel@tonic-gate int length = 0;
1760Sstevel@tonic-gate int rc;
1770Sstevel@tonic-gate
1780Sstevel@tonic-gate if (ddi_get_driver(dip) == NULL)
1790Sstevel@tonic-gate return (0);
1800Sstevel@tonic-gate
1810Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & (PMC_NEEDS_SR|PMC_PARENTAL_SR))
1820Sstevel@tonic-gate return (1);
1830Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_NO_SR)
1840Sstevel@tonic-gate return (0);
1850Sstevel@tonic-gate
1860Sstevel@tonic-gate /*
1870Sstevel@tonic-gate * now the general case
1880Sstevel@tonic-gate */
189506Scth rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
19011651SVijay.Balakrishna@Sun.COM (caddr_t)®buf, &length);
1910Sstevel@tonic-gate ASSERT(rc != DDI_PROP_NO_MEMORY);
1920Sstevel@tonic-gate if (rc != DDI_PROP_SUCCESS) {
1930Sstevel@tonic-gate return (0);
1940Sstevel@tonic-gate } else {
1950Sstevel@tonic-gate if ((length > 0) && (regbuf != NULL))
1960Sstevel@tonic-gate kmem_free(regbuf, length);
1970Sstevel@tonic-gate return (1);
1980Sstevel@tonic-gate }
1990Sstevel@tonic-gate }
2000Sstevel@tonic-gate
2010Sstevel@tonic-gate static int
dr_is_unsafe_major(major_t major)2020Sstevel@tonic-gate dr_is_unsafe_major(major_t major)
2030Sstevel@tonic-gate {
2040Sstevel@tonic-gate char *dname, **cpp;
2050Sstevel@tonic-gate int i, ndevs;
2060Sstevel@tonic-gate
2070Sstevel@tonic-gate if ((dname = ddi_major_to_name(major)) == NULL) {
2080Sstevel@tonic-gate PR_QR("dr_is_unsafe_major: invalid major # %d\n", major);
2090Sstevel@tonic-gate return (0);
2100Sstevel@tonic-gate }
2110Sstevel@tonic-gate
2120Sstevel@tonic-gate ndevs = dr_unsafe_devs.ndevs;
2130Sstevel@tonic-gate for (i = 0, cpp = dr_unsafe_devs.devnames; i < ndevs; i++) {
2140Sstevel@tonic-gate if (strcmp(dname, *cpp++) == 0)
2150Sstevel@tonic-gate return (1);
2160Sstevel@tonic-gate }
2170Sstevel@tonic-gate return (0);
2180Sstevel@tonic-gate }
2190Sstevel@tonic-gate
2200Sstevel@tonic-gate static int
dr_bypass_device(char * dname)2210Sstevel@tonic-gate dr_bypass_device(char *dname)
2220Sstevel@tonic-gate {
2230Sstevel@tonic-gate int i;
2240Sstevel@tonic-gate char **lname;
225*12885SMary.Beale@Sun.COM
226*12885SMary.Beale@Sun.COM if (dname == NULL)
227*12885SMary.Beale@Sun.COM return (0);
228*12885SMary.Beale@Sun.COM
2290Sstevel@tonic-gate /* check the bypass list */
2300Sstevel@tonic-gate for (i = 0, lname = &dr_bypass_list[i]; **lname != '\0'; lname++) {
2310Sstevel@tonic-gate if (strcmp(dname, dr_bypass_list[i++]) == 0)
2320Sstevel@tonic-gate return (1);
2330Sstevel@tonic-gate }
2340Sstevel@tonic-gate return (0);
2350Sstevel@tonic-gate }
2360Sstevel@tonic-gate
2370Sstevel@tonic-gate static int
dr_resolve_devname(dev_info_t * dip,char * buffer,char * alias)2380Sstevel@tonic-gate dr_resolve_devname(dev_info_t *dip, char *buffer, char *alias)
2390Sstevel@tonic-gate {
2400Sstevel@tonic-gate major_t devmajor;
2410Sstevel@tonic-gate char *aka, *name;
2420Sstevel@tonic-gate
2430Sstevel@tonic-gate *buffer = *alias = 0;
2440Sstevel@tonic-gate
2450Sstevel@tonic-gate if (dip == NULL)
2460Sstevel@tonic-gate return (-1);
2470Sstevel@tonic-gate
2480Sstevel@tonic-gate if ((name = ddi_get_name(dip)) == NULL)
2490Sstevel@tonic-gate name = "<null name>";
2500Sstevel@tonic-gate
2510Sstevel@tonic-gate aka = name;
2520Sstevel@tonic-gate
2530Sstevel@tonic-gate if ((devmajor = ddi_name_to_major(aka)) != -1)
2540Sstevel@tonic-gate aka = ddi_major_to_name(devmajor);
2550Sstevel@tonic-gate
25611311SSurya.Prakki@Sun.COM (void) strcpy(buffer, name);
2570Sstevel@tonic-gate
2580Sstevel@tonic-gate if (strcmp(name, aka))
25911311SSurya.Prakki@Sun.COM (void) strcpy(alias, aka);
2600Sstevel@tonic-gate else
2610Sstevel@tonic-gate *alias = 0;
2620Sstevel@tonic-gate
2630Sstevel@tonic-gate return (0);
2640Sstevel@tonic-gate }
2650Sstevel@tonic-gate
2660Sstevel@tonic-gate struct dr_ref {
2670Sstevel@tonic-gate int *refcount;
26811651SVijay.Balakrishna@Sun.COM int *refcount_non_gldv3;
2690Sstevel@tonic-gate uint64_t *arr;
2700Sstevel@tonic-gate int *idx;
2710Sstevel@tonic-gate int len;
2720Sstevel@tonic-gate };
2730Sstevel@tonic-gate
2740Sstevel@tonic-gate /* ARGSUSED */
2750Sstevel@tonic-gate static int
dr_check_dip(dev_info_t * dip,void * arg,uint_t ref)2760Sstevel@tonic-gate dr_check_dip(dev_info_t *dip, void *arg, uint_t ref)
2770Sstevel@tonic-gate {
2780Sstevel@tonic-gate major_t major;
2790Sstevel@tonic-gate char *dname;
2800Sstevel@tonic-gate struct dr_ref *rp = (struct dr_ref *)arg;
2810Sstevel@tonic-gate
2820Sstevel@tonic-gate if (dip == NULL)
2830Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
2840Sstevel@tonic-gate
2850Sstevel@tonic-gate if (!dr_is_real_device(dip))
2860Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
2870Sstevel@tonic-gate
2880Sstevel@tonic-gate dname = ddi_binding_name(dip);
2890Sstevel@tonic-gate
2900Sstevel@tonic-gate if (dr_bypass_device(dname))
2910Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
2920Sstevel@tonic-gate
2930Sstevel@tonic-gate if (dname && ((major = ddi_name_to_major(dname)) != (major_t)-1)) {
2940Sstevel@tonic-gate if (ref && rp->refcount) {
2950Sstevel@tonic-gate *rp->refcount += ref;
29611752STrevor.Thompson@Sun.COM PR_QR("\n %s (major# %d) is referenced(%u)\n", dname,
29711752STrevor.Thompson@Sun.COM major, ref);
29811651SVijay.Balakrishna@Sun.COM }
29911651SVijay.Balakrishna@Sun.COM if (ref && rp->refcount_non_gldv3) {
30011651SVijay.Balakrishna@Sun.COM if (NETWORK_PHYSDRV(major) && !GLDV3_DRV(major))
30111651SVijay.Balakrishna@Sun.COM *rp->refcount_non_gldv3 += ref;
3020Sstevel@tonic-gate }
3031333Scth if (dr_is_unsafe_major(major) && i_ddi_devi_attached(dip)) {
30411752STrevor.Thompson@Sun.COM PR_QR("\n %s (major# %d) not hotpluggable\n", dname,
30511752STrevor.Thompson@Sun.COM major);
3060Sstevel@tonic-gate if (rp->arr != NULL && rp->idx != NULL)
3070Sstevel@tonic-gate *rp->idx = dr_add_int(rp->arr, *rp->idx,
30811651SVijay.Balakrishna@Sun.COM rp->len, (uint64_t)major);
3090Sstevel@tonic-gate }
3100Sstevel@tonic-gate }
3110Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
3120Sstevel@tonic-gate }
3130Sstevel@tonic-gate
3140Sstevel@tonic-gate static int
dr_check_unsafe_major(dev_info_t * dip,void * arg)3150Sstevel@tonic-gate dr_check_unsafe_major(dev_info_t *dip, void *arg)
3160Sstevel@tonic-gate {
3170Sstevel@tonic-gate return (dr_check_dip(dip, arg, 0));
3180Sstevel@tonic-gate }
3190Sstevel@tonic-gate
3200Sstevel@tonic-gate
3210Sstevel@tonic-gate /*ARGSUSED*/
3220Sstevel@tonic-gate 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)3230Sstevel@tonic-gate dr_check_devices(dev_info_t *dip, int *refcount, dr_handle_t *handle,
32411651SVijay.Balakrishna@Sun.COM uint64_t *arr, int *idx, int len, int *refcount_non_gldv3)
3250Sstevel@tonic-gate {
3260Sstevel@tonic-gate struct dr_ref bref = {0};
3270Sstevel@tonic-gate
3280Sstevel@tonic-gate if (dip == NULL)
3290Sstevel@tonic-gate return;
3300Sstevel@tonic-gate
3310Sstevel@tonic-gate bref.refcount = refcount;
33211651SVijay.Balakrishna@Sun.COM bref.refcount_non_gldv3 = refcount_non_gldv3;
3330Sstevel@tonic-gate bref.arr = arr;
3340Sstevel@tonic-gate bref.idx = idx;
3350Sstevel@tonic-gate bref.len = len;
3360Sstevel@tonic-gate
3370Sstevel@tonic-gate ASSERT(e_ddi_branch_held(dip));
3380Sstevel@tonic-gate (void) e_ddi_branch_referenced(dip, dr_check_dip, &bref);
3390Sstevel@tonic-gate }
3400Sstevel@tonic-gate
3410Sstevel@tonic-gate /*
3420Sstevel@tonic-gate * The "dip" argument's parent (if it exists) must be held busy.
3430Sstevel@tonic-gate */
3440Sstevel@tonic-gate static int
dr_suspend_devices(dev_info_t * dip,dr_sr_handle_t * srh)3450Sstevel@tonic-gate dr_suspend_devices(dev_info_t *dip, dr_sr_handle_t *srh)
3460Sstevel@tonic-gate {
3470Sstevel@tonic-gate dr_handle_t *handle;
3480Sstevel@tonic-gate major_t major;
3490Sstevel@tonic-gate char *dname;
3500Sstevel@tonic-gate int circ;
3510Sstevel@tonic-gate
3520Sstevel@tonic-gate /*
3530Sstevel@tonic-gate * If dip is the root node, it has no siblings and it is
3540Sstevel@tonic-gate * always held. If dip is not the root node, dr_suspend_devices()
3550Sstevel@tonic-gate * will be invoked with the parent held busy.
3560Sstevel@tonic-gate */
3570Sstevel@tonic-gate for (; dip != NULL; dip = ddi_get_next_sibling(dip)) {
3580Sstevel@tonic-gate char d_name[40], d_alias[40], *d_info;
3590Sstevel@tonic-gate
3600Sstevel@tonic-gate ndi_devi_enter(dip, &circ);
3610Sstevel@tonic-gate if (dr_suspend_devices(ddi_get_child(dip), srh)) {
3620Sstevel@tonic-gate ndi_devi_exit(dip, circ);
3630Sstevel@tonic-gate return (ENXIO);
3640Sstevel@tonic-gate }
3650Sstevel@tonic-gate ndi_devi_exit(dip, circ);
3660Sstevel@tonic-gate
3670Sstevel@tonic-gate if (!dr_is_real_device(dip))
3680Sstevel@tonic-gate continue;
3690Sstevel@tonic-gate
3700Sstevel@tonic-gate major = (major_t)-1;
3710Sstevel@tonic-gate if ((dname = ddi_binding_name(dip)) != NULL)
3720Sstevel@tonic-gate major = ddi_name_to_major(dname);
3730Sstevel@tonic-gate
3740Sstevel@tonic-gate if (dr_bypass_device(dname)) {
3750Sstevel@tonic-gate PR_QR(" bypassed suspend of %s (major# %d)\n", dname,
37611651SVijay.Balakrishna@Sun.COM major);
3770Sstevel@tonic-gate continue;
3780Sstevel@tonic-gate }
3790Sstevel@tonic-gate
3800Sstevel@tonic-gate if (drmach_verify_sr(dip, 1)) {
3810Sstevel@tonic-gate PR_QR(" bypassed suspend of %s (major# %d)\n", dname,
38211651SVijay.Balakrishna@Sun.COM major);
3830Sstevel@tonic-gate continue;
3840Sstevel@tonic-gate }
3850Sstevel@tonic-gate
3860Sstevel@tonic-gate if ((d_info = ddi_get_name_addr(dip)) == NULL)
3870Sstevel@tonic-gate d_info = "<null>";
3880Sstevel@tonic-gate
3890Sstevel@tonic-gate d_name[0] = 0;
3900Sstevel@tonic-gate if (dr_resolve_devname(dip, d_name, d_alias) == 0) {
3910Sstevel@tonic-gate if (d_alias[0] != 0) {
3920Sstevel@tonic-gate prom_printf("\tsuspending %s@%s (aka %s)\n",
39311651SVijay.Balakrishna@Sun.COM d_name, d_info, d_alias);
3940Sstevel@tonic-gate } else {
39511752STrevor.Thompson@Sun.COM prom_printf("\tsuspending %s@%s\n", d_name,
39611752STrevor.Thompson@Sun.COM d_info);
3970Sstevel@tonic-gate }
3980Sstevel@tonic-gate } else {
3990Sstevel@tonic-gate prom_printf("\tsuspending %s@%s\n", dname, d_info);
4000Sstevel@tonic-gate }
4010Sstevel@tonic-gate
4020Sstevel@tonic-gate if (devi_detach(dip, DDI_SUSPEND) != DDI_SUCCESS) {
4030Sstevel@tonic-gate prom_printf("\tFAILED to suspend %s@%s\n",
40411651SVijay.Balakrishna@Sun.COM d_name[0] ? d_name : dname, d_info);
4050Sstevel@tonic-gate
4060Sstevel@tonic-gate srh->sr_err_idx = dr_add_int(srh->sr_err_ints,
40711651SVijay.Balakrishna@Sun.COM srh->sr_err_idx, DR_MAX_ERR_INT, (uint64_t)major);
4080Sstevel@tonic-gate
4090Sstevel@tonic-gate ndi_hold_devi(dip);
4100Sstevel@tonic-gate srh->sr_failed_dip = dip;
4110Sstevel@tonic-gate
4120Sstevel@tonic-gate handle = srh->sr_dr_handlep;
4130Sstevel@tonic-gate dr_op_err(CE_IGNORE, handle, ESBD_SUSPEND, "%s@%s",
41411651SVijay.Balakrishna@Sun.COM d_name[0] ? d_name : dname, d_info);
4150Sstevel@tonic-gate
4160Sstevel@tonic-gate return (DDI_FAILURE);
4170Sstevel@tonic-gate }
4180Sstevel@tonic-gate }
4190Sstevel@tonic-gate
4200Sstevel@tonic-gate return (DDI_SUCCESS);
4210Sstevel@tonic-gate }
4220Sstevel@tonic-gate
4230Sstevel@tonic-gate static void
dr_resume_devices(dev_info_t * start,dr_sr_handle_t * srh)4240Sstevel@tonic-gate dr_resume_devices(dev_info_t *start, dr_sr_handle_t *srh)
4250Sstevel@tonic-gate {
4260Sstevel@tonic-gate dr_handle_t *handle;
4270Sstevel@tonic-gate dev_info_t *dip, *next, *last = NULL;
4280Sstevel@tonic-gate major_t major;
4290Sstevel@tonic-gate char *bn;
4300Sstevel@tonic-gate int circ;
4310Sstevel@tonic-gate
4320Sstevel@tonic-gate major = (major_t)-1;
4330Sstevel@tonic-gate
4340Sstevel@tonic-gate /* attach in reverse device tree order */
4350Sstevel@tonic-gate while (last != start) {
4360Sstevel@tonic-gate dip = start;
4370Sstevel@tonic-gate next = ddi_get_next_sibling(dip);
4380Sstevel@tonic-gate while (next != last && dip != srh->sr_failed_dip) {
4390Sstevel@tonic-gate dip = next;
4400Sstevel@tonic-gate next = ddi_get_next_sibling(dip);
4410Sstevel@tonic-gate }
4420Sstevel@tonic-gate if (dip == srh->sr_failed_dip) {
4430Sstevel@tonic-gate /* release hold acquired in dr_suspend_devices() */
4440Sstevel@tonic-gate srh->sr_failed_dip = NULL;
4450Sstevel@tonic-gate ndi_rele_devi(dip);
44611752STrevor.Thompson@Sun.COM } else if (dr_is_real_device(dip) &&
44711752STrevor.Thompson@Sun.COM srh->sr_failed_dip == NULL) {
4480Sstevel@tonic-gate
4490Sstevel@tonic-gate if ((bn = ddi_binding_name(dip)) != NULL) {
4500Sstevel@tonic-gate major = ddi_name_to_major(bn);
4510Sstevel@tonic-gate } else {
4520Sstevel@tonic-gate bn = "<null>";
4530Sstevel@tonic-gate }
4540Sstevel@tonic-gate if (!dr_bypass_device(bn) &&
45511651SVijay.Balakrishna@Sun.COM !drmach_verify_sr(dip, 0)) {
4560Sstevel@tonic-gate char d_name[40], d_alias[40], *d_info;
4570Sstevel@tonic-gate
4580Sstevel@tonic-gate d_name[0] = 0;
4590Sstevel@tonic-gate d_info = ddi_get_name_addr(dip);
4600Sstevel@tonic-gate if (d_info == NULL)
4610Sstevel@tonic-gate d_info = "<null>";
4620Sstevel@tonic-gate
46311752STrevor.Thompson@Sun.COM if (!dr_resolve_devname(dip, d_name, d_alias)) {
4640Sstevel@tonic-gate if (d_alias[0] != 0) {
4650Sstevel@tonic-gate prom_printf("\tresuming "
46611752STrevor.Thompson@Sun.COM "%s@%s (aka %s)\n", d_name,
46711752STrevor.Thompson@Sun.COM d_info, d_alias);
4680Sstevel@tonic-gate } else {
4690Sstevel@tonic-gate prom_printf("\tresuming "
47011651SVijay.Balakrishna@Sun.COM "%s@%s\n", d_name, d_info);
4710Sstevel@tonic-gate }
4720Sstevel@tonic-gate } else {
47311752STrevor.Thompson@Sun.COM prom_printf("\tresuming %s@%s\n", bn,
47411752STrevor.Thompson@Sun.COM d_info);
4750Sstevel@tonic-gate }
4760Sstevel@tonic-gate
4770Sstevel@tonic-gate if (devi_attach(dip, DDI_RESUME) !=
47811651SVijay.Balakrishna@Sun.COM DDI_SUCCESS) {
4790Sstevel@tonic-gate /*
4800Sstevel@tonic-gate * Print a console warning,
4810Sstevel@tonic-gate * set an e_code of ESBD_RESUME,
4820Sstevel@tonic-gate * and save the driver major
4830Sstevel@tonic-gate * number in the e_rsc.
4840Sstevel@tonic-gate */
4850Sstevel@tonic-gate prom_printf("\tFAILED to resume %s@%s",
4860Sstevel@tonic-gate d_name[0] ? d_name : bn, d_info);
4870Sstevel@tonic-gate
4880Sstevel@tonic-gate srh->sr_err_idx =
48911651SVijay.Balakrishna@Sun.COM dr_add_int(srh->sr_err_ints,
49011651SVijay.Balakrishna@Sun.COM srh->sr_err_idx, DR_MAX_ERR_INT,
49111651SVijay.Balakrishna@Sun.COM (uint64_t)major);
4920Sstevel@tonic-gate
4930Sstevel@tonic-gate handle = srh->sr_dr_handlep;
4940Sstevel@tonic-gate
4950Sstevel@tonic-gate dr_op_err(CE_IGNORE, handle,
4960Sstevel@tonic-gate ESBD_RESUME, "%s@%s",
4970Sstevel@tonic-gate d_name[0] ? d_name : bn, d_info);
4980Sstevel@tonic-gate }
4990Sstevel@tonic-gate }
5000Sstevel@tonic-gate }
5010Sstevel@tonic-gate
5020Sstevel@tonic-gate /* Hold parent busy while walking its children */
5030Sstevel@tonic-gate ndi_devi_enter(dip, &circ);
5040Sstevel@tonic-gate dr_resume_devices(ddi_get_child(dip), srh);
5050Sstevel@tonic-gate ndi_devi_exit(dip, circ);
5060Sstevel@tonic-gate last = dip;
5070Sstevel@tonic-gate }
5080Sstevel@tonic-gate }
5090Sstevel@tonic-gate
5100Sstevel@tonic-gate /*
5110Sstevel@tonic-gate * True if thread is virtually stopped. Similar to CPR_VSTOPPED
5120Sstevel@tonic-gate * but from DR point of view. These user threads are waiting in
5130Sstevel@tonic-gate * the kernel. Once they complete in the kernel, they will process
5140Sstevel@tonic-gate * the stop signal and stop.
5150Sstevel@tonic-gate */
5160Sstevel@tonic-gate #define DR_VSTOPPED(t) \
5170Sstevel@tonic-gate ((t)->t_state == TS_SLEEP && \
5180Sstevel@tonic-gate (t)->t_wchan != NULL && \
5190Sstevel@tonic-gate (t)->t_astflag && \
5200Sstevel@tonic-gate ((t)->t_proc_flag & TP_CHKPT))
5210Sstevel@tonic-gate
5220Sstevel@tonic-gate /* ARGSUSED */
5230Sstevel@tonic-gate static int
dr_stop_user_threads(dr_sr_handle_t * srh)5240Sstevel@tonic-gate dr_stop_user_threads(dr_sr_handle_t *srh)
5250Sstevel@tonic-gate {
5260Sstevel@tonic-gate int count;
5270Sstevel@tonic-gate int bailout;
5280Sstevel@tonic-gate dr_handle_t *handle = srh->sr_dr_handlep;
5290Sstevel@tonic-gate static fn_t f = "dr_stop_user_threads";
5300Sstevel@tonic-gate kthread_id_t tp;
5310Sstevel@tonic-gate
5320Sstevel@tonic-gate extern void add_one_utstop();
5330Sstevel@tonic-gate extern void utstop_timedwait(clock_t);
5340Sstevel@tonic-gate extern void utstop_init(void);
5350Sstevel@tonic-gate
5360Sstevel@tonic-gate #define DR_UTSTOP_RETRY 4
5370Sstevel@tonic-gate #define DR_UTSTOP_WAIT hz
5380Sstevel@tonic-gate
5390Sstevel@tonic-gate if (dr_skip_user_threads)
5400Sstevel@tonic-gate return (DDI_SUCCESS);
5410Sstevel@tonic-gate
5420Sstevel@tonic-gate utstop_init();
5430Sstevel@tonic-gate
5440Sstevel@tonic-gate /* we need to try a few times to get past fork, etc. */
5450Sstevel@tonic-gate srh->sr_err_idx = 0;
5460Sstevel@tonic-gate for (count = 0; count < DR_UTSTOP_RETRY; count++) {
5470Sstevel@tonic-gate /* walk the entire threadlist */
5480Sstevel@tonic-gate mutex_enter(&pidlock);
5490Sstevel@tonic-gate for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) {
5500Sstevel@tonic-gate proc_t *p = ttoproc(tp);
5510Sstevel@tonic-gate
5520Sstevel@tonic-gate /* handle kernel threads separately */
5530Sstevel@tonic-gate if (p->p_as == &kas || p->p_stat == SZOMB)
5540Sstevel@tonic-gate continue;
5550Sstevel@tonic-gate
5560Sstevel@tonic-gate mutex_enter(&p->p_lock);
5570Sstevel@tonic-gate thread_lock(tp);
5580Sstevel@tonic-gate
5590Sstevel@tonic-gate if (tp->t_state == TS_STOPPED) {
5600Sstevel@tonic-gate /* add another reason to stop this thread */
5610Sstevel@tonic-gate tp->t_schedflag &= ~TS_RESUME;
5620Sstevel@tonic-gate } else {
5630Sstevel@tonic-gate tp->t_proc_flag |= TP_CHKPT;
5640Sstevel@tonic-gate
5650Sstevel@tonic-gate thread_unlock(tp);
5660Sstevel@tonic-gate mutex_exit(&p->p_lock);
5670Sstevel@tonic-gate add_one_utstop();
5680Sstevel@tonic-gate mutex_enter(&p->p_lock);
5690Sstevel@tonic-gate thread_lock(tp);
5700Sstevel@tonic-gate
5710Sstevel@tonic-gate aston(tp);
5720Sstevel@tonic-gate
5733792Sakolb if (ISWAKEABLE(tp) || ISWAITING(tp)) {
5740Sstevel@tonic-gate setrun_locked(tp);
5750Sstevel@tonic-gate }
5760Sstevel@tonic-gate
5770Sstevel@tonic-gate }
5780Sstevel@tonic-gate
5790Sstevel@tonic-gate /* grab thread if needed */
5800Sstevel@tonic-gate if (tp->t_state == TS_ONPROC && tp->t_cpu != CPU)
5810Sstevel@tonic-gate poke_cpu(tp->t_cpu->cpu_id);
5820Sstevel@tonic-gate
5830Sstevel@tonic-gate
5840Sstevel@tonic-gate thread_unlock(tp);
5850Sstevel@tonic-gate mutex_exit(&p->p_lock);
5860Sstevel@tonic-gate }
5870Sstevel@tonic-gate mutex_exit(&pidlock);
5880Sstevel@tonic-gate
5890Sstevel@tonic-gate
5900Sstevel@tonic-gate /* let everything catch up */
5910Sstevel@tonic-gate utstop_timedwait(count * count * DR_UTSTOP_WAIT);
5920Sstevel@tonic-gate
5930Sstevel@tonic-gate
5940Sstevel@tonic-gate /* now, walk the threadlist again to see if we are done */
5950Sstevel@tonic-gate mutex_enter(&pidlock);
5960Sstevel@tonic-gate for (tp = curthread->t_next, bailout = 0;
5970Sstevel@tonic-gate tp != curthread; tp = tp->t_next) {
5980Sstevel@tonic-gate proc_t *p = ttoproc(tp);
5990Sstevel@tonic-gate
6000Sstevel@tonic-gate /* handle kernel threads separately */
6010Sstevel@tonic-gate if (p->p_as == &kas || p->p_stat == SZOMB)
6020Sstevel@tonic-gate continue;
6030Sstevel@tonic-gate
6040Sstevel@tonic-gate /*
6050Sstevel@tonic-gate * If this thread didn't stop, and we don't allow
6060Sstevel@tonic-gate * unstopped blocked threads, bail.
6070Sstevel@tonic-gate */
6080Sstevel@tonic-gate thread_lock(tp);
6090Sstevel@tonic-gate if (!CPR_ISTOPPED(tp) &&
6100Sstevel@tonic-gate !(dr_allow_blocked_threads &&
6110Sstevel@tonic-gate DR_VSTOPPED(tp))) {
6120Sstevel@tonic-gate bailout = 1;
6130Sstevel@tonic-gate if (count == DR_UTSTOP_RETRY - 1) {
6140Sstevel@tonic-gate /*
6150Sstevel@tonic-gate * save the pid for later reporting
6160Sstevel@tonic-gate */
6170Sstevel@tonic-gate srh->sr_err_idx =
6180Sstevel@tonic-gate dr_add_int(srh->sr_err_ints,
6190Sstevel@tonic-gate srh->sr_err_idx, DR_MAX_ERR_INT,
6200Sstevel@tonic-gate (uint64_t)p->p_pid);
6210Sstevel@tonic-gate
6220Sstevel@tonic-gate cmn_err(CE_WARN, "%s: "
6230Sstevel@tonic-gate "failed to stop thread: "
6240Sstevel@tonic-gate "process=%s, pid=%d",
6250Sstevel@tonic-gate f, p->p_user.u_psargs, p->p_pid);
6260Sstevel@tonic-gate
6270Sstevel@tonic-gate PR_QR("%s: failed to stop thread: "
628930Smathue "process=%s, pid=%d, t_id=0x%p, "
6290Sstevel@tonic-gate "t_state=0x%x, t_proc_flag=0x%x, "
6300Sstevel@tonic-gate "t_schedflag=0x%x\n",
6310Sstevel@tonic-gate f, p->p_user.u_psargs, p->p_pid,
63211311SSurya.Prakki@Sun.COM (void *)tp, tp->t_state,
63311311SSurya.Prakki@Sun.COM tp->t_proc_flag, tp->t_schedflag);
6340Sstevel@tonic-gate }
6350Sstevel@tonic-gate
6360Sstevel@tonic-gate }
6370Sstevel@tonic-gate thread_unlock(tp);
6380Sstevel@tonic-gate }
6390Sstevel@tonic-gate mutex_exit(&pidlock);
6400Sstevel@tonic-gate
6410Sstevel@tonic-gate /* were all the threads stopped? */
6420Sstevel@tonic-gate if (!bailout)
6430Sstevel@tonic-gate break;
6440Sstevel@tonic-gate }
6450Sstevel@tonic-gate
6460Sstevel@tonic-gate /* were we unable to stop all threads after a few tries? */
6470Sstevel@tonic-gate if (bailout) {
6480Sstevel@tonic-gate handle->h_err = drerr_int(ESBD_UTHREAD, srh->sr_err_ints,
64911651SVijay.Balakrishna@Sun.COM srh->sr_err_idx, 0);
6500Sstevel@tonic-gate return (ESRCH);
6510Sstevel@tonic-gate }
6520Sstevel@tonic-gate
6530Sstevel@tonic-gate return (DDI_SUCCESS);
6540Sstevel@tonic-gate }
6550Sstevel@tonic-gate
6560Sstevel@tonic-gate static void
dr_start_user_threads(void)6570Sstevel@tonic-gate dr_start_user_threads(void)
6580Sstevel@tonic-gate {
6590Sstevel@tonic-gate kthread_id_t tp;
6600Sstevel@tonic-gate
6610Sstevel@tonic-gate mutex_enter(&pidlock);
6620Sstevel@tonic-gate
6630Sstevel@tonic-gate /* walk all threads and release them */
6640Sstevel@tonic-gate for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) {
6650Sstevel@tonic-gate proc_t *p = ttoproc(tp);
6660Sstevel@tonic-gate
6670Sstevel@tonic-gate /* skip kernel threads */
6680Sstevel@tonic-gate if (ttoproc(tp)->p_as == &kas)
6690Sstevel@tonic-gate continue;
6700Sstevel@tonic-gate
6710Sstevel@tonic-gate mutex_enter(&p->p_lock);
6720Sstevel@tonic-gate tp->t_proc_flag &= ~TP_CHKPT;
6730Sstevel@tonic-gate mutex_exit(&p->p_lock);
6740Sstevel@tonic-gate
6750Sstevel@tonic-gate thread_lock(tp);
6760Sstevel@tonic-gate if (CPR_ISTOPPED(tp)) {
6770Sstevel@tonic-gate /* back on the runq */
6780Sstevel@tonic-gate tp->t_schedflag |= TS_RESUME;
6790Sstevel@tonic-gate setrun_locked(tp);
6800Sstevel@tonic-gate }
6810Sstevel@tonic-gate thread_unlock(tp);
6820Sstevel@tonic-gate }
6830Sstevel@tonic-gate
6840Sstevel@tonic-gate mutex_exit(&pidlock);
6850Sstevel@tonic-gate }
6860Sstevel@tonic-gate
6870Sstevel@tonic-gate static void
dr_signal_user(int sig)6880Sstevel@tonic-gate dr_signal_user(int sig)
6890Sstevel@tonic-gate {
6900Sstevel@tonic-gate struct proc *p;
6910Sstevel@tonic-gate
6920Sstevel@tonic-gate mutex_enter(&pidlock);
6930Sstevel@tonic-gate
6940Sstevel@tonic-gate for (p = practive; p != NULL; p = p->p_next) {
6950Sstevel@tonic-gate /* only user threads */
6960Sstevel@tonic-gate if (p->p_exec == NULL || p->p_stat == SZOMB ||
6970Sstevel@tonic-gate p == proc_init || p == ttoproc(curthread))
6980Sstevel@tonic-gate continue;
6990Sstevel@tonic-gate
7000Sstevel@tonic-gate mutex_enter(&p->p_lock);
7010Sstevel@tonic-gate sigtoproc(p, NULL, sig);
7020Sstevel@tonic-gate mutex_exit(&p->p_lock);
7030Sstevel@tonic-gate }
7040Sstevel@tonic-gate
7050Sstevel@tonic-gate mutex_exit(&pidlock);
7060Sstevel@tonic-gate
7070Sstevel@tonic-gate /* add a bit of delay */
7080Sstevel@tonic-gate delay(hz);
7090Sstevel@tonic-gate }
7100Sstevel@tonic-gate
7110Sstevel@tonic-gate void
dr_resume(dr_sr_handle_t * srh)7120Sstevel@tonic-gate dr_resume(dr_sr_handle_t *srh)
7130Sstevel@tonic-gate {
7140Sstevel@tonic-gate if (srh->sr_suspend_state < DR_SRSTATE_FULL) {
7150Sstevel@tonic-gate /*
7160Sstevel@tonic-gate * Update the signature block.
7170Sstevel@tonic-gate * If cpus are not paused, this can be done now.
7180Sstevel@tonic-gate * See comments below.
7190Sstevel@tonic-gate */
7200Sstevel@tonic-gate CPU_SIGNATURE(OS_SIG, SIGST_RESUME_INPROGRESS, SIGSUBST_NULL,
7210Sstevel@tonic-gate CPU->cpu_id);
7220Sstevel@tonic-gate }
7230Sstevel@tonic-gate
7240Sstevel@tonic-gate switch (srh->sr_suspend_state) {
7250Sstevel@tonic-gate case DR_SRSTATE_FULL:
7260Sstevel@tonic-gate
7270Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock));
7280Sstevel@tonic-gate
7292399Scth /*
7302399Scth * Prevent false alarm in tod_validate() due to tod
7312399Scth * value change between suspend and resume
7322399Scth */
7332399Scth mutex_enter(&tod_lock);
73411752STrevor.Thompson@Sun.COM tod_status_set(TOD_DR_RESUME_DONE);
7352399Scth mutex_exit(&tod_lock);
7362399Scth
7370Sstevel@tonic-gate dr_enable_intr(); /* enable intr & clock */
7380Sstevel@tonic-gate
7390Sstevel@tonic-gate start_cpus();
7400Sstevel@tonic-gate mutex_exit(&cpu_lock);
7410Sstevel@tonic-gate
7420Sstevel@tonic-gate /*
7430Sstevel@tonic-gate * Update the signature block.
7440Sstevel@tonic-gate * This must not be done while cpus are paused, since on
7450Sstevel@tonic-gate * Starcat the cpu signature update aquires an adaptive
7460Sstevel@tonic-gate * mutex in the iosram driver. Blocking with cpus paused
7470Sstevel@tonic-gate * can lead to deadlock.
7480Sstevel@tonic-gate */
7490Sstevel@tonic-gate CPU_SIGNATURE(OS_SIG, SIGST_RESUME_INPROGRESS, SIGSUBST_NULL,
7500Sstevel@tonic-gate CPU->cpu_id);
7510Sstevel@tonic-gate
7520Sstevel@tonic-gate /*
7530Sstevel@tonic-gate * If we suspended hw watchdog at suspend,
7540Sstevel@tonic-gate * re-enable it now.
7550Sstevel@tonic-gate */
7560Sstevel@tonic-gate if (srh->sr_flags & (SR_FLAG_WATCHDOG)) {
7570Sstevel@tonic-gate mutex_enter(&tod_lock);
7580Sstevel@tonic-gate tod_ops.tod_set_watchdog_timer(
75911651SVijay.Balakrishna@Sun.COM watchdog_timeout_seconds);
7600Sstevel@tonic-gate mutex_exit(&tod_lock);
7610Sstevel@tonic-gate }
7620Sstevel@tonic-gate
7630Sstevel@tonic-gate /*
7640Sstevel@tonic-gate * This should only be called if drmach_suspend_last()
7650Sstevel@tonic-gate * was called and state transitioned to DR_SRSTATE_FULL
7660Sstevel@tonic-gate * to prevent resume attempts on device instances that
7670Sstevel@tonic-gate * were not previously suspended.
7680Sstevel@tonic-gate */
7690Sstevel@tonic-gate drmach_resume_first();
7700Sstevel@tonic-gate
7710Sstevel@tonic-gate /* FALLTHROUGH */
7720Sstevel@tonic-gate
7730Sstevel@tonic-gate case DR_SRSTATE_DRIVER:
7740Sstevel@tonic-gate /*
7750Sstevel@tonic-gate * resume drivers
7760Sstevel@tonic-gate */
7770Sstevel@tonic-gate srh->sr_err_idx = 0;
7780Sstevel@tonic-gate
7790Sstevel@tonic-gate /* no parent dip to hold busy */
7800Sstevel@tonic-gate dr_resume_devices(ddi_root_node(), srh);
7810Sstevel@tonic-gate
7820Sstevel@tonic-gate if (srh->sr_err_idx && srh->sr_dr_handlep) {
7830Sstevel@tonic-gate (srh->sr_dr_handlep)->h_err = drerr_int(ESBD_RESUME,
78411651SVijay.Balakrishna@Sun.COM srh->sr_err_ints, srh->sr_err_idx, 1);
7850Sstevel@tonic-gate }
7860Sstevel@tonic-gate
7870Sstevel@tonic-gate /*
7880Sstevel@tonic-gate * resume the lock manager
7890Sstevel@tonic-gate */
7900Sstevel@tonic-gate lm_cprresume();
7910Sstevel@tonic-gate
7920Sstevel@tonic-gate /* FALLTHROUGH */
7930Sstevel@tonic-gate
7940Sstevel@tonic-gate case DR_SRSTATE_USER:
7950Sstevel@tonic-gate /*
7960Sstevel@tonic-gate * finally, resume user threads
7970Sstevel@tonic-gate */
7980Sstevel@tonic-gate if (!dr_skip_user_threads) {
7990Sstevel@tonic-gate prom_printf("DR: resuming user threads...\n");
8000Sstevel@tonic-gate dr_start_user_threads();
8010Sstevel@tonic-gate }
8020Sstevel@tonic-gate /* FALLTHROUGH */
8030Sstevel@tonic-gate
8040Sstevel@tonic-gate case DR_SRSTATE_BEGIN:
8050Sstevel@tonic-gate default:
8060Sstevel@tonic-gate /*
8070Sstevel@tonic-gate * let those who care know that we've just resumed
8080Sstevel@tonic-gate */
8090Sstevel@tonic-gate PR_QR("sending SIGTHAW...\n");
8100Sstevel@tonic-gate dr_signal_user(SIGTHAW);
8110Sstevel@tonic-gate break;
8120Sstevel@tonic-gate }
8130Sstevel@tonic-gate
8140Sstevel@tonic-gate /*
8150Sstevel@tonic-gate * update the signature block
8160Sstevel@tonic-gate */
8170Sstevel@tonic-gate CPU_SIGNATURE(OS_SIG, SIGST_RUN, SIGSUBST_NULL, CPU->cpu_id);
8180Sstevel@tonic-gate
8190Sstevel@tonic-gate prom_printf("DR: resume COMPLETED\n");
8200Sstevel@tonic-gate }
8210Sstevel@tonic-gate
8220Sstevel@tonic-gate int
dr_suspend(dr_sr_handle_t * srh)8230Sstevel@tonic-gate dr_suspend(dr_sr_handle_t *srh)
8240Sstevel@tonic-gate {
8250Sstevel@tonic-gate dr_handle_t *handle;
8260Sstevel@tonic-gate int force;
8270Sstevel@tonic-gate int dev_errs_idx;
8280Sstevel@tonic-gate uint64_t dev_errs[DR_MAX_ERR_INT];
8290Sstevel@tonic-gate int rc = DDI_SUCCESS;
8300Sstevel@tonic-gate
8310Sstevel@tonic-gate handle = srh->sr_dr_handlep;
8320Sstevel@tonic-gate
8330Sstevel@tonic-gate force = dr_cmd_flags(handle) & SBD_FLAG_FORCE;
8340Sstevel@tonic-gate
8350Sstevel@tonic-gate /*
8360Sstevel@tonic-gate * update the signature block
8370Sstevel@tonic-gate */
8380Sstevel@tonic-gate CPU_SIGNATURE(OS_SIG, SIGST_QUIESCE_INPROGRESS, SIGSUBST_NULL,
8390Sstevel@tonic-gate CPU->cpu_id);
8400Sstevel@tonic-gate
8410Sstevel@tonic-gate prom_printf("\nDR: suspending user threads...\n");
8420Sstevel@tonic-gate srh->sr_suspend_state = DR_SRSTATE_USER;
8430Sstevel@tonic-gate if (((rc = dr_stop_user_threads(srh)) != DDI_SUCCESS) &&
8440Sstevel@tonic-gate dr_check_user_stop_result) {
8450Sstevel@tonic-gate dr_resume(srh);
8460Sstevel@tonic-gate return (rc);
8470Sstevel@tonic-gate }
8480Sstevel@tonic-gate
8490Sstevel@tonic-gate if (!force) {
8500Sstevel@tonic-gate struct dr_ref drc = {0};
8510Sstevel@tonic-gate
8520Sstevel@tonic-gate prom_printf("\nDR: checking devices...\n");
8530Sstevel@tonic-gate dev_errs_idx = 0;
8540Sstevel@tonic-gate
8550Sstevel@tonic-gate drc.arr = dev_errs;
8560Sstevel@tonic-gate drc.idx = &dev_errs_idx;
8570Sstevel@tonic-gate drc.len = DR_MAX_ERR_INT;
8580Sstevel@tonic-gate
8590Sstevel@tonic-gate /*
8600Sstevel@tonic-gate * Since the root node can never go away, it
8610Sstevel@tonic-gate * doesn't have to be held.
8620Sstevel@tonic-gate */
8630Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), dr_check_unsafe_major, &drc);
8640Sstevel@tonic-gate if (dev_errs_idx) {
8650Sstevel@tonic-gate handle->h_err = drerr_int(ESBD_UNSAFE, dev_errs,
86611651SVijay.Balakrishna@Sun.COM dev_errs_idx, 1);
8670Sstevel@tonic-gate dr_resume(srh);
8680Sstevel@tonic-gate return (DDI_FAILURE);
8690Sstevel@tonic-gate }
8700Sstevel@tonic-gate PR_QR("done\n");
8710Sstevel@tonic-gate } else {
8720Sstevel@tonic-gate prom_printf("\nDR: dr_suspend invoked with force flag\n");
8730Sstevel@tonic-gate }
8740Sstevel@tonic-gate
8750Sstevel@tonic-gate #ifndef SKIP_SYNC
8760Sstevel@tonic-gate /*
8770Sstevel@tonic-gate * This sync swap out all user pages
8780Sstevel@tonic-gate */
8790Sstevel@tonic-gate vfs_sync(SYNC_ALL);
8800Sstevel@tonic-gate #endif
8810Sstevel@tonic-gate
8820Sstevel@tonic-gate /*
8830Sstevel@tonic-gate * special treatment for lock manager
8840Sstevel@tonic-gate */
8850Sstevel@tonic-gate lm_cprsuspend();
8860Sstevel@tonic-gate
8870Sstevel@tonic-gate #ifndef SKIP_SYNC
8880Sstevel@tonic-gate /*
8890Sstevel@tonic-gate * sync the file system in case we never make it back
8900Sstevel@tonic-gate */
8910Sstevel@tonic-gate sync();
8920Sstevel@tonic-gate #endif
8930Sstevel@tonic-gate
8940Sstevel@tonic-gate /*
8950Sstevel@tonic-gate * now suspend drivers
8960Sstevel@tonic-gate */
8970Sstevel@tonic-gate prom_printf("DR: suspending drivers...\n");
8980Sstevel@tonic-gate srh->sr_suspend_state = DR_SRSTATE_DRIVER;
8990Sstevel@tonic-gate srh->sr_err_idx = 0;
9000Sstevel@tonic-gate /* No parent to hold busy */
9010Sstevel@tonic-gate if ((rc = dr_suspend_devices(ddi_root_node(), srh)) != DDI_SUCCESS) {
9020Sstevel@tonic-gate if (srh->sr_err_idx && srh->sr_dr_handlep) {
9030Sstevel@tonic-gate (srh->sr_dr_handlep)->h_err = drerr_int(ESBD_SUSPEND,
90411651SVijay.Balakrishna@Sun.COM srh->sr_err_ints, srh->sr_err_idx, 1);
9050Sstevel@tonic-gate }
9060Sstevel@tonic-gate dr_resume(srh);
9070Sstevel@tonic-gate return (rc);
9080Sstevel@tonic-gate }
9090Sstevel@tonic-gate
9100Sstevel@tonic-gate drmach_suspend_last();
9110Sstevel@tonic-gate
9120Sstevel@tonic-gate /*
9130Sstevel@tonic-gate * finally, grab all cpus
9140Sstevel@tonic-gate */
9150Sstevel@tonic-gate srh->sr_suspend_state = DR_SRSTATE_FULL;
9160Sstevel@tonic-gate
9170Sstevel@tonic-gate /*
9180Sstevel@tonic-gate * if watchdog was activated, disable it
9190Sstevel@tonic-gate */
9200Sstevel@tonic-gate if (watchdog_activated) {
9210Sstevel@tonic-gate mutex_enter(&tod_lock);
9220Sstevel@tonic-gate tod_ops.tod_clear_watchdog_timer();
9230Sstevel@tonic-gate mutex_exit(&tod_lock);
9240Sstevel@tonic-gate srh->sr_flags |= SR_FLAG_WATCHDOG;
9250Sstevel@tonic-gate } else {
9260Sstevel@tonic-gate srh->sr_flags &= ~(SR_FLAG_WATCHDOG);
9270Sstevel@tonic-gate }
9280Sstevel@tonic-gate
9290Sstevel@tonic-gate /*
9300Sstevel@tonic-gate * Update the signature block.
9310Sstevel@tonic-gate * This must be done before cpus are paused, since on Starcat the
9320Sstevel@tonic-gate * cpu signature update aquires an adaptive mutex in the iosram driver.
9330Sstevel@tonic-gate * Blocking with cpus paused can lead to deadlock.
9340Sstevel@tonic-gate */
9350Sstevel@tonic-gate CPU_SIGNATURE(OS_SIG, SIGST_QUIESCED, SIGSUBST_NULL, CPU->cpu_id);
9360Sstevel@tonic-gate
9370Sstevel@tonic-gate mutex_enter(&cpu_lock);
9380Sstevel@tonic-gate pause_cpus(NULL);
9390Sstevel@tonic-gate dr_stop_intr();
9400Sstevel@tonic-gate
9410Sstevel@tonic-gate return (rc);
9420Sstevel@tonic-gate }
9430Sstevel@tonic-gate
9440Sstevel@tonic-gate int
dr_pt_test_suspend(dr_handle_t * hp)9450Sstevel@tonic-gate dr_pt_test_suspend(dr_handle_t *hp)
9460Sstevel@tonic-gate {
9470Sstevel@tonic-gate dr_sr_handle_t *srh;
9480Sstevel@tonic-gate int err;
9490Sstevel@tonic-gate uint_t psmerr;
9500Sstevel@tonic-gate static fn_t f = "dr_pt_test_suspend";
9510Sstevel@tonic-gate
9520Sstevel@tonic-gate PR_QR("%s...\n", f);
9530Sstevel@tonic-gate
9540Sstevel@tonic-gate srh = dr_get_sr_handle(hp);
9550Sstevel@tonic-gate if ((err = dr_suspend(srh)) == DDI_SUCCESS) {
9560Sstevel@tonic-gate dr_resume(srh);
9570Sstevel@tonic-gate if ((hp->h_err) && ((psmerr = hp->h_err->e_code) != 0)) {
9580Sstevel@tonic-gate PR_QR("%s: error on dr_resume()", f);
9590Sstevel@tonic-gate switch (psmerr) {
9600Sstevel@tonic-gate case ESBD_RESUME:
9610Sstevel@tonic-gate PR_QR("Couldn't resume devices: %s\n",
96211651SVijay.Balakrishna@Sun.COM DR_GET_E_RSC(hp->h_err));
9630Sstevel@tonic-gate break;
9640Sstevel@tonic-gate
9650Sstevel@tonic-gate case ESBD_KTHREAD:
9660Sstevel@tonic-gate PR_ALL("psmerr is ESBD_KTHREAD\n");
9670Sstevel@tonic-gate break;
9680Sstevel@tonic-gate default:
96911752STrevor.Thompson@Sun.COM PR_ALL("Resume error unknown = %d\n", psmerr);
9700Sstevel@tonic-gate break;
9710Sstevel@tonic-gate }
9720Sstevel@tonic-gate }
9730Sstevel@tonic-gate } else {
97411752STrevor.Thompson@Sun.COM PR_ALL("%s: dr_suspend() failed, err = 0x%x\n", f, err);
9750Sstevel@tonic-gate psmerr = hp->h_err ? hp->h_err->e_code : ESBD_NOERROR;
9760Sstevel@tonic-gate switch (psmerr) {
9770Sstevel@tonic-gate case ESBD_UNSAFE:
9780Sstevel@tonic-gate PR_ALL("Unsafe devices (major #): %s\n",
97911651SVijay.Balakrishna@Sun.COM DR_GET_E_RSC(hp->h_err));
9800Sstevel@tonic-gate break;
9810Sstevel@tonic-gate
9820Sstevel@tonic-gate case ESBD_RTTHREAD:
9830Sstevel@tonic-gate PR_ALL("RT threads (PIDs): %s\n",
98411651SVijay.Balakrishna@Sun.COM DR_GET_E_RSC(hp->h_err));
9850Sstevel@tonic-gate break;
9860Sstevel@tonic-gate
9870Sstevel@tonic-gate case ESBD_UTHREAD:
9880Sstevel@tonic-gate PR_ALL("User threads (PIDs): %s\n",
98911651SVijay.Balakrishna@Sun.COM DR_GET_E_RSC(hp->h_err));
9900Sstevel@tonic-gate break;
9910Sstevel@tonic-gate
9920Sstevel@tonic-gate case ESBD_SUSPEND:
9930Sstevel@tonic-gate PR_ALL("Non-suspendable devices (major #): %s\n",
99411651SVijay.Balakrishna@Sun.COM DR_GET_E_RSC(hp->h_err));
9950Sstevel@tonic-gate break;
9960Sstevel@tonic-gate
9970Sstevel@tonic-gate case ESBD_RESUME:
9980Sstevel@tonic-gate PR_ALL("Could not resume devices (major #): %s\n",
99911651SVijay.Balakrishna@Sun.COM DR_GET_E_RSC(hp->h_err));
10000Sstevel@tonic-gate break;
10010Sstevel@tonic-gate
10020Sstevel@tonic-gate case ESBD_KTHREAD:
10030Sstevel@tonic-gate PR_ALL("psmerr is ESBD_KTHREAD\n");
10040Sstevel@tonic-gate break;
10050Sstevel@tonic-gate
10060Sstevel@tonic-gate case ESBD_NOERROR:
10070Sstevel@tonic-gate PR_ALL("sbd_error_t error code not set\n");
10080Sstevel@tonic-gate break;
10090Sstevel@tonic-gate
10100Sstevel@tonic-gate default:
10110Sstevel@tonic-gate PR_ALL("Unknown error psmerr = %d\n", psmerr);
10120Sstevel@tonic-gate break;
10130Sstevel@tonic-gate }
10140Sstevel@tonic-gate }
10150Sstevel@tonic-gate dr_release_sr_handle(srh);
10160Sstevel@tonic-gate
10170Sstevel@tonic-gate return (0);
10180Sstevel@tonic-gate }
10190Sstevel@tonic-gate
10200Sstevel@tonic-gate /*
10210Sstevel@tonic-gate * Add a new integer value to the end of an array. Don't allow duplicates to
10220Sstevel@tonic-gate * appear in the array, and don't allow the array to overflow. Return the new
10230Sstevel@tonic-gate * total number of entries in the array.
10240Sstevel@tonic-gate */
10250Sstevel@tonic-gate static int
dr_add_int(uint64_t * arr,int idx,int len,uint64_t val)10260Sstevel@tonic-gate dr_add_int(uint64_t *arr, int idx, int len, uint64_t val)
10270Sstevel@tonic-gate {
10280Sstevel@tonic-gate int i;
10290Sstevel@tonic-gate
10300Sstevel@tonic-gate if (arr == NULL)
10310Sstevel@tonic-gate return (0);
10320Sstevel@tonic-gate
10330Sstevel@tonic-gate if (idx >= len)
10340Sstevel@tonic-gate return (idx);
10350Sstevel@tonic-gate
10360Sstevel@tonic-gate for (i = 0; i < idx; i++) {
10370Sstevel@tonic-gate if (arr[i] == val)
10380Sstevel@tonic-gate return (idx);
10390Sstevel@tonic-gate }
10400Sstevel@tonic-gate
10410Sstevel@tonic-gate arr[idx++] = val;
10420Sstevel@tonic-gate
10430Sstevel@tonic-gate return (idx);
10440Sstevel@tonic-gate }
10450Sstevel@tonic-gate
10460Sstevel@tonic-gate /*
10470Sstevel@tonic-gate * Construct an sbd_error_t featuring a string representation of an array of
10480Sstevel@tonic-gate * integers as its e_rsc.
10490Sstevel@tonic-gate */
10500Sstevel@tonic-gate static sbd_error_t *
drerr_int(int e_code,uint64_t * arr,int idx,int majors)10510Sstevel@tonic-gate drerr_int(int e_code, uint64_t *arr, int idx, int majors)
10520Sstevel@tonic-gate {
10530Sstevel@tonic-gate int i, n, buf_len, buf_idx, buf_avail;
10540Sstevel@tonic-gate char *dname;
10550Sstevel@tonic-gate char *buf;
10560Sstevel@tonic-gate sbd_error_t *new_sbd_err;
10570Sstevel@tonic-gate static char s_ellipsis[] = "...";
10580Sstevel@tonic-gate
10590Sstevel@tonic-gate if (arr == NULL || idx <= 0)
10600Sstevel@tonic-gate return (NULL);
10610Sstevel@tonic-gate
10620Sstevel@tonic-gate /* MAXPATHLEN is the size of the e_rsc field in sbd_error_t. */
10630Sstevel@tonic-gate buf = (char *)kmem_zalloc(MAXPATHLEN, KM_SLEEP);
10640Sstevel@tonic-gate
10650Sstevel@tonic-gate /*
10660Sstevel@tonic-gate * This is the total working area of the buffer. It must be computed
10670Sstevel@tonic-gate * as the size of 'buf', minus reserved space for the null terminator
10680Sstevel@tonic-gate * and the ellipsis string.
10690Sstevel@tonic-gate */
10700Sstevel@tonic-gate buf_len = MAXPATHLEN - (strlen(s_ellipsis) + 1);
10710Sstevel@tonic-gate
10720Sstevel@tonic-gate /* Construct a string representation of the array values */
10730Sstevel@tonic-gate for (buf_idx = 0, i = 0; i < idx; i++) {
10740Sstevel@tonic-gate buf_avail = buf_len - buf_idx;
10750Sstevel@tonic-gate if (majors) {
10760Sstevel@tonic-gate dname = ddi_major_to_name(arr[i]);
10770Sstevel@tonic-gate if (dname) {
107811752STrevor.Thompson@Sun.COM n = snprintf(&buf[buf_idx], buf_avail, "%s, ",
107911752STrevor.Thompson@Sun.COM dname);
10800Sstevel@tonic-gate } else {
10810Sstevel@tonic-gate n = snprintf(&buf[buf_idx], buf_avail,
108211651SVijay.Balakrishna@Sun.COM "major %lu, ", arr[i]);
10830Sstevel@tonic-gate }
10840Sstevel@tonic-gate } else {
108511651SVijay.Balakrishna@Sun.COM n = snprintf(&buf[buf_idx], buf_avail, "%lu, ", arr[i]);
10860Sstevel@tonic-gate }
10870Sstevel@tonic-gate
10880Sstevel@tonic-gate /* An ellipsis gets appended when no more values fit */
10890Sstevel@tonic-gate if (n >= buf_avail) {
10900Sstevel@tonic-gate (void) strcpy(&buf[buf_idx], s_ellipsis);
10910Sstevel@tonic-gate break;
10920Sstevel@tonic-gate }
10930Sstevel@tonic-gate
10940Sstevel@tonic-gate buf_idx += n;
10950Sstevel@tonic-gate }
10960Sstevel@tonic-gate
10970Sstevel@tonic-gate /* If all the contents fit, remove the trailing comma */
10980Sstevel@tonic-gate if (n < buf_avail) {
10990Sstevel@tonic-gate buf[--buf_idx] = '\0';
11000Sstevel@tonic-gate buf[--buf_idx] = '\0';
11010Sstevel@tonic-gate }
11020Sstevel@tonic-gate
11030Sstevel@tonic-gate /* Return an sbd_error_t with the buffer and e_code */
11040Sstevel@tonic-gate new_sbd_err = drerr_new(1, e_code, buf);
11050Sstevel@tonic-gate kmem_free(buf, MAXPATHLEN);
11060Sstevel@tonic-gate return (new_sbd_err);
11070Sstevel@tonic-gate }
1108