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. */
40u_int wakeup_devices;
41
42void
43device_register_wakeup(struct device *dev)
44{
45	wakeup_devices++;
46}
47
48int
49sleep_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
62top:
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
168fail_pts:
169	config_suspend_all(DVACT_RESUME);
170
171fail_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
195fail_quiesce:
196	config_suspend_all(DVACT_WAKEUP);
197	sensor_restart();
198
199#ifdef HIBERNATE
200	if (sleepmode == SLEEP_HIBERNATE) {
201		hibernate_free();
202fail_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