xref: /illumos-gate/usr/src/uts/common/cpr/cpr_mod.c (revision b97d6ca7)
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.
2489b43686SBayard Bell  * Copyright (c) 2011 Bayard G. Bell. All rights reserved.
25*b97d6ca7SMilan Jurik  * Copyright 2012 Milan Jurik. All rights reserved.
267c478bd9Sstevel@tonic-gate  */
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 *);
47e7cbe64fSgw 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);
57*b97d6ca7SMilan Jurik extern int i_cpr_power_down(int);
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 int cpr_reusable_mode;
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate kmutex_t	cpr_slock;	/* cpr serial lock */
747c478bd9Sstevel@tonic-gate cpr_t		cpr_state;
757c478bd9Sstevel@tonic-gate int		cpr_debug;
767c478bd9Sstevel@tonic-gate int		cpr_test_mode; /* true if called via uadmin testmode */
772df1fe9cSrandyf int		cpr_test_point = LOOP_BACK_NONE;	/* cpr test point */
782df1fe9cSrandyf int		cpr_mp_enable = 0;	/* set to 1 to enable MP suspend */
792df1fe9cSrandyf major_t		cpr_device = 0;		/* major number for S3 on one device */
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate /*
827c478bd9Sstevel@tonic-gate  * All the loadable module related code follows
837c478bd9Sstevel@tonic-gate  */
847c478bd9Sstevel@tonic-gate int
857c478bd9Sstevel@tonic-gate _init(void)
867c478bd9Sstevel@tonic-gate {
877c478bd9Sstevel@tonic-gate 	register int e;
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate 	if ((e = mod_install(&modlinkage)) == 0) {
907c478bd9Sstevel@tonic-gate 		mutex_init(&cpr_slock, NULL, MUTEX_DEFAULT, NULL);
917c478bd9Sstevel@tonic-gate 	}
927c478bd9Sstevel@tonic-gate 	return (e);
937c478bd9Sstevel@tonic-gate }
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate int
967c478bd9Sstevel@tonic-gate _fini(void)
977c478bd9Sstevel@tonic-gate {
987c478bd9Sstevel@tonic-gate 	register int e;
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate 	if ((e = mod_remove(&modlinkage)) == 0) {
1017c478bd9Sstevel@tonic-gate 		mutex_destroy(&cpr_slock);
1027c478bd9Sstevel@tonic-gate 	}
1037c478bd9Sstevel@tonic-gate 	return (e);
1047c478bd9Sstevel@tonic-gate }
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate int
1077c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
1087c478bd9Sstevel@tonic-gate {
1097c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1107c478bd9Sstevel@tonic-gate }
1117c478bd9Sstevel@tonic-gate 
1122df1fe9cSrandyf static
1132df1fe9cSrandyf int
1142df1fe9cSrandyf atoi(char *p)
1152df1fe9cSrandyf {
1162df1fe9cSrandyf 	int	i;
1172df1fe9cSrandyf 
1182df1fe9cSrandyf 	i = (*p++ - '0');
1192df1fe9cSrandyf 
1202df1fe9cSrandyf 	while (*p != '\0')
1212df1fe9cSrandyf 		i = 10 * i + (*p++ - '0');
1222df1fe9cSrandyf 
1232df1fe9cSrandyf 	return (i);
1242df1fe9cSrandyf }
1252df1fe9cSrandyf 
1267c478bd9Sstevel@tonic-gate int
1272df1fe9cSrandyf cpr(int fcn, void *mdep)
1287c478bd9Sstevel@tonic-gate {
1292df1fe9cSrandyf 
1302df1fe9cSrandyf #if defined(__sparc)
1317c478bd9Sstevel@tonic-gate 	static const char noswapstr[] = "reusable statefile requires "
1327c478bd9Sstevel@tonic-gate 	    "that no swap area be configured.\n";
1337c478bd9Sstevel@tonic-gate 	static const char blockstr[] = "reusable statefile must be "
1347c478bd9Sstevel@tonic-gate 	    "a block device.  See power.conf(4) and pmconfig(1M).\n";
1357c478bd9Sstevel@tonic-gate 	static const char normalfmt[] = "cannot run normal "
1367c478bd9Sstevel@tonic-gate 	    "checkpoint/resume when in reusable statefile mode. "
1377c478bd9Sstevel@tonic-gate 	    "use uadmin A_FREEZE AD_REUSEFINI (uadmin %d %d) "
1387c478bd9Sstevel@tonic-gate 	    "to exit reusable statefile mode.\n";
1397c478bd9Sstevel@tonic-gate 	static const char modefmt[] = "%s in reusable mode.\n";
1402df1fe9cSrandyf #endif
1417c478bd9Sstevel@tonic-gate 	register int rc = 0;
1422df1fe9cSrandyf 	int cpr_sleeptype;
1437c478bd9Sstevel@tonic-gate 
1442df1fe9cSrandyf 	/*
1452df1fe9cSrandyf 	 * First, reject commands that we don't (yet) support on this arch.
1462df1fe9cSrandyf 	 * This is easier to understand broken out like this than grotting
1472df1fe9cSrandyf 	 * through the second switch below.
1482df1fe9cSrandyf 	 */
1492df1fe9cSrandyf 
1502df1fe9cSrandyf 	switch (fcn) {
1512df1fe9cSrandyf #if defined(__sparc)
1522df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_RAM:
1532df1fe9cSrandyf 	case AD_SUSPEND_TO_RAM:
1542df1fe9cSrandyf 		return (ENOTSUP);
1552df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_DISK:
1562df1fe9cSrandyf 	case AD_SUSPEND_TO_DISK:
1572df1fe9cSrandyf 	case AD_CPR_REUSEINIT:
1582df1fe9cSrandyf 	case AD_CPR_NOCOMPRESS:
1592df1fe9cSrandyf 	case AD_CPR_FORCE:
1602df1fe9cSrandyf 	case AD_CPR_REUSABLE:
1612df1fe9cSrandyf 	case AD_CPR_REUSEFINI:
1622df1fe9cSrandyf 	case AD_CPR_TESTZ:
1632df1fe9cSrandyf 	case AD_CPR_TESTNOZ:
1642df1fe9cSrandyf 	case AD_CPR_TESTHALT:
1652df1fe9cSrandyf 	case AD_CPR_SUSP_DEVICES:
1662df1fe9cSrandyf 		cpr_sleeptype = CPR_TODISK;
1672df1fe9cSrandyf 		break;
1682df1fe9cSrandyf #endif
1692df1fe9cSrandyf #if defined(__x86)
1702df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_DISK:
1712df1fe9cSrandyf 	case AD_SUSPEND_TO_DISK:
1722df1fe9cSrandyf 	case AD_CPR_REUSEINIT:
1732df1fe9cSrandyf 	case AD_CPR_NOCOMPRESS:
1742df1fe9cSrandyf 	case AD_CPR_FORCE:
1752df1fe9cSrandyf 	case AD_CPR_REUSABLE:
1762df1fe9cSrandyf 	case AD_CPR_REUSEFINI:
1772df1fe9cSrandyf 	case AD_CPR_TESTZ:
1782df1fe9cSrandyf 	case AD_CPR_TESTNOZ:
1792df1fe9cSrandyf 	case AD_CPR_TESTHALT:
1802df1fe9cSrandyf 	case AD_CPR_PRINT:
1812df1fe9cSrandyf 		return (ENOTSUP);
1822df1fe9cSrandyf 	/* The DEV_* values need to be removed after sys-syspend is fixed */
1832df1fe9cSrandyf 	case DEV_CHECK_SUSPEND_TO_RAM:
1842df1fe9cSrandyf 	case DEV_SUSPEND_TO_RAM:
1852df1fe9cSrandyf 	case AD_CPR_SUSP_DEVICES:
1862df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_RAM:
1872df1fe9cSrandyf 	case AD_SUSPEND_TO_RAM:
1882df1fe9cSrandyf 	case AD_LOOPBACK_SUSPEND_TO_RAM_PASS:
1892df1fe9cSrandyf 	case AD_LOOPBACK_SUSPEND_TO_RAM_FAIL:
1902df1fe9cSrandyf 	case AD_FORCE_SUSPEND_TO_RAM:
1912df1fe9cSrandyf 	case AD_DEVICE_SUSPEND_TO_RAM:
1924716fd88Sjan 		cpr_sleeptype = CPR_TORAM;
1932df1fe9cSrandyf 		break;
1942df1fe9cSrandyf #endif
1952df1fe9cSrandyf 	}
1962df1fe9cSrandyf #if defined(__sparc)
1977c478bd9Sstevel@tonic-gate 	/*
1987c478bd9Sstevel@tonic-gate 	 * Need to know if we're in reusable mode, but we will likely have
1997c478bd9Sstevel@tonic-gate 	 * rebooted since REUSEINIT, so we have to get the info from the
2007c478bd9Sstevel@tonic-gate 	 * file system
2017c478bd9Sstevel@tonic-gate 	 */
2027c478bd9Sstevel@tonic-gate 	if (!cpr_reusable_mode)
2037c478bd9Sstevel@tonic-gate 		cpr_reusable_mode = cpr_get_reusable_mode();
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	cpr_forget_cprconfig();
2062df1fe9cSrandyf #endif
2072df1fe9cSrandyf 
2087c478bd9Sstevel@tonic-gate 	switch (fcn) {
2097c478bd9Sstevel@tonic-gate 
2102df1fe9cSrandyf #if defined(__sparc)
2117c478bd9Sstevel@tonic-gate 	case AD_CPR_REUSEINIT:
2127c478bd9Sstevel@tonic-gate 		if (!i_cpr_reusable_supported())
2137c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2147c478bd9Sstevel@tonic-gate 		if (!cpr_statefile_is_spec()) {
2157c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, blockstr);
2167c478bd9Sstevel@tonic-gate 			return (EINVAL);
2177c478bd9Sstevel@tonic-gate 		}
2187c478bd9Sstevel@tonic-gate 		if ((rc = cpr_check_spec_statefile()) != 0)
2197c478bd9Sstevel@tonic-gate 			return (rc);
2207c478bd9Sstevel@tonic-gate 		if (swapinfo) {
2217c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, noswapstr);
2227c478bd9Sstevel@tonic-gate 			return (EINVAL);
2237c478bd9Sstevel@tonic-gate 		}
2247c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
2257c478bd9Sstevel@tonic-gate 		break;
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 	case AD_CPR_NOCOMPRESS:
2287c478bd9Sstevel@tonic-gate 	case AD_CPR_COMPRESS:
2297c478bd9Sstevel@tonic-gate 	case AD_CPR_FORCE:
2307c478bd9Sstevel@tonic-gate 		if (cpr_reusable_mode) {
2317c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, normalfmt, A_FREEZE, AD_REUSEFINI);
2327c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2337c478bd9Sstevel@tonic-gate 		}
2347c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
2357c478bd9Sstevel@tonic-gate 		break;
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	case AD_CPR_REUSABLE:
2387c478bd9Sstevel@tonic-gate 		if (!i_cpr_reusable_supported())
2397c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2407c478bd9Sstevel@tonic-gate 		if (!cpr_statefile_is_spec()) {
2417c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, blockstr);
2427c478bd9Sstevel@tonic-gate 			return (EINVAL);
2437c478bd9Sstevel@tonic-gate 		}
2447c478bd9Sstevel@tonic-gate 		if ((rc = cpr_check_spec_statefile()) != 0)
2457c478bd9Sstevel@tonic-gate 			return (rc);
2467c478bd9Sstevel@tonic-gate 		if (swapinfo) {
2477c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, noswapstr);
2487c478bd9Sstevel@tonic-gate 			return (EINVAL);
2497c478bd9Sstevel@tonic-gate 		}
2507c478bd9Sstevel@tonic-gate 		if ((rc = cpr_reusable_mount_check()) != 0)
2517c478bd9Sstevel@tonic-gate 			return (rc);
2527c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
2537c478bd9Sstevel@tonic-gate 		break;
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 	case AD_CPR_REUSEFINI:
2567c478bd9Sstevel@tonic-gate 		if (!i_cpr_reusable_supported())
2577c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2587c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
2597c478bd9Sstevel@tonic-gate 		break;
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 	case AD_CPR_TESTZ:
2627c478bd9Sstevel@tonic-gate 	case AD_CPR_TESTNOZ:
2637c478bd9Sstevel@tonic-gate 	case AD_CPR_TESTHALT:
2647c478bd9Sstevel@tonic-gate 		if (cpr_reusable_mode) {
2657c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, normalfmt, A_FREEZE, AD_REUSEFINI);
2667c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2677c478bd9Sstevel@tonic-gate 		}
2687c478bd9Sstevel@tonic-gate 		cpr_test_mode = 1;
2697c478bd9Sstevel@tonic-gate 		break;
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 	case AD_CPR_CHECK:
2722df1fe9cSrandyf 		if (!i_cpr_is_supported(cpr_sleeptype) || cpr_reusable_mode)
2737c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
2747c478bd9Sstevel@tonic-gate 		return (0);
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	case AD_CPR_PRINT:
2777c478bd9Sstevel@tonic-gate 		CPR_STAT_EVENT_END("POST CPR DELAY");
2787c478bd9Sstevel@tonic-gate 		cpr_stat_event_print();
2797c478bd9Sstevel@tonic-gate 		return (0);
2802df1fe9cSrandyf #endif
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG0:
2837c478bd9Sstevel@tonic-gate 		cpr_debug = 0;
2847c478bd9Sstevel@tonic-gate 		return (0);
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG1:
2877c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG2:
2887c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG3:
2897c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG4:
2907c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG5:
2917c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG7:
2927c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG8:
2937c478bd9Sstevel@tonic-gate 		cpr_debug |= CPR_DEBUG_BIT(fcn);
2947c478bd9Sstevel@tonic-gate 		return (0);
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG9:
297ae115bc7Smrj 		cpr_debug |= CPR_DEBUG6;
2987c478bd9Sstevel@tonic-gate 		return (0);
2997c478bd9Sstevel@tonic-gate 
3002df1fe9cSrandyf 	/* The DEV_* values need to be removed after sys-syspend is fixed */
3012df1fe9cSrandyf 	case DEV_CHECK_SUSPEND_TO_RAM:
3022df1fe9cSrandyf 	case DEV_SUSPEND_TO_RAM:
3032df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_RAM:
3042df1fe9cSrandyf 	case AD_SUSPEND_TO_RAM:
3052df1fe9cSrandyf 		cpr_test_point = LOOP_BACK_NONE;
3062df1fe9cSrandyf 		break;
3072df1fe9cSrandyf 
3082df1fe9cSrandyf 	case AD_LOOPBACK_SUSPEND_TO_RAM_PASS:
3092df1fe9cSrandyf 		cpr_test_point = LOOP_BACK_PASS;
3102df1fe9cSrandyf 		break;
3112df1fe9cSrandyf 
3122df1fe9cSrandyf 	case AD_LOOPBACK_SUSPEND_TO_RAM_FAIL:
3132df1fe9cSrandyf 		cpr_test_point = LOOP_BACK_FAIL;
3142df1fe9cSrandyf 		break;
3152df1fe9cSrandyf 
3162df1fe9cSrandyf 	case AD_FORCE_SUSPEND_TO_RAM:
3172df1fe9cSrandyf 		cpr_test_point = FORCE_SUSPEND_TO_RAM;
3182df1fe9cSrandyf 		break;
3192df1fe9cSrandyf 
3202df1fe9cSrandyf 	case AD_DEVICE_SUSPEND_TO_RAM:
321c36fc336Srandyf 		if (mdep == NULL) {
322c36fc336Srandyf 			/* Didn't pass enough arguments */
323c36fc336Srandyf 			return (EINVAL);
324c36fc336Srandyf 		}
3252df1fe9cSrandyf 		cpr_test_point = DEVICE_SUSPEND_TO_RAM;
3262df1fe9cSrandyf 		cpr_device = (major_t)atoi((char *)mdep);
3272df1fe9cSrandyf 		break;
3282df1fe9cSrandyf 
3292df1fe9cSrandyf 	case AD_CPR_SUSP_DEVICES:
3302df1fe9cSrandyf 		cpr_test_point = FORCE_SUSPEND_TO_RAM;
3312df1fe9cSrandyf 		if (cpr_suspend_devices(ddi_root_node()) != DDI_SUCCESS)
3322df1fe9cSrandyf 			cmn_err(CE_WARN,
3332df1fe9cSrandyf 			    "Some devices did not suspend "
3342df1fe9cSrandyf 			    "and may be unusable");
3352df1fe9cSrandyf 		(void) cpr_resume_devices(ddi_root_node(), 0);
3362df1fe9cSrandyf 		return (0);
3372df1fe9cSrandyf 
3387c478bd9Sstevel@tonic-gate 	default:
3397c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
3407c478bd9Sstevel@tonic-gate 	}
3417c478bd9Sstevel@tonic-gate 
342c5c327a3SRichard Lowe 	if (!i_cpr_is_supported(cpr_sleeptype))
3437c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
3447c478bd9Sstevel@tonic-gate 
345c5c327a3SRichard Lowe #if defined(__sparc)
346c5c327a3SRichard Lowe 	if ((cpr_sleeptype == CPR_TODISK &&
347c5c327a3SRichard Lowe 	    !cpr_is_ufs(rootvfs) && !cpr_is_zfs(rootvfs)))
348c5c327a3SRichard Lowe 		return (ENOTSUP);
349c5c327a3SRichard Lowe #endif
350c5c327a3SRichard Lowe 
3512df1fe9cSrandyf 	if (fcn == AD_CHECK_SUSPEND_TO_RAM ||
3522df1fe9cSrandyf 	    fcn == DEV_CHECK_SUSPEND_TO_RAM) {
3532df1fe9cSrandyf 		ASSERT(i_cpr_is_supported(cpr_sleeptype));
3542df1fe9cSrandyf 		return (0);
3552df1fe9cSrandyf 	}
3562df1fe9cSrandyf 
3572df1fe9cSrandyf #if defined(__sparc)
3587c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSEINIT) {
3597c478bd9Sstevel@tonic-gate 		if (mutex_tryenter(&cpr_slock) == 0)
3607c478bd9Sstevel@tonic-gate 			return (EBUSY);
3617c478bd9Sstevel@tonic-gate 		if (cpr_reusable_mode) {
3627c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, modefmt, "already");
3637c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
3647c478bd9Sstevel@tonic-gate 			return (EBUSY);
3657c478bd9Sstevel@tonic-gate 		}
3667c478bd9Sstevel@tonic-gate 		rc = i_cpr_reuseinit();
3677c478bd9Sstevel@tonic-gate 		mutex_exit(&cpr_slock);
3687c478bd9Sstevel@tonic-gate 		return (rc);
3697c478bd9Sstevel@tonic-gate 	}
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSEFINI) {
3727c478bd9Sstevel@tonic-gate 		if (mutex_tryenter(&cpr_slock) == 0)
3737c478bd9Sstevel@tonic-gate 			return (EBUSY);
3747c478bd9Sstevel@tonic-gate 		if (!cpr_reusable_mode) {
3757c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, modefmt, "not");
3767c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
3777c478bd9Sstevel@tonic-gate 			return (EINVAL);
3787c478bd9Sstevel@tonic-gate 		}
3797c478bd9Sstevel@tonic-gate 		rc = i_cpr_reusefini();
3807c478bd9Sstevel@tonic-gate 		mutex_exit(&cpr_slock);
3817c478bd9Sstevel@tonic-gate 		return (rc);
3827c478bd9Sstevel@tonic-gate 	}
3832df1fe9cSrandyf #endif
3847c478bd9Sstevel@tonic-gate 
3857c478bd9Sstevel@tonic-gate 	/*
3867c478bd9Sstevel@tonic-gate 	 * acquire cpr serial lock and init cpr state structure.
3877c478bd9Sstevel@tonic-gate 	 */
3887c478bd9Sstevel@tonic-gate 	if (rc = cpr_init(fcn))
3897c478bd9Sstevel@tonic-gate 		return (rc);
3907c478bd9Sstevel@tonic-gate 
3912df1fe9cSrandyf #if defined(__sparc)
3927c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSABLE) {
3937c478bd9Sstevel@tonic-gate 		if ((rc = i_cpr_check_cprinfo()) != 0)  {
3947c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
3957c478bd9Sstevel@tonic-gate 			return (rc);
3967c478bd9Sstevel@tonic-gate 		}
3977c478bd9Sstevel@tonic-gate 	}
3982df1fe9cSrandyf #endif
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate 	/*
4017c478bd9Sstevel@tonic-gate 	 * Call the main cpr routine. If we are successful, we will be coming
4027c478bd9Sstevel@tonic-gate 	 * down from the resume side, otherwise we are still in suspend.
4037c478bd9Sstevel@tonic-gate 	 */
4047c478bd9Sstevel@tonic-gate 	cpr_err(CE_CONT, "System is being suspended");
4052df1fe9cSrandyf 	if (rc = cpr_main(cpr_sleeptype)) {
4067c478bd9Sstevel@tonic-gate 		CPR->c_flags |= C_ERROR;
4072df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: Suspend operation failed.\n"))
4087c478bd9Sstevel@tonic-gate 		cpr_err(CE_NOTE, "Suspend operation failed.");
4097c478bd9Sstevel@tonic-gate 	} else if (CPR->c_flags & C_SUSPENDING) {
4102df1fe9cSrandyf 
4112df1fe9cSrandyf 		/*
4122df1fe9cSrandyf 		 * In the suspend to RAM case, by the time we get
4132df1fe9cSrandyf 		 * control back we're already resumed
4142df1fe9cSrandyf 		 */
4152df1fe9cSrandyf 		if (cpr_sleeptype == CPR_TORAM) {
4162df1fe9cSrandyf 			PMD(PMD_SX, ("cpr: cpr CPR_TORAM done\n"))
4172df1fe9cSrandyf 			cpr_done();
4182df1fe9cSrandyf 			return (rc);
4192df1fe9cSrandyf 		}
4202df1fe9cSrandyf 
4212df1fe9cSrandyf #if defined(__sparc)
4222df1fe9cSrandyf 
4232df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: Suspend operation succeeded.\n"))
4247c478bd9Sstevel@tonic-gate 		/*
4257c478bd9Sstevel@tonic-gate 		 * Back from a successful checkpoint
4267c478bd9Sstevel@tonic-gate 		 */
4277c478bd9Sstevel@tonic-gate 		if (fcn == AD_CPR_TESTZ || fcn == AD_CPR_TESTNOZ) {
428edc40228Sachartre 			mdboot(0, AD_BOOT, "", B_FALSE);
4297c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4307c478bd9Sstevel@tonic-gate 		}
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 		/* make sure there are no more changes to the device tree */
4332df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: dev tree freeze\n"))
4347c478bd9Sstevel@tonic-gate 		devtree_freeze();
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 		/*
4377c478bd9Sstevel@tonic-gate 		 * stop other cpus and raise our priority.  since there is only
4387c478bd9Sstevel@tonic-gate 		 * one active cpu after this, and our priority will be too high
4397c478bd9Sstevel@tonic-gate 		 * for us to be preempted, we're essentially single threaded
4407c478bd9Sstevel@tonic-gate 		 * from here on out.
4417c478bd9Sstevel@tonic-gate 		 */
4422df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: stop other cpus\n"))
4432df1fe9cSrandyf 		i_cpr_stop_other_cpus();
4442df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: spl6\n"))
4457c478bd9Sstevel@tonic-gate 		(void) spl6();
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate 		/*
4487c478bd9Sstevel@tonic-gate 		 * try and reset leaf devices.  reset_leaves() should only
4497c478bd9Sstevel@tonic-gate 		 * be called when there are no other threads that could be
4507c478bd9Sstevel@tonic-gate 		 * accessing devices
4517c478bd9Sstevel@tonic-gate 		 */
4522df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: reset leaves\n"))
4537c478bd9Sstevel@tonic-gate 		reset_leaves();
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 		/*
4562df1fe9cSrandyf 		 * If i_cpr_power_down() succeeds, it'll not return
4577c478bd9Sstevel@tonic-gate 		 *
4587c478bd9Sstevel@tonic-gate 		 * Drives with write-cache enabled need to flush
4597c478bd9Sstevel@tonic-gate 		 * their cache.
4607c478bd9Sstevel@tonic-gate 		 */
4612df1fe9cSrandyf 		if (fcn != AD_CPR_TESTHALT) {
4622df1fe9cSrandyf 			PMD(PMD_SX, ("cpr: power down\n"))
4632df1fe9cSrandyf 			(void) i_cpr_power_down(cpr_sleeptype);
4642df1fe9cSrandyf 		}
4652df1fe9cSrandyf 		ASSERT(cpr_sleeptype == CPR_TODISK);
4662df1fe9cSrandyf 		/* currently CPR_TODISK comes back via a boot path */
467ae115bc7Smrj 		CPR_DEBUG(CPR_DEBUG1, "(Done. Please Switch Off)\n");
4687c478bd9Sstevel@tonic-gate 		halt(NULL);
4697c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
4702df1fe9cSrandyf #endif
4717c478bd9Sstevel@tonic-gate 	}
4722df1fe9cSrandyf 	PMD(PMD_SX, ("cpr: cpr done\n"))
4737c478bd9Sstevel@tonic-gate 	cpr_done();
4747c478bd9Sstevel@tonic-gate 	return (rc);
4757c478bd9Sstevel@tonic-gate }
476