xref: /openbsd-src/sys/kern/subr_suspend.c (revision fc30b644f1922d25f1f8a9187113a50ce3610714)
1 /* $OpenBSD: subr_suspend.c,v 1.17 2024/05/26 13:37:32 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
43 device_register_wakeup(struct device *dev)
44 {
45 	wakeup_devices++;
46 }
47 
48 int
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 			sleep_abort(v);
88 			error = ENOMEM;
89 			goto fail_hiballoc;
90 		}
91 	}
92 #endif /* HIBERNATE */
93 
94 	sensor_quiesce();
95 	if (config_suspend_all(DVACT_QUIESCE)) {
96 		sleep_abort(v);
97 		error = EIO;
98 		goto fail_quiesce;
99 	}
100 
101 	vfs_stall(curproc, 1);
102 #if NSOFTRAID > 0
103 	sr_quiesce();
104 #endif
105 	bufq_quiesce();
106 #ifdef MULTIPROCESSOR
107 	sched_stop_secondary_cpus();
108 	KASSERT(CPU_IS_PRIMARY(curcpu()));
109 #endif
110 #ifdef GPROF
111 	gmon_state = gmoninit;
112 	gmoninit = 0;
113 #endif
114 #ifdef MULTIPROCESSOR
115 	sleep_mp();
116 #endif
117 
118 #ifdef HIBERNATE
119 	if (sleepmode == SLEEP_HIBERNATE) {
120 		/*
121 		 * We've just done various forms of syncing to disk
122 		 * churned lots of memory dirty.  We don't need to
123 		 * save that dirty memory to hibernate, so release it.
124 		 */
125 		hibernate_suspend_bufcache();
126 		uvmpd_hibernate();
127 	}
128 #endif /* HIBERNATE */
129 
130 	resettodr();
131 
132 	s = splhigh();
133 	intr_disable();	/* PSL_I for resume; PIC/APIC broken until repair */
134 	cold = 2;	/* Force other code to delay() instead of tsleep() */
135 	intr_enable_wakeup();
136 
137 	if (config_suspend_all(DVACT_SUSPEND) != 0) {
138 		sleep_abort(v);
139 		error = EDEADLK;
140 		goto fail_suspend;
141 	}
142 	suspend_randomness();
143 	if (sleep_setstate(v)) {
144 		sleep_abort(v);
145 		error = ENOTBLK;
146 		goto fail_pts;
147 	}
148 
149 	if (sleepmode == SLEEP_SUSPEND) {
150 		/*
151 		 * XXX
152 		 * Flag to disk drivers that they should "power down" the disk
153 		 * when we get to DVACT_POWERDOWN.
154 		 */
155 		boothowto |= RB_POWERDOWN;
156 		config_suspend_all(DVACT_POWERDOWN);
157 		boothowto &= ~RB_POWERDOWN;
158 
159 		if (cpu_setperf != NULL)
160 			cpu_setperf(0);
161 	}
162 
163 	error = gosleep(v);
164 
165 #ifdef HIBERNATE
166 	if (sleepmode == SLEEP_HIBERNATE) {
167 		uvm_pmr_dirty_everything();
168 		hib_getentropy(&rndbuf, &rndbuflen);
169 	}
170 #endif /* HIBERNATE */
171 
172 fail_pts:
173 	config_suspend_all(DVACT_RESUME);
174 
175 fail_suspend:
176 	intr_disable_wakeup();
177 	cold = 0;
178 	intr_enable();
179 	splx(s);
180 
181 	inittodr(gettime());
182 	clockintr_cpu_init(NULL);
183 	clockintr_trigger();
184 
185 	sleep_resume(v);
186 	resume_randomness(rndbuf, rndbuflen);
187 #ifdef MULTIPROCESSOR
188 	resume_mp();
189 #endif
190 #ifdef GPROF
191 	gmoninit = gmon_state;
192 #endif
193 #ifdef MULTIPROCESSOR
194 	sched_start_secondary_cpus();
195 #endif
196 	vfs_stall(curproc, 0);
197 	bufq_restart();
198 
199 fail_quiesce:
200 	config_suspend_all(DVACT_WAKEUP);
201 	sensor_restart();
202 
203 #ifdef HIBERNATE
204 	if (sleepmode == SLEEP_HIBERNATE) {
205 		hibernate_free();
206 fail_hiballoc:
207 		hibernate_resume_bufcache();
208 	}
209 #endif /* HIBERNATE */
210 
211 	start_periodic_resettodr();
212 #if NWSDISPLAY > 0
213 	wsdisplay_resume();
214 #endif
215 	sys_sync(curproc, NULL, NULL);
216 	if (cpu_setperf != NULL)
217 		cpu_setperf(perflevel);	/* Restore hw.setperf */
218 	if (suspend_finish(v) == EAGAIN)
219 		goto top;
220 	return (error);
221 }
222