103831d35Sstevel /* 203831d35Sstevel * CDDL HEADER START 303831d35Sstevel * 403831d35Sstevel * The contents of this file are subject to the terms of the 503831d35Sstevel * Common Development and Distribution License (the "License"). 603831d35Sstevel * You may not use this file except in compliance with the License. 703831d35Sstevel * 803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 903831d35Sstevel * or http://www.opensolaris.org/os/licensing. 1003831d35Sstevel * See the License for the specific language governing permissions 1103831d35Sstevel * and limitations under the License. 1203831d35Sstevel * 1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each 1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the 1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 1803831d35Sstevel * 1903831d35Sstevel * CDDL HEADER END 2003831d35Sstevel */ 2103831d35Sstevel 2203831d35Sstevel /* 23df3cd224SVijay S Balakrishna * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 2403831d35Sstevel * Use is subject to license terms. 2503831d35Sstevel */ 2603831d35Sstevel 2703831d35Sstevel /* 2803831d35Sstevel * A CPR derivative specifically for sbd 2903831d35Sstevel */ 3003831d35Sstevel 3103831d35Sstevel #include <sys/types.h> 3203831d35Sstevel #include <sys/systm.h> 3303831d35Sstevel #include <sys/machparam.h> 3403831d35Sstevel #include <sys/machsystm.h> 3503831d35Sstevel #include <sys/ddi.h> 3603831d35Sstevel #define SUNDDI_IMPL 3703831d35Sstevel #include <sys/sunddi.h> 3803831d35Sstevel #include <sys/sunndi.h> 3903831d35Sstevel #include <sys/devctl.h> 4003831d35Sstevel #include <sys/time.h> 4103831d35Sstevel #include <sys/kmem.h> 4203831d35Sstevel #include <nfs/lm.h> 4303831d35Sstevel #include <sys/ddi_impldefs.h> 4403831d35Sstevel #include <sys/ndi_impldefs.h> 4503831d35Sstevel #include <sys/obpdefs.h> 4603831d35Sstevel #include <sys/cmn_err.h> 4703831d35Sstevel #include <sys/debug.h> 4803831d35Sstevel #include <sys/errno.h> 4903831d35Sstevel #include <sys/callb.h> 5003831d35Sstevel #include <sys/clock.h> 5103831d35Sstevel #include <sys/x_call.h> 5203831d35Sstevel #include <sys/cpuvar.h> 5303831d35Sstevel #include <sys/epm.h> 5403831d35Sstevel #include <sys/vfs.h> 5503831d35Sstevel 5603831d35Sstevel #ifdef DEBUG 5703831d35Sstevel #include <sys/note.h> 5803831d35Sstevel #endif 5903831d35Sstevel 6003831d35Sstevel #include <sys/promif.h> 6103831d35Sstevel #include <sys/conf.h> 6203831d35Sstevel #include <sys/cyclic.h> 6303831d35Sstevel 6403831d35Sstevel #include <sys/sbd_ioctl.h> 6503831d35Sstevel #include <sys/sbd.h> 6603831d35Sstevel #include <sys/sbdp_priv.h> 6703831d35Sstevel #include <sys/cpu_sgnblk_defs.h> 6803831d35Sstevel 6903831d35Sstevel static char * 7003831d35Sstevel sbdp_get_err_buf(sbd_error_t *ep) 7103831d35Sstevel { 7203831d35Sstevel return (ep->e_rsc); 7303831d35Sstevel } 7403831d35Sstevel 7503831d35Sstevel extern void e_ddi_enter_driver_list(struct devnames *dnp, int *listcnt); 7603831d35Sstevel extern void e_ddi_exit_driver_list(struct devnames *dnp, int listcnt); 7703831d35Sstevel extern int is_pseudo_device(dev_info_t *dip); 7803831d35Sstevel 7903831d35Sstevel extern kmutex_t cpu_lock; 8003831d35Sstevel 8103831d35Sstevel static int sbdp_is_real_device(dev_info_t *dip); 8203831d35Sstevel #ifdef DEBUG 8303831d35Sstevel static int sbdp_bypass_device(char *dname); 8403831d35Sstevel #endif 8503831d35Sstevel static int sbdp_check_dip(dev_info_t *dip, void *arg, uint_t ref); 8603831d35Sstevel 8703831d35Sstevel static int sbdp_resolve_devname(dev_info_t *dip, char *buffer, 8803831d35Sstevel char *alias); 8903831d35Sstevel 9003831d35Sstevel int sbdp_test_suspend(sbdp_handle_t *hp); 9103831d35Sstevel 9203831d35Sstevel #define SR_STATE(srh) ((srh)->sr_suspend_state) 9303831d35Sstevel #define SR_SET_STATE(srh, state) (SR_STATE((srh)) = (state)) 9403831d35Sstevel #define SR_FAILED_DIP(srh) ((srh)->sr_failed_dip) 9503831d35Sstevel 9603831d35Sstevel #define SR_FLAG_WATCHDOG 0x1 9703831d35Sstevel #define SR_CHECK_FLAG(srh, flag) ((srh)->sr_flags & (flag)) 9803831d35Sstevel #define SR_SET_FLAG(srh, flag) ((srh)->sr_flags |= (flag)) 9903831d35Sstevel #define SR_CLEAR_FLAG(srh, flag) ((srh)->sr_flags &= ~(flag)) 10003831d35Sstevel 10103831d35Sstevel #ifdef DEBUG 10203831d35Sstevel /* 10303831d35Sstevel * Just for testing. List of drivers to bypass when performing a suspend. 10403831d35Sstevel */ 10503831d35Sstevel static char *sbdp_bypass_list[] = { 10603831d35Sstevel /* "sgsbbc", this is an example when needed */ 10703831d35Sstevel "" 10803831d35Sstevel }; 10903831d35Sstevel #endif 11003831d35Sstevel 11103831d35Sstevel #define SKIP_SYNC /* bypass sync ops in sbdp_suspend */ 11203831d35Sstevel 11303831d35Sstevel /* 11403831d35Sstevel * sbdp_skip_user_threads is used to control if user threads should 11503831d35Sstevel * be suspended. If sbdp_skip_user_threads is true, the rest of the 11603831d35Sstevel * flags are not used; if it is false, sbdp_check_user_stop_result 11703831d35Sstevel * will be used to control whether or not we need to check suspend 11803831d35Sstevel * result, and sbdp_allow_blocked_threads will be used to control 11903831d35Sstevel * whether or not we allow suspend to continue if there are blocked 12003831d35Sstevel * threads. We allow all combinations of sbdp_check_user_stop_result 12103831d35Sstevel * and sbdp_allow_block_threads, even though it might not make much 12203831d35Sstevel * sense to not allow block threads when we don't even check stop 12303831d35Sstevel * result. 12403831d35Sstevel */ 12503831d35Sstevel static int sbdp_skip_user_threads = 0; /* default to FALSE */ 12603831d35Sstevel static int sbdp_check_user_stop_result = 1; /* default to TRUE */ 12703831d35Sstevel static int sbdp_allow_blocked_threads = 1; /* default to TRUE */ 12803831d35Sstevel 12903831d35Sstevel 13003831d35Sstevel static void 13103831d35Sstevel sbdp_stop_intr(void) 13203831d35Sstevel { 13303831d35Sstevel kpreempt_disable(); 13403831d35Sstevel cyclic_suspend(); 13503831d35Sstevel } 13603831d35Sstevel 13703831d35Sstevel static void 13803831d35Sstevel sbdp_enable_intr(void) 13903831d35Sstevel { 14003831d35Sstevel cyclic_resume(); 14103831d35Sstevel kpreempt_enable(); 14203831d35Sstevel } 14303831d35Sstevel 14403831d35Sstevel sbdp_sr_handle_t * 14503831d35Sstevel sbdp_get_sr_handle(void) 14603831d35Sstevel { 14703831d35Sstevel sbdp_sr_handle_t *srh; 14803831d35Sstevel srh = kmem_zalloc(sizeof (sbdp_sr_handle_t), KM_SLEEP); 14903831d35Sstevel 15003831d35Sstevel return (srh); 15103831d35Sstevel } 15203831d35Sstevel 15303831d35Sstevel void 15403831d35Sstevel sbdp_release_sr_handle(sbdp_sr_handle_t *srh) 15503831d35Sstevel { 15603831d35Sstevel ASSERT(SR_FAILED_DIP(srh) == NULL); 15703831d35Sstevel kmem_free((caddr_t)srh, sizeof (sbdp_sr_handle_t)); 15803831d35Sstevel } 15903831d35Sstevel 16003831d35Sstevel static int 16103831d35Sstevel sbdp_is_real_device(dev_info_t *dip) 16203831d35Sstevel { 16303831d35Sstevel struct regspec *regbuf = NULL; 16403831d35Sstevel int length = 0; 16503831d35Sstevel int rc; 16603831d35Sstevel 16703831d35Sstevel if (ddi_get_driver(dip) == NULL) 16803831d35Sstevel return (0); 16903831d35Sstevel 17003831d35Sstevel if (DEVI(dip)->devi_pm_flags & (PMC_NEEDS_SR|PMC_PARENTAL_SR)) 17103831d35Sstevel return (1); 17203831d35Sstevel if (DEVI(dip)->devi_pm_flags & PMC_NO_SR) 17303831d35Sstevel return (0); 17403831d35Sstevel 17503831d35Sstevel /* 17603831d35Sstevel * now the general case 17703831d35Sstevel */ 17803831d35Sstevel rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", 17903831d35Sstevel (caddr_t)®buf, &length); 18003831d35Sstevel ASSERT(rc != DDI_PROP_NO_MEMORY); 18103831d35Sstevel if (rc != DDI_PROP_SUCCESS) { 18203831d35Sstevel return (0); 18303831d35Sstevel } else { 18403831d35Sstevel if ((length > 0) && (regbuf != NULL)) 18503831d35Sstevel kmem_free(regbuf, length); 18603831d35Sstevel return (1); 18703831d35Sstevel } 18803831d35Sstevel } 18903831d35Sstevel 19003831d35Sstevel #ifdef DEBUG 19103831d35Sstevel static int 19203831d35Sstevel sbdp_bypass_device(char *dname) 19303831d35Sstevel { 19403831d35Sstevel int i; 19503831d35Sstevel char **lname; 19603831d35Sstevel /* check the bypass list */ 19703831d35Sstevel for (i = 0, lname = &sbdp_bypass_list[i]; **lname != '\0'; lname++) { 19803831d35Sstevel SBDP_DBG_QR("Checking %s\n", *lname); 19903831d35Sstevel if (strcmp(dname, sbdp_bypass_list[i++]) == 0) 20003831d35Sstevel return (1); 20103831d35Sstevel } 20203831d35Sstevel return (0); 20303831d35Sstevel } 20403831d35Sstevel #endif 20503831d35Sstevel 20603831d35Sstevel static int 20703831d35Sstevel sbdp_resolve_devname(dev_info_t *dip, char *buffer, char *alias) 20803831d35Sstevel { 20903831d35Sstevel major_t devmajor; 21003831d35Sstevel char *aka, *name; 21103831d35Sstevel 21203831d35Sstevel *buffer = *alias = 0; 21303831d35Sstevel 21403831d35Sstevel if (dip == NULL) 21503831d35Sstevel return (-1); 21603831d35Sstevel 21703831d35Sstevel if ((name = ddi_get_name(dip)) == NULL) 21803831d35Sstevel name = "<null name>"; 21903831d35Sstevel 22003831d35Sstevel aka = name; 22103831d35Sstevel 22203831d35Sstevel if ((devmajor = ddi_name_to_major(aka)) != -1) 22303831d35Sstevel aka = ddi_major_to_name(devmajor); 22403831d35Sstevel 22503831d35Sstevel (void) strcpy(buffer, name); 22603831d35Sstevel 22703831d35Sstevel if (strcmp(name, aka)) 22803831d35Sstevel (void) strcpy(alias, aka); 22903831d35Sstevel else 23003831d35Sstevel *alias = 0; 23103831d35Sstevel 23203831d35Sstevel return (0); 23303831d35Sstevel } 23403831d35Sstevel 23503831d35Sstevel typedef struct sbdp_ref { 23603831d35Sstevel int *refcount; 237df3cd224SVijay S Balakrishna int *refcount_non_gldv3; 23803831d35Sstevel sbd_error_t *sep; 23903831d35Sstevel } sbdp_ref_t; 24003831d35Sstevel 24103831d35Sstevel static int 24203831d35Sstevel sbdp_check_dip(dev_info_t *dip, void *arg, uint_t ref) 24303831d35Sstevel { 24403831d35Sstevel char *dname; 24503831d35Sstevel sbdp_ref_t *sbrp = (sbdp_ref_t *)arg; 24603831d35Sstevel 24703831d35Sstevel if (dip == NULL) 24803831d35Sstevel return (DDI_WALK_CONTINUE); 24903831d35Sstevel 25003831d35Sstevel ASSERT(sbrp->sep != NULL); 25103831d35Sstevel ASSERT(sbrp->refcount != NULL); 25203831d35Sstevel 25303831d35Sstevel if (!sbdp_is_real_device(dip)) 25403831d35Sstevel return (DDI_WALK_CONTINUE); 25503831d35Sstevel 25603831d35Sstevel dname = ddi_binding_name(dip); 25703831d35Sstevel 25803831d35Sstevel if ((strcmp(dname, "pciclass,060940") == 0) || (strcmp(dname, 25903831d35Sstevel "pciclass,060980") == 0)) { 26003831d35Sstevel (void) ddi_pathname(dip, sbdp_get_err_buf(sbrp->sep)); 26103831d35Sstevel sbdp_set_err(sbrp->sep, ESBD_BUSY, NULL); 26203831d35Sstevel (*sbrp->refcount)++; 26303831d35Sstevel return (DDI_WALK_TERMINATE); 26403831d35Sstevel } 26503831d35Sstevel 26603831d35Sstevel #ifdef DEBUG 26703831d35Sstevel if (sbdp_bypass_device(dname)) 26803831d35Sstevel return (DDI_WALK_CONTINUE); 26903831d35Sstevel #endif 27003831d35Sstevel 27103831d35Sstevel if (ref) { 272df3cd224SVijay S Balakrishna major_t major; 273df3cd224SVijay S Balakrishna 27403831d35Sstevel (*sbrp->refcount)++; 27503831d35Sstevel SBDP_DBG_QR("\n%s (major# %d) is referenced\n", 27603831d35Sstevel dname, ddi_name_to_major(dname)); 27703831d35Sstevel (void) ddi_pathname(dip, sbdp_get_err_buf(sbrp->sep)); 278df3cd224SVijay S Balakrishna major = ddi_driver_major(dip); 279df3cd224SVijay S Balakrishna if (sbrp->refcount_non_gldv3 && NETWORK_PHYSDRV(major) && 280df3cd224SVijay S Balakrishna !GLDV3_DRV(major)) { 281df3cd224SVijay S Balakrishna (*sbrp->refcount_non_gldv3)++; 282df3cd224SVijay S Balakrishna return (DDI_WALK_CONTINUE); 283df3cd224SVijay S Balakrishna } 28403831d35Sstevel sbdp_set_err(sbrp->sep, ESBD_BUSY, NULL); 28503831d35Sstevel return (DDI_WALK_TERMINATE); 28603831d35Sstevel } 28703831d35Sstevel return (DDI_WALK_CONTINUE); 28803831d35Sstevel } 28903831d35Sstevel 29003831d35Sstevel void 291df3cd224SVijay S Balakrishna sbdp_check_devices(dev_info_t *dip, int *refcount, sbd_error_t *sep, 292df3cd224SVijay S Balakrishna int *refcount_non_gldv3) 29303831d35Sstevel { 29403831d35Sstevel sbdp_ref_t sbr; 29503831d35Sstevel 29603831d35Sstevel sbr.refcount = refcount; 297df3cd224SVijay S Balakrishna sbr.refcount_non_gldv3 = refcount_non_gldv3; 29803831d35Sstevel sbr.sep = sep; 29903831d35Sstevel 30003831d35Sstevel ASSERT(e_ddi_branch_held(dip)); 30103831d35Sstevel 30203831d35Sstevel (void) e_ddi_branch_referenced(dip, sbdp_check_dip, &sbr); 30303831d35Sstevel } 30403831d35Sstevel 305b8073120Smb158278 /* 306b8073120Smb158278 * Starting from the root node suspend all devices in the device tree. 307b8073120Smb158278 * Assumes that all devices have already been marked busy. 308b8073120Smb158278 */ 30903831d35Sstevel static int 310b8073120Smb158278 sbdp_suspend_devices_(dev_info_t *dip, sbdp_sr_handle_t *srh) 31103831d35Sstevel { 31203831d35Sstevel major_t major; 31303831d35Sstevel char *dname; 31403831d35Sstevel 31503831d35Sstevel for (; dip != NULL; dip = ddi_get_next_sibling(dip)) { 31603831d35Sstevel char d_name[40], d_alias[40], *d_info; 31703831d35Sstevel 318b8073120Smb158278 if (sbdp_suspend_devices_(ddi_get_child(dip), srh)) { 31903831d35Sstevel return (ENXIO); 32003831d35Sstevel } 32103831d35Sstevel 32203831d35Sstevel if (!sbdp_is_real_device(dip)) 32303831d35Sstevel continue; 32403831d35Sstevel 32503831d35Sstevel major = (major_t)-1; 32603831d35Sstevel if ((dname = DEVI(dip)->devi_binding_name) != NULL) 32703831d35Sstevel major = ddi_name_to_major(dname); 32803831d35Sstevel 32903831d35Sstevel #ifdef DEBUG 33003831d35Sstevel if (sbdp_bypass_device(dname)) { 33103831d35Sstevel SBDP_DBG_QR("bypassed suspend of %s (major# %d)\n", 33203831d35Sstevel dname, major); 33303831d35Sstevel continue; 33403831d35Sstevel } 33503831d35Sstevel #endif 33603831d35Sstevel 33703831d35Sstevel if ((d_info = ddi_get_name_addr(dip)) == NULL) 33803831d35Sstevel d_info = "<null>"; 33903831d35Sstevel 34003831d35Sstevel d_name[0] = 0; 34103831d35Sstevel if (sbdp_resolve_devname(dip, d_name, d_alias) == 0) { 34203831d35Sstevel if (d_alias[0] != 0) { 34303831d35Sstevel SBDP_DBG_QR("\tsuspending %s@%s (aka %s)\n", 34403831d35Sstevel d_name, d_info, d_alias); 34503831d35Sstevel } else { 34603831d35Sstevel SBDP_DBG_QR("\tsuspending %s@%s\n", 34703831d35Sstevel d_name, d_info); 34803831d35Sstevel } 34903831d35Sstevel } else { 35003831d35Sstevel SBDP_DBG_QR("\tsuspending %s@%s\n", dname, d_info); 35103831d35Sstevel } 35203831d35Sstevel 35303831d35Sstevel if (devi_detach(dip, DDI_SUSPEND) != DDI_SUCCESS) { 35403831d35Sstevel (void) sprintf(sbdp_get_err_buf(&srh->sep), 35503831d35Sstevel "%d", major); 35603831d35Sstevel 35703831d35Sstevel sbdp_set_err(&srh->sep, ESGT_SUSPEND, NULL); 35803831d35Sstevel ndi_hold_devi(dip); 35903831d35Sstevel SR_FAILED_DIP(srh) = dip; 36003831d35Sstevel return (DDI_FAILURE); 36103831d35Sstevel } 36203831d35Sstevel } 36303831d35Sstevel 36403831d35Sstevel return (DDI_SUCCESS); 36503831d35Sstevel } 36603831d35Sstevel 367b8073120Smb158278 /*ARGSUSED*/ 368b8073120Smb158278 static int 369b8073120Smb158278 sbdp_suspend_devices_enter(dev_info_t *dip, void *arg) 370b8073120Smb158278 { 371b8073120Smb158278 struct dev_info *devi = DEVI(dip); 372b8073120Smb158278 ndi_devi_enter(dip, &devi->devi_circular); 373b8073120Smb158278 return (DDI_WALK_CONTINUE); 374b8073120Smb158278 } 375b8073120Smb158278 376b8073120Smb158278 /*ARGSUSED*/ 377b8073120Smb158278 static int 378b8073120Smb158278 sbdp_suspend_devices_exit(dev_info_t *dip, void *arg) 379b8073120Smb158278 { 380b8073120Smb158278 struct dev_info *devi = DEVI(dip); 381b8073120Smb158278 ndi_devi_exit(dip, devi->devi_circular); 382b8073120Smb158278 return (DDI_WALK_CONTINUE); 383b8073120Smb158278 } 384b8073120Smb158278 385b8073120Smb158278 /* 386b8073120Smb158278 * Before suspending devices first mark all device nodes busy. This 387b8073120Smb158278 * avoids a deadlock situation when another thread holds a device busy 388b8073120Smb158278 * and accesses an already suspended device. 389b8073120Smb158278 */ 390b8073120Smb158278 static int 391b8073120Smb158278 sbdp_suspend_devices(dev_info_t *dip, sbdp_sr_handle_t *srh) 392b8073120Smb158278 { 393b8073120Smb158278 int rv; 394b8073120Smb158278 395b8073120Smb158278 /* assumes dip is ddi_root_node so no ndi_devi_enter required */ 396b8073120Smb158278 ASSERT(dip == ddi_root_node()); 397b8073120Smb158278 ddi_walk_devs(dip, sbdp_suspend_devices_enter, NULL); 398b8073120Smb158278 rv = sbdp_suspend_devices_(dip, srh); 399b8073120Smb158278 ddi_walk_devs(dip, sbdp_suspend_devices_exit, NULL); 400b8073120Smb158278 return (rv); 401b8073120Smb158278 } 402b8073120Smb158278 40303831d35Sstevel static void 40403831d35Sstevel sbdp_resume_devices(dev_info_t *start, sbdp_sr_handle_t *srh) 40503831d35Sstevel { 40603831d35Sstevel int circ; 40703831d35Sstevel dev_info_t *dip, *next, *last = NULL; 40803831d35Sstevel char *bn; 40903831d35Sstevel sbd_error_t *sep; 41003831d35Sstevel 41103831d35Sstevel sep = &srh->sep; 41203831d35Sstevel 41303831d35Sstevel /* attach in reverse device tree order */ 41403831d35Sstevel while (last != start) { 41503831d35Sstevel dip = start; 41603831d35Sstevel next = ddi_get_next_sibling(dip); 41703831d35Sstevel while (next != last && dip != SR_FAILED_DIP(srh)) { 41803831d35Sstevel dip = next; 41903831d35Sstevel next = ddi_get_next_sibling(dip); 42003831d35Sstevel } 42103831d35Sstevel if (dip == SR_FAILED_DIP(srh)) { 42203831d35Sstevel /* Release hold acquired in sbdp_suspend_devices() */ 42303831d35Sstevel ndi_rele_devi(dip); 42403831d35Sstevel SR_FAILED_DIP(srh) = NULL; 42503831d35Sstevel } else if (sbdp_is_real_device(dip) && 42603831d35Sstevel SR_FAILED_DIP(srh) == NULL) { 42703831d35Sstevel 42803831d35Sstevel if (DEVI(dip)->devi_binding_name != NULL) { 42903831d35Sstevel bn = ddi_binding_name(dip); 43003831d35Sstevel } 43103831d35Sstevel #ifdef DEBUG 43203831d35Sstevel if (!sbdp_bypass_device(bn)) { 43303831d35Sstevel #else 43403831d35Sstevel { 43503831d35Sstevel #endif 43603831d35Sstevel char d_name[40], d_alias[40], *d_info; 43703831d35Sstevel 43803831d35Sstevel d_name[0] = 0; 43903831d35Sstevel d_info = ddi_get_name_addr(dip); 44003831d35Sstevel if (d_info == NULL) 44103831d35Sstevel d_info = "<null>"; 44203831d35Sstevel 44303831d35Sstevel if (!sbdp_resolve_devname(dip, d_name, 44403831d35Sstevel d_alias)) { 44503831d35Sstevel if (d_alias[0] != 0) { 44603831d35Sstevel SBDP_DBG_QR("\tresuming " 44703831d35Sstevel "%s@%s (aka %s)\n", 44803831d35Sstevel d_name, d_info, 44903831d35Sstevel d_alias); 45003831d35Sstevel } else { 45103831d35Sstevel SBDP_DBG_QR("\tresuming " 45203831d35Sstevel "%s@%s\n", 45303831d35Sstevel d_name, d_info); 45403831d35Sstevel } 45503831d35Sstevel } else { 45603831d35Sstevel SBDP_DBG_QR("\tresuming %s@%s\n", 45703831d35Sstevel bn, d_info); 45803831d35Sstevel } 45903831d35Sstevel 46003831d35Sstevel if (devi_attach(dip, DDI_RESUME) != 46103831d35Sstevel DDI_SUCCESS) { 46203831d35Sstevel /* 46303831d35Sstevel * Print a console warning, 46403831d35Sstevel * set an errno of ESGT_RESUME, 46503831d35Sstevel * and save the driver major 46603831d35Sstevel * number in the e_str. 46703831d35Sstevel */ 46803831d35Sstevel 46903831d35Sstevel (void) sprintf(sbdp_get_err_buf(sep), 47003831d35Sstevel "%s@%s", 47103831d35Sstevel d_name[0] ? d_name : bn, d_info); 47203831d35Sstevel SBDP_DBG_QR("\tFAILED to resume " 47303831d35Sstevel "%s\n", sbdp_get_err_buf(sep)); 47403831d35Sstevel sbdp_set_err(sep, 47503831d35Sstevel ESGT_RESUME, NULL); 47603831d35Sstevel } 47703831d35Sstevel } 47803831d35Sstevel } 47903831d35Sstevel ndi_devi_enter(dip, &circ); 48003831d35Sstevel sbdp_resume_devices(ddi_get_child(dip), srh); 48103831d35Sstevel ndi_devi_exit(dip, circ); 48203831d35Sstevel last = dip; 48303831d35Sstevel } 48403831d35Sstevel } 48503831d35Sstevel 48603831d35Sstevel /* 48703831d35Sstevel * True if thread is virtually stopped. Similar to CPR_VSTOPPED 48803831d35Sstevel * but from DR point of view. These user threads are waiting in 48903831d35Sstevel * the kernel. Once they return from kernel, they will process 49003831d35Sstevel * the stop signal and stop. 49103831d35Sstevel */ 49203831d35Sstevel #define SBDP_VSTOPPED(t) \ 49303831d35Sstevel ((t)->t_state == TS_SLEEP && \ 49403831d35Sstevel (t)->t_wchan != NULL && \ 49503831d35Sstevel (t)->t_astflag && \ 49603831d35Sstevel ((t)->t_proc_flag & TP_CHKPT)) 49703831d35Sstevel 49803831d35Sstevel 49903831d35Sstevel static int 50003831d35Sstevel sbdp_stop_user_threads(sbdp_sr_handle_t *srh) 50103831d35Sstevel { 50203831d35Sstevel int count; 50303831d35Sstevel char cache_psargs[PSARGSZ]; 50403831d35Sstevel kthread_id_t cache_tp; 50503831d35Sstevel uint_t cache_t_state; 50603831d35Sstevel int bailout; 50703831d35Sstevel sbd_error_t *sep; 50803831d35Sstevel kthread_id_t tp; 50903831d35Sstevel 51003831d35Sstevel extern void add_one_utstop(); 51103831d35Sstevel extern void utstop_timedwait(clock_t); 51203831d35Sstevel extern void utstop_init(void); 51303831d35Sstevel 51403831d35Sstevel #define SBDP_UTSTOP_RETRY 4 51503831d35Sstevel #define SBDP_UTSTOP_WAIT hz 51603831d35Sstevel 51703831d35Sstevel if (sbdp_skip_user_threads) 51803831d35Sstevel return (DDI_SUCCESS); 51903831d35Sstevel 52003831d35Sstevel sep = &srh->sep; 52103831d35Sstevel ASSERT(sep); 52203831d35Sstevel 52303831d35Sstevel utstop_init(); 52403831d35Sstevel 52503831d35Sstevel /* we need to try a few times to get past fork, etc. */ 52603831d35Sstevel for (count = 0; count < SBDP_UTSTOP_RETRY; count++) { 52703831d35Sstevel /* walk the entire threadlist */ 52803831d35Sstevel mutex_enter(&pidlock); 52903831d35Sstevel for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) { 53003831d35Sstevel proc_t *p = ttoproc(tp); 53103831d35Sstevel 53203831d35Sstevel /* handle kernel threads separately */ 53303831d35Sstevel if (p->p_as == &kas || p->p_stat == SZOMB) 53403831d35Sstevel continue; 53503831d35Sstevel 53603831d35Sstevel mutex_enter(&p->p_lock); 53703831d35Sstevel thread_lock(tp); 53803831d35Sstevel 53903831d35Sstevel if (tp->t_state == TS_STOPPED) { 54003831d35Sstevel /* add another reason to stop this thread */ 54103831d35Sstevel tp->t_schedflag &= ~TS_RESUME; 54203831d35Sstevel } else { 54303831d35Sstevel tp->t_proc_flag |= TP_CHKPT; 54403831d35Sstevel 54503831d35Sstevel thread_unlock(tp); 54603831d35Sstevel mutex_exit(&p->p_lock); 54703831d35Sstevel add_one_utstop(); 54803831d35Sstevel mutex_enter(&p->p_lock); 54903831d35Sstevel thread_lock(tp); 55003831d35Sstevel 55103831d35Sstevel aston(tp); 55203831d35Sstevel 553c97ad5cdSakolb if (ISWAKEABLE(tp) || ISWAITING(tp)) { 55403831d35Sstevel setrun_locked(tp); 55503831d35Sstevel } 55603831d35Sstevel } 55703831d35Sstevel 55803831d35Sstevel /* grab thread if needed */ 55903831d35Sstevel if (tp->t_state == TS_ONPROC && tp->t_cpu != CPU) 56003831d35Sstevel poke_cpu(tp->t_cpu->cpu_id); 56103831d35Sstevel 56203831d35Sstevel 56303831d35Sstevel thread_unlock(tp); 56403831d35Sstevel mutex_exit(&p->p_lock); 56503831d35Sstevel } 56603831d35Sstevel mutex_exit(&pidlock); 56703831d35Sstevel 56803831d35Sstevel 56903831d35Sstevel /* let everything catch up */ 57003831d35Sstevel utstop_timedwait(count * count * SBDP_UTSTOP_WAIT); 57103831d35Sstevel 57203831d35Sstevel 57303831d35Sstevel /* now, walk the threadlist again to see if we are done */ 57403831d35Sstevel mutex_enter(&pidlock); 57503831d35Sstevel for (tp = curthread->t_next, bailout = 0; 57603831d35Sstevel tp != curthread; tp = tp->t_next) { 57703831d35Sstevel proc_t *p = ttoproc(tp); 57803831d35Sstevel 57903831d35Sstevel /* handle kernel threads separately */ 58003831d35Sstevel if (p->p_as == &kas || p->p_stat == SZOMB) 58103831d35Sstevel continue; 58203831d35Sstevel 58303831d35Sstevel /* 58403831d35Sstevel * If this thread didn't stop, and we don't allow 58503831d35Sstevel * unstopped blocked threads, bail. 58603831d35Sstevel */ 58703831d35Sstevel thread_lock(tp); 58803831d35Sstevel if (!CPR_ISTOPPED(tp) && 58903831d35Sstevel !(sbdp_allow_blocked_threads && 59003831d35Sstevel SBDP_VSTOPPED(tp))) { 59103831d35Sstevel 59203831d35Sstevel /* nope, cache the details for later */ 59303831d35Sstevel bcopy(p->p_user.u_psargs, cache_psargs, 59403831d35Sstevel sizeof (cache_psargs)); 59503831d35Sstevel cache_tp = tp; 59603831d35Sstevel cache_t_state = tp->t_state; 59703831d35Sstevel bailout = 1; 59803831d35Sstevel } 59903831d35Sstevel thread_unlock(tp); 60003831d35Sstevel } 60103831d35Sstevel mutex_exit(&pidlock); 60203831d35Sstevel 60303831d35Sstevel /* were all the threads stopped? */ 60403831d35Sstevel if (!bailout) 60503831d35Sstevel break; 60603831d35Sstevel } 60703831d35Sstevel 60803831d35Sstevel /* were we unable to stop all threads after a few tries? */ 60903831d35Sstevel if (bailout) { 61003831d35Sstevel cmn_err(CE_NOTE, "process: %s id: %p state: %x\n", 61107d06da5SSurya Prakki cache_psargs, (void *)cache_tp, cache_t_state); 61203831d35Sstevel 61303831d35Sstevel (void) sprintf(sbdp_get_err_buf(sep), "%s", cache_psargs); 61403831d35Sstevel sbdp_set_err(sep, ESGT_UTHREAD, NULL); 61503831d35Sstevel return (ESRCH); 61603831d35Sstevel } 61703831d35Sstevel 61803831d35Sstevel return (DDI_SUCCESS); 61903831d35Sstevel } 62003831d35Sstevel 62103831d35Sstevel static void 62203831d35Sstevel sbdp_start_user_threads(void) 62303831d35Sstevel { 62403831d35Sstevel kthread_id_t tp; 62503831d35Sstevel 62603831d35Sstevel mutex_enter(&pidlock); 62703831d35Sstevel 62803831d35Sstevel /* walk all threads and release them */ 62903831d35Sstevel for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) { 63003831d35Sstevel proc_t *p = ttoproc(tp); 63103831d35Sstevel 63203831d35Sstevel /* skip kernel threads */ 63303831d35Sstevel if (ttoproc(tp)->p_as == &kas) 63403831d35Sstevel continue; 63503831d35Sstevel 63603831d35Sstevel mutex_enter(&p->p_lock); 63703831d35Sstevel tp->t_proc_flag &= ~TP_CHKPT; 63803831d35Sstevel mutex_exit(&p->p_lock); 63903831d35Sstevel 64003831d35Sstevel thread_lock(tp); 64103831d35Sstevel if (CPR_ISTOPPED(tp)) { 64203831d35Sstevel /* back on the runq */ 64303831d35Sstevel tp->t_schedflag |= TS_RESUME; 64403831d35Sstevel setrun_locked(tp); 64503831d35Sstevel } 64603831d35Sstevel thread_unlock(tp); 64703831d35Sstevel } 64803831d35Sstevel 64903831d35Sstevel mutex_exit(&pidlock); 65003831d35Sstevel } 65103831d35Sstevel 65203831d35Sstevel static void 65303831d35Sstevel sbdp_signal_user(int sig) 65403831d35Sstevel { 65503831d35Sstevel struct proc *p; 65603831d35Sstevel 65703831d35Sstevel mutex_enter(&pidlock); 65803831d35Sstevel 65903831d35Sstevel for (p = practive; p != NULL; p = p->p_next) { 66003831d35Sstevel /* only user threads */ 66103831d35Sstevel if (p->p_exec == NULL || p->p_stat == SZOMB || 66203831d35Sstevel p == proc_init || p == ttoproc(curthread)) 66303831d35Sstevel continue; 66403831d35Sstevel 66503831d35Sstevel mutex_enter(&p->p_lock); 66603831d35Sstevel sigtoproc(p, NULL, sig); 66703831d35Sstevel mutex_exit(&p->p_lock); 66803831d35Sstevel } 66903831d35Sstevel 67003831d35Sstevel mutex_exit(&pidlock); 67103831d35Sstevel 67203831d35Sstevel /* add a bit of delay */ 67303831d35Sstevel delay(hz); 67403831d35Sstevel } 67503831d35Sstevel 67603831d35Sstevel static uint_t saved_watchdog_seconds; 67703831d35Sstevel 67803831d35Sstevel void 67903831d35Sstevel sbdp_resume(sbdp_sr_handle_t *srh) 68003831d35Sstevel { 68103831d35Sstevel /* 68203831d35Sstevel * update the signature block 68303831d35Sstevel */ 68403831d35Sstevel CPU_SIGNATURE(OS_SIG, SIGST_RESUME_INPROGRESS, SIGSUBST_NULL, 68503831d35Sstevel CPU->cpu_id); 68603831d35Sstevel 68703831d35Sstevel switch (SR_STATE(srh)) { 68803831d35Sstevel case SBDP_SRSTATE_FULL: 68903831d35Sstevel 69003831d35Sstevel ASSERT(MUTEX_HELD(&cpu_lock)); 69103831d35Sstevel 692646e55b6Scth /* 693646e55b6Scth * Prevent false alarm in tod_validate() due to tod 694646e55b6Scth * value change between suspend and resume 695646e55b6Scth */ 696646e55b6Scth mutex_enter(&tod_lock); 6978fc99e42STrevor Thompson tod_status_set(TOD_DR_RESUME_DONE); 698646e55b6Scth mutex_exit(&tod_lock); 699646e55b6Scth 70003831d35Sstevel sbdp_enable_intr(); /* enable intr & clock */ 70103831d35Sstevel 70203831d35Sstevel /* 70303831d35Sstevel * release all the other cpus 70403831d35Sstevel * using start_cpus() vice sbdp_release_cpus() 70503831d35Sstevel */ 70603831d35Sstevel start_cpus(); 70703831d35Sstevel mutex_exit(&cpu_lock); 70803831d35Sstevel 70903831d35Sstevel /* 71003831d35Sstevel * If we suspended hw watchdog at suspend, 71103831d35Sstevel * re-enable it now. 71203831d35Sstevel */ 71303831d35Sstevel if (SR_CHECK_FLAG(srh, SR_FLAG_WATCHDOG)) { 71403831d35Sstevel mutex_enter(&tod_lock); 71503831d35Sstevel tod_ops.tod_set_watchdog_timer( 71603831d35Sstevel saved_watchdog_seconds); 71703831d35Sstevel mutex_exit(&tod_lock); 71803831d35Sstevel } 71903831d35Sstevel 72003831d35Sstevel /* FALLTHROUGH */ 72103831d35Sstevel 72203831d35Sstevel case SBDP_SRSTATE_DRIVER: 72303831d35Sstevel /* 72403831d35Sstevel * resume devices: root node doesn't have to 72503831d35Sstevel * be held in any way. 72603831d35Sstevel */ 72703831d35Sstevel sbdp_resume_devices(ddi_root_node(), srh); 72803831d35Sstevel 72903831d35Sstevel /* 73003831d35Sstevel * resume the lock manager 73103831d35Sstevel */ 73203831d35Sstevel lm_cprresume(); 73303831d35Sstevel 73403831d35Sstevel /* FALLTHROUGH */ 73503831d35Sstevel 73603831d35Sstevel case SBDP_SRSTATE_USER: 73703831d35Sstevel /* 73803831d35Sstevel * finally, resume user threads 73903831d35Sstevel */ 74003831d35Sstevel if (!sbdp_skip_user_threads) { 74103831d35Sstevel SBDP_DBG_QR("DR: resuming user threads...\n"); 74203831d35Sstevel sbdp_start_user_threads(); 74303831d35Sstevel } 74403831d35Sstevel /* FALLTHROUGH */ 74503831d35Sstevel 74603831d35Sstevel case SBDP_SRSTATE_BEGIN: 74703831d35Sstevel default: 74803831d35Sstevel /* 74903831d35Sstevel * let those who care know that we've just resumed 75003831d35Sstevel */ 75103831d35Sstevel SBDP_DBG_QR("sending SIGTHAW...\n"); 75203831d35Sstevel sbdp_signal_user(SIGTHAW); 75303831d35Sstevel break; 75403831d35Sstevel } 75503831d35Sstevel 75603831d35Sstevel /* 75703831d35Sstevel * update the signature block 75803831d35Sstevel */ 75903831d35Sstevel CPU_SIGNATURE(OS_SIG, SIGST_RUN, SIGSUBST_NULL, CPU->cpu_id); 76003831d35Sstevel 76103831d35Sstevel SBDP_DBG_QR("DR: resume COMPLETED\n"); 76203831d35Sstevel } 76303831d35Sstevel 76403831d35Sstevel int 76503831d35Sstevel sbdp_suspend(sbdp_sr_handle_t *srh) 76603831d35Sstevel { 76703831d35Sstevel int force; 76803831d35Sstevel int rc = DDI_SUCCESS; 76903831d35Sstevel 77003831d35Sstevel force = (srh && (srh->sr_flags & SBDP_IOCTL_FLAG_FORCE)); 77103831d35Sstevel 77203831d35Sstevel /* 77303831d35Sstevel * if no force flag, check for unsafe drivers 77403831d35Sstevel */ 77503831d35Sstevel if (force) { 77603831d35Sstevel SBDP_DBG_QR("\nsbdp_suspend invoked with force flag"); 77703831d35Sstevel } 77803831d35Sstevel 77903831d35Sstevel /* 78003831d35Sstevel * update the signature block 78103831d35Sstevel */ 78203831d35Sstevel CPU_SIGNATURE(OS_SIG, SIGST_QUIESCE_INPROGRESS, SIGSUBST_NULL, 78303831d35Sstevel CPU->cpu_id); 78403831d35Sstevel 78503831d35Sstevel /* 78603831d35Sstevel * first, stop all user threads 78703831d35Sstevel */ 78803831d35Sstevel SBDP_DBG_QR("SBDP: suspending user threads...\n"); 78903831d35Sstevel SR_SET_STATE(srh, SBDP_SRSTATE_USER); 79003831d35Sstevel if (((rc = sbdp_stop_user_threads(srh)) != DDI_SUCCESS) && 79103831d35Sstevel sbdp_check_user_stop_result) { 79203831d35Sstevel sbdp_resume(srh); 79303831d35Sstevel return (rc); 79403831d35Sstevel } 79503831d35Sstevel 79603831d35Sstevel #ifndef SKIP_SYNC 79703831d35Sstevel /* 79803831d35Sstevel * This sync swap out all user pages 79903831d35Sstevel */ 80003831d35Sstevel vfs_sync(SYNC_ALL); 80103831d35Sstevel #endif 80203831d35Sstevel 80303831d35Sstevel /* 80403831d35Sstevel * special treatment for lock manager 80503831d35Sstevel */ 80603831d35Sstevel lm_cprsuspend(); 80703831d35Sstevel 80803831d35Sstevel #ifndef SKIP_SYNC 80903831d35Sstevel /* 81003831d35Sstevel * sync the file system in case we never make it back 81103831d35Sstevel */ 81203831d35Sstevel sync(); 81303831d35Sstevel 81403831d35Sstevel #endif 81503831d35Sstevel /* 81603831d35Sstevel * now suspend drivers 81703831d35Sstevel */ 81803831d35Sstevel SBDP_DBG_QR("SBDP: suspending drivers...\n"); 81903831d35Sstevel SR_SET_STATE(srh, SBDP_SRSTATE_DRIVER); 82003831d35Sstevel 82103831d35Sstevel /* 82203831d35Sstevel * Root node doesn't have to be held in any way. 82303831d35Sstevel */ 82403831d35Sstevel if ((rc = sbdp_suspend_devices(ddi_root_node(), srh)) != DDI_SUCCESS) { 82503831d35Sstevel sbdp_resume(srh); 82603831d35Sstevel return (rc); 82703831d35Sstevel } 82803831d35Sstevel 82903831d35Sstevel /* 83003831d35Sstevel * finally, grab all cpus 83103831d35Sstevel */ 83203831d35Sstevel SR_SET_STATE(srh, SBDP_SRSTATE_FULL); 83303831d35Sstevel 83403831d35Sstevel /* 83503831d35Sstevel * if watchdog was activated, disable it 83603831d35Sstevel */ 83703831d35Sstevel if (watchdog_activated) { 83803831d35Sstevel mutex_enter(&tod_lock); 83903831d35Sstevel saved_watchdog_seconds = tod_ops.tod_clear_watchdog_timer(); 84003831d35Sstevel mutex_exit(&tod_lock); 84103831d35Sstevel SR_SET_FLAG(srh, SR_FLAG_WATCHDOG); 84203831d35Sstevel } else { 84303831d35Sstevel SR_CLEAR_FLAG(srh, SR_FLAG_WATCHDOG); 84403831d35Sstevel } 84503831d35Sstevel 84603831d35Sstevel mutex_enter(&cpu_lock); 847*0ed5c46eSJosef 'Jeff' Sipek pause_cpus(NULL, NULL); 84803831d35Sstevel sbdp_stop_intr(); 84903831d35Sstevel 85003831d35Sstevel /* 85103831d35Sstevel * update the signature block 85203831d35Sstevel */ 85303831d35Sstevel CPU_SIGNATURE(OS_SIG, SIGST_QUIESCED, SIGSUBST_NULL, CPU->cpu_id); 85403831d35Sstevel 85503831d35Sstevel return (rc); 85603831d35Sstevel } 85703831d35Sstevel 85803831d35Sstevel /*ARGSUSED*/ 85903831d35Sstevel int 86003831d35Sstevel sbdp_test_suspend(sbdp_handle_t *hp) 86103831d35Sstevel { 86203831d35Sstevel sbdp_sr_handle_t *srh; 86303831d35Sstevel int err; 86403831d35Sstevel 86503831d35Sstevel SBDP_DBG_QR("%s...\n", "sbdp_test_suspend"); 86603831d35Sstevel 86703831d35Sstevel srh = sbdp_get_sr_handle(); 86803831d35Sstevel 86903831d35Sstevel srh->sr_flags = hp->h_flags; 87003831d35Sstevel 87103831d35Sstevel if ((err = sbdp_suspend(srh)) == DDI_SUCCESS) { 87203831d35Sstevel sbdp_resume(srh); 87303831d35Sstevel } else { 87403831d35Sstevel SBDP_DBG_MISC("sbdp_suspend() failed, err = 0x%x\n", err); 87503831d35Sstevel } 87603831d35Sstevel sbdp_release_sr_handle(srh); 87703831d35Sstevel 87803831d35Sstevel return (0); 87903831d35Sstevel } 88003831d35Sstevel 88103831d35Sstevel #ifdef DEBUG 88203831d35Sstevel int 88303831d35Sstevel sbdp_passthru_test_quiesce(sbdp_handle_t *hp, void *arg) 88403831d35Sstevel { 88503831d35Sstevel _NOTE(ARGUNUSED(arg)) 88603831d35Sstevel 88703831d35Sstevel return (sbdp_test_suspend(hp)); 88803831d35Sstevel } 88903831d35Sstevel #endif 890