[BACK]Return to subr_suspend.c CVS log [TXT][DIR] Up to [local] / src / sys / kern

File: [local] / src / sys / kern / subr_suspend.c (download)

Revision 1.16, Wed Jul 12 18:40:06 2023 UTC (10 months, 3 weeks ago) by cheloha
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4
Changes since 1.15: +18 -1 lines

GPROF: sleep_state: disable _mcount() across suspend/resume

Something in the amd64 resume path doesn't agree with _mcount(), so
suspend/resume always fails if gmoninit is non-zero.  It would be nice
if GPROF kernels didn't crash during resume.

In sleep_state(), (1) clear gmoninit after sched_stop_secondary_cpus()
so the primary CPU isn't racing sysctl(2) on another CPU, and (2)
restore gmoninit just after resume_mp() so the secondary CPUs are out
of cpu_hatch() and away from whatever is causing the crash before
_mcount() is reenabled.

Lots of input from claudio@, deraadt@, and kettenis@.

Thread 1: https://marc.info/?l=openbsd-tech&m=168721453821801&w=2
Thread 2: https://marc.info/?l=openbsd-tech&m=168892518722935&w=2

ok kettenis@ deraadt@

/* $OpenBSD: subr_suspend.c,v 1.16 2023/07/12 18:40:06 cheloha Exp $ */
/*
 * Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com>
 * Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/clockintr.h>
#include <sys/reboot.h>
#include <sys/sensors.h>
#include <sys/sysctl.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <dev/wscons/wsdisplayvar.h>
#ifdef GPROF
#include <sys/gmon.h>
#endif
#ifdef HIBERNATE
#include <sys/hibernate.h>
#endif

#include "softraid.h"
#include "wsdisplay.h"

/* Number of (active) wakeup devices in the system. */
u_int wakeup_devices;

void
device_register_wakeup(struct device *dev)
{
	wakeup_devices++;
}

int
sleep_state(void *v, int sleepmode)
{
	int error, s;
	extern int perflevel;
	size_t rndbuflen;
	char *rndbuf;
#ifdef GPROF
	int gmon_state;
#endif
#if NSOFTRAID > 0
	extern void sr_quiesce(void);
#endif

top:
	error = ENXIO;
	rndbuf = NULL;
	rndbuflen = 0;

	if (sleepmode == SLEEP_SUSPEND && wakeup_devices == 0)
		return EOPNOTSUPP;

	if (sleep_showstate(v, sleepmode))
		return EOPNOTSUPP;
#if NWSDISPLAY > 0
	wsdisplay_suspend();
#endif
	stop_periodic_resettodr();

#ifdef HIBERNATE
	if (sleepmode == SLEEP_HIBERNATE) {
		/*
		 * Discard useless memory to reduce fragmentation,
		 * and attempt to create a hibernate work area
		 */
		hibernate_suspend_bufcache();
		uvmpd_hibernate();
		if (hibernate_alloc()) {
			printf("failed to allocate hibernate memory\n");
			sleep_abort(v);
			error = ENOMEM;
			goto fail_hiballoc;
		}
	}
#endif /* HIBERNATE */

	sensor_quiesce();
	if (config_suspend_all(DVACT_QUIESCE)) {
		sleep_abort(v);
		error = EIO;
		goto fail_quiesce;
	}

	vfs_stall(curproc, 1);
#if NSOFTRAID > 0
	sr_quiesce();
#endif
	bufq_quiesce();
#ifdef MULTIPROCESSOR
	sched_stop_secondary_cpus();
	KASSERT(CPU_IS_PRIMARY(curcpu()));
#endif
#ifdef GPROF
	gmon_state = gmoninit;
	gmoninit = 0;
#endif
#ifdef MULTIPROCESSOR
	sleep_mp();
#endif

#ifdef HIBERNATE
	if (sleepmode == SLEEP_HIBERNATE) {
		/*
		 * We've just done various forms of syncing to disk
		 * churned lots of memory dirty.  We don't need to
		 * save that dirty memory to hibernate, so release it.
		 */
		hibernate_suspend_bufcache();
		uvmpd_hibernate();
	}
#endif /* HIBERNATE */

	resettodr();

	s = splhigh();
	intr_disable();	/* PSL_I for resume; PIC/APIC broken until repair */
	cold = 2;	/* Force other code to delay() instead of tsleep() */

	if (config_suspend_all(DVACT_SUSPEND) != 0) {
		sleep_abort(v);
		error = EDEADLK;
		goto fail_suspend;
	}
	suspend_randomness();
	if (sleep_setstate(v)) {
		sleep_abort(v);
		error = ENOTBLK;
		goto fail_pts;
	}

	if (sleepmode == SLEEP_SUSPEND) {
		/*
		 * XXX
		 * Flag to disk drivers that they should "power down" the disk
		 * when we get to DVACT_POWERDOWN.
		 */
		boothowto |= RB_POWERDOWN;
		config_suspend_all(DVACT_POWERDOWN);
		boothowto &= ~RB_POWERDOWN;

		if (cpu_setperf != NULL)
			cpu_setperf(0);
	}

	error = gosleep(v);

#ifdef HIBERNATE
	if (sleepmode == SLEEP_HIBERNATE) {
		uvm_pmr_dirty_everything();
		hib_getentropy(&rndbuf, &rndbuflen);
	}
#endif /* HIBERNATE */

fail_pts:
	config_suspend_all(DVACT_RESUME);

fail_suspend:
	cold = 0;
	intr_enable();
	splx(s);

	inittodr(gettime());
	clockintr_cpu_init(NULL);
	clockintr_trigger();

	sleep_resume(v);
	resume_randomness(rndbuf, rndbuflen);
#ifdef MULTIPROCESSOR
	resume_mp();
#endif
#ifdef GPROF
	gmoninit = gmon_state;
#endif
#ifdef MULTIPROCESSOR
	sched_start_secondary_cpus();
#endif
	vfs_stall(curproc, 0);
	bufq_restart();

fail_quiesce:
	config_suspend_all(DVACT_WAKEUP);
	sensor_restart();

#ifdef HIBERNATE
	if (sleepmode == SLEEP_HIBERNATE) {
		hibernate_free();
fail_hiballoc:
		hibernate_resume_bufcache();
	}
#endif /* HIBERNATE */

	start_periodic_resettodr();
#if NWSDISPLAY > 0
	wsdisplay_resume();
#endif
	sys_sync(curproc, NULL, NULL);
	if (cpu_setperf != NULL)
		cpu_setperf(perflevel);	/* Restore hw.setperf */
	if (suspend_finish(v) == EAGAIN)
		goto top;
	return (error);
}