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