1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * System call to checkpoint and resume the currently running kernel 30 */ 31 #include <sys/types.h> 32 #include <sys/errno.h> 33 #include <sys/modctl.h> 34 #include <sys/syscall.h> 35 #include <sys/cred.h> 36 #include <sys/uadmin.h> 37 #include <sys/cmn_err.h> 38 #include <sys/systm.h> 39 #include <sys/cpr.h> 40 #include <sys/swap.h> 41 #include <sys/vfs.h> 42 #include <sys/autoconf.h> 43 #include <sys/machsystm.h> 44 45 extern int i_cpr_is_supported(void); 46 extern int cpr_is_ufs(struct vfs *); 47 extern int cpr_check_spec_statefile(void); 48 extern int cpr_reusable_mount_check(void); 49 extern void cpr_forget_cprconfig(void); 50 extern int i_cpr_reusable_supported(void); 51 extern int i_cpr_reusefini(void); 52 53 extern struct mod_ops mod_miscops; 54 55 static struct modlmisc modlmisc = { 56 &mod_miscops, "checkpoint resume" 57 }; 58 59 static struct modlinkage modlinkage = { 60 MODREV_1, (void *)&modlmisc, NULL 61 }; 62 63 char _depends_on[] = "misc/bootdev"; /* i_devname_to_promname() */ 64 65 int cpr_reusable_mode; 66 67 kmutex_t cpr_slock; /* cpr serial lock */ 68 cpr_t cpr_state; 69 int cpr_debug; 70 int cpr_test_mode; /* true if called via uadmin testmode */ 71 72 /* 73 * All the loadable module related code follows 74 */ 75 int 76 _init(void) 77 { 78 register int e; 79 80 if ((e = mod_install(&modlinkage)) == 0) { 81 mutex_init(&cpr_slock, NULL, MUTEX_DEFAULT, NULL); 82 } 83 return (e); 84 } 85 86 int 87 _fini(void) 88 { 89 register int e; 90 91 if ((e = mod_remove(&modlinkage)) == 0) { 92 mutex_destroy(&cpr_slock); 93 } 94 return (e); 95 } 96 97 int 98 _info(struct modinfo *modinfop) 99 { 100 return (mod_info(&modlinkage, modinfop)); 101 } 102 103 int 104 cpr(int fcn) 105 { 106 static const char noswapstr[] = "reusable statefile requires " 107 "that no swap area be configured.\n"; 108 static const char blockstr[] = "reusable statefile must be " 109 "a block device. See power.conf(4) and pmconfig(1M).\n"; 110 static const char normalfmt[] = "cannot run normal " 111 "checkpoint/resume when in reusable statefile mode. " 112 "use uadmin A_FREEZE AD_REUSEFINI (uadmin %d %d) " 113 "to exit reusable statefile mode.\n"; 114 static const char modefmt[] = "%s in reusable mode.\n"; 115 register int rc = 0; 116 extern int cpr_init(int); 117 extern void cpr_done(void); 118 119 /* 120 * Need to know if we're in reusable mode, but we will likely have 121 * rebooted since REUSEINIT, so we have to get the info from the 122 * file system 123 */ 124 if (!cpr_reusable_mode) 125 cpr_reusable_mode = cpr_get_reusable_mode(); 126 127 cpr_forget_cprconfig(); 128 switch (fcn) { 129 130 case AD_CPR_REUSEINIT: 131 if (!i_cpr_reusable_supported()) 132 return (ENOTSUP); 133 if (!cpr_statefile_is_spec()) { 134 cpr_err(CE_CONT, blockstr); 135 return (EINVAL); 136 } 137 if ((rc = cpr_check_spec_statefile()) != 0) 138 return (rc); 139 if (swapinfo) { 140 cpr_err(CE_CONT, noswapstr); 141 return (EINVAL); 142 } 143 cpr_test_mode = 0; 144 break; 145 146 case AD_CPR_NOCOMPRESS: 147 case AD_CPR_COMPRESS: 148 case AD_CPR_FORCE: 149 if (cpr_reusable_mode) { 150 cpr_err(CE_CONT, normalfmt, A_FREEZE, AD_REUSEFINI); 151 return (ENOTSUP); 152 } 153 cpr_test_mode = 0; 154 break; 155 156 case AD_CPR_REUSABLE: 157 if (!i_cpr_reusable_supported()) 158 return (ENOTSUP); 159 if (!cpr_statefile_is_spec()) { 160 cpr_err(CE_CONT, blockstr); 161 return (EINVAL); 162 } 163 if ((rc = cpr_check_spec_statefile()) != 0) 164 return (rc); 165 if (swapinfo) { 166 cpr_err(CE_CONT, noswapstr); 167 return (EINVAL); 168 } 169 if ((rc = cpr_reusable_mount_check()) != 0) 170 return (rc); 171 cpr_test_mode = 0; 172 break; 173 174 case AD_CPR_REUSEFINI: 175 if (!i_cpr_reusable_supported()) 176 return (ENOTSUP); 177 cpr_test_mode = 0; 178 break; 179 180 case AD_CPR_TESTZ: 181 case AD_CPR_TESTNOZ: 182 case AD_CPR_TESTHALT: 183 if (cpr_reusable_mode) { 184 cpr_err(CE_CONT, normalfmt, A_FREEZE, AD_REUSEFINI); 185 return (ENOTSUP); 186 } 187 cpr_test_mode = 1; 188 break; 189 190 case AD_CPR_CHECK: 191 if (!i_cpr_is_supported() || cpr_reusable_mode) 192 return (ENOTSUP); 193 return (0); 194 195 case AD_CPR_PRINT: 196 CPR_STAT_EVENT_END("POST CPR DELAY"); 197 cpr_stat_event_print(); 198 return (0); 199 200 case AD_CPR_DEBUG0: 201 cpr_debug = 0; 202 return (0); 203 204 case AD_CPR_DEBUG1: 205 case AD_CPR_DEBUG2: 206 case AD_CPR_DEBUG3: 207 case AD_CPR_DEBUG4: 208 case AD_CPR_DEBUG5: 209 case AD_CPR_DEBUG7: 210 case AD_CPR_DEBUG8: 211 cpr_debug |= CPR_DEBUG_BIT(fcn); 212 return (0); 213 214 case AD_CPR_DEBUG9: 215 cpr_debug |= CPR_DEBUG6; 216 return (0); 217 218 default: 219 return (ENOTSUP); 220 } 221 222 if (!i_cpr_is_supported() || !cpr_is_ufs(rootvfs)) 223 return (ENOTSUP); 224 225 if (fcn == AD_CPR_REUSEINIT) { 226 if (mutex_tryenter(&cpr_slock) == 0) 227 return (EBUSY); 228 if (cpr_reusable_mode) { 229 cpr_err(CE_CONT, modefmt, "already"); 230 mutex_exit(&cpr_slock); 231 return (EBUSY); 232 } 233 rc = i_cpr_reuseinit(); 234 mutex_exit(&cpr_slock); 235 return (rc); 236 } 237 238 if (fcn == AD_CPR_REUSEFINI) { 239 if (mutex_tryenter(&cpr_slock) == 0) 240 return (EBUSY); 241 if (!cpr_reusable_mode) { 242 cpr_err(CE_CONT, modefmt, "not"); 243 mutex_exit(&cpr_slock); 244 return (EINVAL); 245 } 246 rc = i_cpr_reusefini(); 247 mutex_exit(&cpr_slock); 248 return (rc); 249 } 250 251 /* 252 * acquire cpr serial lock and init cpr state structure. 253 */ 254 if (rc = cpr_init(fcn)) 255 return (rc); 256 257 if (fcn == AD_CPR_REUSABLE) { 258 if ((rc = i_cpr_check_cprinfo()) != 0) { 259 mutex_exit(&cpr_slock); 260 return (rc); 261 } 262 } 263 264 /* 265 * Call the main cpr routine. If we are successful, we will be coming 266 * down from the resume side, otherwise we are still in suspend. 267 */ 268 cpr_err(CE_CONT, "System is being suspended"); 269 if (rc = cpr_main()) { 270 CPR->c_flags |= C_ERROR; 271 cpr_err(CE_NOTE, "Suspend operation failed."); 272 } else if (CPR->c_flags & C_SUSPENDING) { 273 extern void cpr_power_down(); 274 /* 275 * Back from a successful checkpoint 276 */ 277 if (fcn == AD_CPR_TESTZ || fcn == AD_CPR_TESTNOZ) { 278 mdboot(0, AD_BOOT, "", B_FALSE); 279 /* NOTREACHED */ 280 } 281 282 /* make sure there are no more changes to the device tree */ 283 devtree_freeze(); 284 285 /* 286 * stop other cpus and raise our priority. since there is only 287 * one active cpu after this, and our priority will be too high 288 * for us to be preempted, we're essentially single threaded 289 * from here on out. 290 */ 291 stop_other_cpus(); 292 (void) spl6(); 293 294 /* 295 * try and reset leaf devices. reset_leaves() should only 296 * be called when there are no other threads that could be 297 * accessing devices 298 */ 299 reset_leaves(); 300 301 /* 302 * If cpr_power_down() succeeds, it'll not return. 303 * 304 * Drives with write-cache enabled need to flush 305 * their cache. 306 */ 307 if (fcn != AD_CPR_TESTHALT) 308 cpr_power_down(); 309 310 CPR_DEBUG(CPR_DEBUG1, "(Done. Please Switch Off)\n"); 311 halt(NULL); 312 /* NOTREACHED */ 313 } 314 /* 315 * For resuming: release resources and the serial lock. 316 */ 317 cpr_done(); 318 return (rc); 319 } 320