1/* $OpenBSD: subr_suspend.c,v 1.18 2024/05/28 09:40: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 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. */ 40u_int wakeup_devices; 41 42void 43device_register_wakeup(struct device *dev) 44{ 45 wakeup_devices++; 46} 47 48int 49sleep_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 62top: 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 error = ENOMEM; 88 goto fail_hiballoc; 89 } 90 } 91#endif /* HIBERNATE */ 92 93 sensor_quiesce(); 94 if (config_suspend_all(DVACT_QUIESCE)) { 95 error = EIO; 96 goto fail_quiesce; 97 } 98 99 vfs_stall(curproc, 1); 100#if NSOFTRAID > 0 101 sr_quiesce(); 102#endif 103 bufq_quiesce(); 104#ifdef MULTIPROCESSOR 105 sched_stop_secondary_cpus(); 106 KASSERT(CPU_IS_PRIMARY(curcpu())); 107#endif 108#ifdef GPROF 109 gmon_state = gmoninit; 110 gmoninit = 0; 111#endif 112#ifdef MULTIPROCESSOR 113 sleep_mp(); 114#endif 115 116#ifdef HIBERNATE 117 if (sleepmode == SLEEP_HIBERNATE) { 118 /* 119 * We've just done various forms of syncing to disk 120 * churned lots of memory dirty. We don't need to 121 * save that dirty memory to hibernate, so release it. 122 */ 123 hibernate_suspend_bufcache(); 124 uvmpd_hibernate(); 125 } 126#endif /* HIBERNATE */ 127 128 resettodr(); 129 130 s = splhigh(); 131 intr_disable(); /* PSL_I for resume; PIC/APIC broken until repair */ 132 cold = 2; /* Force other code to delay() instead of tsleep() */ 133 intr_enable_wakeup(); 134 135 if (config_suspend_all(DVACT_SUSPEND) != 0) { 136 error = EDEADLK; 137 goto fail_suspend; 138 } 139 suspend_randomness(); 140 if (sleep_setstate(v)) { 141 error = ENOTBLK; 142 goto fail_pts; 143 } 144 145 if (sleepmode == SLEEP_SUSPEND) { 146 /* 147 * XXX 148 * Flag to disk drivers that they should "power down" the disk 149 * when we get to DVACT_POWERDOWN. 150 */ 151 boothowto |= RB_POWERDOWN; 152 config_suspend_all(DVACT_POWERDOWN); 153 boothowto &= ~RB_POWERDOWN; 154 155 if (cpu_setperf != NULL) 156 cpu_setperf(0); 157 } 158 159 error = gosleep(v); 160 161#ifdef HIBERNATE 162 if (sleepmode == SLEEP_HIBERNATE) { 163 uvm_pmr_dirty_everything(); 164 hib_getentropy(&rndbuf, &rndbuflen); 165 } 166#endif /* HIBERNATE */ 167 168fail_pts: 169 config_suspend_all(DVACT_RESUME); 170 171fail_suspend: 172 intr_disable_wakeup(); 173 cold = 0; 174 intr_enable(); 175 splx(s); 176 177 inittodr(gettime()); 178 clockintr_cpu_init(NULL); 179 clockintr_trigger(); 180 181 sleep_resume(v); 182 resume_randomness(rndbuf, rndbuflen); 183#ifdef MULTIPROCESSOR 184 resume_mp(); 185#endif 186#ifdef GPROF 187 gmoninit = gmon_state; 188#endif 189#ifdef MULTIPROCESSOR 190 sched_start_secondary_cpus(); 191#endif 192 vfs_stall(curproc, 0); 193 bufq_restart(); 194 195fail_quiesce: 196 config_suspend_all(DVACT_WAKEUP); 197 sensor_restart(); 198 199#ifdef HIBERNATE 200 if (sleepmode == SLEEP_HIBERNATE) { 201 hibernate_free(); 202fail_hiballoc: 203 hibernate_resume_bufcache(); 204 } 205#endif /* HIBERNATE */ 206 207 start_periodic_resettodr(); 208#if NWSDISPLAY > 0 209 wsdisplay_resume(); 210#endif 211 sys_sync(curproc, NULL, NULL); 212 if (cpu_setperf != NULL) 213 cpu_setperf(perflevel); /* Restore hw.setperf */ 214 if (suspend_finish(v) == EAGAIN) 215 goto top; 216 return (error); 217} 218