diff options
Diffstat (limited to 'usr/src/uts/common/cpr/cpr_main.c')
-rw-r--r-- | usr/src/uts/common/cpr/cpr_main.c | 692 |
1 files changed, 599 insertions, 93 deletions
diff --git a/usr/src/uts/common/cpr/cpr_main.c b/usr/src/uts/common/cpr/cpr_main.c index 6669469681..65e911cb11 100644 --- a/usr/src/uts/common/cpr/cpr_main.c +++ b/usr/src/uts/common/cpr/cpr_main.c @@ -25,7 +25,6 @@ #pragma ident "%Z%%M% %I% %E% SMI" - /* * This module contains the guts of checkpoint-resume mechanism. * All code in this module is platform independent. @@ -51,6 +50,10 @@ #include <sys/reboot.h> #include <sys/kdi.h> #include <sys/promif.h> +#include <sys/srn.h> +#include <sys/cpr_impl.h> + +#define PPM(dip) ((dev_info_t *)DEVI(dip)->devi_pm_ppm) extern struct cpr_terminator cpr_term; @@ -63,18 +66,47 @@ extern void cpr_set_bitmap_size(void); extern void cpr_stat_init(); extern void cpr_statef_close(void); extern void flush_windows(void); +extern void (*srn_signal)(int, int); +extern void init_cpu_syscall(struct cpu *); +extern void i_cpr_pre_resume_cpus(); +extern void i_cpr_post_resume_cpus(); extern int pm_powering_down; - -static int cpr_suspend(void); -static int cpr_resume(void); -static void cpr_suspend_init(void); +extern kmutex_t srn_clone_lock; +extern int srn_inuse; + +static int cpr_suspend(int); +static int cpr_resume(int); +static void cpr_suspend_init(int); +#if defined(__x86) +static int cpr_suspend_cpus(void); +static void cpr_resume_cpus(void); +#endif +static int cpr_all_online(void); +static void cpr_restore_offline(void); cpr_time_t wholecycle_tv; int cpr_suspend_succeeded; pfn_t curthreadpfn; int curthreadremapped; +extern cpuset_t cpu_ready_set; +extern void *(*cpu_pause_func)(void *); + +extern processorid_t i_cpr_bootcpuid(void); +extern cpu_t *i_cpr_bootcpu(void); +extern void tsc_adjust_delta(hrtime_t tdelta); +extern void tsc_resume(void); +extern int tsc_resume_in_cyclic; + +/* + * Set this variable to 1, to have device drivers resume in an + * uniprocessor environment. This is to allow drivers that assume + * that they resume on a UP machine to continue to work. Should be + * deprecated once the broken drivers are fixed + */ +int cpr_resume_uniproc = 0; + /* * save or restore abort_enable; this prevents a drop * to kadb or prom during cpr_resume_devices() when @@ -101,23 +133,73 @@ cpr_sae(int stash) * returned back to here and it then calls the resume routine. */ int -cpr_main(void) +cpr_main(int sleeptype) { - label_t saveq = ttolwp(curthread)->lwp_qsav; - int rc; + int rc, rc2; + label_t saveq; + klwp_t *tlwp = ttolwp(curthread); - if (rc = cpr_default_setup(1)) - return (rc); + if (sleeptype == CPR_TODISK) { + if ((rc = cpr_default_setup(1)) != 0) + return (rc); + ASSERT(tlwp); + saveq = tlwp->lwp_qsav; + } + + if (sleeptype == CPR_TORAM) { + rc = cpr_suspend(sleeptype); + PMD(PMD_SX, ("cpr_suspend rets %x\n", rc)) + if (rc == 0) { + int i_cpr_power_down(int sleeptype); + + /* + * From this point on, we should be at a high + * spl, interrupts disabled, and all but one + * cpu's paused (effectively UP/single threaded). + * So this is were we want to put ASSERTS() + * to let us know otherwise. + */ + ASSERT(cpus_paused()); + /* + * Now do the work of actually putting this + * machine to sleep! + */ + rc = i_cpr_power_down(sleeptype); + if (rc == 0) { + PMD(PMD_SX, ("back from succssful suspend\n")) + } + /* + * We do care about the return value from cpr_resume + * at this point, as it will tell us if one of the + * resume functions failed (cpr_resume_devices()) + * However, for this to return and _not_ panic, means + * that we must be in one of the test functions. So + * check for that and return an appropriate message. + */ + rc2 = cpr_resume(sleeptype); + if (rc2 != 0) { + ASSERT(cpr_test_point > 0); + cmn_err(CE_NOTE, + "cpr_resume returned non-zero: %d\n", rc2); + PMD(PMD_SX, ("cpr_resume rets %x\n", rc2)) + } + ASSERT(!cpus_paused()); + } else { + PMD(PMD_SX, ("failed suspend, resuming\n")) + rc = cpr_resume(sleeptype); + } + return (rc); + } /* - * Remember where we are for resume + * Remember where we are for resume after reboot */ - if (!setjmp(&ttolwp(curthread)->lwp_qsav)) { + if (!setjmp(&tlwp->lwp_qsav)) { /* * try to checkpoint the system, if failed return back * to userland, otherwise power off. */ - rc = cpr_suspend(); + rc = cpr_suspend(sleeptype); if (rc || cpr_reusable_mode) { /* * We don't really want to go down, or @@ -125,22 +207,28 @@ cpr_main(void) * to put the system back to an operable state then * return back to userland. */ - (void) cpr_resume(); + PMD(PMD_SX, ("failed suspend, resuming\n")) + (void) cpr_resume(sleeptype); + PMD(PMD_SX, ("back from failed suspend resume\n")) } } else { /* * This is the resumed side of longjmp, restore the previous * longjmp pointer if there is one so this will be transparent * to the world. + * This path is only for CPR_TODISK, where we reboot */ - ttolwp(curthread)->lwp_qsav = saveq; + ASSERT(sleeptype == CPR_TODISK); + tlwp->lwp_qsav = saveq; CPR->c_flags &= ~C_SUSPENDING; CPR->c_flags |= C_RESUMING; /* * resume the system back to the original state */ - rc = cpr_resume(); + rc = cpr_resume(sleeptype); + PMD(PMD_SX, ("back from successful suspend; resume rets %x\n", + rc)) } (void) cpr_default_setup(0); @@ -149,6 +237,8 @@ cpr_main(void) } +#if defined(__sparc) + /* * check/disable or re-enable UFS logging */ @@ -180,8 +270,7 @@ cpr_log_status(int enable, int *svstat, vnode_t *vp) *svstat = status; if (cpr_debug & CPR_DEBUG5) { mntpt = vfs_getmntpoint(vp->v_vfsp); - CPR_DEBUG(CPR_DEBUG5, - "%s: \"%s\", logging status = %d\n", + errp("%s: \"%s\", logging status = %d\n", str, refstr_value(mntpt), status); refstr_rele(mntpt); }; @@ -207,11 +296,10 @@ cpr_log_status(int enable, int *svstat, vnode_t *vp) } else { if (cpr_debug & CPR_DEBUG5) { mntpt = vfs_getmntpoint(vp->v_vfsp); - CPR_DEBUG(CPR_DEBUG5, - "%s: \"%s\", logging is now %sd\n", + errp("%s: \"%s\", logging is now %sd\n", str, refstr_value(mntpt), able); refstr_rele(mntpt); - } + }; } } @@ -223,7 +311,6 @@ cpr_log_status(int enable, int *svstat, vnode_t *vp) *svstat = -1; } - /* * enable/disable UFS logging on filesystems containing cpr_default_path * and cpr statefile. since the statefile can be on any fs, that fs @@ -234,6 +321,7 @@ cpr_log_status(int enable, int *svstat, vnode_t *vp) * file outside of rootfs would cause errors during cprboot, plus cpr and * fsck problems with the new fs if logging were enabled. */ + static int cpr_ufs_logging(int enable) { @@ -274,6 +362,7 @@ cpr_ufs_logging(int enable) return (0); } +#endif /* @@ -288,6 +377,54 @@ cpr_lock_mgr(void (*service)(void)) (*service)(); } +int +cpr_suspend_cpus(void) +{ + cpu_t *bootcpu; + int ret = 0; + extern void *i_cpr_save_context(void *arg); + + mutex_enter(&cpu_lock); + + /* + * if bootcpu is offline bring it back online + */ + bootcpu = i_cpr_bootcpu(); + + /* + * the machine could not have booted without a bootcpu + */ + ASSERT(bootcpu != NULL); + + /* + * bring all the offline cpus online + */ + if ((ret = cpr_all_online())) { + mutex_exit(&cpu_lock); + return (ret); + } + + /* + * Set the affinity to be the boot processor + * This is cleared in either cpr_resume_cpus() or cpr_unpause_cpus() + */ + affinity_set(i_cpr_bootcpuid()); + + ASSERT(CPU->cpu_id == 0); + + PMD(PMD_SX, ("curthread running on bootcpu\n")) + + /* + * pause all other running CPUs and save the CPU state at the sametime + */ + cpu_pause_func = i_cpr_save_context; + pause_cpus(NULL); + + mutex_exit(&cpu_lock); + + return (0); +} + /* * Take the system down to a checkpointable state and write * the state file, the following are sequentially executed: @@ -301,41 +438,69 @@ cpr_lock_mgr(void (*service)(void)) * - suspend all devices * - block intrpts * - dump system state and memory to state file + * - SPARC code will not be called with CPR_TORAM, caller filters */ static int -cpr_suspend(void) +cpr_suspend(int sleeptype) { - int sf_realloc, rc, skt_rc, nverr; +#if defined(__sparc) + int sf_realloc, nverr; +#endif + int rc = 0; + int skt_rc = 0; + PMD(PMD_SX, ("cpr_suspend %x\n", sleeptype)) cpr_set_substate(C_ST_SUSPEND_BEGIN); - cpr_suspend_init(); + cpr_suspend_init(sleeptype); cpr_save_time(); cpr_tod_get(&wholecycle_tv); CPR_STAT_EVENT_START("Suspend Total"); + i_cpr_alloc_cpus(); + +#if defined(__sparc) + ASSERT(sleeptype == CPR_TODISK); if (!cpr_reusable_mode) { /* - * We need to validate default file before fs functionality - * is disabled. + * We need to validate default file before fs + * functionality is disabled. */ if (rc = cpr_validate_definfo(0)) return (rc); } - i_cpr_save_machdep_info(); +#endif + PMD(PMD_SX, ("cpr_suspend: stop scans\n")) /* Stop PM scans ASAP */ (void) callb_execute_class(CB_CL_CPR_PM, CB_CODE_CPR_CHKPT); pm_dispatch_to_dep_thread(PM_DEP_WK_CPR_SUSPEND, NULL, NULL, PM_DEP_WAIT, NULL, 0); +#if defined(__sparc) + ASSERT(sleeptype == CPR_TODISK); cpr_set_substate(C_ST_MP_OFFLINE); if (rc = cpr_mp_offline()) return (rc); +#endif + /* + * Ask Xorg to suspend the frame buffer, and wait for it to happen + */ + mutex_enter(&srn_clone_lock); + if (srn_signal) { + PMD(PMD_SX, ("cpr_suspend: (*srn_signal)(..., " + "SRN_SUSPEND_REQ)\n")) + srn_inuse = 1; /* because *(srn_signal) cv_waits */ + (*srn_signal)(SRN_TYPE_APM, SRN_SUSPEND_REQ); + srn_inuse = 0; + } else { + PMD(PMD_SX, ("cpr_suspend: srn_signal NULL\n")) + } + mutex_exit(&srn_clone_lock); /* * Ask the user threads to stop by themselves, but @@ -346,11 +511,13 @@ cpr_suspend(void) CPR_DEBUG(CPR_DEBUG1, "\nstopping user threads..."); CPR_STAT_EVENT_START(" stop users"); cpr_set_substate(C_ST_STOP_USER_THREADS); + PMD(PMD_SX, ("cpr_suspend: stop user threads\n")) if (rc = cpr_stop_user_threads()) return (rc); CPR_STAT_EVENT_END(" stop users"); CPR_DEBUG(CPR_DEBUG1, "done\n"); + PMD(PMD_SX, ("cpr_suspend: save direct levels\n")) pm_save_direct_levels(); /* @@ -360,10 +527,14 @@ cpr_suspend(void) */ (void) callb_execute_class(CB_CL_CPR_PROMPRINTF, CB_CODE_CPR_CHKPT); + PMD(PMD_SX, ("cpr_suspend: send notice\n")) +#ifndef DEBUG cpr_send_notice(); if (cpr_debug) prom_printf("\n"); +#endif + PMD(PMD_SX, ("cpr_suspend: POST USER callback\n")) (void) callb_execute_class(CB_CL_CPR_POST_USER, CB_CODE_CPR_CHKPT); /* @@ -373,9 +544,12 @@ cpr_suspend(void) * a kernel thread. */ cpr_set_substate(C_ST_PM_REATTACH_NOINVOL); + PMD(PMD_SX, ("cpr_suspend: reattach noinvol\n")) if (!pm_reattach_noinvol()) return (ENXIO); +#if defined(__sparc) + ASSERT(sleeptype == CPR_TODISK); /* * if ufs logging is enabled, we need to disable before * stopping kernel threads so that ufs delete and roll @@ -398,8 +572,8 @@ cpr_suspend(void) alloc_statefile: /* - * If our last state was C_ST_DUMP_NOSPC, we're trying to realloc - * the statefile, otherwise this is the first attempt. + * If our last state was C_ST_DUMP_NOSPC, we're trying to + * realloc the statefile, otherwise this is the first attempt. */ sf_realloc = (CPR->c_substate == C_ST_DUMP_NOSPC) ? 1 : 0; @@ -407,7 +581,7 @@ alloc_statefile: cpr_set_substate(C_ST_STATEF_ALLOC); if (rc = cpr_alloc_statefile(sf_realloc)) { if (sf_realloc) - prom_printf("realloc failed\n"); + errp("realloc failed\n"); return (rc); } CPR_STAT_EVENT_END(" alloc statefile"); @@ -415,9 +589,10 @@ alloc_statefile: /* * Sync the filesystem to preserve its integrity. * - * This sync is also used to flush out all B_DELWRI buffers (fs cache) - * which are mapped and neither dirty nor referenced before - * cpr_invalidate_pages destroys them. fsflush does similar thing. + * This sync is also used to flush out all B_DELWRI buffers + * (fs cache) which are mapped and neither dirty nor referenced + * before cpr_invalidate_pages destroys them. + * fsflush does similar thing. */ sync(); @@ -425,16 +600,18 @@ alloc_statefile: * destroy all clean file mapped kernel pages */ CPR_STAT_EVENT_START(" clean pages"); - CPR_DEBUG(CPR_DEBUG1, "cleaning up mapped pages..."); + CPR_DEBUG(CPR_DEBUG1, ("cleaning up mapped pages...")); (void) callb_execute_class(CB_CL_CPR_VM, CB_CODE_CPR_CHKPT); - CPR_DEBUG(CPR_DEBUG1, "done\n"); + CPR_DEBUG(CPR_DEBUG1, ("done\n")); CPR_STAT_EVENT_END(" clean pages"); +#endif /* * Hooks needed by lock manager prior to suspending. * Refer to code for more comments. */ + PMD(PMD_SX, ("cpr_suspend: lock mgr\n")) cpr_lock_mgr(lm_cprsuspend); /* @@ -444,6 +621,7 @@ alloc_statefile: CPR_DEBUG(CPR_DEBUG1, "suspending drivers..."); cpr_set_substate(C_ST_SUSPEND_DEVICES); pm_powering_down = 1; + PMD(PMD_SX, ("cpr_suspend: suspending devices\n")) rc = cpr_suspend_devices(ddi_root_node()); pm_powering_down = 0; if (rc) @@ -455,36 +633,61 @@ alloc_statefile: * Stop all daemon activities */ cpr_set_substate(C_ST_STOP_KERNEL_THREADS); + PMD(PMD_SX, ("cpr_suspend: stopping kernel threads\n")) if (skt_rc = cpr_stop_kernel_threads()) return (skt_rc); + PMD(PMD_SX, ("cpr_suspend: POST KERNEL callback\n")) (void) callb_execute_class(CB_CL_CPR_POST_KERNEL, CB_CODE_CPR_CHKPT); + PMD(PMD_SX, ("cpr_suspend: reattach noinvol fini\n")) pm_reattach_noinvol_fini(); cpr_sae(1); + PMD(PMD_SX, ("cpr_suspend: CPR CALLOUT callback\n")) (void) callb_execute_class(CB_CL_CPR_CALLOUT, CB_CODE_CPR_CHKPT); - /* - * It's safer to do tod_get before we disable all intr. - */ - CPR_STAT_EVENT_START(" write statefile"); + if (sleeptype == CPR_TODISK) { + /* + * It's safer to do tod_get before we disable all intr. + */ + CPR_STAT_EVENT_START(" write statefile"); + } /* * it's time to ignore the outside world, stop the real time * clock and disable any further intrpt activity. */ + PMD(PMD_SX, ("cpr_suspend: handle xc\n")) i_cpr_handle_xc(1); /* turn it on to disable xc assertion */ mutex_enter(&cpu_lock); + PMD(PMD_SX, ("cpr_suspend: cyclic suspend\n")) cyclic_suspend(); mutex_exit(&cpu_lock); - mon_clock_stop(); - mon_clock_unshare(); - mon_clock_start(); + /* + * Due to the different methods of resuming the system between + * CPR_TODISK (boot cprboot on SPARC, which reloads kernel image) + * and CPR_TORAM (restart via reset into existing kernel image) + * cpus are not suspended and restored in the SPARC case, since it + * is necessary to restart the cpus and pause them before restoring + * the OBP image + */ + +#if defined(__x86) + + /* pause aux cpus */ + PMD(PMD_SX, ("pause aux cpus\n")) + + cpr_set_substate(C_ST_MP_PAUSED); + if ((rc = cpr_suspend_cpus()) != 0) + return (rc); +#endif + + PMD(PMD_SX, ("cpr_suspend: stop intr\n")) i_cpr_stop_intr(); CPR_DEBUG(CPR_DEBUG1, "interrupt is stopped\n"); @@ -494,16 +697,28 @@ alloc_statefile: * it must be up now. */ ASSERT(pm_cfb_is_up()); + PMD(PMD_SX, ("cpr_suspend: prom suspend prepost\n")) prom_suspend_prepost(); +#if defined(__sparc) /* * getting ready to write ourself out, flush the register * windows to make sure that our stack is good when we * come back on the resume side. */ flush_windows(); +#endif /* + * For S3, we're done + */ + if (sleeptype == CPR_TORAM) { + PMD(PMD_SX, ("cpr_suspend rets %x\n", rc)) + cpr_set_substate(C_ST_NODUMP); + return (rc); + } +#if defined(__sparc) + /* * FATAL: NO MORE MEMORY ALLOCATION ALLOWED AFTER THIS POINT!!! * * The system is quiesced at this point, we are ready to either dump @@ -535,7 +750,7 @@ alloc_statefile: if (rc == ENOSPC) { cpr_set_substate(C_ST_DUMP_NOSPC); - (void) cpr_resume(); + (void) cpr_resume(sleeptype); goto alloc_statefile; } else if (rc == 0) { if (cpr_reusable_mode) { @@ -544,9 +759,97 @@ alloc_statefile: } else rc = cpr_set_properties(1); } +#endif + PMD(PMD_SX, ("cpr_suspend: return %d\n", rc)) return (rc); } +void +cpr_resume_cpus(void) +{ + /* + * this is a cut down version of start_other_cpus() + * just do the initialization to wake the other cpus + */ + +#if defined(__x86) + /* + * Initialize our syscall handlers + */ + init_cpu_syscall(CPU); + +#endif + + i_cpr_pre_resume_cpus(); + + /* + * Restart the paused cpus + */ + mutex_enter(&cpu_lock); + start_cpus(); + mutex_exit(&cpu_lock); + + /* + * clear the affinity set in cpr_suspend_cpus() + */ + affinity_clear(); + + i_cpr_post_resume_cpus(); + + mutex_enter(&cpu_lock); + /* + * Restore this cpu to use the regular cpu_pause(), so that + * online and offline will work correctly + */ + cpu_pause_func = NULL; + + /* + * offline all the cpus that were brought online during suspend + */ + cpr_restore_offline(); + + /* + * clear the affinity set in cpr_suspend_cpus() + */ + affinity_clear(); + + mutex_exit(&cpu_lock); +} + +void +cpr_unpause_cpus(void) +{ + /* + * Now restore the system back to what it was before we suspended + */ + + PMD(PMD_SX, ("cpr_unpause_cpus: restoring system\n")) + + mutex_enter(&cpu_lock); + + /* + * Restore this cpu to use the regular cpu_pause(), so that + * online and offline will work correctly + */ + cpu_pause_func = NULL; + + /* + * Restart the paused cpus + */ + start_cpus(); + + /* + * offline all the cpus that were brought online during suspend + */ + cpr_restore_offline(); + + /* + * clear the affinity set in cpr_suspend_cpus() + */ + affinity_clear(); + + mutex_exit(&cpu_lock); +} /* * Bring the system back up from a checkpoint, at this point @@ -559,7 +862,7 @@ alloc_statefile: * - put all threads back on run queue */ static int -cpr_resume(void) +cpr_resume(int sleeptype) { cpr_time_t pwron_tv, *ctp; char *str; @@ -570,6 +873,7 @@ cpr_resume(void) * that was suspended to a different level. */ CPR_DEBUG(CPR_DEBUG1, "\nEntering cpr_resume...\n"); + PMD(PMD_SX, ("cpr_resume %x\n", sleeptype)) /* * Note: @@ -584,12 +888,14 @@ cpr_resume(void) * and the one that caused the failure, if necessary." */ switch (CPR->c_substate) { +#if defined(__sparc) case C_ST_DUMP: /* * This is most likely a full-fledged cpr_resume after * a complete and successful cpr suspend. Just roll back * everything. */ + ASSERT(sleeptype == CPR_TODISK); break; case C_ST_REUSABLE: @@ -605,46 +911,60 @@ cpr_resume(void) * is possible that a need for roll back of a state * change arises between these exit points. */ + ASSERT(sleeptype == CPR_TODISK); goto rb_dump; +#endif + + case C_ST_NODUMP: + PMD(PMD_SX, ("cpr_resume: NODUMP\n")) + goto rb_nodump; case C_ST_STOP_KERNEL_THREADS: + PMD(PMD_SX, ("cpr_resume: STOP_KERNEL_THREADS\n")) goto rb_stop_kernel_threads; case C_ST_SUSPEND_DEVICES: + PMD(PMD_SX, ("cpr_resume: SUSPEND_DEVICES\n")) goto rb_suspend_devices; +#if defined(__sparc) case C_ST_STATEF_ALLOC: + ASSERT(sleeptype == CPR_TODISK); goto rb_statef_alloc; case C_ST_DISABLE_UFS_LOGGING: + ASSERT(sleeptype == CPR_TODISK); goto rb_disable_ufs_logging; +#endif case C_ST_PM_REATTACH_NOINVOL: + PMD(PMD_SX, ("cpr_resume: REATTACH_NOINVOL\n")) goto rb_pm_reattach_noinvol; case C_ST_STOP_USER_THREADS: + PMD(PMD_SX, ("cpr_resume: STOP_USER_THREADS\n")) goto rb_stop_user_threads; +#if defined(__sparc) case C_ST_MP_OFFLINE: + PMD(PMD_SX, ("cpr_resume: MP_OFFLINE\n")) goto rb_mp_offline; +#endif + +#if defined(__x86) + case C_ST_MP_PAUSED: + PMD(PMD_SX, ("cpr_resume: MP_PAUSED\n")) + goto rb_mp_paused; +#endif + default: + PMD(PMD_SX, ("cpr_resume: others\n")) goto rb_others; } rb_all: /* - * setup debugger trapping. - */ - if (cpr_suspend_succeeded) - i_cpr_set_tbr(); - - /* - * tell prom to monitor keys before the kernel comes alive - */ - mon_clock_start(); - - /* * perform platform-dependent initialization */ if (cpr_suspend_succeeded) @@ -659,33 +979,65 @@ rb_dump: * * DO NOT ADD ANY INITIALIZATION STEP BEFORE THIS POINT!! */ +rb_nodump: + /* + * If we did suspend to RAM, we didn't generate a dump + */ + PMD(PMD_SX, ("cpr_resume: CPR DMA callback\n")) (void) callb_execute_class(CB_CL_CPR_DMA, CB_CODE_CPR_RESUME); - if (cpr_suspend_succeeded) + if (cpr_suspend_succeeded) { + PMD(PMD_SX, ("cpr_resume: CPR RPC callback\n")) (void) callb_execute_class(CB_CL_CPR_RPC, CB_CODE_CPR_RESUME); + } prom_resume_prepost(); +#if !defined(__sparc) + /* + * Need to sync the software clock with the hardware clock. + * On Sparc, this occurs in the sparc-specific cbe. However + * on x86 this needs to be handled _before_ we bring other cpu's + * back online. So we call a resume function in timestamp.c + */ + if (tsc_resume_in_cyclic == 0) + tsc_resume(); +#endif + +#if defined(__sparc) if (cpr_suspend_succeeded && (boothowto & RB_DEBUG)) kdi_dvec_cpr_restart(); +#endif + + +#if defined(__x86) +rb_mp_paused: + PT(PT_RMPO); + PMD(PMD_SX, ("resume aux cpus\n")) + + if (cpr_suspend_succeeded) { + cpr_resume_cpus(); + } else { + cpr_unpause_cpus(); + } +#endif /* * let the tmp callout catch up. */ + PMD(PMD_SX, ("cpr_resume: CPR CALLOUT callback\n")) (void) callb_execute_class(CB_CL_CPR_CALLOUT, CB_CODE_CPR_RESUME); i_cpr_enable_intr(); - mon_clock_stop(); - mon_clock_share(); - mutex_enter(&cpu_lock); + PMD(PMD_SX, ("cpr_resume: cyclic resume\n")) cyclic_resume(); mutex_exit(&cpu_lock); - mon_clock_start(); - + PMD(PMD_SX, ("cpr_resume: handle xc\n")) i_cpr_handle_xc(0); /* turn it off to allow xc assertion */ + PMD(PMD_SX, ("cpr_resume: CPR POST KERNEL callback\n")) (void) callb_execute_class(CB_CL_CPR_POST_KERNEL, CB_CODE_CPR_RESUME); /* @@ -701,7 +1053,8 @@ rb_dump: cpr_convert_promtime(&pwron_tv); ctp = &cpr_term.tm_shutdown; - CPR_STAT_EVENT_END_TMZ(" write statefile", ctp); + if (sleeptype == CPR_TODISK) + CPR_STAT_EVENT_END_TMZ(" write statefile", ctp); CPR_STAT_EVENT_END_TMZ("Suspend Total", ctp); CPR_STAT_EVENT_START_TMZ("Resume Total", &pwron_tv); @@ -726,62 +1079,116 @@ rb_stop_kernel_threads: * disabled before starting kernel threads, we don't want * modunload thread to start changing device tree underneath. */ + PMD(PMD_SX, ("cpr_resume: modunload disable\n")) modunload_disable(); + PMD(PMD_SX, ("cpr_resume: start kernel threads\n")) cpr_start_kernel_threads(); rb_suspend_devices: CPR_DEBUG(CPR_DEBUG1, "resuming devices..."); CPR_STAT_EVENT_START(" start drivers"); + PMD(PMD_SX, + ("cpr_resume: rb_suspend_devices: cpr_resume_uniproc = %d\n", + cpr_resume_uniproc)) + +#if defined(__x86) + /* + * If cpr_resume_uniproc is set, then pause all the other cpus + * apart from the current cpu, so that broken drivers that think + * that they are on a uniprocessor machine will resume + */ + if (cpr_resume_uniproc) { + mutex_enter(&cpu_lock); + pause_cpus(NULL); + mutex_exit(&cpu_lock); + } +#endif + /* * The policy here is to continue resume everything we can if we did * not successfully finish suspend; and panic if we are coming back * from a fully suspended system. */ + PMD(PMD_SX, ("cpr_resume: resume devices\n")) rc = cpr_resume_devices(ddi_root_node(), 0); cpr_sae(0); str = "Failed to resume one or more devices."; - if (rc && CPR->c_substate == C_ST_DUMP) - cpr_err(CE_PANIC, str); - else if (rc) - cpr_err(CE_WARN, str); + + if (rc) { + if (CPR->c_substate == C_ST_DUMP || + (sleeptype == CPR_TORAM && + CPR->c_substate == C_ST_NODUMP)) { + if (cpr_test_point == FORCE_SUSPEND_TO_RAM) { + PMD(PMD_SX, ("cpr_resume: resume device " + "warn\n")) + cpr_err(CE_WARN, str); + } else { + PMD(PMD_SX, ("cpr_resume: resume device " + "panic\n")) + cpr_err(CE_PANIC, str); + } + } else { + PMD(PMD_SX, ("cpr_resume: resume device warn\n")) + cpr_err(CE_WARN, str); + } + } + CPR_STAT_EVENT_END(" start drivers"); CPR_DEBUG(CPR_DEBUG1, "done\n"); +#if defined(__x86) + /* + * If cpr_resume_uniproc is set, then unpause all the processors + * that were paused before resuming the drivers + */ + if (cpr_resume_uniproc) { + mutex_enter(&cpu_lock); + start_cpus(); + mutex_exit(&cpu_lock); + } +#endif + /* * If we had disabled modunloading in this cpr resume cycle (i.e. we * resumed from a state earlier than C_ST_SUSPEND_DEVICES), re-enable * modunloading now. */ - if (CPR->c_substate != C_ST_SUSPEND_DEVICES) + if (CPR->c_substate != C_ST_SUSPEND_DEVICES) { + PMD(PMD_SX, ("cpr_resume: modload enable\n")) modunload_enable(); + } /* * Hooks needed by lock manager prior to resuming. * Refer to code for more comments. */ + PMD(PMD_SX, ("cpr_resume: lock mgr\n")) cpr_lock_mgr(lm_cprresume); +#if defined(__sparc) /* * This is a partial (half) resume during cpr suspend, we * haven't yet given up on the suspend. On return from here, * cpr_suspend() will try to reallocate and retry the suspend. */ if (CPR->c_substate == C_ST_DUMP_NOSPC) { - mon_clock_stop(); return (0); } + if (sleeptype == CPR_TODISK) { rb_statef_alloc: - cpr_statef_close(); + cpr_statef_close(); rb_disable_ufs_logging: - /* - * if ufs logging was disabled, re-enable - */ - (void) cpr_ufs_logging(1); + /* + * if ufs logging was disabled, re-enable + */ + (void) cpr_ufs_logging(1); + } +#endif rb_pm_reattach_noinvol: /* @@ -795,44 +1202,64 @@ rb_pm_reattach_noinvol: CPR->c_substate == C_ST_STATEF_ALLOC || CPR->c_substate == C_ST_SUSPEND_DEVICES || CPR->c_substate == C_ST_STOP_KERNEL_THREADS) { + PMD(PMD_SX, ("cpr_resume: reattach noinvol fini\n")) pm_reattach_noinvol_fini(); } + PMD(PMD_SX, ("cpr_resume: CPR POST USER callback\n")) (void) callb_execute_class(CB_CL_CPR_POST_USER, CB_CODE_CPR_RESUME); + PMD(PMD_SX, ("cpr_resume: CPR PROMPRINTF callback\n")) (void) callb_execute_class(CB_CL_CPR_PROMPRINTF, CB_CODE_CPR_RESUME); + PMD(PMD_SX, ("cpr_resume: restore direct levels\n")) pm_restore_direct_levels(); rb_stop_user_threads: CPR_DEBUG(CPR_DEBUG1, "starting user threads..."); + PMD(PMD_SX, ("cpr_resume: starting user threads\n")) cpr_start_user_threads(); CPR_DEBUG(CPR_DEBUG1, "done\n"); + /* + * Ask Xorg to resume the frame buffer, and wait for it to happen + */ + mutex_enter(&srn_clone_lock); + if (srn_signal) { + PMD(PMD_SX, ("cpr_suspend: (*srn_signal)(..., " + "SRN_NORMAL_RESUME)\n")) + srn_inuse = 1; /* because (*srn_signal) cv_waits */ + (*srn_signal)(SRN_TYPE_APM, SRN_NORMAL_RESUME); + srn_inuse = 0; + } else { + PMD(PMD_SX, ("cpr_suspend: srn_signal NULL\n")) + } + mutex_exit(&srn_clone_lock); +#if defined(__sparc) rb_mp_offline: if (cpr_mp_online()) cpr_err(CE_WARN, "Failed to online all the processors."); +#endif rb_others: - pm_dispatch_to_dep_thread(PM_DEP_WK_CPR_RESUME, NULL, NULL, PM_DEP_WAIT, - NULL, 0); + PMD(PMD_SX, ("cpr_resume: dep thread\n")) + pm_dispatch_to_dep_thread(PM_DEP_WK_CPR_RESUME, NULL, NULL, + PM_DEP_WAIT, NULL, 0); + PMD(PMD_SX, ("cpr_resume: CPR PM callback\n")) (void) callb_execute_class(CB_CL_CPR_PM, CB_CODE_CPR_RESUME); - /* - * now that all the drivers are going, kernel kbd driver can - * take over, turn off prom monitor clock - */ - mon_clock_stop(); - if (cpr_suspend_succeeded) { - cpr_restore_time(); cpr_stat_record_events(); } - if (!cpr_reusable_mode) +#if defined(__sparc) + if (sleeptype == CPR_TODISK && !cpr_reusable_mode) cpr_clear_definfo(); +#endif + i_cpr_free_cpus(); CPR_DEBUG(CPR_DEBUG1, "Sending SIGTHAW..."); + PMD(PMD_SX, ("cpr_resume: SIGTHAW\n")) cpr_signal_user(SIGTHAW); CPR_DEBUG(CPR_DEBUG1, "done\n"); @@ -854,11 +1281,12 @@ rb_others: CPR_STAT_EVENT_PRINT(); #endif /* CPR_STAT */ + PMD(PMD_SX, ("cpr_resume returns %x\n", rc)) return (rc); } static void -cpr_suspend_init(void) +cpr_suspend_init(int sleeptype) { cpr_time_t *ctp; @@ -880,15 +1308,93 @@ cpr_suspend_init(void) ctp = &cpr_term.tm_cprboot_end; bzero(ctp, sizeof (*ctp)); + if (sleeptype == CPR_TODISK) { + /* + * Lookup the physical address of our thread structure. + * This should never be invalid and the entire thread structure + * is expected to reside within the same pfn. + */ + curthreadpfn = hat_getpfnum(kas.a_hat, (caddr_t)curthread); + ASSERT(curthreadpfn != PFN_INVALID); + ASSERT(curthreadpfn == hat_getpfnum(kas.a_hat, + (caddr_t)curthread + sizeof (kthread_t) - 1)); + } + + cpr_suspend_succeeded = 0; +} + +/* + * bring all the offline cpus online + */ +static int +cpr_all_online(void) +{ + int rc = 0; + +#ifdef __sparc /* - * Lookup the physical address of our thread structure. This should - * never be invalid and the entire thread structure is expected - * to reside within the same pfn. + * do nothing */ - curthreadpfn = hat_getpfnum(kas.a_hat, (caddr_t)curthread); - ASSERT(curthreadpfn != PFN_INVALID); - ASSERT(curthreadpfn == hat_getpfnum(kas.a_hat, - (caddr_t)curthread + sizeof (kthread_t) - 1)); +#else + + cpu_t *cp; + + ASSERT(MUTEX_HELD(&cpu_lock)); + + cp = cpu_list; + do { + cp->cpu_cpr_flags &= ~CPU_CPR_ONLINE; + if (!CPU_ACTIVE(cp)) { + if ((rc = cpu_online(cp)) != 0) + break; + CPU_SET_CPR_FLAGS(cp, CPU_CPR_ONLINE); + } + } while ((cp = cp->cpu_next) != cpu_list); + + if (rc) { + /* + * an online operation failed so offline the cpus + * that were onlined above to restore the system + * to its original state + */ + cpr_restore_offline(); + } +#endif + return (rc); +} + +/* + * offline all the cpus that were brought online by cpr_all_online() + */ +static void +cpr_restore_offline(void) +{ + +#ifdef __sparc + /* + * do nothing + */ +#else + + cpu_t *cp; + int rc = 0; + + ASSERT(MUTEX_HELD(&cpu_lock)); + + cp = cpu_list; + do { + if (CPU_CPR_IS_ONLINE(cp)) { + rc = cpu_offline(cp, 0); + /* + * this offline should work, since the cpu was + * offline originally and was successfully onlined + * by cpr_all_online() + */ + ASSERT(rc == 0); + cp->cpu_cpr_flags &= ~CPU_CPR_ONLINE; + } + } while ((cp = cp->cpu_next) != cpu_list); + +#endif - cpr_suspend_succeeded = 0; } |