sbdp_quiesce.c revision c97ad5cdc75eb73e3cc38542ca3ba783574b0a7a
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* A CPR derivative specifically for sbd
*/
#include <sys/machparam.h>
#include <sys/machsystm.h>
#define SUNDDI_IMPL
#include <sys/ddi_impldefs.h>
#include <sys/ndi_impldefs.h>
#ifdef DEBUG
#endif
#include <sys/sbd_ioctl.h>
#include <sys/sbdp_priv.h>
#include <sys/cpu_sgnblk_defs.h>
static char *
{
}
#ifdef DEBUG
static int sbdp_bypass_device(char *dname);
#endif
char *alias);
#define SR_FLAG_WATCHDOG 0x1
#ifdef DEBUG
/*
* Just for testing. List of drivers to bypass when performing a suspend.
*/
static char *sbdp_bypass_list[] = {
/* "sgsbbc", this is an example when needed */
""
};
#endif
#define SKIP_SYNC /* bypass sync ops in sbdp_suspend */
/*
* sbdp_skip_user_threads is used to control if user threads should
* be suspended. If sbdp_skip_user_threads is true, the rest of the
* flags are not used; if it is false, sbdp_check_user_stop_result
* will be used to control whether or not we need to check suspend
* result, and sbdp_allow_blocked_threads will be used to control
* whether or not we allow suspend to continue if there are blocked
* threads. We allow all combinations of sbdp_check_user_stop_result
* and sbdp_allow_block_threads, even though it might not make much
* sense to not allow block threads when we don't even check stop
* result.
*/
static int sbdp_skip_user_threads = 0; /* default to FALSE */
static void
sbdp_stop_intr(void)
{
}
static void
sbdp_enable_intr(void)
{
}
sbdp_get_sr_handle(void)
{
return (srh);
}
void
{
}
static int
{
int length = 0;
int rc;
return (0);
return (1);
return (0);
/*
* now the general case
*/
if (rc != DDI_PROP_SUCCESS) {
return (0);
} else {
return (1);
}
}
#ifdef DEBUG
static int
sbdp_bypass_device(char *dname)
{
int i;
char **lname;
/* check the bypass list */
return (1);
}
return (0);
}
#endif
static int
{
return (-1);
name = "<null name>";
else
*alias = 0;
return (0);
}
typedef struct sbdp_ref {
int *refcount;
} sbdp_ref_t;
static int
{
char *dname;
return (DDI_WALK_CONTINUE);
if (!sbdp_is_real_device(dip))
return (DDI_WALK_CONTINUE);
"pciclass,060980") == 0)) {
return (DDI_WALK_TERMINATE);
}
#ifdef DEBUG
if (sbdp_bypass_device(dname))
return (DDI_WALK_CONTINUE);
#endif
if (ref) {
SBDP_DBG_QR("\n%s (major# %d) is referenced\n",
return (DDI_WALK_TERMINATE);
}
return (DDI_WALK_CONTINUE);
}
void
{
}
/*
* Starting from the root node suspend all devices in the device tree.
* Assumes that all devices have already been marked busy.
*/
static int
{
char *dname;
return (ENXIO);
}
if (!sbdp_is_real_device(dip))
continue;
#ifdef DEBUG
if (sbdp_bypass_device(dname)) {
SBDP_DBG_QR("bypassed suspend of %s (major# %d)\n",
continue;
}
#endif
d_info = "<null>";
d_name[0] = 0;
if (d_alias[0] != 0) {
SBDP_DBG_QR("\tsuspending %s@%s (aka %s)\n",
} else {
SBDP_DBG_QR("\tsuspending %s@%s\n",
}
} else {
}
"%d", major);
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
return (DDI_WALK_CONTINUE);
}
/*ARGSUSED*/
static int
{
return (DDI_WALK_CONTINUE);
}
/*
* Before suspending devices first mark all device nodes busy. This
* avoids a deadlock situation when another thread holds a device busy
* and accesses an already suspended device.
*/
static int
{
int rv;
/* assumes dip is ddi_root_node so no ndi_devi_enter required */
return (rv);
}
static void
{
int circ;
char *bn;
/* attach in reverse device tree order */
}
/* Release hold acquired in sbdp_suspend_devices() */
} else if (sbdp_is_real_device(dip) &&
}
#ifdef DEBUG
if (!sbdp_bypass_device(bn)) {
#else
{
#endif
d_name[0] = 0;
d_info = "<null>";
d_alias)) {
if (d_alias[0] != 0) {
SBDP_DBG_QR("\tresuming "
"%s@%s (aka %s)\n",
d_alias);
} else {
SBDP_DBG_QR("\tresuming "
"%s@%s\n",
}
} else {
SBDP_DBG_QR("\tresuming %s@%s\n",
}
DDI_SUCCESS) {
/*
* Print a console warning,
* set an errno of ESGT_RESUME,
* and save the driver major
* number in the e_str.
*/
"%s@%s",
SBDP_DBG_QR("\tFAILED to resume "
ESGT_RESUME, NULL);
}
}
}
}
}
/*
* True if thread is virtually stopped. Similar to CPR_VSTOPPED
* but from DR point of view. These user threads are waiting in
* the kernel. Once they return from kernel, they will process
* the stop signal and stop.
*/
#define SBDP_VSTOPPED(t) \
(t)->t_astflag && \
((t)->t_proc_flag & TP_CHKPT))
static int
{
int count;
char cache_psargs[PSARGSZ];
int bailout;
extern void add_one_utstop();
extern void utstop_timedwait(clock_t);
extern void utstop_init(void);
#define SBDP_UTSTOP_RETRY 4
#define SBDP_UTSTOP_WAIT hz
return (DDI_SUCCESS);
utstop_init();
/* we need to try a few times to get past fork, etc. */
/* walk the entire threadlist */
/* handle kernel threads separately */
continue;
mutex_enter(&p->p_lock);
/* add another reason to stop this thread */
} else {
mutex_exit(&p->p_lock);
mutex_enter(&p->p_lock);
}
}
/* grab thread if needed */
mutex_exit(&p->p_lock);
}
/* let everything catch up */
/* now, walk the threadlist again to see if we are done */
/* handle kernel threads separately */
continue;
/*
* If this thread didn't stop, and we don't allow
* unstopped blocked threads, bail.
*/
if (!CPR_ISTOPPED(tp) &&
SBDP_VSTOPPED(tp))) {
/* nope, cache the details for later */
sizeof (cache_psargs));
bailout = 1;
}
}
/* were all the threads stopped? */
if (!bailout)
break;
}
/* were we unable to stop all threads after a few tries? */
if (bailout) {
return (ESRCH);
}
return (DDI_SUCCESS);
}
static void
sbdp_start_user_threads(void)
{
/* walk all threads and release them */
/* skip kernel threads */
continue;
mutex_enter(&p->p_lock);
mutex_exit(&p->p_lock);
if (CPR_ISTOPPED(tp)) {
/* back on the runq */
}
}
}
static void
sbdp_signal_user(int sig)
{
struct proc *p;
/* only user threads */
continue;
mutex_enter(&p->p_lock);
mutex_exit(&p->p_lock);
}
/* add a bit of delay */
}
static uint_t saved_watchdog_seconds;
void
{
/*
* update the signature block
*/
case SBDP_SRSTATE_FULL:
/*
* Prevent false alarm in tod_validate() due to tod
* value change between suspend and resume
*/
sbdp_enable_intr(); /* enable intr & clock */
/*
* release all the other cpus
* using start_cpus() vice sbdp_release_cpus()
*/
start_cpus();
/*
* If we suspended hw watchdog at suspend,
* re-enable it now.
*/
}
/* FALLTHROUGH */
case SBDP_SRSTATE_DRIVER:
/*
* resume devices: root node doesn't have to
* be held in any way.
*/
/*
* resume the lock manager
*/
lm_cprresume();
/* FALLTHROUGH */
case SBDP_SRSTATE_USER:
/*
* finally, resume user threads
*/
if (!sbdp_skip_user_threads) {
SBDP_DBG_QR("DR: resuming user threads...\n");
}
/* FALLTHROUGH */
case SBDP_SRSTATE_BEGIN:
default:
/*
* let those who care know that we've just resumed
*/
SBDP_DBG_QR("sending SIGTHAW...\n");
break;
}
/*
* update the signature block
*/
SBDP_DBG_QR("DR: resume COMPLETED\n");
}
int
{
int force;
int rc = DDI_SUCCESS;
/*
* if no force flag, check for unsafe drivers
*/
if (force) {
SBDP_DBG_QR("\nsbdp_suspend invoked with force flag");
}
/*
* update the signature block
*/
/*
* first, stop all user threads
*/
SBDP_DBG_QR("SBDP: suspending user threads...\n");
return (rc);
}
#ifndef SKIP_SYNC
/*
* This sync swap out all user pages
*/
#endif
/*
* special treatment for lock manager
*/
#ifndef SKIP_SYNC
/*
* sync the file system in case we never make it back
*/
sync();
#endif
/*
* now suspend drivers
*/
SBDP_DBG_QR("SBDP: suspending drivers...\n");
/*
* Root node doesn't have to be held in any way.
*/
return (rc);
}
/*
* finally, grab all cpus
*/
/*
* if watchdog was activated, disable it
*/
if (watchdog_activated) {
} else {
}
/*
* update the signature block
*/
return (rc);
}
/*ARGSUSED*/
int
{
int err;
srh = sbdp_get_sr_handle();
} else {
}
return (0);
}
#ifdef DEBUG
int
{
return (sbdp_test_suspend(hp));
}
#endif