xref: /illumos-gate/usr/src/uts/common/cpr/cpr_mod.c (revision edc40228)
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
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
23*edc40228Sachartre  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * System call to checkpoint and resume the currently running kernel
317c478bd9Sstevel@tonic-gate  */
327c478bd9Sstevel@tonic-gate #include <sys/types.h>
337c478bd9Sstevel@tonic-gate #include <sys/errno.h>
347c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
357c478bd9Sstevel@tonic-gate #include <sys/syscall.h>
367c478bd9Sstevel@tonic-gate #include <sys/cred.h>
377c478bd9Sstevel@tonic-gate #include <sys/uadmin.h>
387c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
397c478bd9Sstevel@tonic-gate #include <sys/systm.h>
407c478bd9Sstevel@tonic-gate #include <sys/cpr.h>
417c478bd9Sstevel@tonic-gate #include <sys/swap.h>
427c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
437c478bd9Sstevel@tonic-gate #include <sys/autoconf.h>
447c478bd9Sstevel@tonic-gate #include <sys/machsystm.h>
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate extern int i_cpr_is_supported(void);
477c478bd9Sstevel@tonic-gate extern int cpr_is_ufs(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 void cpr_forget_cprconfig(void);
517c478bd9Sstevel@tonic-gate extern int i_cpr_reusable_supported(void);
527c478bd9Sstevel@tonic-gate extern int i_cpr_reusefini(void);
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate extern struct mod_ops mod_miscops;
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate static struct modlmisc modlmisc = {
577c478bd9Sstevel@tonic-gate 	&mod_miscops, "checkpoint resume"
587c478bd9Sstevel@tonic-gate };
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
617c478bd9Sstevel@tonic-gate 	MODREV_1, (void *)&modlmisc, NULL
627c478bd9Sstevel@tonic-gate };
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate char _depends_on[] = "misc/bootdev";	/* i_devname_to_promname() */
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate int cpr_reusable_mode;
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate kmutex_t	cpr_slock;	/* cpr serial lock */
697c478bd9Sstevel@tonic-gate cpr_t		cpr_state;
707c478bd9Sstevel@tonic-gate int		cpr_debug;
717c478bd9Sstevel@tonic-gate int		cpr_test_mode; /* true if called via uadmin testmode */
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate /*
747c478bd9Sstevel@tonic-gate  * All the loadable module related code follows
757c478bd9Sstevel@tonic-gate  */
767c478bd9Sstevel@tonic-gate int
777c478bd9Sstevel@tonic-gate _init(void)
787c478bd9Sstevel@tonic-gate {
797c478bd9Sstevel@tonic-gate 	register int e;
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate 	if ((e = mod_install(&modlinkage)) == 0) {
827c478bd9Sstevel@tonic-gate 		mutex_init(&cpr_slock, NULL, MUTEX_DEFAULT, NULL);
837c478bd9Sstevel@tonic-gate 	}
847c478bd9Sstevel@tonic-gate 	return (e);
857c478bd9Sstevel@tonic-gate }
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate int
887c478bd9Sstevel@tonic-gate _fini(void)
897c478bd9Sstevel@tonic-gate {
907c478bd9Sstevel@tonic-gate 	register int e;
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 	if ((e = mod_remove(&modlinkage)) == 0) {
937c478bd9Sstevel@tonic-gate 		mutex_destroy(&cpr_slock);
947c478bd9Sstevel@tonic-gate 	}
957c478bd9Sstevel@tonic-gate 	return (e);
967c478bd9Sstevel@tonic-gate }
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate int
997c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
1007c478bd9Sstevel@tonic-gate {
1017c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1027c478bd9Sstevel@tonic-gate }
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate int
1057c478bd9Sstevel@tonic-gate cpr(int fcn)
1067c478bd9Sstevel@tonic-gate {
1077c478bd9Sstevel@tonic-gate 	static const char noswapstr[] = "reusable statefile requires "
1087c478bd9Sstevel@tonic-gate 	    "that no swap area be configured.\n";
1097c478bd9Sstevel@tonic-gate 	static const char blockstr[] = "reusable statefile must be "
1107c478bd9Sstevel@tonic-gate 	    "a block device.  See power.conf(4) and pmconfig(1M).\n";
1117c478bd9Sstevel@tonic-gate 	static const char normalfmt[] = "cannot run normal "
1127c478bd9Sstevel@tonic-gate 	    "checkpoint/resume when in reusable statefile mode. "
1137c478bd9Sstevel@tonic-gate 	    "use uadmin A_FREEZE AD_REUSEFINI (uadmin %d %d) "
1147c478bd9Sstevel@tonic-gate 	    "to exit reusable statefile mode.\n";
1157c478bd9Sstevel@tonic-gate 	static const char modefmt[] = "%s in reusable mode.\n";
1167c478bd9Sstevel@tonic-gate 	register int rc = 0;
1177c478bd9Sstevel@tonic-gate 	extern int cpr_init(int);
1187c478bd9Sstevel@tonic-gate 	extern void cpr_done(void);
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate 	/*
1217c478bd9Sstevel@tonic-gate 	 * Need to know if we're in reusable mode, but we will likely have
1227c478bd9Sstevel@tonic-gate 	 * rebooted since REUSEINIT, so we have to get the info from the
1237c478bd9Sstevel@tonic-gate 	 * file system
1247c478bd9Sstevel@tonic-gate 	 */
1257c478bd9Sstevel@tonic-gate 	if (!cpr_reusable_mode)
1267c478bd9Sstevel@tonic-gate 		cpr_reusable_mode = cpr_get_reusable_mode();
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate 	cpr_forget_cprconfig();
1297c478bd9Sstevel@tonic-gate 	switch (fcn) {
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	case AD_CPR_REUSEINIT:
1327c478bd9Sstevel@tonic-gate 		if (!i_cpr_reusable_supported())
1337c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
1347c478bd9Sstevel@tonic-gate 		if (!cpr_statefile_is_spec()) {
1357c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, blockstr);
1367c478bd9Sstevel@tonic-gate 			return (EINVAL);
1377c478bd9Sstevel@tonic-gate 		}
1387c478bd9Sstevel@tonic-gate 		if ((rc = cpr_check_spec_statefile()) != 0)
1397c478bd9Sstevel@tonic-gate 			return (rc);
1407c478bd9Sstevel@tonic-gate 		if (swapinfo) {
1417c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, noswapstr);
1427c478bd9Sstevel@tonic-gate 			return (EINVAL);
1437c478bd9Sstevel@tonic-gate 		}
1447c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
1457c478bd9Sstevel@tonic-gate 		break;
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate 	case AD_CPR_NOCOMPRESS:
1487c478bd9Sstevel@tonic-gate 	case AD_CPR_COMPRESS:
1497c478bd9Sstevel@tonic-gate 	case AD_CPR_FORCE:
1507c478bd9Sstevel@tonic-gate 		if (cpr_reusable_mode) {
1517c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, normalfmt, A_FREEZE, AD_REUSEFINI);
1527c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
1537c478bd9Sstevel@tonic-gate 		}
1547c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
1557c478bd9Sstevel@tonic-gate 		break;
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate 	case AD_CPR_REUSABLE:
1587c478bd9Sstevel@tonic-gate 		if (!i_cpr_reusable_supported())
1597c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
1607c478bd9Sstevel@tonic-gate 		if (!cpr_statefile_is_spec()) {
1617c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, blockstr);
1627c478bd9Sstevel@tonic-gate 			return (EINVAL);
1637c478bd9Sstevel@tonic-gate 		}
1647c478bd9Sstevel@tonic-gate 		if ((rc = cpr_check_spec_statefile()) != 0)
1657c478bd9Sstevel@tonic-gate 			return (rc);
1667c478bd9Sstevel@tonic-gate 		if (swapinfo) {
1677c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, noswapstr);
1687c478bd9Sstevel@tonic-gate 			return (EINVAL);
1697c478bd9Sstevel@tonic-gate 		}
1707c478bd9Sstevel@tonic-gate 		if ((rc = cpr_reusable_mount_check()) != 0)
1717c478bd9Sstevel@tonic-gate 			return (rc);
1727c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
1737c478bd9Sstevel@tonic-gate 		break;
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate 	case AD_CPR_REUSEFINI:
1767c478bd9Sstevel@tonic-gate 		if (!i_cpr_reusable_supported())
1777c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
1787c478bd9Sstevel@tonic-gate 		cpr_test_mode = 0;
1797c478bd9Sstevel@tonic-gate 		break;
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate 	case AD_CPR_TESTZ:
1827c478bd9Sstevel@tonic-gate 	case AD_CPR_TESTNOZ:
1837c478bd9Sstevel@tonic-gate 	case AD_CPR_TESTHALT:
1847c478bd9Sstevel@tonic-gate 		if (cpr_reusable_mode) {
1857c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, normalfmt, A_FREEZE, AD_REUSEFINI);
1867c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
1877c478bd9Sstevel@tonic-gate 		}
1887c478bd9Sstevel@tonic-gate 		cpr_test_mode = 1;
1897c478bd9Sstevel@tonic-gate 		break;
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 	case AD_CPR_CHECK:
1927c478bd9Sstevel@tonic-gate 		if (!i_cpr_is_supported() || cpr_reusable_mode)
1937c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
1947c478bd9Sstevel@tonic-gate 		return (0);
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	case AD_CPR_PRINT:
1977c478bd9Sstevel@tonic-gate 		CPR_STAT_EVENT_END("POST CPR DELAY");
1987c478bd9Sstevel@tonic-gate 		cpr_stat_event_print();
1997c478bd9Sstevel@tonic-gate 		return (0);
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG0:
2027c478bd9Sstevel@tonic-gate 		cpr_debug = 0;
2037c478bd9Sstevel@tonic-gate 		return (0);
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG1:
2067c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG2:
2077c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG3:
2087c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG4:
2097c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG5:
2107c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG7:
2117c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG8:
2127c478bd9Sstevel@tonic-gate 		cpr_debug |= CPR_DEBUG_BIT(fcn);
2137c478bd9Sstevel@tonic-gate 		return (0);
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate 	case AD_CPR_DEBUG9:
2167c478bd9Sstevel@tonic-gate 		cpr_debug |= LEVEL6;
2177c478bd9Sstevel@tonic-gate 		return (0);
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 	default:
2207c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
2217c478bd9Sstevel@tonic-gate 	}
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 	if (!i_cpr_is_supported() || !cpr_is_ufs(rootvfs))
2247c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSEINIT) {
2277c478bd9Sstevel@tonic-gate 		if (mutex_tryenter(&cpr_slock) == 0)
2287c478bd9Sstevel@tonic-gate 			return (EBUSY);
2297c478bd9Sstevel@tonic-gate 		if (cpr_reusable_mode) {
2307c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, modefmt, "already");
2317c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
2327c478bd9Sstevel@tonic-gate 			return (EBUSY);
2337c478bd9Sstevel@tonic-gate 		}
2347c478bd9Sstevel@tonic-gate 		rc = i_cpr_reuseinit();
2357c478bd9Sstevel@tonic-gate 		mutex_exit(&cpr_slock);
2367c478bd9Sstevel@tonic-gate 		return (rc);
2377c478bd9Sstevel@tonic-gate 	}
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSEFINI) {
2407c478bd9Sstevel@tonic-gate 		if (mutex_tryenter(&cpr_slock) == 0)
2417c478bd9Sstevel@tonic-gate 			return (EBUSY);
2427c478bd9Sstevel@tonic-gate 		if (!cpr_reusable_mode) {
2437c478bd9Sstevel@tonic-gate 			cpr_err(CE_CONT, modefmt, "not");
2447c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
2457c478bd9Sstevel@tonic-gate 			return (EINVAL);
2467c478bd9Sstevel@tonic-gate 		}
2477c478bd9Sstevel@tonic-gate 		rc = i_cpr_reusefini();
2487c478bd9Sstevel@tonic-gate 		mutex_exit(&cpr_slock);
2497c478bd9Sstevel@tonic-gate 		return (rc);
2507c478bd9Sstevel@tonic-gate 	}
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 	/*
2537c478bd9Sstevel@tonic-gate 	 * acquire cpr serial lock and init cpr state structure.
2547c478bd9Sstevel@tonic-gate 	 */
2557c478bd9Sstevel@tonic-gate 	if (rc = cpr_init(fcn))
2567c478bd9Sstevel@tonic-gate 		return (rc);
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSABLE) {
2597c478bd9Sstevel@tonic-gate 		if ((rc = i_cpr_check_cprinfo()) != 0)  {
2607c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_slock);
2617c478bd9Sstevel@tonic-gate 			return (rc);
2627c478bd9Sstevel@tonic-gate 		}
2637c478bd9Sstevel@tonic-gate 	}
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 	/*
2667c478bd9Sstevel@tonic-gate 	 * Call the main cpr routine. If we are successful, we will be coming
2677c478bd9Sstevel@tonic-gate 	 * down from the resume side, otherwise we are still in suspend.
2687c478bd9Sstevel@tonic-gate 	 */
2697c478bd9Sstevel@tonic-gate 	cpr_err(CE_CONT, "System is being suspended");
2707c478bd9Sstevel@tonic-gate 	if (rc = cpr_main()) {
2717c478bd9Sstevel@tonic-gate 		CPR->c_flags |= C_ERROR;
2727c478bd9Sstevel@tonic-gate 		cpr_err(CE_NOTE, "Suspend operation failed.");
2737c478bd9Sstevel@tonic-gate 	} else if (CPR->c_flags & C_SUSPENDING) {
2747c478bd9Sstevel@tonic-gate 		extern void cpr_power_down();
2757c478bd9Sstevel@tonic-gate 		/*
2767c478bd9Sstevel@tonic-gate 		 * Back from a successful checkpoint
2777c478bd9Sstevel@tonic-gate 		 */
2787c478bd9Sstevel@tonic-gate 		if (fcn == AD_CPR_TESTZ || fcn == AD_CPR_TESTNOZ) {
279*edc40228Sachartre 			mdboot(0, AD_BOOT, "", B_FALSE);
2807c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
2817c478bd9Sstevel@tonic-gate 		}
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate 		/* make sure there are no more changes to the device tree */
2847c478bd9Sstevel@tonic-gate 		devtree_freeze();
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 		/*
2877c478bd9Sstevel@tonic-gate 		 * stop other cpus and raise our priority.  since there is only
2887c478bd9Sstevel@tonic-gate 		 * one active cpu after this, and our priority will be too high
2897c478bd9Sstevel@tonic-gate 		 * for us to be preempted, we're essentially single threaded
2907c478bd9Sstevel@tonic-gate 		 * from here on out.
2917c478bd9Sstevel@tonic-gate 		 */
2927c478bd9Sstevel@tonic-gate 		stop_other_cpus();
2937c478bd9Sstevel@tonic-gate 		(void) spl6();
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate 		/*
2967c478bd9Sstevel@tonic-gate 		 * try and reset leaf devices.  reset_leaves() should only
2977c478bd9Sstevel@tonic-gate 		 * be called when there are no other threads that could be
2987c478bd9Sstevel@tonic-gate 		 * accessing devices
2997c478bd9Sstevel@tonic-gate 		 */
3007c478bd9Sstevel@tonic-gate 		reset_leaves();
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 		/*
3037c478bd9Sstevel@tonic-gate 		 * If cpr_power_down() succeeds, it'll not return.
3047c478bd9Sstevel@tonic-gate 		 *
3057c478bd9Sstevel@tonic-gate 		 * Drives with write-cache enabled need to flush
3067c478bd9Sstevel@tonic-gate 		 * their cache.
3077c478bd9Sstevel@tonic-gate 		 */
3087c478bd9Sstevel@tonic-gate 		if (fcn != AD_CPR_TESTHALT)
3097c478bd9Sstevel@tonic-gate 			cpr_power_down();
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 		errp("(Done. Please Switch Off)\n");
3127c478bd9Sstevel@tonic-gate 		halt(NULL);
3137c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
3147c478bd9Sstevel@tonic-gate 	}
3157c478bd9Sstevel@tonic-gate 	/*
3167c478bd9Sstevel@tonic-gate 	 * For resuming: release resources and the serial lock.
3177c478bd9Sstevel@tonic-gate 	 */
3187c478bd9Sstevel@tonic-gate 	cpr_done();
3197c478bd9Sstevel@tonic-gate 	return (rc);
3207c478bd9Sstevel@tonic-gate }
321