1*9a6a36e2Skettenis /* $OpenBSD: subr_suspend.c,v 1.18 2024/05/28 09:40:40 kettenis Exp $ */
2ad814436Sderaadt /*
3ad814436Sderaadt * Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com>
4ad814436Sderaadt * Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org>
5ad814436Sderaadt *
6ad814436Sderaadt * Permission to use, copy, modify, and distribute this software for any
7ad814436Sderaadt * purpose with or without fee is hereby granted, provided that the above
8ad814436Sderaadt * copyright notice and this permission notice appear in all copies.
9ad814436Sderaadt *
10ad814436Sderaadt * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11ad814436Sderaadt * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12ad814436Sderaadt * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13ad814436Sderaadt * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14ad814436Sderaadt * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15ad814436Sderaadt * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16ad814436Sderaadt * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17ad814436Sderaadt */
18ad814436Sderaadt
19ad814436Sderaadt #include <sys/param.h>
20ad814436Sderaadt #include <sys/systm.h>
21ad814436Sderaadt #include <sys/buf.h>
22329e3480Scheloha #include <sys/clockintr.h>
237f8722a4Sderaadt #include <sys/reboot.h>
24ad814436Sderaadt #include <sys/sensors.h>
25ad814436Sderaadt #include <sys/sysctl.h>
26ad814436Sderaadt #include <sys/mount.h>
27ad814436Sderaadt #include <sys/syscallargs.h>
28ad814436Sderaadt #include <dev/wscons/wsdisplayvar.h>
293772e814Scheloha #ifdef GPROF
303772e814Scheloha #include <sys/gmon.h>
313772e814Scheloha #endif
32ad814436Sderaadt #ifdef HIBERNATE
33ad814436Sderaadt #include <sys/hibernate.h>
34ad814436Sderaadt #endif
35ad814436Sderaadt
36ad814436Sderaadt #include "softraid.h"
37cb9e2b65Sderaadt #include "wsdisplay.h"
38ad814436Sderaadt
3917b371d9Skettenis /* Number of (active) wakeup devices in the system. */
4017b371d9Skettenis u_int wakeup_devices;
4117b371d9Skettenis
4217b371d9Skettenis void
device_register_wakeup(struct device * dev)4317b371d9Skettenis device_register_wakeup(struct device *dev)
4417b371d9Skettenis {
4517b371d9Skettenis wakeup_devices++;
4617b371d9Skettenis }
4717b371d9Skettenis
48ad814436Sderaadt int
sleep_state(void * v,int sleepmode)49ad814436Sderaadt sleep_state(void *v, int sleepmode)
50ad814436Sderaadt {
51fd33afa7Sderaadt int error, s;
52ad814436Sderaadt extern int perflevel;
53fd33afa7Sderaadt size_t rndbuflen;
54fd33afa7Sderaadt char *rndbuf;
553772e814Scheloha #ifdef GPROF
563772e814Scheloha int gmon_state;
573772e814Scheloha #endif
58ad814436Sderaadt #if NSOFTRAID > 0
59ad814436Sderaadt extern void sr_quiesce(void);
60ad814436Sderaadt #endif
61ad814436Sderaadt
62fd33afa7Sderaadt top:
63fd33afa7Sderaadt error = ENXIO;
64fd33afa7Sderaadt rndbuf = NULL;
65fd33afa7Sderaadt rndbuflen = 0;
66fd33afa7Sderaadt
6717b371d9Skettenis if (sleepmode == SLEEP_SUSPEND && wakeup_devices == 0)
6817b371d9Skettenis return EOPNOTSUPP;
6917b371d9Skettenis
70ad814436Sderaadt if (sleep_showstate(v, sleepmode))
71ad814436Sderaadt return EOPNOTSUPP;
72cb9e2b65Sderaadt #if NWSDISPLAY > 0
73cb9e2b65Sderaadt wsdisplay_suspend();
74cb9e2b65Sderaadt #endif
75ad814436Sderaadt stop_periodic_resettodr();
76ad814436Sderaadt
77ad814436Sderaadt #ifdef HIBERNATE
78ad814436Sderaadt if (sleepmode == SLEEP_HIBERNATE) {
79ad814436Sderaadt /*
80ad814436Sderaadt * Discard useless memory to reduce fragmentation,
81ad814436Sderaadt * and attempt to create a hibernate work area
82ad814436Sderaadt */
83ad814436Sderaadt hibernate_suspend_bufcache();
84ad814436Sderaadt uvmpd_hibernate();
85ad814436Sderaadt if (hibernate_alloc()) {
86ad814436Sderaadt printf("failed to allocate hibernate memory\n");
87f134d78dSderaadt error = ENOMEM;
88f134d78dSderaadt goto fail_hiballoc;
89ad814436Sderaadt }
90ad814436Sderaadt }
91ad814436Sderaadt #endif /* HIBERNATE */
92ad814436Sderaadt
93ad814436Sderaadt sensor_quiesce();
941f9ba6f4Sderaadt if (config_suspend_all(DVACT_QUIESCE)) {
95f134d78dSderaadt error = EIO;
96ad814436Sderaadt goto fail_quiesce;
971f9ba6f4Sderaadt }
98ad814436Sderaadt
99ad814436Sderaadt vfs_stall(curproc, 1);
100ad814436Sderaadt #if NSOFTRAID > 0
101ad814436Sderaadt sr_quiesce();
102ad814436Sderaadt #endif
103ad814436Sderaadt bufq_quiesce();
104ad814436Sderaadt #ifdef MULTIPROCESSOR
1050a23802aSderaadt sched_stop_secondary_cpus();
1060a23802aSderaadt KASSERT(CPU_IS_PRIMARY(curcpu()));
1073772e814Scheloha #endif
1083772e814Scheloha #ifdef GPROF
1093772e814Scheloha gmon_state = gmoninit;
1103772e814Scheloha gmoninit = 0;
1113772e814Scheloha #endif
1123772e814Scheloha #ifdef MULTIPROCESSOR
113ad814436Sderaadt sleep_mp();
114ad814436Sderaadt #endif
115ad814436Sderaadt
116ad814436Sderaadt #ifdef HIBERNATE
117ad814436Sderaadt if (sleepmode == SLEEP_HIBERNATE) {
118ad814436Sderaadt /*
119ad814436Sderaadt * We've just done various forms of syncing to disk
120ad814436Sderaadt * churned lots of memory dirty. We don't need to
121ad814436Sderaadt * save that dirty memory to hibernate, so release it.
122ad814436Sderaadt */
123ad814436Sderaadt hibernate_suspend_bufcache();
124ad814436Sderaadt uvmpd_hibernate();
125ad814436Sderaadt }
126ad814436Sderaadt #endif /* HIBERNATE */
127ad814436Sderaadt
128ad814436Sderaadt resettodr();
129ad814436Sderaadt
130ad814436Sderaadt s = splhigh();
131ad814436Sderaadt intr_disable(); /* PSL_I for resume; PIC/APIC broken until repair */
132ad814436Sderaadt cold = 2; /* Force other code to delay() instead of tsleep() */
133fc30b644Skettenis intr_enable_wakeup();
134ad814436Sderaadt
1351f9ba6f4Sderaadt if (config_suspend_all(DVACT_SUSPEND) != 0) {
136f134d78dSderaadt error = EDEADLK;
137ad814436Sderaadt goto fail_suspend;
1381f9ba6f4Sderaadt }
139ad814436Sderaadt suspend_randomness();
1401f9ba6f4Sderaadt if (sleep_setstate(v)) {
141f134d78dSderaadt error = ENOTBLK;
142ad814436Sderaadt goto fail_pts;
1431f9ba6f4Sderaadt }
144ad814436Sderaadt
1457f8722a4Sderaadt if (sleepmode == SLEEP_SUSPEND) {
1467f8722a4Sderaadt /*
1477f8722a4Sderaadt * XXX
1487f8722a4Sderaadt * Flag to disk drivers that they should "power down" the disk
1497f8722a4Sderaadt * when we get to DVACT_POWERDOWN.
1507f8722a4Sderaadt */
1517f8722a4Sderaadt boothowto |= RB_POWERDOWN;
1527f8722a4Sderaadt config_suspend_all(DVACT_POWERDOWN);
1537f8722a4Sderaadt boothowto &= ~RB_POWERDOWN;
154b396fb91Skettenis
155b396fb91Skettenis if (cpu_setperf != NULL)
156b396fb91Skettenis cpu_setperf(0);
1577f8722a4Sderaadt }
1587f8722a4Sderaadt
1594a9a38d5Sderaadt error = gosleep(v);
160ad814436Sderaadt
161ad814436Sderaadt #ifdef HIBERNATE
162ad814436Sderaadt if (sleepmode == SLEEP_HIBERNATE) {
163ad814436Sderaadt uvm_pmr_dirty_everything();
164ad814436Sderaadt hib_getentropy(&rndbuf, &rndbuflen);
165ad814436Sderaadt }
166ad814436Sderaadt #endif /* HIBERNATE */
167ad814436Sderaadt
168ad814436Sderaadt fail_pts:
169ad814436Sderaadt config_suspend_all(DVACT_RESUME);
170ad814436Sderaadt
171ad814436Sderaadt fail_suspend:
172fc30b644Skettenis intr_disable_wakeup();
173ad814436Sderaadt cold = 0;
174ad814436Sderaadt intr_enable();
175ad814436Sderaadt splx(s);
176ad814436Sderaadt
177ad814436Sderaadt inittodr(gettime());
178329e3480Scheloha clockintr_cpu_init(NULL);
179329e3480Scheloha clockintr_trigger();
180b2653891Scheloha
181ad814436Sderaadt sleep_resume(v);
182ad814436Sderaadt resume_randomness(rndbuf, rndbuflen);
183ad814436Sderaadt #ifdef MULTIPROCESSOR
184ad814436Sderaadt resume_mp();
1853772e814Scheloha #endif
1863772e814Scheloha #ifdef GPROF
1873772e814Scheloha gmoninit = gmon_state;
1883772e814Scheloha #endif
1893772e814Scheloha #ifdef MULTIPROCESSOR
1900a23802aSderaadt sched_start_secondary_cpus();
191ad814436Sderaadt #endif
192ad814436Sderaadt vfs_stall(curproc, 0);
193ad814436Sderaadt bufq_restart();
194ad814436Sderaadt
195ad814436Sderaadt fail_quiesce:
196ad814436Sderaadt config_suspend_all(DVACT_WAKEUP);
197ad814436Sderaadt sensor_restart();
198ad814436Sderaadt
199ad814436Sderaadt #ifdef HIBERNATE
200ad814436Sderaadt if (sleepmode == SLEEP_HIBERNATE) {
201ad814436Sderaadt hibernate_free();
202f134d78dSderaadt fail_hiballoc:
203ad814436Sderaadt hibernate_resume_bufcache();
204ad814436Sderaadt }
205ad814436Sderaadt #endif /* HIBERNATE */
206ad814436Sderaadt
207ad814436Sderaadt start_periodic_resettodr();
208cb9e2b65Sderaadt #if NWSDISPLAY > 0
209cb9e2b65Sderaadt wsdisplay_resume();
210cb9e2b65Sderaadt #endif
211ad814436Sderaadt sys_sync(curproc, NULL, NULL);
212ad814436Sderaadt if (cpu_setperf != NULL)
213f134d78dSderaadt cpu_setperf(perflevel); /* Restore hw.setperf */
214fd33afa7Sderaadt if (suspend_finish(v) == EAGAIN)
215fd33afa7Sderaadt goto top;
216ad814436Sderaadt return (error);
217ad814436Sderaadt }
218