1 /* $OpenBSD: subr_suspend.c,v 1.16 2023/07/12 18:40:06 cheloha 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/clockintr.h> 23 #include <sys/reboot.h> 24 #include <sys/sensors.h> 25 #include <sys/sysctl.h> 26 #include <sys/mount.h> 27 #include <sys/syscallargs.h> 28 #include <dev/wscons/wsdisplayvar.h> 29 #ifdef GPROF 30 #include <sys/gmon.h> 31 #endif 32 #ifdef HIBERNATE 33 #include <sys/hibernate.h> 34 #endif 35 36 #include "softraid.h" 37 #include "wsdisplay.h" 38 39 /* Number of (active) wakeup devices in the system. */ 40 u_int wakeup_devices; 41 42 void 43 device_register_wakeup(struct device *dev) 44 { 45 wakeup_devices++; 46 } 47 48 int 49 sleep_state(void *v, int sleepmode) 50 { 51 int error, s; 52 extern int perflevel; 53 size_t rndbuflen; 54 char *rndbuf; 55 #ifdef GPROF 56 int gmon_state; 57 #endif 58 #if NSOFTRAID > 0 59 extern void sr_quiesce(void); 60 #endif 61 62 top: 63 error = ENXIO; 64 rndbuf = NULL; 65 rndbuflen = 0; 66 67 if (sleepmode == SLEEP_SUSPEND && wakeup_devices == 0) 68 return EOPNOTSUPP; 69 70 if (sleep_showstate(v, sleepmode)) 71 return EOPNOTSUPP; 72 #if NWSDISPLAY > 0 73 wsdisplay_suspend(); 74 #endif 75 stop_periodic_resettodr(); 76 77 #ifdef HIBERNATE 78 if (sleepmode == SLEEP_HIBERNATE) { 79 /* 80 * Discard useless memory to reduce fragmentation, 81 * and attempt to create a hibernate work area 82 */ 83 hibernate_suspend_bufcache(); 84 uvmpd_hibernate(); 85 if (hibernate_alloc()) { 86 printf("failed to allocate hibernate memory\n"); 87 sleep_abort(v); 88 error = ENOMEM; 89 goto fail_hiballoc; 90 } 91 } 92 #endif /* HIBERNATE */ 93 94 sensor_quiesce(); 95 if (config_suspend_all(DVACT_QUIESCE)) { 96 sleep_abort(v); 97 error = EIO; 98 goto fail_quiesce; 99 } 100 101 vfs_stall(curproc, 1); 102 #if NSOFTRAID > 0 103 sr_quiesce(); 104 #endif 105 bufq_quiesce(); 106 #ifdef MULTIPROCESSOR 107 sched_stop_secondary_cpus(); 108 KASSERT(CPU_IS_PRIMARY(curcpu())); 109 #endif 110 #ifdef GPROF 111 gmon_state = gmoninit; 112 gmoninit = 0; 113 #endif 114 #ifdef MULTIPROCESSOR 115 sleep_mp(); 116 #endif 117 118 #ifdef HIBERNATE 119 if (sleepmode == SLEEP_HIBERNATE) { 120 /* 121 * We've just done various forms of syncing to disk 122 * churned lots of memory dirty. We don't need to 123 * save that dirty memory to hibernate, so release it. 124 */ 125 hibernate_suspend_bufcache(); 126 uvmpd_hibernate(); 127 } 128 #endif /* HIBERNATE */ 129 130 resettodr(); 131 132 s = splhigh(); 133 intr_disable(); /* PSL_I for resume; PIC/APIC broken until repair */ 134 cold = 2; /* Force other code to delay() instead of tsleep() */ 135 136 if (config_suspend_all(DVACT_SUSPEND) != 0) { 137 sleep_abort(v); 138 error = EDEADLK; 139 goto fail_suspend; 140 } 141 suspend_randomness(); 142 if (sleep_setstate(v)) { 143 sleep_abort(v); 144 error = ENOTBLK; 145 goto fail_pts; 146 } 147 148 if (sleepmode == SLEEP_SUSPEND) { 149 /* 150 * XXX 151 * Flag to disk drivers that they should "power down" the disk 152 * when we get to DVACT_POWERDOWN. 153 */ 154 boothowto |= RB_POWERDOWN; 155 config_suspend_all(DVACT_POWERDOWN); 156 boothowto &= ~RB_POWERDOWN; 157 158 if (cpu_setperf != NULL) 159 cpu_setperf(0); 160 } 161 162 error = gosleep(v); 163 164 #ifdef HIBERNATE 165 if (sleepmode == SLEEP_HIBERNATE) { 166 uvm_pmr_dirty_everything(); 167 hib_getentropy(&rndbuf, &rndbuflen); 168 } 169 #endif /* HIBERNATE */ 170 171 fail_pts: 172 config_suspend_all(DVACT_RESUME); 173 174 fail_suspend: 175 cold = 0; 176 intr_enable(); 177 splx(s); 178 179 inittodr(gettime()); 180 clockintr_cpu_init(NULL); 181 clockintr_trigger(); 182 183 sleep_resume(v); 184 resume_randomness(rndbuf, rndbuflen); 185 #ifdef MULTIPROCESSOR 186 resume_mp(); 187 #endif 188 #ifdef GPROF 189 gmoninit = gmon_state; 190 #endif 191 #ifdef MULTIPROCESSOR 192 sched_start_secondary_cpus(); 193 #endif 194 vfs_stall(curproc, 0); 195 bufq_restart(); 196 197 fail_quiesce: 198 config_suspend_all(DVACT_WAKEUP); 199 sensor_restart(); 200 201 #ifdef HIBERNATE 202 if (sleepmode == SLEEP_HIBERNATE) { 203 hibernate_free(); 204 fail_hiballoc: 205 hibernate_resume_bufcache(); 206 } 207 #endif /* HIBERNATE */ 208 209 start_periodic_resettodr(); 210 #if NWSDISPLAY > 0 211 wsdisplay_resume(); 212 #endif 213 sys_sync(curproc, NULL, NULL); 214 if (cpu_setperf != NULL) 215 cpu_setperf(perflevel); /* Restore hw.setperf */ 216 if (suspend_finish(v) == EAGAIN) 217 goto top; 218 return (error); 219 } 220