1 /* $OpenBSD: subr_suspend.c,v 1.9 2022/02/16 06:47:28 deraadt Exp $ */ 2 /* 3 * Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com> 4 * Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/buf.h> 22 #include <sys/malloc.h> 23 #include <sys/pool.h> 24 #include <sys/reboot.h> 25 #include <sys/proc.h> 26 #include <sys/sensors.h> 27 #include <sys/sysctl.h> 28 #include <sys/mount.h> 29 #include <sys/syscallargs.h> 30 #include <dev/wscons/wsdisplayvar.h> 31 #ifdef HIBERNATE 32 #include <sys/hibernate.h> 33 #endif 34 35 #include "softraid.h" 36 #include "wsdisplay.h" 37 38 int 39 sleep_state(void *v, int sleepmode) 40 { 41 int error, s; 42 extern int perflevel; 43 size_t rndbuflen; 44 char *rndbuf; 45 #if NSOFTRAID > 0 46 extern void sr_quiesce(void); 47 #endif 48 49 top: 50 error = ENXIO; 51 rndbuf = NULL; 52 rndbuflen = 0; 53 54 if (sleep_showstate(v, sleepmode)) 55 return EOPNOTSUPP; 56 57 #if NWSDISPLAY > 0 58 wsdisplay_suspend(); 59 #endif 60 61 stop_periodic_resettodr(); 62 63 #ifdef HIBERNATE 64 if (sleepmode == SLEEP_HIBERNATE) { 65 /* 66 * Discard useless memory to reduce fragmentation, 67 * and attempt to create a hibernate work area 68 */ 69 hibernate_suspend_bufcache(); 70 uvmpd_hibernate(); 71 if (hibernate_alloc()) { 72 printf("failed to allocate hibernate memory\n"); 73 sleep_abort(v); 74 goto fail_alloc; 75 } 76 } 77 #endif /* HIBERNATE */ 78 79 sensor_quiesce(); 80 if (config_suspend_all(DVACT_QUIESCE)) { 81 sleep_abort(v); 82 goto fail_quiesce; 83 } 84 85 vfs_stall(curproc, 1); 86 #if NSOFTRAID > 0 87 sr_quiesce(); 88 #endif 89 bufq_quiesce(); 90 91 92 #ifdef MULTIPROCESSOR 93 sched_stop_secondary_cpus(); 94 KASSERT(CPU_IS_PRIMARY(curcpu())); 95 sleep_mp(); 96 #endif 97 98 #ifdef HIBERNATE 99 if (sleepmode == SLEEP_HIBERNATE) { 100 /* 101 * We've just done various forms of syncing to disk 102 * churned lots of memory dirty. We don't need to 103 * save that dirty memory to hibernate, so release it. 104 */ 105 hibernate_suspend_bufcache(); 106 uvmpd_hibernate(); 107 } 108 #endif /* HIBERNATE */ 109 110 resettodr(); 111 112 s = splhigh(); 113 intr_disable(); /* PSL_I for resume; PIC/APIC broken until repair */ 114 cold = 2; /* Force other code to delay() instead of tsleep() */ 115 116 if (config_suspend_all(DVACT_SUSPEND) != 0) { 117 sleep_abort(v); 118 goto fail_suspend; 119 } 120 121 suspend_randomness(); 122 123 if (sleep_setstate(v)) { 124 sleep_abort(v); 125 goto fail_pts; 126 } 127 128 if (sleepmode == SLEEP_SUSPEND) { 129 /* 130 * XXX 131 * Flag to disk drivers that they should "power down" the disk 132 * when we get to DVACT_POWERDOWN. 133 */ 134 boothowto |= RB_POWERDOWN; 135 config_suspend_all(DVACT_POWERDOWN); 136 boothowto &= ~RB_POWERDOWN; 137 } 138 139 error = gosleep(v); 140 141 #ifdef HIBERNATE 142 if (sleepmode == SLEEP_HIBERNATE) { 143 uvm_pmr_dirty_everything(); 144 hib_getentropy(&rndbuf, &rndbuflen); 145 } 146 #endif /* HIBERNATE */ 147 148 fail_pts: 149 config_suspend_all(DVACT_RESUME); 150 151 fail_suspend: 152 cold = 0; 153 intr_enable(); 154 splx(s); 155 156 inittodr(gettime()); 157 158 sleep_resume(v); 159 160 /* force RNG upper level reseed */ 161 resume_randomness(rndbuf, rndbuflen); 162 163 #ifdef MULTIPROCESSOR 164 resume_mp(); 165 sched_start_secondary_cpus(); 166 #endif 167 168 vfs_stall(curproc, 0); 169 bufq_restart(); 170 171 fail_quiesce: 172 config_suspend_all(DVACT_WAKEUP); 173 sensor_restart(); 174 175 #ifdef HIBERNATE 176 if (sleepmode == SLEEP_HIBERNATE) { 177 hibernate_free(); 178 fail_alloc: 179 hibernate_resume_bufcache(); 180 } 181 #endif /* HIBERNATE */ 182 183 start_periodic_resettodr(); 184 185 #if NWSDISPLAY > 0 186 wsdisplay_resume(); 187 #endif 188 sys_sync(curproc, NULL, NULL); 189 190 /* Restore hw.setperf */ 191 if (cpu_setperf != NULL) 192 cpu_setperf(perflevel); 193 194 if (suspend_finish(v) == EAGAIN) 195 goto top; 196 197 return (error); 198 } 199