xref: /illumos-gate/usr/src/uts/common/cpr/cpr_mod.c (revision e7cbe64f)
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 /*
224716fd88Sjan  * Copyright 2008 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 *);
47*e7cbe64fSgw extern int cpr_is_zfs(struct vfs *);
487c478bd9Sstevel@tonic-gate extern int cpr_check_spec_statefile(void);
497c478bd9Sstevel@tonic-gate extern int cpr_reusable_mount_check(void);
507c478bd9Sstevel@tonic-gate extern int i_cpr_reusable_supported(void);
517c478bd9Sstevel@tonic-gate extern int i_cpr_reusefini(void);
527c478bd9Sstevel@tonic-gate extern struct mod_ops mod_miscops;
537c478bd9Sstevel@tonic-gate 
542df1fe9cSrandyf extern int cpr_init(int);
552df1fe9cSrandyf extern void cpr_done(void);
562df1fe9cSrandyf extern void i_cpr_stop_other_cpus(void);
572df1fe9cSrandyf extern int i_cpr_power_down();
582df1fe9cSrandyf 
592df1fe9cSrandyf #if defined(__sparc)
602df1fe9cSrandyf extern void cpr_forget_cprconfig(void);
612df1fe9cSrandyf #endif
622df1fe9cSrandyf 
637c478bd9Sstevel@tonic-gate static struct modlmisc modlmisc = {
647c478bd9Sstevel@tonic-gate 	&mod_miscops, "checkpoint resume"
657c478bd9Sstevel@tonic-gate };
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
687c478bd9Sstevel@tonic-gate 	MODREV_1, (void *)&modlmisc, NULL
697c478bd9Sstevel@tonic-gate };
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate char _depends_on[] = "misc/bootdev";	/* i_devname_to_promname() */
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate int cpr_reusable_mode;
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate kmutex_t	cpr_slock;	/* cpr serial lock */
767c478bd9Sstevel@tonic-gate cpr_t		cpr_state;
777c478bd9Sstevel@tonic-gate int		cpr_debug;
787c478bd9Sstevel@tonic-gate int		cpr_test_mode; /* true if called via uadmin testmode */
792df1fe9cSrandyf int		cpr_test_point = LOOP_BACK_NONE;	/* cpr test point */
802df1fe9cSrandyf int		cpr_mp_enable = 0;	/* set to 1 to enable MP suspend */
812df1fe9cSrandyf major_t		cpr_device = 0;		/* major number for S3 on one device */
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate /*
847c478bd9Sstevel@tonic-gate  * All the loadable module related code follows
857c478bd9Sstevel@tonic-gate  */
867c478bd9Sstevel@tonic-gate int
877c478bd9Sstevel@tonic-gate _init(void)
887c478bd9Sstevel@tonic-gate {
897c478bd9Sstevel@tonic-gate 	register int e;
907c478bd9Sstevel@tonic-gate 
917c478bd9Sstevel@tonic-gate 	if ((e = mod_install(&modlinkage)) == 0) {
927c478bd9Sstevel@tonic-gate 		mutex_init(&cpr_slock, NULL, MUTEX_DEFAULT, NULL);
937c478bd9Sstevel@tonic-gate 	}
947c478bd9Sstevel@tonic-gate 	return (e);
957c478bd9Sstevel@tonic-gate }
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate int
987c478bd9Sstevel@tonic-gate _fini(void)
997c478bd9Sstevel@tonic-gate {
1007c478bd9Sstevel@tonic-gate 	register int e;
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate 	if ((e = mod_remove(&modlinkage)) == 0) {
1037c478bd9Sstevel@tonic-gate 		mutex_destroy(&cpr_slock);
1047c478bd9Sstevel@tonic-gate 	}
1057c478bd9Sstevel@tonic-gate 	return (e);
1067c478bd9Sstevel@tonic-gate }
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate int
1097c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
1107c478bd9Sstevel@tonic-gate {
1117c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1127c478bd9Sstevel@tonic-gate }
1137c478bd9Sstevel@tonic-gate 
1142df1fe9cSrandyf static
1152df1fe9cSrandyf int
1162df1fe9cSrandyf atoi(char *p)
1172df1fe9cSrandyf {
1182df1fe9cSrandyf 	int	i;
1192df1fe9cSrandyf 
1202df1fe9cSrandyf 	i = (*p++ - '0');
1212df1fe9cSrandyf 
1222df1fe9cSrandyf 	while (*p != '\0')
1232df1fe9cSrandyf 		i = 10 * i + (*p++ - '0');
1242df1fe9cSrandyf 
1252df1fe9cSrandyf 	return (i);
1262df1fe9cSrandyf }
1272df1fe9cSrandyf 
1287c478bd9Sstevel@tonic-gate int
1292df1fe9cSrandyf cpr(int fcn, void *mdep)
1307c478bd9Sstevel@tonic-gate {
1312df1fe9cSrandyf 
1322df1fe9cSrandyf #if defined(__sparc)
1337c478bd9Sstevel@tonic-gate 	static const char noswapstr[] = "reusable statefile requires "
1347c478bd9Sstevel@tonic-gate 	    "that no swap area be configured.\n";
1357c478bd9Sstevel@tonic-gate 	static const char blockstr[] = "reusable statefile must be "
1367c478bd9Sstevel@tonic-gate 	    "a block device.  See power.conf(4) and pmconfig(1M).\n";
1377c478bd9Sstevel@tonic-gate 	static const char normalfmt[] = "cannot run normal "
1387c478bd9Sstevel@tonic-gate 	    "checkpoint/resume when in reusable statefile mode. "
1397c478bd9Sstevel@tonic-gate 	    "use uadmin A_FREEZE AD_REUSEFINI (uadmin %d %d) "
1407c478bd9Sstevel@tonic-gate 	    "to exit reusable statefile mode.\n";
1417c478bd9Sstevel@tonic-gate 	static const char modefmt[] = "%s in reusable mode.\n";
1422df1fe9cSrandyf #endif
1437c478bd9Sstevel@tonic-gate 	register int rc = 0;
1442df1fe9cSrandyf 	int cpr_sleeptype;
1457c478bd9Sstevel@tonic-gate 
1462df1fe9cSrandyf 	/*
1472df1fe9cSrandyf 	 * First, reject commands that we don't (yet) support on this arch.
1482df1fe9cSrandyf 	 * This is easier to understand broken out like this than grotting
1492df1fe9cSrandyf 	 * through the second switch below.
1502df1fe9cSrandyf 	 */
1512df1fe9cSrandyf 
1522df1fe9cSrandyf 	switch (fcn) {
1532df1fe9cSrandyf #if defined(__sparc)
1542df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_RAM:
1552df1fe9cSrandyf 	case AD_SUSPEND_TO_RAM:
1562df1fe9cSrandyf 		return (ENOTSUP);
1572df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_DISK:
1582df1fe9cSrandyf 	case AD_SUSPEND_TO_DISK:
1592df1fe9cSrandyf 	case AD_CPR_REUSEINIT:
1602df1fe9cSrandyf 	case AD_CPR_NOCOMPRESS:
1612df1fe9cSrandyf 	case AD_CPR_FORCE:
1622df1fe9cSrandyf 	case AD_CPR_REUSABLE:
1632df1fe9cSrandyf 	case AD_CPR_REUSEFINI:
1642df1fe9cSrandyf 	case AD_CPR_TESTZ:
1652df1fe9cSrandyf 	case AD_CPR_TESTNOZ:
1662df1fe9cSrandyf 	case AD_CPR_TESTHALT:
1672df1fe9cSrandyf 	case AD_CPR_SUSP_DEVICES:
1682df1fe9cSrandyf 		cpr_sleeptype = CPR_TODISK;
1692df1fe9cSrandyf 		break;
1702df1fe9cSrandyf #endif
1712df1fe9cSrandyf #if defined(__x86)
1722df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_DISK:
1732df1fe9cSrandyf 	case AD_SUSPEND_TO_DISK:
1742df1fe9cSrandyf 	case AD_CPR_REUSEINIT:
1752df1fe9cSrandyf 	case AD_CPR_NOCOMPRESS:
1762df1fe9cSrandyf 	case AD_CPR_FORCE:
1772df1fe9cSrandyf 	case AD_CPR_REUSABLE:
1782df1fe9cSrandyf 	case AD_CPR_REUSEFINI:
1792df1fe9cSrandyf 	case AD_CPR_TESTZ:
1802df1fe9cSrandyf 	case AD_CPR_TESTNOZ:
1812df1fe9cSrandyf 	case AD_CPR_TESTHALT:
1822df1fe9cSrandyf 	case AD_CPR_PRINT:
1832df1fe9cSrandyf 		return (ENOTSUP);
1842df1fe9cSrandyf 	/* The DEV_* values need to be removed after sys-syspend is fixed */
1852df1fe9cSrandyf 	case DEV_CHECK_SUSPEND_TO_RAM:
1862df1fe9cSrandyf 	case DEV_SUSPEND_TO_RAM:
1872df1fe9cSrandyf 	case AD_CPR_SUSP_DEVICES:
1882df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_RAM:
1892df1fe9cSrandyf 	case AD_SUSPEND_TO_RAM:
1902df1fe9cSrandyf 	case AD_LOOPBACK_SUSPEND_TO_RAM_PASS:
1912df1fe9cSrandyf 	case AD_LOOPBACK_SUSPEND_TO_RAM_FAIL:
1922df1fe9cSrandyf 	case AD_FORCE_SUSPEND_TO_RAM:
1932df1fe9cSrandyf 	case AD_DEVICE_SUSPEND_TO_RAM:
1944716fd88Sjan 		cpr_sleeptype = CPR_TORAM;
1952df1fe9cSrandyf 		break;
1962df1fe9cSrandyf #endif
1972df1fe9cSrandyf 	}
1982df1fe9cSrandyf #if defined(__sparc)
1997c478bd9Sstevel@tonic-gate 	/*
2007c478bd9Sstevel@tonic-gate 	 * Need to know if we're in reusable mode, but we will likely have
2017c478bd9Sstevel@tonic-gate 	 * rebooted since REUSEINIT, so we have to get the info from the
2027c478bd9Sstevel@tonic-gate 	 * file system
2037c478bd9Sstevel@tonic-gate 	 */
2047c478bd9Sstevel@tonic-gate 	if (!cpr_reusable_mode)
2057c478bd9Sstevel@tonic-gate 		cpr_reusable_mode = cpr_get_reusable_mode();
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	cpr_forget_cprconfig();
2082df1fe9cSrandyf #endif
2092df1fe9cSrandyf 
2107c478bd9Sstevel@tonic-gate 	switch (fcn) {
2117c478bd9Sstevel@tonic-gate 
2122df1fe9cSrandyf #if defined(__sparc)
2137c478bd9Sstevel@tonic-gate 	case AD_CPR_REUSEINIT:
2147c478bd9Sstevel@tonic-gate 		if (!i_cpr_reusable_supported())
2157c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2167c478bd9Sstevel@tonic-gate 		if (!cpr_statefile_is_spec()) {
2177c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, blockstr);
2187c478bd9Sstevel@tonic-gate 			return (EINVAL);
2197c478bd9Sstevel@tonic-gate 		}
2207c478bd9Sstevel@tonic-gate 		if ((rc = cpr_check_spec_statefile()) != 0)
2217c478bd9Sstevel@tonic-gate 			return (rc);
2227c478bd9Sstevel@tonic-gate 		if (swapinfo) {
2237c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, noswapstr);
2247c478bd9Sstevel@tonic-gate 			return (EINVAL);
2257c478bd9Sstevel@tonic-gate 		}
2267c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
2277c478bd9Sstevel@tonic-gate 		break;
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate 	case AD_CPR_NOCOMPRESS:
2307c478bd9Sstevel@tonic-gate 	case AD_CPR_COMPRESS:
2317c478bd9Sstevel@tonic-gate 	case AD_CPR_FORCE:
2327c478bd9Sstevel@tonic-gate 		if (cpr_reusable_mode) {
2337c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, normalfmt, A_FREEZE, AD_REUSEFINI);
2347c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2357c478bd9Sstevel@tonic-gate 		}
2367c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
2377c478bd9Sstevel@tonic-gate 		break;
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 	case AD_CPR_REUSABLE:
2407c478bd9Sstevel@tonic-gate 		if (!i_cpr_reusable_supported())
2417c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2427c478bd9Sstevel@tonic-gate 		if (!cpr_statefile_is_spec()) {
2437c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, blockstr);
2447c478bd9Sstevel@tonic-gate 			return (EINVAL);
2457c478bd9Sstevel@tonic-gate 		}
2467c478bd9Sstevel@tonic-gate 		if ((rc = cpr_check_spec_statefile()) != 0)
2477c478bd9Sstevel@tonic-gate 			return (rc);
2487c478bd9Sstevel@tonic-gate 		if (swapinfo) {
2497c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, noswapstr);
2507c478bd9Sstevel@tonic-gate 			return (EINVAL);
2517c478bd9Sstevel@tonic-gate 		}
2527c478bd9Sstevel@tonic-gate 		if ((rc = cpr_reusable_mount_check()) != 0)
2537c478bd9Sstevel@tonic-gate 			return (rc);
2547c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
2557c478bd9Sstevel@tonic-gate 		break;
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 	case AD_CPR_REUSEFINI:
2587c478bd9Sstevel@tonic-gate 		if (!i_cpr_reusable_supported())
2597c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2607c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
2617c478bd9Sstevel@tonic-gate 		break;
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate 	case AD_CPR_TESTZ:
2647c478bd9Sstevel@tonic-gate 	case AD_CPR_TESTNOZ:
2657c478bd9Sstevel@tonic-gate 	case AD_CPR_TESTHALT:
2667c478bd9Sstevel@tonic-gate 		if (cpr_reusable_mode) {
2677c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, normalfmt, A_FREEZE, AD_REUSEFINI);
2687c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2697c478bd9Sstevel@tonic-gate 		}
2707c478bd9Sstevel@tonic-gate 		cpr_test_mode = 1;
2717c478bd9Sstevel@tonic-gate 		break;
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate 	case AD_CPR_CHECK:
2742df1fe9cSrandyf 		if (!i_cpr_is_supported(cpr_sleeptype) || cpr_reusable_mode)
2757c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2767c478bd9Sstevel@tonic-gate 		return (0);
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 	case AD_CPR_PRINT:
2797c478bd9Sstevel@tonic-gate 		CPR_STAT_EVENT_END("POST CPR DELAY");
2807c478bd9Sstevel@tonic-gate 		cpr_stat_event_print();
2817c478bd9Sstevel@tonic-gate 		return (0);
2822df1fe9cSrandyf #endif
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG0:
2857c478bd9Sstevel@tonic-gate 		cpr_debug = 0;
2867c478bd9Sstevel@tonic-gate 		return (0);
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG1:
2897c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG2:
2907c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG3:
2917c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG4:
2927c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG5:
2937c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG7:
2947c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG8:
2957c478bd9Sstevel@tonic-gate 		cpr_debug |= CPR_DEBUG_BIT(fcn);
2967c478bd9Sstevel@tonic-gate 		return (0);
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG9:
299ae115bc7Smrj 		cpr_debug |= CPR_DEBUG6;
3007c478bd9Sstevel@tonic-gate 		return (0);
3017c478bd9Sstevel@tonic-gate 
3022df1fe9cSrandyf 	/* The DEV_* values need to be removed after sys-syspend is fixed */
3032df1fe9cSrandyf 	case DEV_CHECK_SUSPEND_TO_RAM:
3042df1fe9cSrandyf 	case DEV_SUSPEND_TO_RAM:
3052df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_RAM:
3062df1fe9cSrandyf 	case AD_SUSPEND_TO_RAM:
3072df1fe9cSrandyf 		cpr_test_point = LOOP_BACK_NONE;
3082df1fe9cSrandyf 		break;
3092df1fe9cSrandyf 
3102df1fe9cSrandyf 	case AD_LOOPBACK_SUSPEND_TO_RAM_PASS:
3112df1fe9cSrandyf 		cpr_test_point = LOOP_BACK_PASS;
3122df1fe9cSrandyf 		break;
3132df1fe9cSrandyf 
3142df1fe9cSrandyf 	case AD_LOOPBACK_SUSPEND_TO_RAM_FAIL:
3152df1fe9cSrandyf 		cpr_test_point = LOOP_BACK_FAIL;
3162df1fe9cSrandyf 		break;
3172df1fe9cSrandyf 
3182df1fe9cSrandyf 	case AD_FORCE_SUSPEND_TO_RAM:
3192df1fe9cSrandyf 		cpr_test_point = FORCE_SUSPEND_TO_RAM;
3202df1fe9cSrandyf 		break;
3212df1fe9cSrandyf 
3222df1fe9cSrandyf 	case AD_DEVICE_SUSPEND_TO_RAM:
323c36fc336Srandyf 		if (mdep == NULL) {
324c36fc336Srandyf 			/* Didn't pass enough arguments */
325c36fc336Srandyf 			return (EINVAL);
326c36fc336Srandyf 		}
3272df1fe9cSrandyf 		cpr_test_point = DEVICE_SUSPEND_TO_RAM;
3282df1fe9cSrandyf 		cpr_device = (major_t)atoi((char *)mdep);
3292df1fe9cSrandyf 		break;
3302df1fe9cSrandyf 
3312df1fe9cSrandyf 	case AD_CPR_SUSP_DEVICES:
3322df1fe9cSrandyf 		cpr_test_point = FORCE_SUSPEND_TO_RAM;
3332df1fe9cSrandyf 		if (cpr_suspend_devices(ddi_root_node()) != DDI_SUCCESS)
3342df1fe9cSrandyf 			cmn_err(CE_WARN,
3352df1fe9cSrandyf 			    "Some devices did not suspend "
3362df1fe9cSrandyf 			    "and may be unusable");
3372df1fe9cSrandyf 		(void) cpr_resume_devices(ddi_root_node(), 0);
3382df1fe9cSrandyf 		return (0);
3392df1fe9cSrandyf 
3407c478bd9Sstevel@tonic-gate 	default:
3417c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
3427c478bd9Sstevel@tonic-gate 	}
3437c478bd9Sstevel@tonic-gate 
3442df1fe9cSrandyf 	if (!i_cpr_is_supported(cpr_sleeptype) ||
345*e7cbe64fSgw 	    (cpr_sleeptype == CPR_TODISK &&
346*e7cbe64fSgw 	    !cpr_is_ufs(rootvfs)&& !cpr_is_zfs(rootvfs)))
3477c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
3487c478bd9Sstevel@tonic-gate 
3492df1fe9cSrandyf 	if (fcn == AD_CHECK_SUSPEND_TO_RAM ||
3502df1fe9cSrandyf 	    fcn == DEV_CHECK_SUSPEND_TO_RAM) {
3512df1fe9cSrandyf 		ASSERT(i_cpr_is_supported(cpr_sleeptype));
3522df1fe9cSrandyf 		return (0);
3532df1fe9cSrandyf 	}
3542df1fe9cSrandyf 
3552df1fe9cSrandyf #if defined(__sparc)
3567c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSEINIT) {
3577c478bd9Sstevel@tonic-gate 		if (mutex_tryenter(&cpr_slock) == 0)
3587c478bd9Sstevel@tonic-gate 			return (EBUSY);
3597c478bd9Sstevel@tonic-gate 		if (cpr_reusable_mode) {
3607c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, modefmt, "already");
3617c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
3627c478bd9Sstevel@tonic-gate 			return (EBUSY);
3637c478bd9Sstevel@tonic-gate 		}
3647c478bd9Sstevel@tonic-gate 		rc = i_cpr_reuseinit();
3657c478bd9Sstevel@tonic-gate 		mutex_exit(&cpr_slock);
3667c478bd9Sstevel@tonic-gate 		return (rc);
3677c478bd9Sstevel@tonic-gate 	}
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSEFINI) {
3707c478bd9Sstevel@tonic-gate 		if (mutex_tryenter(&cpr_slock) == 0)
3717c478bd9Sstevel@tonic-gate 			return (EBUSY);
3727c478bd9Sstevel@tonic-gate 		if (!cpr_reusable_mode) {
3737c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, modefmt, "not");
3747c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
3757c478bd9Sstevel@tonic-gate 			return (EINVAL);
3767c478bd9Sstevel@tonic-gate 		}
3777c478bd9Sstevel@tonic-gate 		rc = i_cpr_reusefini();
3787c478bd9Sstevel@tonic-gate 		mutex_exit(&cpr_slock);
3797c478bd9Sstevel@tonic-gate 		return (rc);
3807c478bd9Sstevel@tonic-gate 	}
3812df1fe9cSrandyf #endif
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 	/*
3847c478bd9Sstevel@tonic-gate 	 * acquire cpr serial lock and init cpr state structure.
3857c478bd9Sstevel@tonic-gate 	 */
3867c478bd9Sstevel@tonic-gate 	if (rc = cpr_init(fcn))
3877c478bd9Sstevel@tonic-gate 		return (rc);
3887c478bd9Sstevel@tonic-gate 
3892df1fe9cSrandyf #if defined(__sparc)
3907c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSABLE) {
3917c478bd9Sstevel@tonic-gate 		if ((rc = i_cpr_check_cprinfo()) != 0)  {
3927c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
3937c478bd9Sstevel@tonic-gate 			return (rc);
3947c478bd9Sstevel@tonic-gate 		}
3957c478bd9Sstevel@tonic-gate 	}
3962df1fe9cSrandyf #endif
3977c478bd9Sstevel@tonic-gate 
3987c478bd9Sstevel@tonic-gate 	/*
3997c478bd9Sstevel@tonic-gate 	 * Call the main cpr routine. If we are successful, we will be coming
4007c478bd9Sstevel@tonic-gate 	 * down from the resume side, otherwise we are still in suspend.
4017c478bd9Sstevel@tonic-gate 	 */
4027c478bd9Sstevel@tonic-gate 	cpr_err(CE_CONT, "System is being suspended");
4032df1fe9cSrandyf 	if (rc = cpr_main(cpr_sleeptype)) {
4047c478bd9Sstevel@tonic-gate 		CPR->c_flags |= C_ERROR;
4052df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: Suspend operation failed.\n"))
4067c478bd9Sstevel@tonic-gate 		cpr_err(CE_NOTE, "Suspend operation failed.");
4077c478bd9Sstevel@tonic-gate 	} else if (CPR->c_flags & C_SUSPENDING) {
4082df1fe9cSrandyf 
4092df1fe9cSrandyf 		/*
4102df1fe9cSrandyf 		 * In the suspend to RAM case, by the time we get
4112df1fe9cSrandyf 		 * control back we're already resumed
4122df1fe9cSrandyf 		 */
4132df1fe9cSrandyf 		if (cpr_sleeptype == CPR_TORAM) {
4142df1fe9cSrandyf 			PMD(PMD_SX, ("cpr: cpr CPR_TORAM done\n"))
4152df1fe9cSrandyf 			cpr_done();
4162df1fe9cSrandyf 			return (rc);
4172df1fe9cSrandyf 		}
4182df1fe9cSrandyf 
4192df1fe9cSrandyf #if defined(__sparc)
4202df1fe9cSrandyf 
4212df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: Suspend operation succeeded.\n"))
4227c478bd9Sstevel@tonic-gate 		/*
4237c478bd9Sstevel@tonic-gate 		 * Back from a successful checkpoint
4247c478bd9Sstevel@tonic-gate 		 */
4257c478bd9Sstevel@tonic-gate 		if (fcn == AD_CPR_TESTZ || fcn == AD_CPR_TESTNOZ) {
426edc40228Sachartre 			mdboot(0, AD_BOOT, "", B_FALSE);
4277c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4287c478bd9Sstevel@tonic-gate 		}
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 		/* make sure there are no more changes to the device tree */
4312df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: dev tree freeze\n"))
4327c478bd9Sstevel@tonic-gate 		devtree_freeze();
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 		/*
4357c478bd9Sstevel@tonic-gate 		 * stop other cpus and raise our priority.  since there is only
4367c478bd9Sstevel@tonic-gate 		 * one active cpu after this, and our priority will be too high
4377c478bd9Sstevel@tonic-gate 		 * for us to be preempted, we're essentially single threaded
4387c478bd9Sstevel@tonic-gate 		 * from here on out.
4397c478bd9Sstevel@tonic-gate 		 */
4402df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: stop other cpus\n"))
4412df1fe9cSrandyf 		i_cpr_stop_other_cpus();
4422df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: spl6\n"))
4437c478bd9Sstevel@tonic-gate 		(void) spl6();
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate 		/*
4467c478bd9Sstevel@tonic-gate 		 * try and reset leaf devices.  reset_leaves() should only
4477c478bd9Sstevel@tonic-gate 		 * be called when there are no other threads that could be
4487c478bd9Sstevel@tonic-gate 		 * accessing devices
4497c478bd9Sstevel@tonic-gate 		 */
4502df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: reset leaves\n"))
4517c478bd9Sstevel@tonic-gate 		reset_leaves();
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate 		/*
4542df1fe9cSrandyf 		 * If i_cpr_power_down() succeeds, it'll not return
4557c478bd9Sstevel@tonic-gate 		 *
4567c478bd9Sstevel@tonic-gate 		 * Drives with write-cache enabled need to flush
4577c478bd9Sstevel@tonic-gate 		 * their cache.
4587c478bd9Sstevel@tonic-gate 		 */
4592df1fe9cSrandyf 		if (fcn != AD_CPR_TESTHALT) {
4602df1fe9cSrandyf 			PMD(PMD_SX, ("cpr: power down\n"))
4612df1fe9cSrandyf 			(void) i_cpr_power_down(cpr_sleeptype);
4622df1fe9cSrandyf 		}
4632df1fe9cSrandyf 		ASSERT(cpr_sleeptype == CPR_TODISK);
4642df1fe9cSrandyf 		/* currently CPR_TODISK comes back via a boot path */
465ae115bc7Smrj 		CPR_DEBUG(CPR_DEBUG1, "(Done. Please Switch Off)\n");
4667c478bd9Sstevel@tonic-gate 		halt(NULL);
4677c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
4682df1fe9cSrandyf #endif
4697c478bd9Sstevel@tonic-gate 	}
4702df1fe9cSrandyf 	PMD(PMD_SX, ("cpr: cpr done\n"))
4717c478bd9Sstevel@tonic-gate 	cpr_done();
4727c478bd9Sstevel@tonic-gate 	return (rc);
4737c478bd9Sstevel@tonic-gate }
474