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