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