xref: /openbsd/sys/kern/subr_suspend.c (revision d415bd75)
1 /* $OpenBSD: subr_suspend.c,v 1.16 2023/07/12 18:40:06 cheloha 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 
136 	if (config_suspend_all(DVACT_SUSPEND) != 0) {
137 		sleep_abort(v);
138 		error = EDEADLK;
139 		goto fail_suspend;
140 	}
141 	suspend_randomness();
142 	if (sleep_setstate(v)) {
143 		sleep_abort(v);
144 		error = ENOTBLK;
145 		goto fail_pts;
146 	}
147 
148 	if (sleepmode == SLEEP_SUSPEND) {
149 		/*
150 		 * XXX
151 		 * Flag to disk drivers that they should "power down" the disk
152 		 * when we get to DVACT_POWERDOWN.
153 		 */
154 		boothowto |= RB_POWERDOWN;
155 		config_suspend_all(DVACT_POWERDOWN);
156 		boothowto &= ~RB_POWERDOWN;
157 
158 		if (cpu_setperf != NULL)
159 			cpu_setperf(0);
160 	}
161 
162 	error = gosleep(v);
163 
164 #ifdef HIBERNATE
165 	if (sleepmode == SLEEP_HIBERNATE) {
166 		uvm_pmr_dirty_everything();
167 		hib_getentropy(&rndbuf, &rndbuflen);
168 	}
169 #endif /* HIBERNATE */
170 
171 fail_pts:
172 	config_suspend_all(DVACT_RESUME);
173 
174 fail_suspend:
175 	cold = 0;
176 	intr_enable();
177 	splx(s);
178 
179 	inittodr(gettime());
180 	clockintr_cpu_init(NULL);
181 	clockintr_trigger();
182 
183 	sleep_resume(v);
184 	resume_randomness(rndbuf, rndbuflen);
185 #ifdef MULTIPROCESSOR
186 	resume_mp();
187 #endif
188 #ifdef GPROF
189 	gmoninit = gmon_state;
190 #endif
191 #ifdef MULTIPROCESSOR
192 	sched_start_secondary_cpus();
193 #endif
194 	vfs_stall(curproc, 0);
195 	bufq_restart();
196 
197 fail_quiesce:
198 	config_suspend_all(DVACT_WAKEUP);
199 	sensor_restart();
200 
201 #ifdef HIBERNATE
202 	if (sleepmode == SLEEP_HIBERNATE) {
203 		hibernate_free();
204 fail_hiballoc:
205 		hibernate_resume_bufcache();
206 	}
207 #endif /* HIBERNATE */
208 
209 	start_periodic_resettodr();
210 #if NWSDISPLAY > 0
211 	wsdisplay_resume();
212 #endif
213 	sys_sync(curproc, NULL, NULL);
214 	if (cpu_setperf != NULL)
215 		cpu_setperf(perflevel);	/* Restore hw.setperf */
216 	if (suspend_finish(v) == EAGAIN)
217 		goto top;
218 	return (error);
219 }
220