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