dr_quiesce.c revision df3cd224ef765c29101e4110546062199562f757
/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
*/
#include <sys/machparam.h>
#include <sys/machsystm.h>
#define SUNDDI_IMPL
#include <sys/ddi_impldefs.h>
#include <sys/ndi_impldefs.h>
#include <sys/cpu_sgnblk_defs.h>
extern dr_unsafe_devs_t dr_unsafe_devs;
static int dr_bypass_device(char *dname);
char *alias);
int majors);
/*
* dr_quiesce.c interface
* NOTE: states used internally by dr_suspend and dr_resume
*/
typedef enum dr_suspend_state {
DR_SRSTATE_BEGIN = 0,
struct dr_sr_handle {
int sr_err_idx;
};
#define SR_FLAG_WATCHDOG 0x1
/*
* XXX
* This hack will go away before RTI. Just for testing.
* List of drivers to bypass when performing a suspend.
*/
static char *dr_bypass_list[] = {
""
};
#define SKIP_SYNC /* bypass sync ops in dr_suspend */
/*
* dr_skip_user_threads is used to control if user threads should
* be suspended. If dr_skip_user_threads is true, the rest of the
* flags are not used; if it is false, dr_check_user_stop_result
* will be used to control whether or not we need to check suspend
* result, and dr_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 dr_check_user_stop_result
* and dr_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 dr_skip_user_threads = 0; /* default to FALSE */
#define DR_CPU_LOOP_MSEC 1000
static void
dr_stop_intr(void)
{
}
static void
dr_enable_intr(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);
}
}
static int
{
int i, ndevs;
return (0);
}
return (1);
}
return (0);
}
static int
dr_bypass_device(char *dname)
{
int i;
char **lname;
/* check the bypass list */
return (1);
}
return (0);
}
static int
{
return (-1);
name = "<null name>";
else
*alias = 0;
return (0);
}
struct dr_ref {
int *refcount;
int *refcount_non_gldv3;
int *idx;
int len;
};
/* ARGSUSED */
static int
{
char *dname;
return (DDI_WALK_CONTINUE);
if (!dr_is_real_device(dip))
return (DDI_WALK_CONTINUE);
if (dr_bypass_device(dname))
return (DDI_WALK_CONTINUE);
PR_QR("\n %s (major# %d) is referenced(%u)\n",
}
}
PR_QR("\n %s (major# %d) not hotpluggable\n",
}
}
return (DDI_WALK_CONTINUE);
}
static int
{
}
/*ARGSUSED*/
void
{
return;
}
/*
* The "dip" argument's parent (if it exists) must be held busy.
*/
static int
{
char *dname;
int circ;
/*
* If dip is the root node, it has no siblings and it is
* always held. If dip is not the root node, dr_suspend_devices()
* will be invoked with the parent held busy.
*/
return (ENXIO);
}
if (!dr_is_real_device(dip))
continue;
if (dr_bypass_device(dname)) {
major);
continue;
}
major);
continue;
}
d_info = "<null>";
d_name[0] = 0;
if (d_alias[0] != 0) {
prom_printf("\tsuspending %s@%s (aka %s)\n",
} else {
prom_printf("\tsuspending %s@%s\n",
}
} else {
}
prom_printf("\tFAILED to suspend %s@%s\n",
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
static void
{
char *bn;
int circ;
/* attach in reverse device tree order */
}
/* release hold acquired in dr_suspend_devices() */
} else
} else {
bn = "<null>";
}
if (!dr_bypass_device(bn) &&
!drmach_verify_sr(dip, 0)) {
d_name[0] = 0;
d_info = "<null>";
d_alias)) {
if (d_alias[0] != 0) {
prom_printf("\tresuming "
"%s@%s (aka %s)\n",
} else {
prom_printf("\tresuming "
}
} else {
prom_printf("\tresuming %s@%s\n",
}
DDI_SUCCESS) {
/*
* Print a console warning,
* set an e_code of ESBD_RESUME,
* and save the driver major
* number in the e_rsc.
*/
prom_printf("\tFAILED to resume %s@%s",
srh->sr_err_idx =
ESBD_RESUME, "%s@%s",
}
}
}
/* Hold parent busy while walking its children */
}
}
/*
* 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 complete in the kernel, they will process
* the stop signal and stop.
*/
#define DR_VSTOPPED(t) \
(t)->t_astflag && \
((t)->t_proc_flag & TP_CHKPT))
/* ARGSUSED */
static int
{
int count;
int bailout;
static fn_t f = "dr_stop_user_threads";
extern void add_one_utstop();
extern void utstop_timedwait(clock_t);
extern void utstop_init(void);
#define DR_UTSTOP_RETRY 4
#define DR_UTSTOP_WAIT hz
if (dr_skip_user_threads)
return (DDI_SUCCESS);
utstop_init();
/* we need to try a few times to get past fork, etc. */
srh->sr_err_idx = 0;
/* 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) &&
DR_VSTOPPED(tp))) {
bailout = 1;
/*
* save the pid for later reporting
*/
srh->sr_err_idx =
"failed to stop thread: "
"process=%s, pid=%d",
PR_QR("%s: failed to stop thread: "
"process=%s, pid=%d, t_id=0x%p, "
"t_state=0x%x, t_proc_flag=0x%x, "
"t_schedflag=0x%x\n",
}
}
}
/* were all the threads stopped? */
if (!bailout)
break;
}
/* were we unable to stop all threads after a few tries? */
if (bailout) {
srh->sr_err_idx, 0);
return (ESRCH);
}
return (DDI_SUCCESS);
}
static void
dr_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
dr_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 */
}
void
{
/*
* Update the signature block.
* If cpus are not paused, this can be done now.
* See comments below.
*/
}
switch (srh->sr_suspend_state) {
case DR_SRSTATE_FULL:
/*
* Prevent false alarm in tod_validate() due to tod
* value change between suspend and resume
*/
dr_enable_intr(); /* enable intr & clock */
start_cpus();
/*
* Update the signature block.
* This must not be done while cpus are paused, since on
* Starcat the cpu signature update aquires an adaptive
* mutex in the iosram driver. Blocking with cpus paused
* can lead to deadlock.
*/
/*
* If we suspended hw watchdog at suspend,
* re-enable it now.
*/
}
/*
* This should only be called if drmach_suspend_last()
* was called and state transitioned to DR_SRSTATE_FULL
* to prevent resume attempts on device instances that
* were not previously suspended.
*/
/* FALLTHROUGH */
case DR_SRSTATE_DRIVER:
/*
* resume drivers
*/
srh->sr_err_idx = 0;
/* no parent dip to hold busy */
}
/*
* resume the lock manager
*/
lm_cprresume();
/* FALLTHROUGH */
case DR_SRSTATE_USER:
/*
* finally, resume user threads
*/
if (!dr_skip_user_threads) {
prom_printf("DR: resuming user threads...\n");
}
/* FALLTHROUGH */
case DR_SRSTATE_BEGIN:
default:
/*
* let those who care know that we've just resumed
*/
PR_QR("sending SIGTHAW...\n");
break;
}
/*
* update the signature block
*/
prom_printf("DR: resume COMPLETED\n");
}
int
{
int force;
int dev_errs_idx;
int rc = DDI_SUCCESS;
/*
* update the signature block
*/
prom_printf("\nDR: suspending user threads...\n");
return (rc);
}
if (!force) {
prom_printf("\nDR: checking devices...\n");
dev_errs_idx = 0;
/*
* Since the root node can never go away, it
* doesn't have to be held.
*/
if (dev_errs_idx) {
dev_errs_idx, 1);
return (DDI_FAILURE);
}
PR_QR("done\n");
} else {
prom_printf("\nDR: dr_suspend invoked with force flag\n");
}
#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
*/
prom_printf("DR: suspending drivers...\n");
srh->sr_err_idx = 0;
/* No parent to hold busy */
}
return (rc);
}
/*
* finally, grab all cpus
*/
/*
* if watchdog was activated, disable it
*/
if (watchdog_activated) {
} else {
}
/*
* Update the signature block.
* This must be done before cpus are paused, since on Starcat the
* cpu signature update aquires an adaptive mutex in the iosram driver.
* Blocking with cpus paused can lead to deadlock.
*/
dr_stop_intr();
return (rc);
}
int
{
int err;
static fn_t f = "dr_pt_test_suspend";
PR_QR("%s...\n", f);
PR_QR("%s: error on dr_resume()", f);
switch (psmerr) {
case ESBD_RESUME:
PR_QR("Couldn't resume devices: %s\n",
break;
case ESBD_KTHREAD:
PR_ALL("psmerr is ESBD_KTHREAD\n");
break;
default:
PR_ALL("Resume error unknown = %d\n",
psmerr);
break;
}
}
} else {
PR_ALL("%s: dr_suspend() failed, err = 0x%x\n",
f, err);
switch (psmerr) {
case ESBD_UNSAFE:
PR_ALL("Unsafe devices (major #): %s\n",
break;
case ESBD_RTTHREAD:
PR_ALL("RT threads (PIDs): %s\n",
break;
case ESBD_UTHREAD:
PR_ALL("User threads (PIDs): %s\n",
break;
case ESBD_SUSPEND:
PR_ALL("Non-suspendable devices (major #): %s\n",
break;
case ESBD_RESUME:
PR_ALL("Could not resume devices (major #): %s\n",
break;
case ESBD_KTHREAD:
PR_ALL("psmerr is ESBD_KTHREAD\n");
break;
case ESBD_NOERROR:
PR_ALL("sbd_error_t error code not set\n");
break;
default:
break;
}
}
return (0);
}
/*
* Add a new integer value to the end of an array. Don't allow duplicates to
* appear in the array, and don't allow the array to overflow. Return the new
* total number of entries in the array.
*/
static int
{
int i;
return (0);
return (idx);
for (i = 0; i < idx; i++) {
return (idx);
}
return (idx);
}
/*
* Construct an sbd_error_t featuring a string representation of an array of
* integers as its e_rsc.
*/
static sbd_error_t *
{
char *dname;
char *buf;
static char s_ellipsis[] = "...";
return (NULL);
/* MAXPATHLEN is the size of the e_rsc field in sbd_error_t. */
/*
* This is the total working area of the buffer. It must be computed
* as the size of 'buf', minus reserved space for the null terminator
* and the ellipsis string.
*/
/* Construct a string representation of the array values */
if (majors) {
if (dname) {
"%s, ", dname);
} else {
"major %lu, ", arr[i]);
}
} else {
}
/* An ellipsis gets appended when no more values fit */
if (n >= buf_avail) {
break;
}
buf_idx += n;
}
/* If all the contents fit, remove the trailing comma */
if (n < buf_avail) {
}
/* Return an sbd_error_t with the buffer and e_code */
return (new_sbd_err);
}