xref: /illumos-gate/usr/src/uts/common/cpr/cpr_mod.c (revision 2df1fe9c)
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 
45*2df1fe9cSrandyf 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 
53*2df1fe9cSrandyf extern int cpr_init(int);
54*2df1fe9cSrandyf extern void cpr_done(void);
55*2df1fe9cSrandyf extern void i_cpr_stop_other_cpus(void);
56*2df1fe9cSrandyf extern int i_cpr_power_down();
57*2df1fe9cSrandyf 
58*2df1fe9cSrandyf #if defined(__sparc)
59*2df1fe9cSrandyf extern void cpr_forget_cprconfig(void);
60*2df1fe9cSrandyf #endif
61*2df1fe9cSrandyf 
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 */
78*2df1fe9cSrandyf int		cpr_test_point = LOOP_BACK_NONE;	/* cpr test point */
79*2df1fe9cSrandyf int		cpr_mp_enable = 0;	/* set to 1 to enable MP suspend */
80*2df1fe9cSrandyf 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 
113*2df1fe9cSrandyf static
114*2df1fe9cSrandyf int
115*2df1fe9cSrandyf atoi(char *p)
116*2df1fe9cSrandyf {
117*2df1fe9cSrandyf 	int	i;
118*2df1fe9cSrandyf 
119*2df1fe9cSrandyf 	i = (*p++ - '0');
120*2df1fe9cSrandyf 
121*2df1fe9cSrandyf 	while (*p != '\0')
122*2df1fe9cSrandyf 		i = 10 * i + (*p++ - '0');
123*2df1fe9cSrandyf 
124*2df1fe9cSrandyf 	return (i);
125*2df1fe9cSrandyf }
126*2df1fe9cSrandyf 
1277c478bd9Sstevel@tonic-gate int
128*2df1fe9cSrandyf cpr(int fcn, void *mdep)
1297c478bd9Sstevel@tonic-gate {
130*2df1fe9cSrandyf 
131*2df1fe9cSrandyf #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";
141*2df1fe9cSrandyf #endif
1427c478bd9Sstevel@tonic-gate 	register int rc = 0;
143*2df1fe9cSrandyf 	int cpr_sleeptype;
1447c478bd9Sstevel@tonic-gate 
145*2df1fe9cSrandyf 	/*
146*2df1fe9cSrandyf 	 * First, reject commands that we don't (yet) support on this arch.
147*2df1fe9cSrandyf 	 * This is easier to understand broken out like this than grotting
148*2df1fe9cSrandyf 	 * through the second switch below.
149*2df1fe9cSrandyf 	 */
150*2df1fe9cSrandyf 
151*2df1fe9cSrandyf 	switch (fcn) {
152*2df1fe9cSrandyf #if defined(__sparc)
153*2df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_RAM:
154*2df1fe9cSrandyf 	case AD_SUSPEND_TO_RAM:
155*2df1fe9cSrandyf 		return (ENOTSUP);
156*2df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_DISK:
157*2df1fe9cSrandyf 	case AD_SUSPEND_TO_DISK:
158*2df1fe9cSrandyf 	case AD_CPR_REUSEINIT:
159*2df1fe9cSrandyf 	case AD_CPR_NOCOMPRESS:
160*2df1fe9cSrandyf 	case AD_CPR_FORCE:
161*2df1fe9cSrandyf 	case AD_CPR_REUSABLE:
162*2df1fe9cSrandyf 	case AD_CPR_REUSEFINI:
163*2df1fe9cSrandyf 	case AD_CPR_TESTZ:
164*2df1fe9cSrandyf 	case AD_CPR_TESTNOZ:
165*2df1fe9cSrandyf 	case AD_CPR_TESTHALT:
166*2df1fe9cSrandyf 	case AD_CPR_SUSP_DEVICES:
167*2df1fe9cSrandyf 		cpr_sleeptype = CPR_TODISK;
168*2df1fe9cSrandyf 		break;
169*2df1fe9cSrandyf #endif
170*2df1fe9cSrandyf #if defined(__x86)
171*2df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_DISK:
172*2df1fe9cSrandyf 	case AD_SUSPEND_TO_DISK:
173*2df1fe9cSrandyf 	case AD_CPR_REUSEINIT:
174*2df1fe9cSrandyf 	case AD_CPR_NOCOMPRESS:
175*2df1fe9cSrandyf 	case AD_CPR_FORCE:
176*2df1fe9cSrandyf 	case AD_CPR_REUSABLE:
177*2df1fe9cSrandyf 	case AD_CPR_REUSEFINI:
178*2df1fe9cSrandyf 	case AD_CPR_TESTZ:
179*2df1fe9cSrandyf 	case AD_CPR_TESTNOZ:
180*2df1fe9cSrandyf 	case AD_CPR_TESTHALT:
181*2df1fe9cSrandyf 	case AD_CPR_PRINT:
182*2df1fe9cSrandyf 		return (ENOTSUP);
183*2df1fe9cSrandyf 	/* The DEV_* values need to be removed after sys-syspend is fixed */
184*2df1fe9cSrandyf 	case DEV_CHECK_SUSPEND_TO_RAM:
185*2df1fe9cSrandyf 	case DEV_SUSPEND_TO_RAM:
186*2df1fe9cSrandyf 	case AD_CPR_SUSP_DEVICES:
187*2df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_RAM:
188*2df1fe9cSrandyf 	case AD_SUSPEND_TO_RAM:
189*2df1fe9cSrandyf 	case AD_LOOPBACK_SUSPEND_TO_RAM_PASS:
190*2df1fe9cSrandyf 	case AD_LOOPBACK_SUSPEND_TO_RAM_FAIL:
191*2df1fe9cSrandyf 	case AD_FORCE_SUSPEND_TO_RAM:
192*2df1fe9cSrandyf 	case AD_DEVICE_SUSPEND_TO_RAM:
193*2df1fe9cSrandyf 		/*
194*2df1fe9cSrandyf 		 * if MP then do not support suspend to RAM, however override
195*2df1fe9cSrandyf 		 * the MP restriction if cpr_mp_enable has been set
196*2df1fe9cSrandyf 		 */
197*2df1fe9cSrandyf 		if (ncpus > 1 && cpr_mp_enable == 0)
198*2df1fe9cSrandyf 			return (ENOTSUP);
199*2df1fe9cSrandyf 		else
200*2df1fe9cSrandyf 			cpr_sleeptype = CPR_TORAM;
201*2df1fe9cSrandyf 		break;
202*2df1fe9cSrandyf #endif
203*2df1fe9cSrandyf 	}
204*2df1fe9cSrandyf #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();
214*2df1fe9cSrandyf #endif
215*2df1fe9cSrandyf 
2167c478bd9Sstevel@tonic-gate 	switch (fcn) {
2177c478bd9Sstevel@tonic-gate 
218*2df1fe9cSrandyf #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:
280*2df1fe9cSrandyf 		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);
288*2df1fe9cSrandyf #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 
308*2df1fe9cSrandyf 	/* The DEV_* values need to be removed after sys-syspend is fixed */
309*2df1fe9cSrandyf 	case DEV_CHECK_SUSPEND_TO_RAM:
310*2df1fe9cSrandyf 	case DEV_SUSPEND_TO_RAM:
311*2df1fe9cSrandyf 	case AD_CHECK_SUSPEND_TO_RAM:
312*2df1fe9cSrandyf 	case AD_SUSPEND_TO_RAM:
313*2df1fe9cSrandyf 		cpr_test_point = LOOP_BACK_NONE;
314*2df1fe9cSrandyf 		break;
315*2df1fe9cSrandyf 
316*2df1fe9cSrandyf 	case AD_LOOPBACK_SUSPEND_TO_RAM_PASS:
317*2df1fe9cSrandyf 		cpr_test_point = LOOP_BACK_PASS;
318*2df1fe9cSrandyf 		break;
319*2df1fe9cSrandyf 
320*2df1fe9cSrandyf 	case AD_LOOPBACK_SUSPEND_TO_RAM_FAIL:
321*2df1fe9cSrandyf 		cpr_test_point = LOOP_BACK_FAIL;
322*2df1fe9cSrandyf 		break;
323*2df1fe9cSrandyf 
324*2df1fe9cSrandyf 	case AD_FORCE_SUSPEND_TO_RAM:
325*2df1fe9cSrandyf 		cpr_test_point = FORCE_SUSPEND_TO_RAM;
326*2df1fe9cSrandyf 		break;
327*2df1fe9cSrandyf 
328*2df1fe9cSrandyf 	case AD_DEVICE_SUSPEND_TO_RAM:
329*2df1fe9cSrandyf 		cpr_test_point = DEVICE_SUSPEND_TO_RAM;
330*2df1fe9cSrandyf 		cpr_device = (major_t)atoi((char *)mdep);
331*2df1fe9cSrandyf 		break;
332*2df1fe9cSrandyf 
333*2df1fe9cSrandyf 	case AD_CPR_SUSP_DEVICES:
334*2df1fe9cSrandyf 		cpr_test_point = FORCE_SUSPEND_TO_RAM;
335*2df1fe9cSrandyf 		if (cpr_suspend_devices(ddi_root_node()) != DDI_SUCCESS)
336*2df1fe9cSrandyf 			cmn_err(CE_WARN,
337*2df1fe9cSrandyf 			    "Some devices did not suspend "
338*2df1fe9cSrandyf 			    "and may be unusable");
339*2df1fe9cSrandyf 		(void) cpr_resume_devices(ddi_root_node(), 0);
340*2df1fe9cSrandyf 		return (0);
341*2df1fe9cSrandyf 
3427c478bd9Sstevel@tonic-gate 	default:
3437c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
3447c478bd9Sstevel@tonic-gate 	}
3457c478bd9Sstevel@tonic-gate 
346*2df1fe9cSrandyf 	if (!i_cpr_is_supported(cpr_sleeptype) ||
347*2df1fe9cSrandyf 	    (cpr_sleeptype == CPR_TODISK && !cpr_is_ufs(rootvfs)))
3487c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
3497c478bd9Sstevel@tonic-gate 
350*2df1fe9cSrandyf 	if (fcn == AD_CHECK_SUSPEND_TO_RAM ||
351*2df1fe9cSrandyf 	    fcn == DEV_CHECK_SUSPEND_TO_RAM) {
352*2df1fe9cSrandyf 		ASSERT(i_cpr_is_supported(cpr_sleeptype));
353*2df1fe9cSrandyf 		return (0);
354*2df1fe9cSrandyf 	}
355*2df1fe9cSrandyf 
356*2df1fe9cSrandyf #if defined(__sparc)
3577c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSEINIT) {
3587c478bd9Sstevel@tonic-gate 		if (mutex_tryenter(&cpr_slock) == 0)
3597c478bd9Sstevel@tonic-gate 			return (EBUSY);
3607c478bd9Sstevel@tonic-gate 		if (cpr_reusable_mode) {
3617c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, modefmt, "already");
3627c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
3637c478bd9Sstevel@tonic-gate 			return (EBUSY);
3647c478bd9Sstevel@tonic-gate 		}
3657c478bd9Sstevel@tonic-gate 		rc = i_cpr_reuseinit();
3667c478bd9Sstevel@tonic-gate 		mutex_exit(&cpr_slock);
3677c478bd9Sstevel@tonic-gate 		return (rc);
3687c478bd9Sstevel@tonic-gate 	}
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSEFINI) {
3717c478bd9Sstevel@tonic-gate 		if (mutex_tryenter(&cpr_slock) == 0)
3727c478bd9Sstevel@tonic-gate 			return (EBUSY);
3737c478bd9Sstevel@tonic-gate 		if (!cpr_reusable_mode) {
3747c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, modefmt, "not");
3757c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
3767c478bd9Sstevel@tonic-gate 			return (EINVAL);
3777c478bd9Sstevel@tonic-gate 		}
3787c478bd9Sstevel@tonic-gate 		rc = i_cpr_reusefini();
3797c478bd9Sstevel@tonic-gate 		mutex_exit(&cpr_slock);
3807c478bd9Sstevel@tonic-gate 		return (rc);
3817c478bd9Sstevel@tonic-gate 	}
382*2df1fe9cSrandyf #endif
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 	/*
3857c478bd9Sstevel@tonic-gate 	 * acquire cpr serial lock and init cpr state structure.
3867c478bd9Sstevel@tonic-gate 	 */
3877c478bd9Sstevel@tonic-gate 	if (rc = cpr_init(fcn))
3887c478bd9Sstevel@tonic-gate 		return (rc);
3897c478bd9Sstevel@tonic-gate 
390*2df1fe9cSrandyf #if defined(__sparc)
3917c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSABLE) {
3927c478bd9Sstevel@tonic-gate 		if ((rc = i_cpr_check_cprinfo()) != 0)  {
3937c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
3947c478bd9Sstevel@tonic-gate 			return (rc);
3957c478bd9Sstevel@tonic-gate 		}
3967c478bd9Sstevel@tonic-gate 	}
397*2df1fe9cSrandyf #endif
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 	/*
4007c478bd9Sstevel@tonic-gate 	 * Call the main cpr routine. If we are successful, we will be coming
4017c478bd9Sstevel@tonic-gate 	 * down from the resume side, otherwise we are still in suspend.
4027c478bd9Sstevel@tonic-gate 	 */
4037c478bd9Sstevel@tonic-gate 	cpr_err(CE_CONT, "System is being suspended");
404*2df1fe9cSrandyf 	if (rc = cpr_main(cpr_sleeptype)) {
4057c478bd9Sstevel@tonic-gate 		CPR->c_flags |= C_ERROR;
406*2df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: Suspend operation failed.\n"))
4077c478bd9Sstevel@tonic-gate 		cpr_err(CE_NOTE, "Suspend operation failed.");
4087c478bd9Sstevel@tonic-gate 	} else if (CPR->c_flags & C_SUSPENDING) {
409*2df1fe9cSrandyf 
410*2df1fe9cSrandyf 		/*
411*2df1fe9cSrandyf 		 * In the suspend to RAM case, by the time we get
412*2df1fe9cSrandyf 		 * control back we're already resumed
413*2df1fe9cSrandyf 		 */
414*2df1fe9cSrandyf 		if (cpr_sleeptype == CPR_TORAM) {
415*2df1fe9cSrandyf 			PMD(PMD_SX, ("cpr: cpr CPR_TORAM done\n"))
416*2df1fe9cSrandyf 			cpr_done();
417*2df1fe9cSrandyf 			return (rc);
418*2df1fe9cSrandyf 		}
419*2df1fe9cSrandyf 
420*2df1fe9cSrandyf #if defined(__sparc)
421*2df1fe9cSrandyf 
422*2df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: Suspend operation succeeded.\n"))
4237c478bd9Sstevel@tonic-gate 		/*
4247c478bd9Sstevel@tonic-gate 		 * Back from a successful checkpoint
4257c478bd9Sstevel@tonic-gate 		 */
4267c478bd9Sstevel@tonic-gate 		if (fcn == AD_CPR_TESTZ || fcn == AD_CPR_TESTNOZ) {
427edc40228Sachartre 			mdboot(0, AD_BOOT, "", B_FALSE);
4287c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4297c478bd9Sstevel@tonic-gate 		}
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate 		/* make sure there are no more changes to the device tree */
432*2df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: dev tree freeze\n"))
4337c478bd9Sstevel@tonic-gate 		devtree_freeze();
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 		/*
4367c478bd9Sstevel@tonic-gate 		 * stop other cpus and raise our priority.  since there is only
4377c478bd9Sstevel@tonic-gate 		 * one active cpu after this, and our priority will be too high
4387c478bd9Sstevel@tonic-gate 		 * for us to be preempted, we're essentially single threaded
4397c478bd9Sstevel@tonic-gate 		 * from here on out.
4407c478bd9Sstevel@tonic-gate 		 */
441*2df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: stop other cpus\n"))
442*2df1fe9cSrandyf 		i_cpr_stop_other_cpus();
443*2df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: spl6\n"))
4447c478bd9Sstevel@tonic-gate 		(void) spl6();
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 		/*
4477c478bd9Sstevel@tonic-gate 		 * try and reset leaf devices.  reset_leaves() should only
4487c478bd9Sstevel@tonic-gate 		 * be called when there are no other threads that could be
4497c478bd9Sstevel@tonic-gate 		 * accessing devices
4507c478bd9Sstevel@tonic-gate 		 */
451*2df1fe9cSrandyf 		PMD(PMD_SX, ("cpr: reset leaves\n"))
4527c478bd9Sstevel@tonic-gate 		reset_leaves();
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate 		/*
455*2df1fe9cSrandyf 		 * If i_cpr_power_down() succeeds, it'll not return
4567c478bd9Sstevel@tonic-gate 		 *
4577c478bd9Sstevel@tonic-gate 		 * Drives with write-cache enabled need to flush
4587c478bd9Sstevel@tonic-gate 		 * their cache.
4597c478bd9Sstevel@tonic-gate 		 */
460*2df1fe9cSrandyf 		if (fcn != AD_CPR_TESTHALT) {
461*2df1fe9cSrandyf 			PMD(PMD_SX, ("cpr: power down\n"))
462*2df1fe9cSrandyf 			(void) i_cpr_power_down(cpr_sleeptype);
463*2df1fe9cSrandyf 		}
464*2df1fe9cSrandyf 		ASSERT(cpr_sleeptype == CPR_TODISK);
465*2df1fe9cSrandyf 		/* currently CPR_TODISK comes back via a boot path */
466ae115bc7Smrj 		CPR_DEBUG(CPR_DEBUG1, "(Done. Please Switch Off)\n");
4677c478bd9Sstevel@tonic-gate 		halt(NULL);
4687c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
469*2df1fe9cSrandyf #endif
4707c478bd9Sstevel@tonic-gate 	}
471*2df1fe9cSrandyf 	PMD(PMD_SX, ("cpr: cpr done\n"))
4727c478bd9Sstevel@tonic-gate 	cpr_done();
4737c478bd9Sstevel@tonic-gate 	return (rc);
4747c478bd9Sstevel@tonic-gate }
475