xref: /openbsd-src/sys/kern/subr_suspend.c (revision 9a6a36e21c00109d38c66a0c32c85221f8e0940c)
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