xref: /illumos-gate/usr/src/uts/common/cpr/cpr_mod.c (revision ae115bc7)
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
5*ae115bc7Smrj  * Common Development and Distribution License (the "License").
6*ae115bc7Smrj  * 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 /*
22*ae115bc7Smrj  * 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 
457c478bd9Sstevel@tonic-gate extern int i_cpr_is_supported(void);
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 void cpr_forget_cprconfig(void);
507c478bd9Sstevel@tonic-gate extern int i_cpr_reusable_supported(void);
517c478bd9Sstevel@tonic-gate extern int i_cpr_reusefini(void);
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate extern struct mod_ops mod_miscops;
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate static struct modlmisc modlmisc = {
567c478bd9Sstevel@tonic-gate 	&mod_miscops, "checkpoint resume"
577c478bd9Sstevel@tonic-gate };
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
607c478bd9Sstevel@tonic-gate 	MODREV_1, (void *)&modlmisc, NULL
617c478bd9Sstevel@tonic-gate };
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate char _depends_on[] = "misc/bootdev";	/* i_devname_to_promname() */
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate int cpr_reusable_mode;
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate kmutex_t	cpr_slock;	/* cpr serial lock */
687c478bd9Sstevel@tonic-gate cpr_t		cpr_state;
697c478bd9Sstevel@tonic-gate int		cpr_debug;
707c478bd9Sstevel@tonic-gate int		cpr_test_mode; /* true if called via uadmin testmode */
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate /*
737c478bd9Sstevel@tonic-gate  * All the loadable module related code follows
747c478bd9Sstevel@tonic-gate  */
757c478bd9Sstevel@tonic-gate int
767c478bd9Sstevel@tonic-gate _init(void)
777c478bd9Sstevel@tonic-gate {
787c478bd9Sstevel@tonic-gate 	register int e;
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate 	if ((e = mod_install(&modlinkage)) == 0) {
817c478bd9Sstevel@tonic-gate 		mutex_init(&cpr_slock, NULL, MUTEX_DEFAULT, NULL);
827c478bd9Sstevel@tonic-gate 	}
837c478bd9Sstevel@tonic-gate 	return (e);
847c478bd9Sstevel@tonic-gate }
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate int
877c478bd9Sstevel@tonic-gate _fini(void)
887c478bd9Sstevel@tonic-gate {
897c478bd9Sstevel@tonic-gate 	register int e;
907c478bd9Sstevel@tonic-gate 
917c478bd9Sstevel@tonic-gate 	if ((e = mod_remove(&modlinkage)) == 0) {
927c478bd9Sstevel@tonic-gate 		mutex_destroy(&cpr_slock);
937c478bd9Sstevel@tonic-gate 	}
947c478bd9Sstevel@tonic-gate 	return (e);
957c478bd9Sstevel@tonic-gate }
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate int
987c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
997c478bd9Sstevel@tonic-gate {
1007c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1017c478bd9Sstevel@tonic-gate }
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate int
1047c478bd9Sstevel@tonic-gate cpr(int fcn)
1057c478bd9Sstevel@tonic-gate {
1067c478bd9Sstevel@tonic-gate 	static const char noswapstr[] = "reusable statefile requires "
1077c478bd9Sstevel@tonic-gate 	    "that no swap area be configured.\n";
1087c478bd9Sstevel@tonic-gate 	static const char blockstr[] = "reusable statefile must be "
1097c478bd9Sstevel@tonic-gate 	    "a block device.  See power.conf(4) and pmconfig(1M).\n";
1107c478bd9Sstevel@tonic-gate 	static const char normalfmt[] = "cannot run normal "
1117c478bd9Sstevel@tonic-gate 	    "checkpoint/resume when in reusable statefile mode. "
1127c478bd9Sstevel@tonic-gate 	    "use uadmin A_FREEZE AD_REUSEFINI (uadmin %d %d) "
1137c478bd9Sstevel@tonic-gate 	    "to exit reusable statefile mode.\n";
1147c478bd9Sstevel@tonic-gate 	static const char modefmt[] = "%s in reusable mode.\n";
1157c478bd9Sstevel@tonic-gate 	register int rc = 0;
1167c478bd9Sstevel@tonic-gate 	extern int cpr_init(int);
1177c478bd9Sstevel@tonic-gate 	extern void cpr_done(void);
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate 	/*
1207c478bd9Sstevel@tonic-gate 	 * Need to know if we're in reusable mode, but we will likely have
1217c478bd9Sstevel@tonic-gate 	 * rebooted since REUSEINIT, so we have to get the info from the
1227c478bd9Sstevel@tonic-gate 	 * file system
1237c478bd9Sstevel@tonic-gate 	 */
1247c478bd9Sstevel@tonic-gate 	if (!cpr_reusable_mode)
1257c478bd9Sstevel@tonic-gate 		cpr_reusable_mode = cpr_get_reusable_mode();
1267c478bd9Sstevel@tonic-gate 
1277c478bd9Sstevel@tonic-gate 	cpr_forget_cprconfig();
1287c478bd9Sstevel@tonic-gate 	switch (fcn) {
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 	case AD_CPR_REUSEINIT:
1317c478bd9Sstevel@tonic-gate 		if (!i_cpr_reusable_supported())
1327c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
1337c478bd9Sstevel@tonic-gate 		if (!cpr_statefile_is_spec()) {
1347c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, blockstr);
1357c478bd9Sstevel@tonic-gate 			return (EINVAL);
1367c478bd9Sstevel@tonic-gate 		}
1377c478bd9Sstevel@tonic-gate 		if ((rc = cpr_check_spec_statefile()) != 0)
1387c478bd9Sstevel@tonic-gate 			return (rc);
1397c478bd9Sstevel@tonic-gate 		if (swapinfo) {
1407c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, noswapstr);
1417c478bd9Sstevel@tonic-gate 			return (EINVAL);
1427c478bd9Sstevel@tonic-gate 		}
1437c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
1447c478bd9Sstevel@tonic-gate 		break;
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate 	case AD_CPR_NOCOMPRESS:
1477c478bd9Sstevel@tonic-gate 	case AD_CPR_COMPRESS:
1487c478bd9Sstevel@tonic-gate 	case AD_CPR_FORCE:
1497c478bd9Sstevel@tonic-gate 		if (cpr_reusable_mode) {
1507c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, normalfmt, A_FREEZE, AD_REUSEFINI);
1517c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
1527c478bd9Sstevel@tonic-gate 		}
1537c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
1547c478bd9Sstevel@tonic-gate 		break;
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate 	case AD_CPR_REUSABLE:
1577c478bd9Sstevel@tonic-gate 		if (!i_cpr_reusable_supported())
1587c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
1597c478bd9Sstevel@tonic-gate 		if (!cpr_statefile_is_spec()) {
1607c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, blockstr);
1617c478bd9Sstevel@tonic-gate 			return (EINVAL);
1627c478bd9Sstevel@tonic-gate 		}
1637c478bd9Sstevel@tonic-gate 		if ((rc = cpr_check_spec_statefile()) != 0)
1647c478bd9Sstevel@tonic-gate 			return (rc);
1657c478bd9Sstevel@tonic-gate 		if (swapinfo) {
1667c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, noswapstr);
1677c478bd9Sstevel@tonic-gate 			return (EINVAL);
1687c478bd9Sstevel@tonic-gate 		}
1697c478bd9Sstevel@tonic-gate 		if ((rc = cpr_reusable_mount_check()) != 0)
1707c478bd9Sstevel@tonic-gate 			return (rc);
1717c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
1727c478bd9Sstevel@tonic-gate 		break;
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 	case AD_CPR_REUSEFINI:
1757c478bd9Sstevel@tonic-gate 		if (!i_cpr_reusable_supported())
1767c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
1777c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
1787c478bd9Sstevel@tonic-gate 		break;
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 	case AD_CPR_TESTZ:
1817c478bd9Sstevel@tonic-gate 	case AD_CPR_TESTNOZ:
1827c478bd9Sstevel@tonic-gate 	case AD_CPR_TESTHALT:
1837c478bd9Sstevel@tonic-gate 		if (cpr_reusable_mode) {
1847c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, normalfmt, A_FREEZE, AD_REUSEFINI);
1857c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
1867c478bd9Sstevel@tonic-gate 		}
1877c478bd9Sstevel@tonic-gate 		cpr_test_mode = 1;
1887c478bd9Sstevel@tonic-gate 		break;
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 	case AD_CPR_CHECK:
1917c478bd9Sstevel@tonic-gate 		if (!i_cpr_is_supported() || cpr_reusable_mode)
1927c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
1937c478bd9Sstevel@tonic-gate 		return (0);
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 	case AD_CPR_PRINT:
1967c478bd9Sstevel@tonic-gate 		CPR_STAT_EVENT_END("POST CPR DELAY");
1977c478bd9Sstevel@tonic-gate 		cpr_stat_event_print();
1987c478bd9Sstevel@tonic-gate 		return (0);
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG0:
2017c478bd9Sstevel@tonic-gate 		cpr_debug = 0;
2027c478bd9Sstevel@tonic-gate 		return (0);
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG1:
2057c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG2:
2067c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG3:
2077c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG4:
2087c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG5:
2097c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG7:
2107c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG8:
2117c478bd9Sstevel@tonic-gate 		cpr_debug |= CPR_DEBUG_BIT(fcn);
2127c478bd9Sstevel@tonic-gate 		return (0);
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG9:
215*ae115bc7Smrj 		cpr_debug |= CPR_DEBUG6;
2167c478bd9Sstevel@tonic-gate 		return (0);
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate 	default:
2197c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
2207c478bd9Sstevel@tonic-gate 	}
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	if (!i_cpr_is_supported() || !cpr_is_ufs(rootvfs))
2237c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSEINIT) {
2267c478bd9Sstevel@tonic-gate 		if (mutex_tryenter(&cpr_slock) == 0)
2277c478bd9Sstevel@tonic-gate 			return (EBUSY);
2287c478bd9Sstevel@tonic-gate 		if (cpr_reusable_mode) {
2297c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, modefmt, "already");
2307c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
2317c478bd9Sstevel@tonic-gate 			return (EBUSY);
2327c478bd9Sstevel@tonic-gate 		}
2337c478bd9Sstevel@tonic-gate 		rc = i_cpr_reuseinit();
2347c478bd9Sstevel@tonic-gate 		mutex_exit(&cpr_slock);
2357c478bd9Sstevel@tonic-gate 		return (rc);
2367c478bd9Sstevel@tonic-gate 	}
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSEFINI) {
2397c478bd9Sstevel@tonic-gate 		if (mutex_tryenter(&cpr_slock) == 0)
2407c478bd9Sstevel@tonic-gate 			return (EBUSY);
2417c478bd9Sstevel@tonic-gate 		if (!cpr_reusable_mode) {
2427c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, modefmt, "not");
2437c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
2447c478bd9Sstevel@tonic-gate 			return (EINVAL);
2457c478bd9Sstevel@tonic-gate 		}
2467c478bd9Sstevel@tonic-gate 		rc = i_cpr_reusefini();
2477c478bd9Sstevel@tonic-gate 		mutex_exit(&cpr_slock);
2487c478bd9Sstevel@tonic-gate 		return (rc);
2497c478bd9Sstevel@tonic-gate 	}
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 	/*
2527c478bd9Sstevel@tonic-gate 	 * acquire cpr serial lock and init cpr state structure.
2537c478bd9Sstevel@tonic-gate 	 */
2547c478bd9Sstevel@tonic-gate 	if (rc = cpr_init(fcn))
2557c478bd9Sstevel@tonic-gate 		return (rc);
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSABLE) {
2587c478bd9Sstevel@tonic-gate 		if ((rc = i_cpr_check_cprinfo()) != 0)  {
2597c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
2607c478bd9Sstevel@tonic-gate 			return (rc);
2617c478bd9Sstevel@tonic-gate 		}
2627c478bd9Sstevel@tonic-gate 	}
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	/*
2657c478bd9Sstevel@tonic-gate 	 * Call the main cpr routine. If we are successful, we will be coming
2667c478bd9Sstevel@tonic-gate 	 * down from the resume side, otherwise we are still in suspend.
2677c478bd9Sstevel@tonic-gate 	 */
2687c478bd9Sstevel@tonic-gate 	cpr_err(CE_CONT, "System is being suspended");
2697c478bd9Sstevel@tonic-gate 	if (rc = cpr_main()) {
2707c478bd9Sstevel@tonic-gate 		CPR->c_flags |= C_ERROR;
2717c478bd9Sstevel@tonic-gate 		cpr_err(CE_NOTE, "Suspend operation failed.");
2727c478bd9Sstevel@tonic-gate 	} else if (CPR->c_flags & C_SUSPENDING) {
2737c478bd9Sstevel@tonic-gate 		extern void cpr_power_down();
2747c478bd9Sstevel@tonic-gate 		/*
2757c478bd9Sstevel@tonic-gate 		 * Back from a successful checkpoint
2767c478bd9Sstevel@tonic-gate 		 */
2777c478bd9Sstevel@tonic-gate 		if (fcn == AD_CPR_TESTZ || fcn == AD_CPR_TESTNOZ) {
278edc40228Sachartre 			mdboot(0, AD_BOOT, "", B_FALSE);
2797c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
2807c478bd9Sstevel@tonic-gate 		}
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 		/* make sure there are no more changes to the device tree */
2837c478bd9Sstevel@tonic-gate 		devtree_freeze();
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 		/*
2867c478bd9Sstevel@tonic-gate 		 * stop other cpus and raise our priority.  since there is only
2877c478bd9Sstevel@tonic-gate 		 * one active cpu after this, and our priority will be too high
2887c478bd9Sstevel@tonic-gate 		 * for us to be preempted, we're essentially single threaded
2897c478bd9Sstevel@tonic-gate 		 * from here on out.
2907c478bd9Sstevel@tonic-gate 		 */
2917c478bd9Sstevel@tonic-gate 		stop_other_cpus();
2927c478bd9Sstevel@tonic-gate 		(void) spl6();
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 		/*
2957c478bd9Sstevel@tonic-gate 		 * try and reset leaf devices.  reset_leaves() should only
2967c478bd9Sstevel@tonic-gate 		 * be called when there are no other threads that could be
2977c478bd9Sstevel@tonic-gate 		 * accessing devices
2987c478bd9Sstevel@tonic-gate 		 */
2997c478bd9Sstevel@tonic-gate 		reset_leaves();
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate 		/*
3027c478bd9Sstevel@tonic-gate 		 * If cpr_power_down() succeeds, it'll not return.
3037c478bd9Sstevel@tonic-gate 		 *
3047c478bd9Sstevel@tonic-gate 		 * Drives with write-cache enabled need to flush
3057c478bd9Sstevel@tonic-gate 		 * their cache.
3067c478bd9Sstevel@tonic-gate 		 */
3077c478bd9Sstevel@tonic-gate 		if (fcn != AD_CPR_TESTHALT)
3087c478bd9Sstevel@tonic-gate 			cpr_power_down();
3097c478bd9Sstevel@tonic-gate 
310*ae115bc7Smrj 		CPR_DEBUG(CPR_DEBUG1, "(Done. Please Switch Off)\n");
3117c478bd9Sstevel@tonic-gate 		halt(NULL);
3127c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
3137c478bd9Sstevel@tonic-gate 	}
3147c478bd9Sstevel@tonic-gate 	/*
3157c478bd9Sstevel@tonic-gate 	 * For resuming: release resources and the serial lock.
3167c478bd9Sstevel@tonic-gate 	 */
3177c478bd9Sstevel@tonic-gate 	cpr_done();
3187c478bd9Sstevel@tonic-gate 	return (rc);
3197c478bd9Sstevel@tonic-gate }
320