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 *
sbdp_get_err_buf(sbd_error_t * ep)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
sbdp_stop_intr(void)13103831d35Sstevel sbdp_stop_intr(void)
13203831d35Sstevel {
13303831d35Sstevel kpreempt_disable();
13403831d35Sstevel cyclic_suspend();
13503831d35Sstevel }
13603831d35Sstevel
13703831d35Sstevel static void
sbdp_enable_intr(void)13803831d35Sstevel sbdp_enable_intr(void)
13903831d35Sstevel {
14003831d35Sstevel cyclic_resume();
14103831d35Sstevel kpreempt_enable();
14203831d35Sstevel }
14303831d35Sstevel
14403831d35Sstevel sbdp_sr_handle_t *
sbdp_get_sr_handle(void)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
sbdp_release_sr_handle(sbdp_sr_handle_t * srh)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
sbdp_is_real_device(dev_info_t * dip)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
sbdp_bypass_device(char * dname)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
sbdp_resolve_devname(dev_info_t * dip,char * buffer,char * alias)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
sbdp_check_dip(dev_info_t * dip,void * arg,uint_t ref)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
sbdp_check_devices(dev_info_t * dip,int * refcount,sbd_error_t * sep,int * refcount_non_gldv3)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
sbdp_suspend_devices_(dev_info_t * dip,sbdp_sr_handle_t * srh)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
sbdp_suspend_devices_enter(dev_info_t * dip,void * arg)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
sbdp_suspend_devices_exit(dev_info_t * dip,void * arg)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
sbdp_suspend_devices(dev_info_t * dip,sbdp_sr_handle_t * srh)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
sbdp_resume_devices(dev_info_t * start,sbdp_sr_handle_t * srh)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