xref: /illumos-gate/usr/src/uts/common/cpr/cpr_mod.c (revision c36fc336)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5ae115bc7Smrj  * Common Development and Distribution License (the "License").
6ae115bc7Smrj  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22ae115bc7Smrj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * System call to checkpoint and resume the currently running kernel
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate #include <sys/types.h>
327c478bd9Sstevel@tonic-gate #include <sys/errno.h>
337c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
347c478bd9Sstevel@tonic-gate #include <sys/syscall.h>
357c478bd9Sstevel@tonic-gate #include <sys/cred.h>
367c478bd9Sstevel@tonic-gate #include <sys/uadmin.h>
377c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
387c478bd9Sstevel@tonic-gate #include <sys/systm.h>
397c478bd9Sstevel@tonic-gate #include <sys/cpr.h>
407c478bd9Sstevel@tonic-gate #include <sys/swap.h>
417c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
427c478bd9Sstevel@tonic-gate #include <sys/autoconf.h>
437c478bd9Sstevel@tonic-gate #include <sys/machsystm.h>
447c478bd9Sstevel@tonic-gate 
452df1fe9cSrandyf extern int i_cpr_is_supported(int sleeptype);
467c478bd9Sstevel@tonic-gate extern int cpr_is_ufs(struct vfs *);
477c478bd9Sstevel@tonic-gate extern int cpr_check_spec_statefile(void);
487c478bd9Sstevel@tonic-gate extern int cpr_reusable_mount_check(void);
497c478bd9Sstevel@tonic-gate extern int i_cpr_reusable_supported(void);
507c478bd9Sstevel@tonic-gate extern int i_cpr_reusefini(void);
517c478bd9Sstevel@tonic-gate extern struct mod_ops mod_miscops;
527c478bd9Sstevel@tonic-gate 
532df1fe9cSrandyf extern int cpr_init(int);
542df1fe9cSrandyf extern void cpr_done(void);
552df1fe9cSrandyf extern void i_cpr_stop_other_cpus(void);
562df1fe9cSrandyf extern int i_cpr_power_down();
572df1fe9cSrandyf 
582df1fe9cSrandyf #if defined(__sparc)
592df1fe9cSrandyf extern void cpr_forget_cprconfig(void);
602df1fe9cSrandyf #endif
612df1fe9cSrandyf 
627c478bd9Sstevel@tonic-gate static struct modlmisc modlmisc = {
637c478bd9Sstevel@tonic-gate 	&mod_miscops, "checkpoint resume"
647c478bd9Sstevel@tonic-gate };
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
677c478bd9Sstevel@tonic-gate 	MODREV_1, (void *)&modlmisc, NULL
687c478bd9Sstevel@tonic-gate };
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate char _depends_on[] = "misc/bootdev";	/* i_devname_to_promname() */
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate int cpr_reusable_mode;
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate kmutex_t	cpr_slock;	/* cpr serial lock */
757c478bd9Sstevel@tonic-gate cpr_t		cpr_state;
767c478bd9Sstevel@tonic-gate int		cpr_debug;
777c478bd9Sstevel@tonic-gate int		cpr_test_mode; /* true if called via uadmin testmode */
782df1fe9cSrandyf int		cpr_test_point = LOOP_BACK_NONE;	/* cpr test point */
792df1fe9cSrandyf int		cpr_mp_enable = 0;	/* set to 1 to enable MP suspend */
802df1fe9cSrandyf major_t		cpr_device = 0;		/* major number for S3 on one device */
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate /*
837c478bd9Sstevel@tonic-gate  * All the loadable module related code follows
847c478bd9Sstevel@tonic-gate  */
857c478bd9Sstevel@tonic-gate int
867c478bd9Sstevel@tonic-gate _init(void)
877c478bd9Sstevel@tonic-gate {
887c478bd9Sstevel@tonic-gate 	register int e;
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate 	if ((e = mod_install(&modlinkage)) == 0) {
917c478bd9Sstevel@tonic-gate 		mutex_init(&cpr_slock, NULL, MUTEX_DEFAULT, NULL);
927c478bd9Sstevel@tonic-gate 	}
937c478bd9Sstevel@tonic-gate 	return (e);
947c478bd9Sstevel@tonic-gate }
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate int
977c478bd9Sstevel@tonic-gate _fini(void)
987c478bd9Sstevel@tonic-gate {
997c478bd9Sstevel@tonic-gate 	register int e;
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate 	if ((e = mod_remove(&modlinkage)) == 0) {
1027c478bd9Sstevel@tonic-gate 		mutex_destroy(&cpr_slock);
1037c478bd9Sstevel@tonic-gate 	}
1047c478bd9Sstevel@tonic-gate 	return (e);
1057c478bd9Sstevel@tonic-gate }
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate int
1087c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
1097c478bd9Sstevel@tonic-gate {
1107c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1117c478bd9Sstevel@tonic-gate }
1127c478bd9Sstevel@tonic-gate 
1132df1fe9cSrandyf static
1142df1fe9cSrandyf int
1152df1fe9cSrandyf atoi(char *p)
1162df1fe9cSrandyf {
1172df1fe9cSrandyf 	int	i;
1182df1fe9cSrandyf 
1192df1fe9cSrandyf 	i = (*p++ - '0');
1202df1fe9cSrandyf 
1212df1fe9cSrandyf 	while (*p != '\0')
1222df1fe9cSrandyf 		i = 10 * i + (*p++ - '0');
1232df1fe9cSrandyf 
1242df1fe9cSrandyf 	return (i);
1252df1fe9cSrandyf }
1262df1fe9cSrandyf 
1277c478bd9Sstevel@tonic-gate int
1282df1fe9cSrandyf cpr(int fcn, void *mdep)
1297c478bd9Sstevel@tonic-gate {
1302df1fe9cSrandyf 
1312df1fe9cSrandyf #if defined(__sparc)
1327c478bd9Sstevel@tonic-gate 	static const char noswapstr[] = "reusable statefile requires "
1337c478bd9Sstevel@tonic-gate 	    "that no swap area be configured.\n";
1347c478bd9Sstevel@tonic-gate 	static const char blockstr[] = "reusable statefile must be "
1357c478bd9Sstevel@tonic-gate 	    "a block device.  See power.conf(4) and pmconfig(1M).\n";
1367c478bd9Sstevel@tonic-gate 	static const char normalfmt[] = "cannot run normal "
1377c478bd9Sstevel@tonic-gate 	    "checkpoint/resume when in reusable statefile mode. "
1387c478bd9Sstevel@tonic-gate 	    "use uadmin A_FREEZE AD_REUSEFINI (uadmin %d %d) "
1397c478bd9Sstevel@tonic-gate 	    "to exit reusable statefile mode.\n";
1407c478bd9Sstevel@tonic-gate 	static const char modefmt[] = "%s in reusable mode.\n";
1412df1fe9cSrandyf #endif
1427c478bd9Sstevel@tonic-gate 	register int rc = 0;
1432df1fe9cSrandyf 	int cpr_sleeptype;
1447c478bd9Sstevel@tonic-gate 
1452df1fe9cSrandyf 	/*
1462df1fe9cSrandyf 	 * First, reject commands that we don't (yet) support on this arch.
1472df1fe9cSrandyf 	 * This is easier to understand broken out like this than grotting
1482df1fe9cSrandyf 	 * through the second switch below.
1492df1fe9cSrandyf 	 */
1502df1fe9cSrandyf 
1512df1fe9cSrandyf 	switch (fcn) {
1522df1fe9cSrandyf #if defined(__sparc)
1532df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_RAM:
1542df1fe9cSrandyf 	case AD_SUSPEND_TO_RAM:
1552df1fe9cSrandyf 		return (ENOTSUP);
1562df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_DISK:
1572df1fe9cSrandyf 	case AD_SUSPEND_TO_DISK:
1582df1fe9cSrandyf 	case AD_CPR_REUSEINIT:
1592df1fe9cSrandyf 	case AD_CPR_NOCOMPRESS:
1602df1fe9cSrandyf 	case AD_CPR_FORCE:
1612df1fe9cSrandyf 	case AD_CPR_REUSABLE:
1622df1fe9cSrandyf 	case AD_CPR_REUSEFINI:
1632df1fe9cSrandyf 	case AD_CPR_TESTZ:
1642df1fe9cSrandyf 	case AD_CPR_TESTNOZ:
1652df1fe9cSrandyf 	case AD_CPR_TESTHALT:
1662df1fe9cSrandyf 	case AD_CPR_SUSP_DEVICES:
1672df1fe9cSrandyf 		cpr_sleeptype = CPR_TODISK;
1682df1fe9cSrandyf 		break;
1692df1fe9cSrandyf #endif
1702df1fe9cSrandyf #if defined(__x86)
1712df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_DISK:
1722df1fe9cSrandyf 	case AD_SUSPEND_TO_DISK:
1732df1fe9cSrandyf 	case AD_CPR_REUSEINIT:
1742df1fe9cSrandyf 	case AD_CPR_NOCOMPRESS:
1752df1fe9cSrandyf 	case AD_CPR_FORCE:
1762df1fe9cSrandyf 	case AD_CPR_REUSABLE:
1772df1fe9cSrandyf 	case AD_CPR_REUSEFINI:
1782df1fe9cSrandyf 	case AD_CPR_TESTZ:
1792df1fe9cSrandyf 	case AD_CPR_TESTNOZ:
1802df1fe9cSrandyf 	case AD_CPR_TESTHALT:
1812df1fe9cSrandyf 	case AD_CPR_PRINT:
1822df1fe9cSrandyf 		return (ENOTSUP);
1832df1fe9cSrandyf 	/* The DEV_* values need to be removed after sys-syspend is fixed */
1842df1fe9cSrandyf 	case DEV_CHECK_SUSPEND_TO_RAM:
1852df1fe9cSrandyf 	case DEV_SUSPEND_TO_RAM:
1862df1fe9cSrandyf 	case AD_CPR_SUSP_DEVICES:
1872df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_RAM:
1882df1fe9cSrandyf 	case AD_SUSPEND_TO_RAM:
1892df1fe9cSrandyf 	case AD_LOOPBACK_SUSPEND_TO_RAM_PASS:
1902df1fe9cSrandyf 	case AD_LOOPBACK_SUSPEND_TO_RAM_FAIL:
1912df1fe9cSrandyf 	case AD_FORCE_SUSPEND_TO_RAM:
1922df1fe9cSrandyf 	case AD_DEVICE_SUSPEND_TO_RAM:
1932df1fe9cSrandyf 		/*
1942df1fe9cSrandyf 		 * if MP then do not support suspend to RAM, however override
1952df1fe9cSrandyf 		 * the MP restriction if cpr_mp_enable has been set
1962df1fe9cSrandyf 		 */
1972df1fe9cSrandyf 		if (ncpus > 1 && cpr_mp_enable == 0)
1982df1fe9cSrandyf 			return (ENOTSUP);
1992df1fe9cSrandyf 		else
2002df1fe9cSrandyf 			cpr_sleeptype = CPR_TORAM;
2012df1fe9cSrandyf 		break;
2022df1fe9cSrandyf #endif
2032df1fe9cSrandyf 	}
2042df1fe9cSrandyf #if defined(__sparc)
2057c478bd9Sstevel@tonic-gate 	/*
2067c478bd9Sstevel@tonic-gate 	 * Need to know if we're in reusable mode, but we will likely have
2077c478bd9Sstevel@tonic-gate 	 * rebooted since REUSEINIT, so we have to get the info from the
2087c478bd9Sstevel@tonic-gate 	 * file system
2097c478bd9Sstevel@tonic-gate 	 */
2107c478bd9Sstevel@tonic-gate 	if (!cpr_reusable_mode)
2117c478bd9Sstevel@tonic-gate 		cpr_reusable_mode = cpr_get_reusable_mode();
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	cpr_forget_cprconfig();
2142df1fe9cSrandyf #endif
2152df1fe9cSrandyf 
2167c478bd9Sstevel@tonic-gate 	switch (fcn) {
2177c478bd9Sstevel@tonic-gate 
2182df1fe9cSrandyf #if defined(__sparc)
2197c478bd9Sstevel@tonic-gate 	case AD_CPR_REUSEINIT:
2207c478bd9Sstevel@tonic-gate 		if (!i_cpr_reusable_supported())
2217c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2227c478bd9Sstevel@tonic-gate 		if (!cpr_statefile_is_spec()) {
2237c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, blockstr);
2247c478bd9Sstevel@tonic-gate 			return (EINVAL);
2257c478bd9Sstevel@tonic-gate 		}
2267c478bd9Sstevel@tonic-gate 		if ((rc = cpr_check_spec_statefile()) != 0)
2277c478bd9Sstevel@tonic-gate 			return (rc);
2287c478bd9Sstevel@tonic-gate 		if (swapinfo) {
2297c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, noswapstr);
2307c478bd9Sstevel@tonic-gate 			return (EINVAL);
2317c478bd9Sstevel@tonic-gate 		}
2327c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
2337c478bd9Sstevel@tonic-gate 		break;
2347c478bd9Sstevel@tonic-gate 
2357c478bd9Sstevel@tonic-gate 	case AD_CPR_NOCOMPRESS:
2367c478bd9Sstevel@tonic-gate 	case AD_CPR_COMPRESS:
2377c478bd9Sstevel@tonic-gate 	case AD_CPR_FORCE:
2387c478bd9Sstevel@tonic-gate 		if (cpr_reusable_mode) {
2397c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, normalfmt, A_FREEZE, AD_REUSEFINI);
2407c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2417c478bd9Sstevel@tonic-gate 		}
2427c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
2437c478bd9Sstevel@tonic-gate 		break;
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 	case AD_CPR_REUSABLE:
2467c478bd9Sstevel@tonic-gate 		if (!i_cpr_reusable_supported())
2477c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2487c478bd9Sstevel@tonic-gate 		if (!cpr_statefile_is_spec()) {
2497c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, blockstr);
2507c478bd9Sstevel@tonic-gate 			return (EINVAL);
2517c478bd9Sstevel@tonic-gate 		}
2527c478bd9Sstevel@tonic-gate 		if ((rc = cpr_check_spec_statefile()) != 0)
2537c478bd9Sstevel@tonic-gate 			return (rc);
2547c478bd9Sstevel@tonic-gate 		if (swapinfo) {
2557c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, noswapstr);
2567c478bd9Sstevel@tonic-gate 			return (EINVAL);
2577c478bd9Sstevel@tonic-gate 		}
2587c478bd9Sstevel@tonic-gate 		if ((rc = cpr_reusable_mount_check()) != 0)
2597c478bd9Sstevel@tonic-gate 			return (rc);
2607c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
2617c478bd9Sstevel@tonic-gate 		break;
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate 	case AD_CPR_REUSEFINI:
2647c478bd9Sstevel@tonic-gate 		if (!i_cpr_reusable_supported())
2657c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2667c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
2677c478bd9Sstevel@tonic-gate 		break;
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	case AD_CPR_TESTZ:
2707c478bd9Sstevel@tonic-gate 	case AD_CPR_TESTNOZ:
2717c478bd9Sstevel@tonic-gate 	case AD_CPR_TESTHALT:
2727c478bd9Sstevel@tonic-gate 		if (cpr_reusable_mode) {
2737c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, normalfmt, A_FREEZE, AD_REUSEFINI);
2747c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2757c478bd9Sstevel@tonic-gate 		}
2767c478bd9Sstevel@tonic-gate 		cpr_test_mode = 1;
2777c478bd9Sstevel@tonic-gate 		break;
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate 	case AD_CPR_CHECK:
2802df1fe9cSrandyf 		if (!i_cpr_is_supported(cpr_sleeptype) || cpr_reusable_mode)
2817c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2827c478bd9Sstevel@tonic-gate 		return (0);
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 	case AD_CPR_PRINT:
2857c478bd9Sstevel@tonic-gate 		CPR_STAT_EVENT_END("POST CPR DELAY");
2867c478bd9Sstevel@tonic-gate 		cpr_stat_event_print();
2877c478bd9Sstevel@tonic-gate 		return (0);
2882df1fe9cSrandyf #endif
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG0:
2917c478bd9Sstevel@tonic-gate 		cpr_debug = 0;
2927c478bd9Sstevel@tonic-gate 		return (0);
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG1:
2957c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG2:
2967c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG3:
2977c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG4:
2987c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG5:
2997c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG7:
3007c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG8:
3017c478bd9Sstevel@tonic-gate 		cpr_debug |= CPR_DEBUG_BIT(fcn);
3027c478bd9Sstevel@tonic-gate 		return (0);
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG9:
305ae115bc7Smrj 		cpr_debug |= CPR_DEBUG6;
3067c478bd9Sstevel@tonic-gate 		return (0);
3077c478bd9Sstevel@tonic-gate 
3082df1fe9cSrandyf 	/* The DEV_* values need to be removed after sys-syspend is fixed */
3092df1fe9cSrandyf 	case DEV_CHECK_SUSPEND_TO_RAM:
3102df1fe9cSrandyf 	case DEV_SUSPEND_TO_RAM:
3112df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_RAM:
3122df1fe9cSrandyf 	case AD_SUSPEND_TO_RAM:
3132df1fe9cSrandyf 		cpr_test_point = LOOP_BACK_NONE;
3142df1fe9cSrandyf 		break;
3152df1fe9cSrandyf 
3162df1fe9cSrandyf 	case AD_LOOPBACK_SUSPEND_TO_RAM_PASS:
3172df1fe9cSrandyf 		cpr_test_point = LOOP_BACK_PASS;
3182df1fe9cSrandyf 		break;
3192df1fe9cSrandyf 
3202df1fe9cSrandyf 	case AD_LOOPBACK_SUSPEND_TO_RAM_FAIL:
3212df1fe9cSrandyf 		cpr_test_point = LOOP_BACK_FAIL;
3222df1fe9cSrandyf 		break;
3232df1fe9cSrandyf 
3242df1fe9cSrandyf 	case AD_FORCE_SUSPEND_TO_RAM:
3252df1fe9cSrandyf 		cpr_test_point = FORCE_SUSPEND_TO_RAM;
3262df1fe9cSrandyf 		break;
3272df1fe9cSrandyf 
3282df1fe9cSrandyf 	case AD_DEVICE_SUSPEND_TO_RAM:
329*c36fc336Srandyf 		if (mdep == NULL) {
330*c36fc336Srandyf 			/* Didn't pass enough arguments */
331*c36fc336Srandyf 			return (EINVAL);
332*c36fc336Srandyf 		}
3332df1fe9cSrandyf 		cpr_test_point = DEVICE_SUSPEND_TO_RAM;
3342df1fe9cSrandyf 		cpr_device = (major_t)atoi((char *)mdep);
3352df1fe9cSrandyf 		break;
3362df1fe9cSrandyf 
3372df1fe9cSrandyf 	case AD_CPR_SUSP_DEVICES:
3382df1fe9cSrandyf 		cpr_test_point = FORCE_SUSPEND_TO_RAM;
3392df1fe9cSrandyf 		if (cpr_suspend_devices(ddi_root_node()) != DDI_SUCCESS)
3402df1fe9cSrandyf 			cmn_err(CE_WARN,
3412df1fe9cSrandyf 			    "Some devices did not suspend "
3422df1fe9cSrandyf 			    "and may be unusable");
3432df1fe9cSrandyf 		(void) cpr_resume_devices(ddi_root_node(), 0);
3442df1fe9cSrandyf 		return (0);
3452df1fe9cSrandyf 
3467c478bd9Sstevel@tonic-gate 	default:
3477c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
3487c478bd9Sstevel@tonic-gate 	}
3497c478bd9Sstevel@tonic-gate 
3502df1fe9cSrandyf 	if (!i_cpr_is_supported(cpr_sleeptype) ||
3512df1fe9cSrandyf 	    (cpr_sleeptype == CPR_TODISK && !cpr_is_ufs(rootvfs)))
3527c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
3537c478bd9Sstevel@tonic-gate 
3542df1fe9cSrandyf 	if (fcn == AD_CHECK_SUSPEND_TO_RAM ||
3552df1fe9cSrandyf 	    fcn == DEV_CHECK_SUSPEND_TO_RAM) {
3562df1fe9cSrandyf 		ASSERT(i_cpr_is_supported(cpr_sleeptype));
3572df1fe9cSrandyf 		return (0);
3582df1fe9cSrandyf 	}
3592df1fe9cSrandyf 
3602df1fe9cSrandyf #if defined(__sparc)
3617c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSEINIT) {
3627c478bd9Sstevel@tonic-gate 		if (mutex_tryenter(&cpr_slock) == 0)
3637c478bd9Sstevel@tonic-gate 			return (EBUSY);
3647c478bd9Sstevel@tonic-gate 		if (cpr_reusable_mode) {
3657c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, modefmt, "already");
3667c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
3677c478bd9Sstevel@tonic-gate 			return (EBUSY);
3687c478bd9Sstevel@tonic-gate 		}
3697c478bd9Sstevel@tonic-gate 		rc = i_cpr_reuseinit();
3707c478bd9Sstevel@tonic-gate 		mutex_exit(&cpr_slock);
3717c478bd9Sstevel@tonic-gate 		return (rc);
3727c478bd9Sstevel@tonic-gate 	}
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSEFINI) {
3757c478bd9Sstevel@tonic-gate 		if (mutex_tryenter(&cpr_slock) == 0)
3767c478bd9Sstevel@tonic-gate 			return (EBUSY);
3777c478bd9Sstevel@tonic-gate 		if (!cpr_reusable_mode) {
3787c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, modefmt, "not");
3797c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
3807c478bd9Sstevel@tonic-gate 			return (EINVAL);
3817c478bd9Sstevel@tonic-gate 		}
3827c478bd9Sstevel@tonic-gate 		rc = i_cpr_reusefini();
3837c478bd9Sstevel@tonic-gate 		mutex_exit(&cpr_slock);
3847c478bd9Sstevel@tonic-gate 		return (rc);
3857c478bd9Sstevel@tonic-gate 	}
3862df1fe9cSrandyf #endif
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 	/*
3897c478bd9Sstevel@tonic-gate 	 * acquire cpr serial lock and init cpr state structure.
3907c478bd9Sstevel@tonic-gate 	 */
3917c478bd9Sstevel@tonic-gate 	if (rc = cpr_init(fcn))
3927c478bd9Sstevel@tonic-gate 		return (rc);
3937c478bd9Sstevel@tonic-gate 
3942df1fe9cSrandyf #if defined(__sparc)
3957c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSABLE) {
3967c478bd9Sstevel@tonic-gate 		if ((rc = i_cpr_check_cprinfo()) != 0)  {
3977c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
3987c478bd9Sstevel@tonic-gate 			return (rc);
3997c478bd9Sstevel@tonic-gate 		}
4007c478bd9Sstevel@tonic-gate 	}
4012df1fe9cSrandyf #endif
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 	/*
4047c478bd9Sstevel@tonic-gate 	 * Call the main cpr routine. If we are successful, we will be coming
4057c478bd9Sstevel@tonic-gate 	 * down from the resume side, otherwise we are still in suspend.
4067c478bd9Sstevel@tonic-gate 	 */
4077c478bd9Sstevel@tonic-gate 	cpr_err(CE_CONT, "System is being suspended");
4082df1fe9cSrandyf 	if (rc = cpr_main(cpr_sleeptype)) {
4097c478bd9Sstevel@tonic-gate 		CPR->c_flags |= C_ERROR;
4102df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: Suspend operation failed.\n"))
4117c478bd9Sstevel@tonic-gate 		cpr_err(CE_NOTE, "Suspend operation failed.");
4127c478bd9Sstevel@tonic-gate 	} else if (CPR->c_flags & C_SUSPENDING) {
4132df1fe9cSrandyf 
4142df1fe9cSrandyf 		/*
4152df1fe9cSrandyf 		 * In the suspend to RAM case, by the time we get
4162df1fe9cSrandyf 		 * control back we're already resumed
4172df1fe9cSrandyf 		 */
4182df1fe9cSrandyf 		if (cpr_sleeptype == CPR_TORAM) {
4192df1fe9cSrandyf 			PMD(PMD_SX, ("cpr: cpr CPR_TORAM done\n"))
4202df1fe9cSrandyf 			cpr_done();
4212df1fe9cSrandyf 			return (rc);
4222df1fe9cSrandyf 		}
4232df1fe9cSrandyf 
4242df1fe9cSrandyf #if defined(__sparc)
4252df1fe9cSrandyf 
4262df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: Suspend operation succeeded.\n"))
4277c478bd9Sstevel@tonic-gate 		/*
4287c478bd9Sstevel@tonic-gate 		 * Back from a successful checkpoint
4297c478bd9Sstevel@tonic-gate 		 */
4307c478bd9Sstevel@tonic-gate 		if (fcn == AD_CPR_TESTZ || fcn == AD_CPR_TESTNOZ) {
431edc40228Sachartre 			mdboot(0, AD_BOOT, "", B_FALSE);
4327c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4337c478bd9Sstevel@tonic-gate 		}
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 		/* make sure there are no more changes to the device tree */
4362df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: dev tree freeze\n"))
4377c478bd9Sstevel@tonic-gate 		devtree_freeze();
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 		/*
4407c478bd9Sstevel@tonic-gate 		 * stop other cpus and raise our priority.  since there is only
4417c478bd9Sstevel@tonic-gate 		 * one active cpu after this, and our priority will be too high
4427c478bd9Sstevel@tonic-gate 		 * for us to be preempted, we're essentially single threaded
4437c478bd9Sstevel@tonic-gate 		 * from here on out.
4447c478bd9Sstevel@tonic-gate 		 */
4452df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: stop other cpus\n"))
4462df1fe9cSrandyf 		i_cpr_stop_other_cpus();
4472df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: spl6\n"))
4487c478bd9Sstevel@tonic-gate 		(void) spl6();
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate 		/*
4517c478bd9Sstevel@tonic-gate 		 * try and reset leaf devices.  reset_leaves() should only
4527c478bd9Sstevel@tonic-gate 		 * be called when there are no other threads that could be
4537c478bd9Sstevel@tonic-gate 		 * accessing devices
4547c478bd9Sstevel@tonic-gate 		 */
4552df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: reset leaves\n"))
4567c478bd9Sstevel@tonic-gate 		reset_leaves();
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 		/*
4592df1fe9cSrandyf 		 * If i_cpr_power_down() succeeds, it'll not return
4607c478bd9Sstevel@tonic-gate 		 *
4617c478bd9Sstevel@tonic-gate 		 * Drives with write-cache enabled need to flush
4627c478bd9Sstevel@tonic-gate 		 * their cache.
4637c478bd9Sstevel@tonic-gate 		 */
4642df1fe9cSrandyf 		if (fcn != AD_CPR_TESTHALT) {
4652df1fe9cSrandyf 			PMD(PMD_SX, ("cpr: power down\n"))
4662df1fe9cSrandyf 			(void) i_cpr_power_down(cpr_sleeptype);
4672df1fe9cSrandyf 		}
4682df1fe9cSrandyf 		ASSERT(cpr_sleeptype == CPR_TODISK);
4692df1fe9cSrandyf 		/* currently CPR_TODISK comes back via a boot path */
470ae115bc7Smrj 		CPR_DEBUG(CPR_DEBUG1, "(Done. Please Switch Off)\n");
4717c478bd9Sstevel@tonic-gate 		halt(NULL);
4727c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
4732df1fe9cSrandyf #endif
4747c478bd9Sstevel@tonic-gate 	}
4752df1fe9cSrandyf 	PMD(PMD_SX, ("cpr: cpr done\n"))
4767c478bd9Sstevel@tonic-gate 	cpr_done();
4777c478bd9Sstevel@tonic-gate 	return (rc);
4787c478bd9Sstevel@tonic-gate }
479