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. */
40 u_int wakeup_devices;
41
42 void
device_register_wakeup(struct device * dev)43 device_register_wakeup(struct device *dev)
44 {
45 wakeup_devices++;
46 }
47
48 int
sleep_state(void * v,int sleepmode)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 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
168 fail_pts:
169 config_suspend_all(DVACT_RESUME);
170
171 fail_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
195 fail_quiesce:
196 config_suspend_all(DVACT_WAKEUP);
197 sensor_restart();
198
199 #ifdef HIBERNATE
200 if (sleepmode == SLEEP_HIBERNATE) {
201 hibernate_free();
202 fail_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