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